在前两次学习的基础上,这次试着解释一个简单的由实模式进入保护模式的代码,当然进入保护模式的方法不止这一种
看注释就可以了。
;; 文件:pmode.asm
;; 工具:UltraEdit14.12编辑,Nasm2.02汇编
;; 创建日期:2009/02/4 HouRj
;; 作用:由实模式进入保护模式的一种方法
;; 备注:没有文件系统,1.44M 512bits/80sec 软盘启动,
;; hourj_er@yahoo.cn
;;================================================
[bits 16] ;告诉编译器,编译成16位指令,前面代码缺省默认
[org 0x7c00]
cli ;关闭中断,保证引导程序在执行时不被打扰
xor ax,ax ;GDTR的地址是 DS:offset,这里对DS进行初始化,下面lgdt指令是对offset初始化
mov ds,ax ;GDT将被加载的地址,00h
lgdt[gdt_desc] ;加载GDT,将GDT的地址及其大小属性载入到GDT描述符中,即GDTR(48) = limit(16) + base(32)
mov eax,cr0
or eax,1 ;设置eax的第0位,
mov cr0,eax ;置pe位
jmp 08h:clear_pipe ;08h的算法:由于GDT第一个段是Intel保留的空段,而一个段64b的8B个区域,
;所以跳过空段(00h-07h),即08h
[BITS 32]
clear_pipe: ;这里由于要进入保护模式,所以要对DS,CS,ES,SS,FS,GS进行重新写
mov ax,10h ;10h的算法:第一段是空段(00h-07h)
mov ds,ax ;第二段是代码段(08-0fh),第三段是数据段(10h-17h)
mov ss,ax ;堆栈段与数据相同
mov esp,090000h ;
;由于在保护模式下不能直接使用BIOS中断,显示的方法是向显存的缓冲里直接写入
;显存位于:0xA0000---0xbffff,帧缓冲位于0xb8000处,我们将要向这里输入
;每个字符需要2个字节,第一个代表ASCII,第二个属性字节的不同位,代表不同属性
;0-3前景色,4-6背景色,7闪烁,
mov byte [ds:0B8000h], 'M' ; Move the ASCII-code of 'H' into first video memory
mov byte [ds:0B8001h], 1ah ; Assign a color code
mov byte [ds:0B8002h], 'u'
mov byte [ds:0B8003h], 9bh
mov byte [ds:0B8004h], 'm'
mov byte [ds:0B8005h], 1ch
mov byte [ds:0B8006h], 'O'
mov byte [ds:0B8007h], 9dh
mov byte [ds:0B8008h], 's'
mov byte [ds:0B8009h], 1eh
hang:
jmp hang
gdt: ;填写GDT
gdt_null: ;空段,Intel保留的区域,用零填充
dd 0 ;共64位的0
dd 0
gdt_code: ;填写代码段的GDT,参照GDT的结构
dw 0ffffh ;填充limit(15-0),共16位1,偏移即寻址为
dw 0 ;基地址为0
db 0 ;十六位,低八位仍是基址,填0
db 10011010b ;高八位需要分别开一下,低到高:A,R/W,ED/C,E,S,DPL,P 它们分别为:
;A是访问标志,由CPU在第一次访问时设置,置0;
;R/W置为1使段可读;
;ED/C表示顺从性,如果设置位1,则表示低优先级的代码段可以跳转到或调用该段。此处置0,不需要此功能;
;E置为1,表示描述符描述的是代码段;E置为0,表示描述符描述的是数据段
;S表示该段是代码段或数据段,置为1;系统置0
;DPL表示优先级,由于是引导程序,所以要把优先级设为00;
;最后,P设置为1,段有有效的基址和界限。没有定义描述置0
db 11001111b ;这里的十六位,低八位分别:四位偏移,AV,0,D,G
;首先4位偏移量,设置为0Fh;
;AV为表示Available,AV=1表示segment is available,此处忽略该位,设为0;
;Intel保留了一位必须设为0;
;D表示大小位,置为1,它告诉CPU使用32位代码而不是16位代码;
;G表示粒度,如果G=0,则Limit所表示的段偏移是00000H-FFFFFH,
;如果G=1,则Limit表示的段偏移是00000XXXH-FFFFFXXXH,
;即Limit所表示的段偏移实际上是它的值再乘以4K。此处设置G=1。于是得到前8位:11001111
db 0 ;高八位基址,全置0
gdt_data: ;填写数据段GDT,同样参照GDT结构
dw 0ffffh ;这里只看跟代码段不通的位
dw 0 ;相同
db 0 ;相同
db 10010010b ;看低位第4位,这里是0,表示描述符描述的是数据段,上面代码段置的是1
db 11001111b ;相同
db 0 ;相同
gdt_end: ;由于在lgdt的时候需要把GDT的地址和大小加载到GDTR中,
;本条指令GDT的结束,是为了计算GDT的大小
gdt_desc: ;GDT的描述符,被lgdt加载到GDTR=基址+大小
dw gdt_end-gdt-1 ;计算大小
dd gdt ;基址
times 510-($-$$) db 0
dw 0aa55h
;;================================================