x86保护模式 实模式与保护模式切换实例
实例一
逻辑功能 以十六进制数的形式显示从内存地址110000h开始的256个字节的值
实现步骤:
1 切换保护方式的准备
2. 切换到保护方式
3. 把指定内存区域的内容传送到位于常规内存的缓冲区中
4. 切换回实模式
5. 显示缓冲区内容
代码:
386保护模式汇编语言程序用到的包含文件如下所示,该包含文件在后面的程序中还要用到。
;名称:386SCD.INC ;功能:符号常量等的定义 ;---------------------------------------------------------------------------- ;IFNDEF __386SCD_INC //宏定义 ;__386SCD_INC EQU 1 ;---------------------------------------------------------------------------- .386P ;---------------------------------------------------------------------------- ;打开A20地址线 宏定义 ;---------------------------------------------------------------------------- EnableA20 MACRO // 使用32为地址线 push ax //ax压栈保存 in al,92h //读端口指令 92h端口的值送到al寄存器 or al,00000010b //处理al的值 将位1变为1 out 92h,al //写端口指令 将al的值送到92h 此时就是打开a20地址线 pop ax //弹出栈中的ax值 ENDM ;---------------------------------------------------------------------------- ;关闭A20地址线 ;---------------------------------------------------------------------------- DisableA20 MACRO push ax in al,92h and al,11111101b //将位1置为0 就是关闭a20地址线 out 92h,al pop ax ENDM ;---------------------------------------------------------------------------- ;16位偏移的段间直接转移指令的宏定义(在16位代码段中使用) ;---------------------------------------------------------------------------- JUMP16 MACRO Selector,Offset //宏定义: 【宏指令名】 MACRO 【形式参数,......】 DB 0eah ;操作码 //宏使用格式 【宏指令名】【实在参数】 DW Offset ;16位偏移量 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;32位偏移的段间直接转移指令的宏定义(在32位代码段中使用) ;---------------------------------------------------------------------------- COMMENT <JUMP32> JUMP32 MACRO Selector,Offset //宏定义 DB 0eah ;操作码 //根据x86操作码结构和指令表 0eah的指令为jmpf DD OFFSET //此时偏移为32位 选择子为16位 DW Selector ;段值或段选择子 ENDM <JUMP32> ;------------------------------------------------- JUMP32 MACRO Selector,Offset DB 0eah ;操作码 DW OFFSET //此时偏移为16位 DW 0 //?高位是0 还是低位是0 DW Selector ;段值或段选择子 //选择子为16位 ENDM ;---------------------------------------------------------------------------- ;16位偏移的段间调用指令的宏定义(在16位代码段中使用) ;---------------------------------------------------------------------------- CALL16 MACRO Selector,Offset //16位调用 宏定义 DB 9ah ;操作码 //操作码9ah 指令为callf DW Offset ;16位偏移量 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;32位偏移的段间调用指令的宏定义(在32位代码段中使用) ;---------------------------------------------------------------------------- COMMENT <CALL32> CALL32 MACRO Selector,Offset DB 9ah ;操作码 DD Offset //偏移为32位 DW Selector ;段值或段选择子 ENDM <CALL32> ;------------------------------------------------- CALL32 MACRO Selector,Offset DB 9ah ;操作码 DW Offset //偏移为16位 DW 0 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;存储段描述符结构类型定义 ;---------------------------------------------------------------------------- Desc STRUC LimitL DW 0 ;段界限(BIT0-15) BaseL DW 0 ;段基地址(BIT0-15) BaseM DB 0 ;段基地址(BIT16-23) Attributes DB 0 ;段属性 LimitH DB 0 ;段界限(BIT16-19)(含段属性的高4位) BaseH DB 0 ;段基地址(BIT24-31) Desc ENDS ;---------------------------------------------------------------------------- ;门描述符结构类型定义 ;---------------------------------------------------------------------------- Gate STRUC OffsetL DW 0 ;32位偏移的低16位 Selector DW 0 ;选择子 DCount DB 0 ;双字计数 //参数 个数 GType DB 0 ;类型 OffsetH DW 0 ;32位偏移的高16位 Gate ENDS ;---------------------------------------------------------------------------- ;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器) ;---------------------------------------------------------------------------- PDesc STRUC //gdtr寄存器 idtr寄存器为48位 Limit DW 0 ;16位界限 //规定gdtr表的大小 项目 Base DD 0 ;32位基地址 //gdtr表的存储位置 PDesc ENDS ;---------------------------------------------------------------------------- ;任务状态段结构类型定义 ;---------------------------------------------------------------------------- TSS STRUC TRLink DW 0 ;链接字段//是否被其他任务使用 DW 0 ;不使用,置为0 TRESP0 DD 0 ;0级堆栈指针 //不同级别的堆栈 TRSS0 DW 0 ;0级堆栈段寄存器 DW 0 ;不使用,置为0 TRESP1 DD 0 ;1级堆栈指针 TRSS1 DW 0 ;1级堆栈段寄存器 DW 0 ;不使用,置为0 TRESP2 DD 0 ;2级堆栈指针 TRSS2 DW 0 ;2级堆栈段寄存器 DW 0 ;不使用,置为0 TRCR3 DD 0 ;CR3 //cr3寄存器 分页机制使用 TREIP DD 0 ;EIP TREFlag DD 0 ;EFLAGS TREAX DD 0 ;EAX TRECX DD 0 ;ECX TREDX DD 0 ;EDX TREBX DD 0 ;EBX TRESP DD 0 ;ESP TREBP DD 0 ;EBP TRESI DD 0 ;ESI TREDI DD 0 ;EDI TRES DW 0 ;ES DW 0 ;不使用,置为0 TRCS DW 0 ;CS DW 0 ;不使用,置为0 TRSS DW 0 ;SS DW 0 ;不使用,置为0 TRDS DW 0 ;DS DW 0 ;不使用,置为0 TRFS DW 0 ;FS DW 0 ;不使用,置为0 TRGS DW 0 ;GS DW 0 ;不使用,置为0 TRLDTR DW 0 ;LDTR DW 0 ;不使用,置为0 TRTrip DW 0 ;调试陷阱标志(只用位0) TRIOMap DW $+2 ;指向I/O许可位图区的段内偏移 TSS ENDS ;---------------------------------------------------------------------------- ;存储段描述符类型值说明 数据段和代码段 ;---------------------------------------------------------------------------- ATDR EQU 90h ;存在的只读数据段类型值 ATDW EQU 92h ;存在的可读写数据段属性值 ATDWA EQU 93h ;存在的已访问可读写数据段类型值 ATCE EQU 98h ;存在的只执行代码段属性值 ATCER EQU 9ah ;存在的可执行可读代码段属性值 ATCCO EQU 9ch ;存在的只执行一致代码段属性值 ATCCOR EQU 9eh ;存在的可执行可读一致代码段属性值 ;---------------------------------------------------------------------------- ;系统段描述符类型值说明 门描述符 ldt tss 三种 ;---------------------------------------------------------------------------- ATLDT EQU 82h ;局部描述符表段类型值 ATTaskGate EQU 85h ;任务门类型值 AT386TSS EQU 89h ;可用386任务状态段类型值 AT386CGate EQU 8ch ;386调用门类型值 AT386IGate EQU 8eh ;386中断门类型值 AT386TGate EQU 8fh ;386陷阱门类型值 ;---------------------------------------------------------------------------- ;DPL值说明 当前段的访问权限级别 ;---------------------------------------------------------------------------- DPL0 EQU 00h ;DPL=0 DPL1 EQU 20h ;DPL=1 DPL2 EQU 40h ;DPL=2 DPL3 EQU 60h ;DPL=3 ;---------------------------------------------------------------------------- ;RPL值说明 ;---------------------------------------------------------------------------- RPL0 EQU 00h ;RPL=0 RPL1 EQU 01h ;RPL=1 RPL2 EQU 02h ;RPL=2 RPL3 EQU 03h ;RPL=3 ;---------------------------------------------------------------------------- ;IOPL值说明 ;---------------------------------------------------------------------------- IOPL0 EQU 0000h ;IOPL=0 IOPL1 EQU 1000h ;IOPL=1 IOPL2 EQU 2000h ;IOPL=2 IOPL3 EQU 3000h ;IOPL=3 ;---------------------------------------------------------------------------- ;其它常量值说明 ;---------------------------------------------------------------------------- D32 EQU 40h ;32位代码段标志 GL EQU 80h ;段界限以4K为单位标志 TIL EQU 04h ;TI=1(局部描述符表标志) VMFL EQU 00020000h ;VMF=1 VMFLW EQU 0002h IFL EQU 00000200h ;IF=1 RFL EQU 00010000h ;RF=1(重启动标志,为1表示忽略调试故障) RFLW EQU 0001h NTL EQU 00004000h ;NT=1 ;---------------------------------------------------------------------------- ;分页机制使用的常量说明 ;---------------------------------------------------------------------------- PL EQU 1 ;页存在属性位 RWR EQU 0 ;R/W属性位值,读/执行 RWW EQU 2 ;R/W属性位值,读/写/执行 USS EQU 0 ;U/S属性位值,系统级 USU EQU 4 ;U/S属性位值,用户级 ;---------------------------------------------------------------------------- ;ENDIF
2.实例源程序
实例一的源程序如下所示:
;名称:ASM1.ASM ;功能:演示实方式和保护方式切换(切换到16位代码段) ;---------------------------------------------------------------------------- INCLUDE 386SCD.INC //包含上面的定义的文件 ;---------------------------------------------------------------------------- ;字符显示宏指令的定义 ;---------------------------------------------------------------------------- EchoCh MACRO ascii //dos系统功能调用int21h ah=02 表示显示输出
mov ah,2 //dl=输出字符 mov dl,ascii int 21h ENDM ;---------------------------------------------------------------------------- DSEG SEGMENT USE16 ;16位数据段 ;---------------------------------------------------------------------------- GDT LABEL BYTE ;全局描述符表 DUMMY Desc <> ;空描述符 Code Desc <0ffffh,,,ATCE,,> ;代码段描述符 以下分别为8字节长 空为全0 DataS Desc <0ffffh,0,11h,ATDW,,> ;源数据段描述符 DataD Desc <0ffffh,,,ATDW,,> ;目标数据段描述符 ;---------------------------------------------------------------------------- GDTLen = $-GDT ;全局描述符表长度 当前值-gdt首地址 为字节长度
VGDTR PDesc <GDTLen-1,> ;伪描述符 GDtr寄存器值 16位的界限值定义 基地址为空
;---------------------------------------------------------------------------- Code_Sel = Code-GDT ;代码段选择子 以下为定义16为选择子相对于与表首的偏移值 DataS_Sel = Datas-GDT ;源数据段选择子 DataD_Sel = DataD-GDT ;目标数据段选择子 ;---------------------------------------------------------------------------- BufLen = 256 ;缓冲区字节长度 Buffer DB BufLen DUP(0) ;缓冲区 ;---------------------------------------------------------------------------- DSEG ENDS ;数据段定义结束
;---------------------------------------------------------------------------- CSEG SEGMENT USE16 ;16位代码段 ASSUME CS:CSEG,DS:DSEG ;---------------------------------------------------------------------------- Start PROC mov ax,DSEG mov ds,ax //设置数据段寄存器 指向dseg定义处 ;准备要加载到GDTR的伪描述符 //设置基地址 32位 界限已经定义过了 mov bx,16 mul bx //mul是进行无符号乘法的指令 ax*bx,结果高16位存dx 低16位存ax
//相当于ax的值向左移4位
add ax,OFFSET GDT ;计算并设置基地址 实模式下段寄存器左移4位变为20位的段基地址再加偏移 adc dx,0 ;界限已在定义时设置好 注意dx需要带进位 根据上一个操作 mov WORD PTR VGDTR.Base,ax//高16位 mov WORD PTR VGDTR.Base+2,dx//低16位 并且带进位 dx:ax共同组成32位的基地址 ;设置代码段描述符 mov ax,cs mul bx mov WORD PTR Code.BaseL,ax ;代码段开始偏移为0 mov BYTE PTR Code.BaseM,dl ;代码段界限已在定义时设置好 mov BYTE PTR Code.BaseH,dh ;设置目标数据段描述符 mov ax,ds mul bx ;计算并设置目标数据段基址 add ax,OFFSET Buffer adc dx,0 mov WORD PTR DataD.BaseL,ax mov BYTE PTR DataD.BaseM,dl mov BYTE PTR DataD.BaseH,dh ;加载GDTR lgdt QWORD PTR VGDTR cli ;关中断 开中断sti EnableA20 ;打开地址线A20 ;切换到保护方式 mov eax,cr0 or eax,1 //cr0中的位0置为1 mov cr0,eax //进入保护模式 ;清指令预取队列,并真正进入保护方式 JUMP16 Code_Sel,<OFFSET Virtual> //宏调用 实参:选择子,偏移值 Virtual: ;现在开始在保护方式下运行 mov ax,DataS_Sel mov ds,ax ;加载源数据段描述符 mov ax,DataD_Sel mov es,ax ;加载目标数据段描述符 cld //si di 变化方向 加还是减 xor si,si //si和di清零 xor di,di ;设置指针初值 mov cx,BufLen/4 ;设置4字节为单位的缓冲区长度 设置计数器 repz movsd ;传送双字 ;切换回实模式 mov eax,cr0 and al,11111110b //最低位清0 进入实模式 mov cr0,eax ;清指令预取队列,进入实方式 JUMP16 <SEG Real>,<OFFSET Real> Real: ;现在又回到实方式 DisableA20 开中断 sti //开中断 mov ax,DSEG mov ds,ax mov si,OFFSET Buffer //ds:si cld mov bp,BufLen/16 //bp 外循环次数 16行 NextLine: mov cx,16 //内循环次数 一行16个字符 NextCh: lodsb //目的地址的内容读到源地址 串操作 块读出指令
// 即目标地址为es:di 源地址为ds:si 字节为单位传送
push ax
shr al,1 //右移1位
call ToASCII //调用子程序 toascii
EchoCh al //宏展开 echoch pop ax call ToASCII EchoCh al EchoCh ' ' loop NextCh //计数器为16 为0时跳出 EchoCh 0dh //实参传递给宏 EchoCh 0ah dec bp //外循环的次数减1 jnz NextLine mov ax,4c00h //中断 int 21h Start ENDP
;----------------------------------------------------------------------------
//子程序定义 ToASCII PROC //转换为ascii码 and al,0fh //al低4位不变 高四位 变为0 add al,90h //高4位变为9 低4位不变 daa //说明三 adc al,40h //带进位加法 al变为对应的ascii码 daa ret ToASCII ENDP ;---------------------------------------------------------------------------- CSEG ENDS ;代码段定义结束 ;---------------------------------------------------------------------------- END Start
说明:一mul指令
1将8位的操作数与al相乘 2将16位的操作数与ax相乘 3是将32位的操作数与eax相乘 乘积是乘数大小的2倍 三种格式都接受寄存器操作数和内存操作数 但是不接受立即数;
被乘数 乘数 积
al 8位 ax
ax 16位 dx:ax dx存高16位 ax存低16位
eax 32位 edx:eax
二 lodsb lodsw 与stosb stosw 分别对应
串操作指令 lodsb lodsw是块读出指令 具体操作是把si指向的存储单元读入累加器 器中lodsb是写入al lodsw写入
ax ,然后si自动增加或减少1或2位 当方向df为0时 si自增 ;df为1则自减
stosb从al中读取 stosw从ax读取
三 daa bcd码的加法调整指令
将al的内容调整为两位的组合型的二进制数 daa指令要分别考虑al的高4位和低4位
如果al的低4位大于9或af=1 ,则al的内容加06h 并将af置1;然后如果al的高4位大于9或cf=1 则al的内容加60h,且将cf置为1 如果两个都不满足 则将af,cf清零。
四切换到保护方式的准备工作
1.建立合适的全局描述符表 并使gdtr指向该gdt 在切换到保护方式时 至少要把代码段的选择子装载到cs 所以gdt中至少含有代码段的描述符
实例中各使用的存储段的描述符的界限都定义为0ffffh 根据属性可知三个段都是16位段
加载gdtr
LGDT QWORD PTR VGDTR
将存储器中的伪描述符VGDTR装入全局描述符表寄存器GDTR中
2.由实模式切换到保护模式
cr0中的PE位置置为1即可
之后要马上把代码段的选择子存入cs
jmp16 code_sel,<OFFSET VIRTUAL>
上面的段间转移指令在实模式下被预取 并在保护模式下被执行
3.由保护模式切换到实模式
cr0中的pe位为0 同时后面也要有一条段间转移指令 目的1为清除指令序列 目的2为将实模式下的代码段的段值送cs 此指令在保护方式下被预取 但是在实模式下执行
4.保护模式下的数据传送
源数据段和目的数据段的选择子装入ds 和es寄存器 这两个描述符已经在实模式下设置好 并把选择子装入段寄存器同时把描述符的信息装入到对应的高速缓冲寄存器 再设置si和di指针 cs计数器 ;根据段属性的值可以判断都为16位的段 串操作指令只是用16位的si和di 和cx寄存器 最后利用串操作指令实施传送
5.显示缓冲区的内容
缓冲区在常规内存中 即1m之内 所以需要在实模式下按要求以16进制数的形式显示其内容
五 内存映像
六特别说明
本实例简化程序 未定义中断描述符表 所以整个过程实在关中断的情况下运行的
未定义ldt表 所以进入保护模式后默认的段选择子都位于gdt中
未定义保护模式下的堆栈段 gdt中没有堆栈描述符 所以程序不涉及堆栈的操作
各个描述符的特权级别均为0 dpl rpl cpl 均为0
未采用分页管理机制 cr0中的PG位为0 线性地址就是存储单元的物理地址
打开和关闭a20地址线 pc兼容机中的第21根地址线
系统中的一个门控制该地址线 是否有效
为了访问地址在1m以上的存储段安源 应该先打开控制地址线a20的门 ;此设置与实模式只用1m内的空间有关 ,而与cpu是否工作在实模式还是保护模式无关 即使关闭a20地址线 也可以进入保护模式
实例二 32位代码段和16位代码段切换的实例
低声飞过 同实例一的逻辑功能相同
具体实现步骤:
1.切换保护方式准备
2.切换到保护方式的一个32位代码段
3.将指定内存区域的内容以字节为单位 转换成对应的十六进制数的ascii码 并填入显示缓冲区实现显示
4.再变换到保护方式下的一个16代码段
5.将指定内存区域的内容直接作为ascii码填入显示缓冲区中实现显示
6.切换到实模式
源程序如下:
1.实例二源程序
实例二的源程序如下所示:
;名称:ASM2.ASM ;功能:演示实方式和保护方式切换(切换到32位代码段) ;---------------------------------------------------------------------------- INCLUDE 386SCD.INC ;---------------------------------------------------------------------------- DSEG SEGMENT USE16 ;16位数据段定义 ;---------------------------------------------------------------------------- GDT LABEL BYTE ;全局描述符表 DUMMY Desc <> ;空描述符 Normal Desc <0ffffh,,,ATDW,,> ;规范段描述符 Code32 Desc <C32Len-1,,,ATCE,D32,> ;32位代码段描述符 Code16 Desc <0ffffh,,,ATCE,,> ;16位代码段描述符 DataS Desc <DataLen-1,0,10h,ATDR,,> ;源数据段描述符 DataD Desc <3999,8000h,0bh,ATDW,,> ;显示缓冲区描述符 Stacks Desc <StackLen-1,,,ATDW,,> ;堆栈段描述符 ;---------------------------------------------------------------------------- GDTLen = $-GDT ;全局描述符表长度 VGDTR PDesc <GDTLen-1,> ;伪描述符 ;---------------------------------------------------------------------------- SaveSP DW ? ;用于保存SP寄存器 SaveSS DW ? ;用于保存SS寄存器 ;---------------------------------------------------------------------------- Normal_Sel = Normal-GDT ;规范段描述符选择子 ? Code32_Sel = Code32-GDT ;32位代码段选择子 Code16_Sel = Code16-GDT ;16位代码段选择子 DataS_Sel = Datas-GDT ;源数据段选择子 DataD_Sel = DataD-GDT ;目标数据段选择子 Stacks_Sel = Stacks-GDT ;堆栈段描述符选择子 ;---------------------------------------------------------------------------- DataLen = 16 //? 需要显示的数据长度 ;---------------------------------------------------------------------------- DSEG ENDS ;数据段定义结束
;---------------------------------------------------------------------------- StackSeg SEGMENT PARA STACK USE16 StackLen = 256 DB StackLen DUP(0) //定义256个字节长度 StackSeg ENDS ;----------------------------------------------------------------------------
CSEG1 SEGMENT USE16 'REAL' ;16位代码段 貌似为实模式下调用 ASSUME CS:CSEG1,DS:DSEG ;---------------------------------------------------------------------------- Start PROC mov ax,DSEG mov ds,ax ;准备要加载到GDTR的伪描述符 mov bx,16 mul bx add ax,OFFSET GDT ;计算并设置基地址 adc dx,0 ;界限已在定义时设置好 mov WORD PTR VGDTR.Base,ax mov WORD PTR VGDTR.Base+2,dx ;设置32位代码段描述符 mov ax,CSEG2 // 代码段开始偏移为0 mul bx mov WORD PTR Code32.BaseL,ax mov BYTE PTR Code32.BaseM,dl mov BYTE PTR Code32.BaseH,dh ;设置16位代码段描述符 mov ax,CSEG3 mul bx mov WORD PTR Code16.BaseL,ax ;代码段开始偏移为0 mov BYTE PTR Code16.BaseM,dl ;代码段界限已在定义时设置好 mov BYTE PTR Code16.BaseH,dh ;设置堆栈段描述符 mov ax,ss mov WORD PTR SaveSS,ax //用来保存ss段寄存器中的值 mov WORD PTR SaveSP,sp //用来保存sp段寄存器中的值 movax,StackSeg
mulbxmovWORDPTR Stacks.BaseL,axmovBYTEPTR Stacks.BaseM,dlmovBYTEPTR Stacks.BaseH,dh;加载GDTRlgdtQWORDPTR VGDTR //加载伪描述符 到gdtr寄存器 cli;关中断EnableA20;打开地址线A20;切换到保护方式moveax,cr0oral,1movcr0,eax;清指令预取队列,并真正进入保护方式JUMP16Code32_Sel,<OFFSET SPM32> //跳转指令 传给宏JUMP16 实参值ToReal:;现在又回到实方式 movax,DSEG movds,axmovsp,SaveSP movss,SaveSS DisableA20 //关闭a20地址线sti //打开中断movax,4c00h int21h Start ENDP //程序的末尾;---------------------------------------------------------------------------- CSEG1 ENDS;代码段定义结束
;---------------------------------------------------------------------------- CSEG2 SEGMENTUSE32'PM32' //32位 代码段 保护模式下执行ASSUMECS:CSEG2 ;---------------------------------------------------------------------------- SPM32 PROCmovax,Stacks_Sel movss,ax //将堆栈的选择子装入到ss段寄存器movesp,StackLen //esp指向栈顶 movax,DataS_Sel //将源数据段选择子装入ds段寄存器 movds,ax movax,DataD_Sel //目的数据段选择子装入es寄存器 moves,axxoresi,esi //指针清零 用 ds:esixoredi,edi //用es:edimovecx,DataLen //计数器赋初值 cldNext:lodsb //块传送 si指向的字节内容写入al 算法过程看备注pushax 入栈CALL ToASCII //调用子程序 显示ascii码 movah,7shleax,16 //左移 16位popaxshral,4 //右移4位CALL ToASCII movah,7stosd //将al的内容存入edi指向的内存单元中moval,20h //空格stosw //从ax读取出数据 存入edi指向的内存单元
//如果使用的是stosd 则将eax的内容存入edi指向的内存单元
loop Next //循环 ecx-1 直到ecx=0为止
JUMP32 Code16_Sel,<OFFSET SPM16> //跳转到16位代码段
SPM32 ENDP
;----------------------------------------------------------------------------
ToASCII PROC
and al,00001111b //高4位清0
add al,30h //加30h 0+30h=30h 对应的ascii码为0
cmp al,39h //比较低4位与9的大小
jbe Isdig //小于等于 39h 跳转 说明是数字 判断是数字还是字母
add al,7 //大于39h的就是字母 对应的
IsDig: ret
ToASCII ENDP
;----------------------------------------------------------------------------
C32Len = $
;----------------------------------------------------------------------------
CSEG2 ENDS
;---------------------------------------------------------------------------- CSEG3 SEGMENT USE16 'PM16' //16位代码段
ASSUME CS:CSEG3 ;---------------------------------------------------------------------------- SPM16 PROC xor si,si mov di,DataLen*3*2 mov ah,7 mov cx,DataLen AGain: lodsb //从di指向的内存地址 取数据存入al stosw //读ax数据 写到edi指向的内存单元 ah为7 loop AGain //循环块传送 直到cx值为0 mov ax,Normal_sel //规范段选择子赋值 mov ds,ax mov es,ax mov ss,ax mov eax,cr0 //准备实模式 and al,11111110b mov cr0,eax jmp FAR PTR ToReal //跳转到实模式 SPM16 ENDP ;---------------------------------------------------------------------------- CSEG3 ENDS ;---------------------------------------------------------------------------- END Start
注释
1.切换到保护模式的准备工作
建立全局描述符表,含有两个16位数据段的描述符 一个16位代码段的描述符和一个16位堆栈段描述符
一个32位代码段描述符
2.实模式切换到保护模式
JUMP32 CODE16_SEL,<OFFSET SPM16>
该转移指令含48位指针 其中高16位是选择子 低32位是16位代码段的入口偏移
3.显示指定内存区域的内容
直接写显示缓冲区的方法实现显示
4.特别说明 在程序的结尾 给各个段寄存器传递一个normal的选择子
在分段管理机制中 每个段寄存器都有高速缓冲寄存器 这些寄存器在实模式下仍然有作用 仅仅是内容上与保护方式不同;段属性值在实模式下没有意义 实模式下不可设置; 而且段的基地址位数不同保护为32位 而实模式下位20位
所以在准备结束保护模式回到实模式之前,要通过加载一个合适的描述符选择子到有关段寄存器 以使得对应段描述符高速缓冲寄存器中含有合适的段界限和属性值
需要注意的是不能从32位代码段返回到实模式 而是需要从16位代码段返回
在32位代码段中 缺省的操作数大小是32位 缺省的存储单元地址大小是32位