; 描述符图示
; 图示一
;
; ------ ┏━━━┳━━━━━┓高地址
; ┃ 7 段 ┃
; ┣━━━┫ ┃
; 基
; 字节 7 ┆ ┆ ┆
; 址
; ┣━━━┫ ② ┃
; ┃ 0 ┃
; ------ ┣━━━╋━━━━━┫
; ┃ 7 ┃ G ┃
; ┣━━━╉━━━──┨
; ┃ 6 ┃ D ┃
; ┣━━━╉━━━──┨
; ┃ 5 ┃ 0 ┃
; ┣━━━╉━━━──┨
; ┃ 4 ┃ AVL ┃
; 字节 6 ┣━━━╉━━━──┨
; ┃ 3 ┃ ┃
; ┣━━━┫ 段 ┃
; ┃ 2 ┃ 界 ┃
; ┣━━━┫ 限 ┃
; ┃ 1 ┃ ┃
; ┣━━━┫ ② ┃
; ┃ 0 ┃ ┃
; ------ ┣━━━╋━━━━━┫
; ┃ 7 ┃ P ┃
; ┣━━━╉━━━──┨
; ┃ 6 ┃ ┃
; ┣━━━┫ DPL ┃
; ┃ 5 ┃ ┃
; ┣━━━╉━━━──┨
; ┃ 4 ┃ S ┃
; 字节 5 ┣━━━━╉━━──┨
; ┃ 3 ┃ ┃
; ┣━━━━┫ T ┃
; ┃ 2 ┃ Y ┃
; ┣━━━━┫ P ┃
; ┃ 1 ┃ E ┃
; ┣━━━━┫ ┃
; ┃ 0 ┃ ┃
; ------ ┣━━━━╋━━━━┫
; ┃ 23 ┃ ┃
; ┣━━━━┫ ┃
; ┃ 22 ┃ ┃
; ┣━━━━┫ 段 ┃
;
; 字节 ┆ ┆ 基 ┆
; 2, 3, 4
; ┣━━━━┫ 址 ┃
; ┃ 1 ┃ ① ┃
; ┣━━━━┫ ┃
; ┃ 0 ┃ ┃
; ------ ┣━━━━╋━━━━┫
; ┃ 15 ┃ ┃
; ┣━━━━┫ ┃
; ┃ 14 ┃ ┃
; ┣━━━━┫ 段 ┃
;
; 字节 0,1┆ ┆ 界 ┆
;
; ┣━━━━┫ 限 ┃
; ┃ 1 ┃ ① ┃
; ┣━━━━┫ ┃
; ┃ 0 ┃ ┃
; ------ ┗━━━━┻━━━━┛低地址
;
; 图示二
; 高地址………………………………………………………………………低地址
; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; |7654321076543210765432107654321076543210765432107654321076543210| <- 共 8 字节
; |--------========--------========--------========--------========|
; ┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
; ┃31..24 ┃ (见下图) ┃ 段基址(23..0) ┃ 段界限(15..0) ┃
; ┃ ┃ ┃ ┃ ┃
; ┃ 基址2 ┃ ③ │ ② │ ① ┃ 基址1b │ 基址1a ┃ 段界限1 ┃
; ┣━━━━━━━╋━━━━━━━┳━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━┫
; ┃ %6 ┃%5 ┃%4 ┃ %3 ┃ %2 ┃ %1 ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┛
; │ \_________
; │ \__________________
; │ \________________________________________________
; │ \
; ┏━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┓
; ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━━━╋━━━━╋━━━━╋━━━━╋━━━━┻━━━━┻━━━━┻━━━━╋━━━━╋━━━━┻━━━━╋━━━━╋━━━━┻━━━━┻━━━━┻━━━━┫
; ┃ G ┃D/B ┃ 0 ┃ AVL┃段界限 2 (19..16) ┃ P ┃ DPL ┃ S ┃ TYPE ┃
; ┣━━━━┻━━━━┻━━━━┻━━━━╋━━━━━━━━━━━━━━━━━━╋━━━━┻━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━┫
; ┃ ③: 属性 2 ┃ ②: 段界限 2 ┃ ①: 属性1 ┃
; ┗━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
; 高地址 低地址
;
;
; 说明:
;
; (1) P: 存在(Present)位。
; P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;
; P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。
;
; (2) DPL: 表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,
; 用于特权检查,以决定对该段能否访问。
;
; (3) S: 说明描述符的类型。
; 对于存储段描述符而言,S=1,以区别与系统段描述符和门描述符(S=0)。
;
; (4) TYPE: 说明存储段描述符所描述的存储段的具体属性。
;数据段类型 类型值 说明
; ----------------------------------
; 0 只读
; 1 只读、已访问
; 2 读/写
; 3 读/写、已访问
; 4 只读、向下扩展
; 5 只读、向下扩展、已访问
; 6 读/写、向下扩展
; 7 读/写、向下扩展、已访问
;
;
;代码段类型 类型值 说明
; ----------------------------------
; 8 只执行
; 9 只执行、已访问
; A 执行/读
; B 执行/读、已访问
; C 只执行、一致码段
; D 只执行、一致码段、已访问
; E 执行/读、一致码段
; F 执行/读、一致码段、已访问
;
;
;系统段类型 类型编码 说明
; ----------------------------------
; 0 <未定义>
; 1 可用286TSS
; 2 LDT
; 3 忙的286TSS
; 4 286调用门
; 5 任务门
; 6 286中断门
; 7 286陷阱门
; 8 未定义
; 9 可用386TSS
; A <未定义>
; B 忙的386TSS
; C 386调用门
; D <未定义>
; E 386中断门
; F 386陷阱门
;
; (5) G: 段界限粒度(Granularity)位。
; G=0 表示界限粒度为字节;
; G=1 表示界限粒度为4K 字节。
; 注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。
;
; (6) D/B: 此位是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段
; (通常是堆栈段)的三种描述符中的意义各不相同。
; ⑴ 在描述可执行段的描述符中,D位决定了指令使用的地址及操作数所默认的大小。
; ① D=1表示默认情况下指令使用32位地址及32位或8位操作数,这样的代码段也称为32位代码段;
; ② D=0 表示默认情况下,使用16位地址及16位或8位操作数,这样的代码段也称为16位代码段,
; 它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。
; ⑵ 在向下扩展数据段的描述符中,D 位决定段的上部边界。
; ① D=1表示段的上部界限为4G;
; ② D=0表示段的上部界限为64K,这是为了与80286兼容。
; ⑶ 在描述由SS寄存器寻址的段描述符中,D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用
; 何种堆栈指针寄存器。
; ① D=1表示使用32位堆栈指针寄存器ESP;
; ② D=0表示使用16位堆栈指针寄存器SP,这与80286兼容。
;
; (7) AVL: 软件可利用位。80386对该位的使用未左规定,Intel公司也保证今后开发生产的处理器
; 只要与80386兼容,就不会对该位的使用做任何定义或规定。
;
;----------------------------------------------------------------------------
; 描述符类型值说明
; 其中:
; DA_ : Descriptor Attribute
; D : 数据段
; C : 代码段
; S : 系统段
; R : 只读
; RW : 读写
; A : 已访问
; 其它 : 可按照字面意思理解
;----------------------------------------------------------------------------
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 陷阱门类型值
;----------------------------------------------------------------------------
; 选择子图示:
; ┏━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━┓
; ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9 ┃ 8 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━╋━━━━╋━━━━┻━━━━┫
; ┃ 描述符索引 ┃ TI ┃ RPL ┃
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━┛
;
; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
; TI=0 指示从全局描述符表GDT中读取描述符;
; TI=1 指示从局部描述符表LDT中读取描述符。
;
;----------------------------------------------------------------------------
; 选择子类型值说明
; 其中:SA_ : Selector Attribute
SA_RPL0 EQU 0 ; ┓
SA_RPL1 EQU 1 ; ┣ RPL
SA_RPL2 EQU 2 ; ┃
SA_RPL3 EQU 3 ; ┛
SA_TIG EQU 0 ; ┓TI
SA_TIL EQU 4 ; ┛
;----------------------------------------------------------------------------
; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; 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 ; 段界限(2 字节)
dw %1 & 0FFFFh ; 段基址1(2 字节)
db (%1 >> 16) & 0FFh ; 段基址 2(1 字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2(2 字节)
db (%1 >> 24) & 0FFh ; 段基址 3(1 字节)
%endmacro ; 共 8 字节
;
; 门
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子(2 字节)
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性(2 字节)
dw ((%2 >> 16) & 0FFFFh) ; 偏移 2(2 字节)
%endmacro ; 共 8 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; ===========================================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================================
%include "pm.inc" ;定义一些常量,宏,以及一些说明
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
; GDT
;段基址,段界限,属性
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ;normal 描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ;非一致代码段
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C; 非一致代码段 16
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ;data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32 ;Stack 32位
LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT 结束
GdtLen equ $ - LABEL_GDT ;GDT 的长度
GdtPtr dw GdtLen - 1 ;GDT界限
dd 0 ; GDT基地址
;GDT选择子
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorTest equ LABEL_DESC_TEST - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
;END of [SECTION .gdt]
[SECTION .data1] ;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
;字符串
PMMessage: db "In.Protect.Mode.now. ^-^", 0 ;在保护模式中显示
OffsetPMMessage equ PMMessage - $$
strTest: db "ABCDEFGGIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest equ strTest - $$
DataLen equ $ - LABEL_DATA
; END of [Section .data1]
;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $ - LABEL_STACK - 1
;end of [SECTION .gs]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h ;实模式下所用内存空间为64K(0100h),故指定最高地址为栈顶
mov [LABEL_GO_BACK_TO_REAL + 3], ax
mov [SPValueInRealMode], sp
;;初始化15位段描述符
mov ax, cs
movzx eax, ax
shl eax, 4
add eax, LABEL_SEG_CODE16
mov word [LABEL_DESC_CODE16 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16 + 4], al
mov byte [LABEL_DESC_CODE16 + 7], ah
;;初始化32位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4 ;此时处于实模式运行 其段基址与偏移的计算方式位 cs<<4 + offset
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
shl eax, 4
add eax, LABEL_DATA
mov word [LABEL_DESC_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah
;;初始化堆栈描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DESC_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK + 4], al
mov byte [LABEL_DESC_STACK + 7], ah
;;为加载GDTR做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ;eax <-gdt基地址
mov dword [GdtPtr + 2], eax ; 将地址赋给GdtPtr高2个字节
;;加载GDTR
lgdt [GdtPtr]
;;关闭中断
cli
;;打开地址线 A20
in al, 92h
or al, 00000010b
out 92h, al
;;准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
;;真正进入保护模式
jmp dword SelectorCode32:0 ;;执行此句会将Selectorcode32装入cs,
;;并跳转到Code32Selector:0处,
LABEL_REAL_ENTRY:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, [SPValueInRealMode]
in al, 92h
or al, 11111101b
out 92h, al
sti
mov ax, 4c00h
int 21h ;;回到dos
;;END of [SECTION .s16]
[SECTION .s32] ;;进入32位代码 由实模式跳入
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ;视频段选择子
mov ax, SelectorTest
mov es, ax
mov ax, SelectorData
mov ds, ax
mov ax, SelectorStack
mov ss, ax
mov esp, TopOfStack
;下面显示一个字符
mov ah, 0ch ;; 0000 黑底, 1100:红字
xor esi, esi
xor edi, edi
mov esi, OffsetPMMessage ;;源数据偏移
mov edi (80*10 + 0) *2
cld ;;//清除方向标识,使其为 0
.1:
lodsb ;;//将ds中si所指向的内容按字节加载到al中,由于方向标志为0,故si自动+1
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ;显示完毕
call DispReturn
call TestRead
call TestWrite
call TestRead
;;到此停止,再次跳转回 实模式
jmp SelectCode16:0
TestRead:
xor esi, esi
mov ecx, 8
.loop:
mov al, [es:esi]
call DispAL
inc esi
loop .loop
call DispReturn
ret
;;Testread 结束
TestWrite:
push esi
push edi
xor esi, esi
xor edi, edi
mov esi, OffsetStrTest ;;源数据
cld
.1:
lodsb
test al, al
jz .2
mov [es:edi], al
inc edi
jmp .1
.2:
pop edi
pop esi
ret
;;Testwrite 结束
;;
;;显示AL中的数字
;;默认地:
;;数字已经存在AL中
;;edi始终指向要显示的下一个字符的位置
;;被改变的寄存器 ax, edi
DispAL:
push edx
push ecx
mov ah, 0ch
mov dl, al
shr al, 4
mov ecx, 2
.begin:
and al, 01111b
cmp al, 9
ja .1
add al, '0'
jmp .2
.1:
sub al, 0Ah
add al, 'A'
.2:
mov [gs:edi], ax
add edi, 2
mov al, dl
loop .begin
add edi, 2
pop edx
pop ecx
ret
;;DispAL 结束
DispReturn:
push eax
push ebx
mov eax, edi
mov bl, 160
div bl
and eax, 0ffh
inc eax
mov bl, 160
mul bl
mov edi, eax
pop ebx
pop eax
ret
;;Dispreturn 结束
;mov edi, (80 * 11 + 79) * 2
;mov ah, 0ch
;mov al, 'D'
;mov [gs:edi], ax
;;end
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
;;end of [SECTION .s32]
;;16位代码段,由32位跳入,从而进入实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
mov ax, SelectorNormal
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 11111110b
mov cr0, eax
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY
Code16Len equ $-LABEL_SEG_CODE16
保护模式与实模式跳转的步骤:
- BIOS完成后,程序到0100h处执行,接着跳转到16位代码段进行执行(注意此时 cs为实模式下16位代码段基址),在16位dos实模式下堆栈初始化位置位0100h。接下来进行数据段,代码段,堆栈段,段描述符的初始化工作。
- 此处为后面从保护模式跳转回实模式的一步关键准备工作:
mov [LABEL_GO_BACK_TO_REAL + 3], ax ; mov [SPValueInRealMode], sp
此处将LABEL_GO_BACK_TO_REAL 标签后第3字节开始,填充位实模式的代码段基址。并保存堆栈地址。 - 打开地址线 A20,设置保护模式开启位
- 跳转进入保护模式,
jmp dword SelectorCode32:0
。 - 实模式完成显示任务
- 执行'jmp SelectCode:0'跳转到16位的保护模式到实模式过渡代码段(.s16code)执行。
- 此处注意
mov ax, SelectorNormal
的作用为:因为保护模式下GDT的高速缓冲寄存器中内容必须进行清理,这里利用SelectorNormal将GDT的高速缓冲寄存器中内容替换为实模式下中的内容,注意:实模式下所有段的fffffh--00000h,且读写属性应该为可读写。
8.由于jmp的跳转地址已经在步骤2中改写为实模式下代码段的基址。
代码中有几点需要注意的地方:
org 0100h
org不是操作符而是编译时的标记,最终的二进制代码中并不存在此操作符。该标记粗略来说有两个作用:
- 指定程序的加载到内存的实际开始地址
- 指明对于程序中的常量要进行相对编址(例如:若加org,常量字符串位于 org + offset;若不加 org,则常量字符串的编址为 0 + offset)
初始化GDT描述符基址的方法
xor eax, eax ;清除eax
mov ax, ds ;此时 ds 为实模式下代码段基址
shl eax, 4 ;此时处于实模式运行 其段基址与偏移的计算方式位 cs<<4 + offset
add eax, LABEL_DATA ;此处为offset
mov word [LABEL_DESC_DATA + 2], ax ;将基址的0-15位赋予GDT的2,3字节
shr eax, 16 ;将基址右移16位,ax变为基址的3,4字节
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah
网友评论