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位