• 第十三课 从保护模式返回实模式


      前几节课我们演示了从实模式进入到保护模式,那么从保护模式返回到实模式具体怎么操作呢?

      先将上一节的程序列出:

      1 %include "inc.asm"
      2 
      3 org 0x9000
      4 
      5 jmp CODE16_SEGMENT
      6 
      7 [section .gdt]
      8 ; GDT definition
      9 ;                                 段基址,       段界限,       段属性
     10 GDT_ENTRY       :     Descriptor    0,            0,           0
     11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
     12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
     13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32
     14 STACK_DESC      :     Descriptor    0,      TopOfStackInit,    DA_DRW + DA_32        
     15 ; GDT end
     16 
     17 GdtLen    equ   $ - GDT_ENTRY
     18 
     19 GdtPtr:
     20           dw   GdtLen - 1
     21           dd   0
     22           
     23           
     24 ; GDT Selector
     25 
     26 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
     27 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
     28 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
     29 StackSelector     equ (0x0004 << 3) + SA_TIG + SA_RPL0
     30 
     31 ; end of [section .gdt]
     32 
     33 TopOfStackInit    equ  0x7c00
     34 
     35 [section .dat]
     36 [bits 32]
     37 DATA32_SEGMENT:
     38     DTOS                 db    "D.T.OS!", 0
     39     DTOS_OFFSET          equ   DTOS - $$
     40     HELLO_WORLD          db    "Hello World!", 0
     41     HELLO_WORLD_OFFSET   equ  HELLO_WORLD - $$
     42 
     43 Data32SegLen  equ $ - DATA32_SEGMENT
     44 
     45 [section .s16]
     46 [bits 16]
     47 CODE16_SEGMENT:
     48     mov ax, cs
     49     mov ds, ax
     50     mov es, ax
     51     mov ss, ax
     52     mov sp, TopOfStackInit
     53     
     54     ; initialize GDT for 32 bits code segment
     55     mov esi, CODE32_SEGMENT
     56     mov edi, CODE32_DESC
     57     
     58     call InitDescItem
     59     
     60     mov esi, DATA32_SEGMENT
     61     mov edi, DATA32_DESC
     62     
     63     call InitDescItem
     64     
     65     ; initialize GDT pointer struct
     66     mov eax, 0
     67     mov ax, ds
     68     shl eax, 4
     69     add eax, GDT_ENTRY
     70     mov dword [GdtPtr + 2], eax
     71 
     72     ; 1. load GDT
     73     lgdt [GdtPtr]
     74     
     75     ; 2. close interrupt
     76     cli 
     77     
     78     ; 3. open A20
     79     in al, 0x92
     80     or al, 00000010b
     81     out 0x92, al
     82     
     83     ; 4. enter protect mode
     84     mov eax, cr0
     85     or eax, 0x01
     86     mov cr0, eax
     87     
     88     ; 5. jump to 32 bits code
     89     jmp dword Code32Selector : 0
     90 
     91     
     92 ; esi    --> code segment label
     93 ; edi    --> descriptor label
     94 InitDescItem:
     95     push eax
     96     
     97     mov eax, 0
     98     mov ax, cs
     99     shl eax, 4
    100     add eax, esi
    101     mov word [edi + 2], ax
    102     shr eax, 16
    103     mov byte [edi + 4], al
    104     mov byte [edi + 7], ah
    105     
    106     pop eax
    107     
    108     ret
    109     
    110     
    111 [section .s32]
    112 [bits 32]
    113 CODE32_SEGMENT:
    114     mov ax, VideoSelector
    115     mov gs, ax
    116     
    117     mov ax, StackSelector
    118     mov ss, ax
    119     
    120     mov ax, Data32Selector
    121     mov ds, ax
    122     
    123     mov ebp, DTOS_OFFSET
    124     mov bx, 0x0C
    125     mov dh, 12
    126     mov dl, 33
    127     
    128     call PrintString
    129     
    130     mov ebp, HELLO_WORLD_OFFSET
    131     mov bx, 0x0C
    132     mov dh, 13
    133     mov dl, 30
    134     
    135     call PrintString
    136     
    137     jmp $
    138 
    139 ; ds:ebp   --> string address
    140 ; bx       --> attribute
    141 ; dx       --> dh : row, dl : col
    142 PrintString:
    143     push ebp
    144     push eax
    145     push edi 
    146     push cx
    147     push dx
    148     
    149 print:
    150     mov cl, [ds:ebp]
    151     cmp cl, 0
    152     je end
    153     mov eax, 80
    154     mul dh
    155     add al, dl
    156     shl eax, 1
    157     mov edi, eax
    158     mov ah, bl
    159     mov al, cl
    160     mov [gs:edi], ax
    161     inc ebp
    162     inc dl
    163     jmp print
    164     
    165 end:
    166     pop dx
    167     pop cx
    168     pop edi
    169     pop eax
    170     pop ebp
    171     
    172     ret
    173 
    174 Code32SegLen    equ    $ - CODE32_SEGMENT

    上一节中,我们跳到32位保护模式后,并没有设置栈顶指针esp,但是程序依然可以正常运行,这时怎么回事呢?原因是我们在第52行设置了栈顶指针,而我们的程序中,16位的实模式和32位的保护模式使用的栈是一样的,因此,无需重新设置程序也可以正常运行。第14行的段描述符描述了32位保护模式下的栈的信息,在保护模式下即使我们将这个段的选择子,赋值给ss,那么由于段基址是0,得到最终的栈顶指针依然是 段基址+esp=0+esp,所以不给ss赋值和给ss赋值的结果是一样的。如果在32位保护时使用的栈和16位实模式使用的栈不一样的话,就不能这样操作了,而必须在进入32位保护模式后设置ss段寄存和esp栈顶指针。

     保护模式下的栈段,我们一般要进行以下步骤的设置:

    1、指定一段空间,并为其定义段描述符

    2、根据段描述表中的位置定义段选择子

    3、初始化栈段寄存器(ss <- StackSelector)

    4、初始化栈顶指针(esp <- TopOfStack )

    下面定义32位保护模式下的专用栈:

      1 %include "inc.asm"
      2 
      3 org 0x9000
      4 
      5 jmp CODE16_SEGMENT
      6 
      7 [section .gdt]
      8 ; GDT definition
      9 ;                                 段基址,       段界限,       段属性
     10 GDT_ENTRY       :     Descriptor    0,            0,           0
     11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
     12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
     13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32
     14 STACK32_DESC    :     Descriptor    0,      TopOfStack32,      DA_DRW + DA_32        
     15 ; GDT end
     16 
     17 GdtLen    equ   $ - GDT_ENTRY
     18 
     19 GdtPtr:
     20           dw   GdtLen - 1
     21           dd   0
     22           
     23           
     24 ; GDT Selector
     25 
     26 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
     27 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
     28 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
     29 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
     30 
     31 ; end of [section .gdt]
     32 
     33 TopOfStack16    equ  0x7c00
     34 
     35 [section .dat]
     36 [bits 32]
     37 DATA32_SEGMENT:
     38     DTOS                 db    "D.T.OS!", 0
     39     DTOS_OFFSET          equ   DTOS - $$
     40     HELLO_WORLD          db    "Hello World!", 0
     41     HELLO_WORLD_OFFSET   equ  HELLO_WORLD - $$
     42 
     43 Data32SegLen  equ $ - DATA32_SEGMENT
     44 
     45 [section .s16]
     46 [bits 16]
     47 CODE16_SEGMENT:
     48     mov ax, cs
     49     mov ds, ax
     50     mov es, ax
     51     mov ss, ax
     52     mov sp, TopOfStack16
     53     
     54     ; initialize GDT for 32 bits code segment
     55     mov esi, CODE32_SEGMENT
     56     mov edi, CODE32_DESC
     57     
     58     call InitDescItem
     59     
     60     mov esi, DATA32_SEGMENT
     61     mov edi, DATA32_DESC
     62     
     63     call InitDescItem
     64     
     65     mov esi, DATA32_SEGMENT
     66     mov edi, STACK32_DESC
     67     
     68     call InitDescItem
     69     
     70     ; initialize GDT pointer struct
     71     mov eax, 0
     72     mov ax, ds
     73     shl eax, 4
     74     add eax, GDT_ENTRY
     75     mov dword [GdtPtr + 2], eax
     76 
     77     ; 1. load GDT
     78     lgdt [GdtPtr]
     79     
     80     ; 2. close interrupt
     81     cli 
     82     
     83     ; 3. open A20
     84     in al, 0x92
     85     or al, 00000010b
     86     out 0x92, al
     87     
     88     ; 4. enter protect mode
     89     mov eax, cr0
     90     or eax, 0x01
     91     mov cr0, eax
     92     
     93     ; 5. jump to 32 bits code
     94     jmp dword Code32Selector : 0
     95 
     96     
     97 ; esi    --> code segment label
     98 ; edi    --> descriptor label
     99 InitDescItem:
    100     push eax
    101     
    102     mov eax, 0
    103     mov ax, cs
    104     shl eax, 4
    105     add eax, esi
    106     mov word [edi + 2], ax
    107     shr eax, 16
    108     mov byte [edi + 4], al
    109     mov byte [edi + 7], ah
    110     
    111     pop eax
    112     
    113     ret
    114     
    115     
    116 [section .s32]
    117 [bits 32]
    118 CODE32_SEGMENT:
    119     mov ax, VideoSelector
    120     mov gs, ax
    121     
    122     mov ax, Stack32Selector
    123     mov ss, ax
    124     
    125     mov eax, TopOfStack32
    126     mov esp, eax
    127     
    128     mov ax, Data32Selector
    129     mov ds, ax
    130     
    131     mov ebp, DTOS_OFFSET
    132     mov bx, 0x0C
    133     mov dh, 12
    134     mov dl, 33
    135     
    136     call PrintString
    137     
    138     mov ebp, HELLO_WORLD_OFFSET
    139     mov bx, 0x0C
    140     mov dh, 13
    141     mov dl, 30
    142     
    143     call PrintString
    144     
    145     jmp $
    146 
    147 ; ds:ebp   --> string address
    148 ; bx       --> attribute
    149 ; dx       --> dh : row, dl : col
    150 PrintString:
    151     push ebp
    152     push eax
    153     push edi 
    154     push cx
    155     push dx
    156     
    157 print:
    158     mov cl, [ds:ebp]
    159     cmp cl, 0
    160     je end
    161     mov eax, 80
    162     mul dh
    163     add al, dl
    164     shl eax, 1
    165     mov edi, eax
    166     mov ah, bl
    167     mov al, cl
    168     mov [gs:edi], ax
    169     inc ebp
    170     inc dl
    171     jmp print
    172     
    173 end:
    174     pop dx
    175     pop cx
    176     pop edi
    177     pop eax
    178     pop ebp
    179     
    180     ret
    181 
    182 Code32SegLen    equ    $ - CODE32_SEGMENT
    183 
    184 [section .gs]
    185 [bits 32]
    186 STACK32_SEGMENT:
    187     times 1024 * 4 db 0
    188     
    189 Stack32SegLen    equ $ - STACK32_SEGMENT
    190 TopOfStack32    equ Stack32SegLen - 1

    184-190行我们重新定义了32位保护模式下的栈段。并在14行和19行为其填充了段描述符表项和段选择子。我们在94行打上断点,看看程序执行到这里时栈顶指针寄存器的值是多少。启动bochs开始运行,结果如下:

    可以看到这时的esp是0x7c00。

    122-126行,我们在32位保护模式中设置了栈的段基址和栈顶指针。继续单步执行程序,如下:

    图中可以看出,我们将段选择子赋值给了ss,将栈的段界限赋值给了esp,因为栈是向下生长的,所以就应该将段界限赋值给esp。

    继续执行程序,最终结果如下:

    从保护模式返回时模式:

    8086中的一个神秘限制:

      无法直接从32位代码段回到实模式

      只能从16位代码段间接返回实模式

      在返回前必须用合适的选择子对段寄存器赋值

    可以从16位实模式代码段跳到32位保护模式代码段,但是返回的话不能直接进行。

    返回流程:先从32位保护模式的代码段返回16位保护模式的代码段(保护模式下也可以定义16位的代码段),然后从16位保护模式代码段跳到16位实模式代码段。

    16位保护模式的代码段在这里作为一个中间过渡过程,我们在这个段只干一件事,就是用合适的段选择子对段寄存器进行赋值。除此之外不做其他的逻辑上的操作。

    在操作之前,我们先介绍一下处理器中的设计:

    80286之后的处理器都提供兼容8086的实模式

    然而,绝大多数时候处理器都运行于保护模式

    因此,保护模式的运行效率至关重要

    那么,处理器如何高效的访问内存中的段描述符呢?

    运行于保护模式时,性能瓶颈在于:段描述符定义在内存中,如果每次都要访问内存,效率会比较低。如何快速高效的访问内存中的段描述符呢?解决方案如下:

    使用高速缓冲存储器

    当使用选择子设置段寄存器时,会触发处理器的内部操作:

      根据选择子访问内存中的段描述符

      将段描述符加载到段寄存器的高速缓冲存储器

      需要段描述符信息时,直接从高速缓冲器中获得

    处于实模式时也会用到这个段寄存器高速缓冲存储器。会用到其中的段基地址和段界限。

    注意事项:

      在实模式 下,高速缓冲存储器仍然发挥着作用

      段基址是32位,其值是相应段寄存器的值乘以16

      实模式下段基址有效位为20位(高速缓存中的32段基址足以容纳),段界限固定为0xFFFF(64K)

      段属性的值不可设置,只能继续沿用保护方式下所设置的值

     高速缓冲存储器不可以直接访问设置值。只能通过特殊的方法:

      通过加载一个合适的描述符选择子到有关段寄存器,以使得对应的段描述符高速缓冲寄存器中含有合适的段界限和段属性。

     跳到16位实模式的具体流程:

      32位保护模式代码段 -> 16位保护模式代码段(刷新段寄存器,退出保护模式) -> 16位实模式代码段(设置段寄存器的值,关闭A20地址线,启用硬件中断)

    汇编小知识:深入理解jmp指令

    段内跳转: 指令是三个字节,操作码(E9)为1个字节(低地址),操作数是两个字节(高地址)(也就是段内偏移地址)。

    段间跳转:指令时5个字节,操作码(EA)为1个字节(低地址),操作数是四个字节(偏移地址、段基址)(高地址)

     段间跳转时,我们可以修改指令中的偏移地址和段基址就可以跳转到另一个期望的段中去了。修改指令是运行时修改内存中的指令,而不是在源程序中修改。

     从保护模式返回到实模式的程序如下:

      1 %include "inc.asm"
      2 
      3 org 0x9000
      4 
      5 jmp ENTRY_SEGMENT
      6 
      7 [section .gdt]
      8 ; GDT definition
      9 ;                                 段基址,       段界限,       段属性
     10 GDT_ENTRY       :     Descriptor    0,            0,           0
     11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
     12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
     13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32
     14 STACK32_DESC    :     Descriptor    0,      TopOfStack32,      DA_DRW + DA_32    
     15 CODE16_DESC     :     Descriptor    0,           0xFFFF,       DA_C
     16 UPDATE_DESC     :     Descriptor    0,           0xFFFF,       DA_DRW
     17 ; GDT end
     18 
     19 GdtLen    equ   $ - GDT_ENTRY
     20 
     21 GdtPtr:
     22           dw   GdtLen - 1
     23           dd   0
     24           
     25           
     26 ; GDT Selector
     27 
     28 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
     29 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
     30 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
     31 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
     32 Code16Selector    equ (0x0005 << 3) + SA_TIG + SA_RPL0
     33 UpdateSelector    equ (0x0006 << 3) + SA_TIG + SA_RPL0
     34 ; end of [section .gdt]
     35 
     36 TopOfStack16    equ  0x7c00
     37 
     38 [section .dat]
     39 [bits 32]
     40 DATA32_SEGMENT:
     41     DTOS                 db    "D.T.OS!", 0
     42     DTOS_OFFSET          equ   DTOS - $$
     43     HELLO_WORLD          db    "Hello World!", 0
     44     HELLO_WORLD_OFFSET   equ  HELLO_WORLD - $$
     45 
     46 Data32SegLen  equ $ - DATA32_SEGMENT
     47 
     48 [section .s16]
     49 [bits 16]
     50 ENTRY_SEGMENT:
     51     mov ax, cs
     52     mov ds, ax
     53     mov es, ax
     54     mov ss, ax
     55     mov sp, TopOfStack16
     56     
     57     mov [BACK_TO_REAL_MODE + 3], ax 
     58     
     59     ; initialize GDT for 32 bits code segment
     60     mov esi, CODE32_SEGMENT
     61     mov edi, CODE32_DESC
     62     
     63     call InitDescItem
     64     
     65     mov esi, DATA32_SEGMENT
     66     mov edi, DATA32_DESC
     67     
     68     call InitDescItem
     69     
     70     mov esi, DATA32_SEGMENT
     71     mov edi, STACK32_DESC
     72     
     73     call InitDescItem
     74     
     75     mov esi, CODE16_SEGMENT
     76     mov edi, CODE16_DESC
     77     
     78     call InitDescItem
     79     
     80     ; initialize GDT pointer struct
     81     mov eax, 0
     82     mov ax, ds
     83     shl eax, 4
     84     add eax, GDT_ENTRY
     85     mov dword [GdtPtr + 2], eax
     86 
     87     ; 1. load GDT
     88     lgdt [GdtPtr]
     89     
     90     ; 2. close interrupt
     91     cli 
     92     
     93     ; 3. open A20
     94     in al, 0x92
     95     or al, 00000010b
     96     out 0x92, al
     97     
     98     ; 4. enter protect mode
     99     mov eax, cr0
    100     or eax, 0x01
    101     mov cr0, eax
    102     
    103     ; 5. jump to 32 bits code
    104     jmp dword Code32Selector : 0
    105 
    106 BACK_ENTRY_SEGMENT:
    107         mov ax, cs
    108         mov ds, ax
    109         mov es, ax
    110         mov ss, ax
    111         mov sp, TopOfStack16
    112         
    113         in al, 0x92
    114         and al, 11111101b
    115         out 0x92, al
    116         
    117         sti 
    118         
    119         mov bp, HELLO_WORLD
    120         mov cx, 12
    121         mov dx, 0
    122         mov ax, 0x1301
    123         mov bx, 0x0007
    124         int 0x10
    125         
    126         jmp $
    127 
    128 ; esi    --> code segment label
    129 ; edi    --> descriptor label
    130 InitDescItem:
    131     push eax
    132     
    133     mov eax, 0
    134     mov ax, cs
    135     shl eax, 4
    136     add eax, esi
    137     mov word [edi + 2], ax
    138     shr eax, 16
    139     mov byte [edi + 4], al
    140     mov byte [edi + 7], ah
    141     
    142     pop eax
    143     
    144     ret
    145     
    146 
    147 [section .16]
    148 [bits 16]
    149 CODE16_SEGMENT:
    150     mov ax, UpdateSelector
    151     mov ds, ax
    152     mov es, ax
    153     mov fs, ax
    154     mov gs, ax
    155     mov ss, ax
    156     
    157     mov eax, cr0
    158     and al, 11111110b
    159     mov cr0, eax
    160     
    161 BACK_TO_REAL_MODE:
    162     jmp 0 : BACK_ENTRY_SEGMENT
    163     
    164 Code16SegLen    equ $ - CODE16_SEGMENT
    165 
    166     
    167 [section .s32]
    168 [bits 32]
    169 CODE32_SEGMENT:
    170     mov ax, VideoSelector
    171     mov gs, ax
    172     
    173     mov ax, Stack32Selector
    174     mov ss, ax
    175     
    176     mov eax, TopOfStack32
    177     mov esp, eax
    178     
    179     mov ax, Data32Selector
    180     mov ds, ax
    181     
    182     mov ebp, DTOS_OFFSET
    183     mov bx, 0x0C
    184     mov dh, 12
    185     mov dl, 33
    186     
    187     call PrintString
    188     
    189     mov ebp, HELLO_WORLD_OFFSET
    190     mov bx, 0x0C
    191     mov dh, 13
    192     mov dl, 30
    193     
    194     call PrintString
    195     
    196     jmp Code16Selector : 0
    197 
    198 ; ds:ebp   --> string address
    199 ; bx       --> attribute
    200 ; dx       --> dh : row, dl : col
    201 PrintString:
    202     push ebp
    203     push eax
    204     push edi 
    205     push cx
    206     push dx
    207     
    208 print:
    209     mov cl, [ds:ebp]
    210     cmp cl, 0
    211     je end
    212     mov eax, 80
    213     mul dh
    214     add al, dl
    215     shl eax, 1
    216     mov edi, eax
    217     mov ah, bl
    218     mov al, cl
    219     mov [gs:edi], ax
    220     inc ebp
    221     inc dl
    222     jmp print
    223     
    224 end:
    225     pop dx
    226     pop cx
    227     pop edi
    228     pop eax
    229     pop ebp
    230     
    231     ret
    232 
    233 Code32SegLen    equ    $ - CODE32_SEGMENT
    234 
    235 [section .gs]
    236 [bits 32]
    237 STACK32_SEGMENT:
    238     times 1014 * 4 db 0
    239     
    240 Stack32SegLen    equ $ - STACK32_SEGMENT
    241 TopOfStack32    equ Stack32SegLen - 1

      147-164行定义了16位的保护模式代码,106-126行定义了另一个16位实模式代码段。15、16行定义了新的段描述符,15行的段描述符是描述16位保护模式的代码段的。16行的段描述符是描述16位实模式的代码段的。75-78行我们初始化了16位保护模式下的段描述符。程序从32位保护模式的196行跳转到16位保护模式的代码段,然后将16位实模式代码段的段选择子分别赋给ds、es、fs、gs、ss段寄存器(赋值的意义就是刷新对应的段描述符对应的高速缓冲存储器),赋值的同时,处理器的内部机制会读取内存,并初始化段寄存器高速缓存。这样这些寄存器高速缓存中保存的就是16位实模式代码段的信息了。然后,157-159行使处理器进入实模式。当执行162行跳转时,处理器已经处于16位实模式。注意,在16位保护模式的代码中,我们没有给cs赋值,因为这时程序还处于16位保护模式,如果这时候我们给cs赋值16位实模式,那么程序会出错,因为这时代码还在16位保护模式执行中。

      162行的跳转我们要跳到16位的实模式代码段处,这是一个段间跳转,因此使用jmp 0 : BACK_ENTRY_SEGMENT(按照16位实模式进行跳转,偏移地址是16位的,寻址范围是64kb),这里的0我们应该填入cs的值,这个值是程序执行到第50行处cs的值,这个cs的值是代表16位实模式代码的基地址,因此我们在57行加了mov [BACK_TO_REAL_MODE + 3], ax,标签BACK_TO_REAL_MODE是在161行定义的,这句代码的意思是,我们直接修改内存中的指令,使得跳转指令中的基地址变为cs的值。这样就实现了运行时动态的修改指令。因此,当第162行我们执行跳转时,jmp 0 : BACK_ENTRY_SEGMENT指令完成跳转的同时,也会改变cs的值和ip的值,程序可以正确的跳转到16位实模式 BACK_ENTRY_SEGMENT代码处,在BACK_ENTRY_SEGMENT处,cs已经处于16位实模式下正确的值。程序执行到第162行时,cs段寄存器对应的高速缓存中存储的还是16位保护模式的信息,也就是在第15行的16位保护模式代码段描述符中如果我们填入的段界限是Code16SegLen - 1,那么执行到162行时,cs高速缓存中的段界限就是这个值,而这个值比较小(因为16位保护模式的代码我们写的比较小),因此,执行第162行时,会发生越界访问异常(虽然这时候是处于实模式,但是跳转时段界限依旧起作用),因为BACK_ENTRY_SEGMENT一般是大于Code16SegLen - 1那个值的,因此,15行中的段界限我们要按16位实模式的段界限64k来填,也就是0xFFFF。

      最终进入到16位实模式代码段BACK_ENTRY_SEGMENT处,在这个段中,cs的值已经是16位实模式代码段的地址了,将cs的值一次赋值给其他的段寄存器,在这里已经进入了16位实模式代码段,对段寄存器的赋值只会改变相应高速缓冲存储器中的段基址,段界限和段属性沿用UPDATE_DESC中定义的值。然后设置栈,然后打开A20地址线,然后开中断,最后执行一个打印。

     执行结果如下:

  • 相关阅读:
    2017icpc青岛
    训练赛
    CF1598F
    CF1581
    CF1594
    CF1581D
    codeforces round 746 div2 C-E
    How I Think About Learning
    Linux Sysadmin Basics 4.1 -- Filtering Output and Finding Things (&&, cut, sort, uniq, wc, grep)
    Linux Sysadmin Basics 04 -- Shell Features -- Pipes and Redirection
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9462704.html
Copyright © 2020-2023  润新知