下面以pmtest2.asm为例,来讲述“实模式--保护模式--实模式”的转换过程。
1、“实模式--保护模式--实模式”的转换过程。
2、介绍段描述符属性
3、pm.inc 中的宏定义
4、pmtest2.asm源代码
一、“实模式--保护模式--实模式”的转换过程
1、“实模式--保护模式”的跳转
(1)关中断
(2)打开地址线A20
(3)置cr0寄存器的末位为1
(4)实现跳转,进入到保护模式
2、“保护模式--实模式”的跳转
(1)从保护模式下的32位代码段跳转到16位代码段
(2)在16位代码段下初始化所有段寄存器
(3)置cr0的末位为0
(4)实现跳转,返回到实模式
3、pmtest2.asm中“实模式--保护模式--实模式”的跳转过程
(1)下面是“实模式--保护模式--实模式”的跳转过程图
其中 LABEL_BEGIN、LABEL_REAL_ENTRY运行于实模式下;LEBEL_SEG_CODE32、LABEL_SEG_CODE16运行于保护模式下。所以该程序实现了“实模式--保护模式--实模式”的跳转。
(2)“实模式--保护模式”的跳转
这个跳转过程主要存在于LABEL_BEGIN代码段中:
70- 71行:保存实模式下的SP内的值
73-111行:初始化段描述符
113-121行:加载GDTR
123-124行:关中断
126-129行:打开地址线A20
141-144行:准备切换到保护模式,置cr0的末位为1
146-147行:跳转到保护模式
(3)“保护模式--实模式”的跳转
208-209行:从保护模式下的32位代码段跳转到16位代码段
316-322行:为跳回实模式做准备,将段寄存器初始化为符合实模式下的代码段规范。即用SelectorNormal来初始化段寄存器。
324-326行:置cr0的末位为0
328-329行:实现跳转,进入到实模式下的 LABEL_REAL_ENTRY段
158-158行:恢复实模式下的SP
160-162行:关闭A20地址线
164-164行:开中断
166-168行:返回DOS
二、段描述符的属性
段描述符属性占5、6字节,其具体特性如下:
在这里主要介绍第5字节的内容。
1、 P:存在(Present)位。
1 表示段在内存中存在
0 表示段在内存中不存在
2、 DPL:表示描述符特权级(Descriptor Privilege level),共2位。
它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。
3、 S:说明描述符的类型。
1 数据段和代码段描述符
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陷阱门
三、pm.inc中的宏定义
在程序中,我们定义属性时,使用了pm.inc中的宏定义DA_DRW、DA_C、DA_32、DA_DRW、DA_DRWA。下面让我来向大家解释这些宏定义。
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 陷阱门类型值
四、pmtest2.asm源代码:
;===================================================== ;pmtest2.asm ;编译方法:nasm pmtest2.asm -o pmtest2.com %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 ;非一致代码段,32 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ;非一致代码段,16 LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW + DA_DPL1 ;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 + SA_RPL3 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 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 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 mov [LABEL_GO_BACK_TO_REAL + 3], ax mov [SPValueInRealMode], sp ;初始化16位代码段描述符 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 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] <- gdt 基地址 ;加载GDTR lgdt [GdtPtr] ;关中断 cli ;打开地址线A20 in al, 92h or al, 00000010b out 92h, al ;清屏操作(用以指定色彩) mov ah, 06H ;功能06H和07H mov ch, 00 ;功能描述:初始化屏幕或滚屏 mov cl, 00 ;入口参数:AH=06H——向上滚屏,07H——向下滚屏 mov dh, 24 ;AL=滚动行数(0——清窗口) mov dl, 79 ;BH=空白区域的缺省属性 mov bh, 7 ;(CH、CL)=窗口的左上角位置(Y坐标,X坐标) mov al, 00 ;(DH、DL)=窗口的右下角位置(Y坐标,X坐标) int 10H ;出口参数:无 ;准备切换到保护模式 mov eax, cr0 or eax, 1 mov cr0, eax ;真正进入保护模式 jmp dword SelectorCode32:0 ;执行这一句会把SelectorCode32装入cs,并跳转到SelectorCode32:0处 ;...................................................................... ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, LABEL_REAL_ENTRY: ;从保护模式跳回到实模式就到了这里 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, [SPValueInRealMode] in al, 92h ;关闭A20地址线 and al, 11111101b out 92h, al sti ;开中断 ;回到DOS mov ax, 4C00h int 21h; ;END of [SECTION .s16] [SECTION .s32] ;32位代码段,由实模式跳入 [BITS 32] LABEL_SEG_CODE32: mov ax, SelectorData mov ds, ax ;数据段选择子 mov ax, SelectorTest mov es, ax ;测试段选择子 mov ax, SelectorVideo mov gs, 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 ;目的数据偏移。屏幕第10行,第0列 cld .1: lodsb test al, al jz .2 mov [gs:edi], ax ;将ax中的数据送入到显存 add edi, 2 jmp .1 .2: ;显示完毕 call DispReturn call TestRead call TestWrite Call TestRead ;到此停止 jmp SelectorCode16: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 ;源数据偏移 .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 ecx push edx 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结束-------------------------------------------------------------- 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 ;END of [SECTION .s16code]