书中使用的一个宏定义来初始化描述符, 把3个参数按照规则填充到8个字节中,非常省事,那么后面有相同的表项,也可以按照这种方法来做
; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限1
dw %1 & 0FFFFh ; 段基址1
db (%1 >> 16) & 0FFh ; 段基址2
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2
db (%1 >> 24) & 0FFh ; 段基址3
%endmacro ; 共 8 字节
本节代码无难点,还是得自己手写一遍,
进入保护模式的主要步骤
- 初始化GDT
- 将32位的代码段入口地址回填GDT
- 使用lgdt加载GDTR
- 打开A20
- 关中断
- 置cr0的PE位
- Jmp 保护模式
下面是记录自己出错的地方
%include "pm.inc"
org 100h ;使用com,所以不用0x7c00
jmp LABEL_BEGIN
;1. 初始化GDT和GDTR
[SECTION .gdt]
; 段基址 段界限 段属性
LABEL_GDT: Descriptor 0, 0, 0
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW
Gdtlen equ $ - LABEL_GDT
GdtPtr dw Gdtlen - 1
dd 0 ;加载的时候回填在内存中的位置
;选择子别名 ,第一次写直接LABEL_DESC_CODE32 ,debug大半天
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
[SECTION .S16]
[BITS 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov ss,ax
mov es,ax
mov sp,0100h
;2. 地址回填GDT/GDTR,描述符2,3 4,7字节
xor eax,eax
mov ax,cs
shl eax,4
add eax, LABEL_SEG_CODE32 ;逻辑地址换成线性地址
mov word [LABEL_DESC_CODE32 + 2],ax
shr eax,16
mov byte [LABEL_DESC_CODE32 + 4],al
mov byte [LABEL_DESC_CODE32 + 7],ah
xor eax,eax
mov ax,ds ;数据段使用ds
shl eax,4
add eax, LABEL_GDT
mov dword [GdtPtr + 2],eax
;3. 使用lgdt加载GDTR
lgdt [GdtPtr]
;4. 关中断
cli
;5. 打开A20
in al,92h
or al,00000010b
out 92h,al
;6. 置cr0的PE位
mov eax,cr0
or eax,1
mov cr0,eax
;7. Jmp 保护模式
jmp dword SelectorCode32:0
[SECTION .S32]
[BITS 32]
LABEL_SEG_CODE32:
;8. 对显存操作,输出一个字符
mov ax, SelectorVideo
mov es, ax
mov edi,(80 * 11 + 79) * 2
mov ah,0ch
mov al,'D'
mov [es:edi],ax
jmp $
SegCode32Len equ $- LABEL_SEG_CODE32