一、宏定义和属性常量
(代码段/数据段描述符见P32;门描述符见P51)
下面是对代码段/数据段描述符的宏定义,目的是为了方便编写描述符。
; 描述符 ; usage: Descriptor Base, Limit, Attr ; Base: dd -->段基址4字节 ; Limit: dd (low 20 bits available) -->段界限20位,放心大胆地用4字节(32位)表示界限,只不过经转换只用到其中20位 ; Attr: dw (lower 4 bits of higher byte are always 0) -->%3 & 0F0FFh,因为高字节的第4位为段界限2的位置 %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 字节
下面定义一些常量,便于在编写描述符时设置属性:
; 描述符类型 DA_32 EQU 4000h ; 32 位段 DA_DPL0 EQU 00h ; DPL = 0 DA_DPL1 EQU 20h ; DPL = 1 DA_DPL2 EQU 40h ; DPL = 2 DA_DPL3 EQU 60h ; DPL = 3 ; 存储段描述符类型 DA_DR EQU 90h ; 存在的只读数据段类型值 DA_DRW EQU 92h ; 存在的可读写数据段属性值 DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值 DA_C EQU 98h ; 存在的只执行代码段属性值 DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值 DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值 DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值 ; 系统段描述符类型 DA_LDT EQU 82h ; 局部描述符表段类型值 DA_TaskGate EQU 85h ; 任务门类型值 DA_386TSS EQU 89h ; 可用 386 任务状态段类型值 DA_386CGate EQU 8Ch ; 386 调用门类型值 DA_386IGate EQU 8Eh ; 386 中断门类型值 DA_386TGate EQU 8Fh ; 386 陷阱门类型值
注意到代码段和数据段描述符中和属性相关的字段(从Byte0算起,Byte5和Byte6),如下:
Byte5:
0~3位: TYPE
4位: S
5~6位: DPL
7位: P
Byte6:
0~3位: 段界限2
4位: AVL
5位: 固定为0
6位: D/B
7位: G
二、宏和属性常量的使用(重点在属性的设置上):
[SECTION .gdt] ; 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 ; 显存首地址 ; GDT 结束 GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址,在后面代码中再指定(略) ; GDT 选择子 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; END of [SECTION .gdt] ...
1、设置属性:
理解“32位代码段”中对属性的设置(DA_C+DA_32):
1)DA_32——32位段
DA_32 EQU 4000h ; 32 位段
4000h=0100,0000,0000,0000b,也就是将“D/B位”设置为1,由P36对“D/B位”的说明可知,当这个段是可执行代码段时,称为D位。D=1,则在默认情况下指令使用32位地址及8位操作数(因此,在这个段中用“选择子”代替“段基值”);D=0,则默认情况下用16位地址及16位或8位操作数。
2)DA_C——在内存中存在的可执行代码段/数据段
DA_C EQU 98h ; 存在的只执行代码段属性值
98h=0000,0000,1001,1000b,也就是将
TYPE设置为1000,表示可执行。见P36
S设置为1,表示是数据段/代码段描述符(如果S=0,表示是系统段/门描述符)。见P36
P设置为1,表示在内存中存在。见P35
理解“显存段”中对属性的设置(DA_DRW):
1)DA_DRW——在内存中存在的可读写代码段/数据段
DA_DRW EQU 92h ; 存在的可读写数据段属性值
92h=0000,0000,1001,0010,也就是将
TYPE设置为0010,表示可读写。见P36
S设置为1,表示是数据段/代码段描述符(如果S=0,表示是系统段/门描述符)。见P36
P设置为1,表示在内存中存在。见P35
2、设置段基址:
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 其中段基址在后面代码中再指定(略) LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;
1)在[SECTION .s16]中设置了第一行这个32位对应的段基址(该段做从实模式转入保护模式前的准备工作):
... ...
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
... ...
; 初始化 32 位代码段描述符
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
... ...
; 真正进入保护模式
jmp dword SelectorCode32:0
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]
LABEL_SEG_CODE32:
... ...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
预习:下面是门描述符
; 门
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移1
dw %1 ; 选择子
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性
dw ((%2 >> 16) & 0FFFFh) ; 偏移2
%endmacro ; 共 8 字节