这几天还是在学习《自己动手写操作系统》,终于将保护模式了了。
上来贴上代码:
%include "pm.inc" org 0100h xchg bx,bx jmp START [SECTION .gdt] GDT_DEC: Descriptor 0 , 0 , 0 CODE32_DEC: Descriptor 0 ,Code32SegLen-1,DA_32+DA_CE VIDEO_DEC: Descriptor 0b8000h , 0ffffh,DA_DRW DATA32_DEC: Descriptor 0 ,Data32SegLen-1,DA_DRW STACK32_DEC: Descriptor 0 ,StackTop ,DA_32+DA_DRW EXTRA_DEC: Descriptor 050000h , 0ffffh ,DA_DRW CODE16_DEC: Descriptor 0 , 0ffffh ,DA_CE NORMAL_DEC: Descriptor 0 , 0ffffh ,DA_DRW GdtLen equ $ - GDT_DEC Gdtptr dw GdtLen-1 ;Length of GDT dd 0 ;base of GDT SelectorCode32 equ CODE32_DEC - GDT_DEC SelectorVideo equ VIDEO_DEC - GDT_DEC SelectorData equ DATA32_DEC - GDT_DEC SelectorStack equ STACK32_DEC - GDT_DEC SelectorExtra equ EXTRA_DEC - GDT_DEC SelectorCode16 equ CODE16_DEC - GDT_DEC SelectorNormal equ NORMAL_DEC - GDT_DEC [SECTION .s16] [BITS 16] START: mov ax,cs mov ds,ax mov ss,ax mov es,ax mov sp,100h mov [BACK_TO_REAL+3],ax mov [RealModeSp],sp ;load Code32Segment Base xor eax,eax mov ax,cs shl eax,4 add eax,CODE32_SEG mov [CODE32_DEC+2],ax shr eax,16 mov [CODE32_DEC+4],al mov [CODE32_DEC+7],ah ;load data segment xor eax,eax mov ax,ds shl eax,4 add eax,DATA32_SEG mov [DATA32_DEC+2],ax shr eax,16 mov [DATA32_DEC+4],al mov [DATA32_DEC+7],ah ;load stack segment base xor eax,eax mov ax,ss shl eax,4 add eax,STACK_SEG mov [STACK32_DEC+2],ax shr eax,16 mov [STACK32_DEC+4],al mov [STACK32_DEC+7],ah ;load code16 segment base xor eax,eax mov ax,cs shl eax,4 add eax,CODE16_SEG mov [CODE16_DEC+2],ax shr eax,16 mov [CODE16_DEC+4],al mov [CODE16_DEC+7],ah ;load GDT Base xor eax,eax mov ax,cs shl eax,4 add eax,GDT_DEC mov [Gdtptr+2],eax lgdt [Gdtptr] cli in al,92h or al,02h out 92h,al mov eax,cr0 or eax,01h mov cr0,eax jmp dword SelectorCode32:0 ;Enter to Protected mode! ENTER_REAL_MODE: mov ax,cs mov ds,ax mov es,ax mov ss,ax mov fs,ax mov gs,ax mov sp,[RealModeSp] in al,92h and al,11111101b out 92h,al sti ;call BIOS int 10h to test if back to real mov cx,22 mov bh,0 mov bl,89h mov dx,1000h mov bp,BackRealMeg mov ah,13h mov al,01h int 10h ;back to dos mov ax,4c00h int 21h [SECTION .p16] ALIGN 32 [BITS 16] CODE16_SEG: mov ax,SelectorNormal mov ds,ax mov ss,ax mov gs,ax mov fs,ax mov es,ax mov eax,cr0 and eax,0feh mov cr0,eax BACK_TO_REAL: jmp 0:ENTER_REAL_MODE Code16SegLen equ $ - $$ [SECTION .s32] ALIGN 32 [BITS 32] DATA32_SEG: Message: db "Welcome to Protected Mode!",0 MessageLen equ $ - Message offMessage equ Message - $$ RealModeSp dw 0 Letters: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ",0 offLetters equ Letters -$$ BackRealMeg: db "Welcome back to real!\n" Data32SegLen equ $ - $$ [SECTION .stack] ALIGN 32 [BITS 32] STACK_SEG: times 512 db 0 StackTop equ $ - STACK_SEG - 1 [SECTION .s32] [BITS 32] CODE32_SEG: mov ax,SelectorVideoto mov gs,ax mov ax,SelectorData mov ds,ax mov ax,SelectorStack mov ss,ax mov esp,StackTop mov ax,SelectorExtra mov es,ax call DispMessage call DispReturn call ReadFromExtraSeg call WriteToExtraSeg call ReadFromExtraSeg jmp SelectorCode16:0 DispMessage: mov ah,0ch mov esi,offMessage mov edi,(80*12+20)*2 mov ecx,MessageLen .loop: mov al,[esi] mov [gs:edi],ax add edi,2 inc esi loop .loop ret ReadFromExtraSeg: xor esi,esi mov ecx,08h .loop: mov al,[es:esi] call DispAlAsNum inc esi loop .loop call DispReturn ret DispAlAsNum: push ecx push edx mov ecx,02h mov dl,al shr al,4 mov ah,0ch .loop: and al,0fh cmp al,09h ja .letter add al,'0' jmp .Disp .letter: sub al,0ah add al,'A' .Disp: mov [gs:edi],ax mov al,dl add edi,2 loop .loop add edi,2 pop edx pop ecx ret DispReturn: push eax push ebx mov eax,edi mov bl,160 div bl and eax,0ffh inc eax mul bl mov edi,eax pop ebx pop eax ret WriteToExtraSeg: push esi push edi xor esi,esi xor edi,edi mov esi,offLetters cld .loop: lodsb test al,al jz .ok mov [es:edi],al inc edi loop .loop .ok: pop edi pop esi ret Code32SegLen equ $ - $$
然后运行结果图:
这个程序先从实模式进入保护模式,然后从附加段中读数据并输出,再从数据段向附加段写数据,然后输出。最后返回实模式。
刚开始时,没注意,还是利用org 0x7c00开头,直接从该软驱启动,但是提示找不到启动设备。然后利用Hex Edit查看,发现 510 511 单元不是55 aa而是00 00 ,再查看源代码,可以发现这块已经被Stack占用,也就是说,我们得程序已经超过了512B,那么,只好利用其他东西(如Freedos)来做引导.最后将改程序放入其一个软驱中。在DOS中运行这个程序。
还有就是,在DOS中如果程序出问题了该怎么办,我一开始想到得是Debug,但是遗憾得是没有这个命令。所以只好另图他径。首先在Bochs配置文件中添加magic_break : enabled=1,然后在程序中利用xchg bx,bx来设置断点。当你运行改程序时,在这里会自动将控制权交给Bochs,然后你就可以在Bochs中调试了。
再后呢就是关于从保护模式跳入实模式得步骤:
1. 关中断, 因为一开始中断就关掉了, 这步可以省略
2. 将程序从一个32位的代码段转移到16位的代码段, 这个代码段的段界限必须是64KB, 即FFFFh, 就是程序中的
SelectorCode16描述的那个段
这个的作用是刷新CS寄存器, 使它符合实模式下cs寄存器的要求
3. 刷新数据段的寄存器, 包括DS, ES, FS, GS, SS, 使得它们也符合实模式下的要求, 就是mov ax,
SelectorNormal的作用, SelectorNormal的要求是:
Limit = 64KB ( 0FFFFh )
Byte granular = 0 ( G = 0 )
Expand up ( E = 0 )
Writable ( W = 1 )
Present ( P = 1 )
Base = any value
也就是说, SelectorNormal描述的段要完全符合实模式下的数据段的要求
4. 清Cr0的PE位, 真正的转换到实模式, 需要注意的是SelectorCode16描述的那个段中的代码仍然是保护模式下的, 只不过是16
位的代码
只有当PE位清0的时候, 才真正的转换到实模式, 这个时候, 需要有一个跳转, 真正的跳转到实模式下的代码段, 而这个跳转的寻址方式是实
模式寻址方式
5. 经过第4步的关键跳转, CS被更新成了真正的实模式下的段地址, 这个时候, 需要重新刷新数据段寄存器, 使他们工作在实模式下,
这些数据寄存器包括上述提到的所有的段寄存器, 即DS, ES, FS, GS, SS, 如果你不用它们的话, 就直接赋值为0也是可以的
6. 开中断, 这步完成之后, 就完全完成了保护模式到实模式的切换
此时,你可以调用dos中断返回dos操作系统, 书上就是这么做的