前几节课我们演示了从实模式进入到保护模式,那么从保护模式返回到实模式具体怎么操作呢?
先将上一节的程序列出:
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位的保护模式使用的栈是一样的,因此,无需重新设置程序也可以正常运行。如果在32位保护时使用的栈和16位实模式使用的栈不一样的话,就不能这样操作了,而必须在进入32位保护模式后设置esp栈顶指针。