程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 匯編實模式與保護模式切換實例

匯編實模式與保護模式切換實例

編輯:匯編語言

本文介紹兩個實現實模式與保護模式切換的實例,通過他們說明如何實現實模式與保護模式的切換,也說明保護模式下的80386及其編程。

<一>演示實模式和保護模式切換的實例(實例一)

實例一的邏輯功能是,以十六進制數的形式顯示從內存地址110000H開始的256個字節的值。本實例指定該內存區域的目的僅僅是想說明切換到保護模式的必要性,因為在實模式下不能訪問該指定內存區域,只有在保護模式下才能訪問到該指定區域。

本實例的具體實現步驟是:(1)作切換到保護方式的准備;(2)切換到保護方式;(3)把指定內存區域的內容傳送到位於常規內存的緩沖區中;(4)切換回實模式;(5)顯示緩沖區內容。

1.包含文件

386保護模式匯編語言程序用到的包含文件如下所示,該包含文件在後面的程序中還要用到。

;名稱:386SCD.INC
;功能:符號常量等的定義
;----------------------------------------------------------------------------
;IFNDEF __386SCD_INC
;__386SCD_INC EQU 1
;----------------------------------------------------------------------------
.386P
;----------------------------------------------------------------------------
;打開A20地址線
;----------------------------------------------------------------------------
EnableA20 MACRO
push ax
in al,92h
or al,00000010b
out 92h,al
pop ax
ENDM
;----------------------------------------------------------------------------
;關閉A20地址線
;----------------------------------------------------------------------------
DisableA20 MACRO
push ax
in al,92h
and al,11111101b
out 92h,al
pop ax
ENDM
;----------------------------------------------------------------------------
;16位偏移的段間直接轉移指令的宏定義(在16位代碼段中使用)
;----------------------------------------------------------------------------
JUMP16 MACRO Selector,Offset
DB 0eah ;操作碼
DW Offset ;16位偏移量
DW Selector ;段值或段選擇子
ENDM
;----------------------------------------------------------------------------
;32位偏移的段間直接轉移指令的宏定義(在32位代碼段中使用)
;----------------------------------------------------------------------------
COMMENT <JUMP32>
JUMP32 MACRO Selector,Offset
DB 0eah ;操作碼
DD OFFSET
DW Selector ;段值或段選擇子
ENDM
<JUMP32>
;-------------------------------------------------
JUMP32 MACRO Selector,Offset
DB 0eah ;操作碼
DW OFFSET
DW 0
DW Selector ;段值或段選擇子
ENDM
;----------------------------------------------------------------------------
;16位偏移的段間調用指令的宏定義(在16位代碼段中使用)
;----------------------------------------------------------------------------
CALL16 MACRO Selector,Offset
DB 9ah ;操作碼
DW Offset ;16位偏移量
DW Selector ;段值或段選擇子
ENDM
;----------------------------------------------------------------------------
;32位偏移的段間調用指令的宏定義(在32位代碼段中使用)
;----------------------------------------------------------------------------
COMMENT <CALL32>
CALL32 MACRO Selector,Offset
DB 9ah ;操作碼
DD Offset
DW Selector ;段值或段選擇子
ENDM
<CALL32>
;-------------------------------------------------
CALL32 MACRO Selector,Offset
DB 9ah ;操作碼
DW Offset
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
Limit DW 0 ;16位界限
Base DD 0 ;32位基地址
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
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 ;存在的可執行可讀一致代碼段屬性值
;----------------------------------------------------------------------------
;系統段描述符類型值說明
;----------------------------------------------------------------------------
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
mov ah,2
mov dl,ascii
int 21h
ENDM
;----------------------------------------------------------------------------
DSEG SEGMENT USE16 ;16位數據段
;----------------------------------------------------------------------------
GDT LABEL BYTE ;全局描述符表
DUMMY Desc <> ;空描述符
Code Desc <0ffffh,,,ATCE,,> ;代碼段描述符
DataS Desc <0ffffh,0,11h,ATDW,,> ;源數據段描述符
DataD Desc <0ffffh,,,ATDW,,> ;目標數據段描述符
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表長度
VGDTR PDesc <GDTLen-1,> ;偽描述符
;----------------------------------------------------------------------------
Code_Sel = Code-GDT ;代碼段選擇子
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
;准備要加載到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
;設置代碼段描述符
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 ;關中斷
EnableA20 ;打開地址線A20
;切換到保護方式
mov eax,cr0
or eax,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
xor si,si
xor di,di ;設置指針初值
mov cx,BufLen/4 ;設置4字節為單位的緩沖區長度
repz movsd ;傳送
;切換回實模式
mov eax,cr0
and al,11111110b
mov cr0,eax
;清指令預取隊列,進入實方式
JUMP16 <SEG Real>,<OFFSET Real>
Real: ;現在又回到實方式
DisableA20
sti
mov ax,DSEG
mov ds,ax
mov si,OFFSET Buffer
cld
mov bp,BufLen/16
NextLine: mov cx,16
NextCh: lodsb
push ax
shr al,1
call ToASCII
EchoCh al
pop ax
call ToASCII
EchoCh al
EchoCh ' '
loop NextCh
EchoCh 0dh
EchoCh 0ah
dec bp
jnz NextLine
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
ToASCII PROC
and al,0fh
add al,90h
daa
adc al,40h
daa
ret
ToASCII ENDP
;----------------------------------------------------------------------------
CSEG ENDS ;代碼段定義結束
;----------------------------------------------------------------------------
END Start

3.關於實例步驟的注釋

在源程序的開頭首先包含了文件“386SCD.INC”,在此包含文件中定義了保護模式程序設計要用到的一些結構、宏及常量。下面對各實現步驟作些說明。

(1)切換到保護方式的准備工作

在從實模式切換到保護模式之前,必須作必要的准備。准備工作的內容根據實際而定。最起碼的准備工作是建立合適的全局描述符表,並使用GDTR指向該GDT。因為在切換到保護方式時,至少要把代碼段的選擇子裝載到CS,所以GDT中至少含有代碼段的描述符。

從本實例源程序可見,全局描述符表GDT僅有四個描述符:第一個是空描述符;第二個是代碼段描述符;第三個和第四個分別為源數據段及目標數據段描述符。本實例各描述符中的段界限是在定義時設置的,並且除偽描述符VGDTR中的界限按GDT的實際長度設置外,各使用的存儲段描述符的界限都規定為0FFFFH。另外,描述符中的段屬性也根據所描述段的類型被預置,各屬性的定義在包含文件386SCD.INC中均有說明。從屬性值可知,這三個段都是16位段。

由於在切換到保護方式後就要引用GDT,所以在切換到保護方式前必須裝載GDTR。實例中使用如下指令裝載GDTR:

LGDT QWORD PTR VGDTR

該指令的功能是把存儲器中的偽描述符VGDTR裝入到全局描述符表寄存器GDTR中。偽描述符VGDTR的結構如前所述結構類型PDESC所示,低字是以字節位單位的全局描述符表段的界限,高雙字為描述符表段的線性基地址(本實例不啟用分頁機制,所以線性地址等同於物理地址)。本實例中未涉及到局部描述符表及中斷描述符表,後面的文章將作詳細說明。

(2)由實模式切換到保護模式

在做好准備後,從實模式切換到保護模式並不難。原則上只要把控制寄存器CR0中的PE位置1即可。本實例采用如下三條指令設置PE位:

   mov eax,cr0
   or eax,1
   mov cr0,eax

實際情況要比這復雜些。執行上面的三條指令後,處理器轉入保護模式,但CS中的內容還是實模式下代碼段的段值,而不是保護模式下代碼段的選擇子,所以在取指令之前得把代碼段的選擇子裝入CS。為此,緊接著這三條指令,安排一條如下所示的段間轉移指令:

JUMP16 Code_Sel ,< OFFSET Virtual >

這條段間轉移指令 在實模式下被預取並在保護方式下被執行 。利用這條段間轉移指令可把保護模式下代碼段的選擇子裝入CS,同時也刷新指令預取隊列。從此真正進入保護模式。

(3)由保護模式切換到實模式

在80386上,從保護模式切換到實模式的過程類似於從實模式切換到保護模式。原則上只要把控制寄存器CR0中的PE位清0即可。實際上,在此之後也要安排一條段間轉移指令,一方面清指令預取隊列,另一方面把實模式下代碼段的段值送CS。 這條段間轉移指令在保護方式下被預取並在實模式下被執行 。

(4)保護模式下的數據傳送

首先,把源數據段和目標數據段的選擇子裝入DS和ES寄存器,這兩個描述符已在實模式下設置好,把選擇子裝入段寄存器就意味著把包括基地址在內的段信息裝入到了段描述符高速緩沖寄存器。然後設置指針寄存器SI和DI的初值,也設置計數器CX的初值。根據預置的段屬性,在保護方式下,代碼段也僅是16位段,串操作指令只使用16位的SI、DI和CX等寄存器。最後利用串操作指令實施傳送。

(5)顯示緩沖區中的內容

由於緩沖區在常規內存中,所以在實模式下根據要求按十六進制顯示其內容是很容易理解的,這裡就不再多說。

4.內存映象

在源程序中沒有把GDT作為一個單獨的段對待,但在進入保護方式後,它是一個獨立的段。從對代碼段和源數據段描述符所賦的基地址和段界限值可見,代碼段和數據段有部分覆蓋。盡管這樣做不利於代碼和數據的安全,但如果需要,這樣做是可行的。本實例運行時的內存映象如下圖所示。

5.特別說明

作為第一個實模式和保護模式切換的例子,本實例作了大量的簡化處理。

通常,由實模式切換到保護模式的准備工作還應包含建立中斷描述符表。但本實例沒有建立中斷描述符表。為此,要求整個過程在關中斷的情況下進行;要求不使用軟中斷指令;假設不發生任何異常。否則會導致系統崩潰。

本實例未使用局部描述符表,所以在進入保護模式後沒有設置局部描述符表寄存器LDTR。為此,在保護模式下使用的段選擇子都指定GDT中的描述符。

本實例未定義保護模式下的堆棧段,GDT中沒有堆棧段描述符,在保護模式下沒有設置SS,所以在保護方式下沒有涉及堆棧操作的指令。

本實例各描述符特權級DPL和各選擇子的請求特權級RPL均為0,在保護方式下運行時的當前特權級CPL也是0。

本實例沒有采用分頁管理機制,也即CR0中的PG位為0,線性地址就是存儲單元的物理地址。

6.打開和關閉地址線A20

PC及其兼容機的第21根地址線(A20)較特殊,計算機系統中一般安排一個 “門”控制該地址線是否有效。為了訪問地址在1M以上的存儲單元,應先打開控制地址線A20的“門”。這種設置與實模式下只使用最低端的1M字節存儲空間有關,與處理器是否工作在實模式或保護方式無關,即使在關閉地址線A20時,也可進入保護模式。

如何打開和關閉地址線A20與計算機系統的具體設置有關。在本文中介紹的包含文件386SCD.INC中定義了兩個宏,打開地址線A20的宏EnableA20和關閉地址線A20的宏DisableA20,此兩個宏指令在一般的PC兼容機上都是可行的。

<二>演示32位代碼段和16位代碼段切換的實例(實例二)

實例二的邏輯功能是,以十六進制數和ASCII字符兩種形式顯示從內存地址100000H開始的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)
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
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
mov WORD PTR SaveSP,sp
mov ax,StackSeg
mul bx
mov WORD PTR Stacks.BaseL,ax
mov BYTE PTR Stacks.BaseM,dl
mov BYTE PTR Stacks.BaseH,dh
;加載GDTR
lgdt QWORD PTR VGDTR
cli ;關中斷
EnableA20 ;打開地址線A20
;切換到保護方式
mov eax,cr0
or al,1
mov cr0,eax
;清指令預取隊列,並真正進入保護方式
JUMP16 Code32_Sel,<OFFSET SPM32>
ToReal: ;現在又回到實方式
mov ax,DSEG
mov ds,ax
mov sp,SaveSP
mov ss,SaveSS
DisableA20
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
CSEG1 ENDS ;代碼段定義結束
;----------------------------------------------------------------------------
CSEG2 SEGMENT USE32 'PM32'
ASSUME CS:CSEG2
;----------------------------------------------------------------------------
SPM32 PROC
mov ax,Stacks_Sel
mov ss,ax
mov esp,StackLen
mov ax,DataS_Sel
mov ds,ax
mov ax,DataD_Sel
mov es,ax
xor esi,esi
xor edi,edi
mov ecx,DataLen
cld
Next: lodsb
push ax
CALL ToASCII
mov ah,7
shl eax,16
pop ax
shr al,4
CALL ToASCII
mov ah,7
stosd
mov al,20h
stosw
loop Next
JUMP32 Code16_Sel,<OFFSET SPM16>
SPM32 ENDP
;----------------------------------------------------------------------------
ToASCII PROC
and al,00001111b
add al,30h
cmp al,39h
jbe Isdig
add al,7
IsDig: ret
ToASCII ENDP
;----------------------------------------------------------------------------
C32Len = $
;----------------------------------------------------------------------------
CSEG2 ENDS
;----------------------------------------------------------------------------
CSEG3 SEGMENT USE16 'PM16'
ASSUME CS:CSEG3
;----------------------------------------------------------------------------
SPM16 PROC
xor si,si
mov di,DataLen*3*2
mov ah,7
mov cx,DataLen
AGain: lodsb
stosw
loop AGain
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

2.關於實現步驟的注釋

(1)切換到保護模式的准備工作

建立全局描述符表,這裡的全局描述符表含有兩個16位數據段的描述符、一個16位代碼段的描述符和一個16位的堆棧段描述符。此外,GDT中還有一個32位的代碼段描述符,描述32位代碼段,該描述符的屬性字段中的D位為1。

(2)由實模式切換到保護模式

由實模式切換到保護模式32位代碼段的方法與切換到16位代碼段的方法相同。由保護模式16位代碼段切換回實模式的方法與實例一相似。

在保護模式下,通過如下直接段間轉移指令從32位代碼段切換到16位代碼段:

JUMP32 Code16_Sel ,< OFFSET SPM16 >

從該宏指令的定義可知,該轉移指令含48位指針,其高16位是16位代碼段的選擇子,低32位是16位代碼段的入口偏移。 該指令在32位方式下預取並執行 。由於在32位方式下執行,所以要使用48位指針。

(3)顯示指定內存區域的內容

在本實例中,采用直接寫顯示緩沖區的方法實現顯示。假設顯示緩沖區的開始物理地址是0B8000H, 3號文本顯示模式,在屏幕的第一行進行顯示。

3.特別說明

本實例在保護方式下使用了涉及堆棧操作的指令,因此建立了一個16位的保護模式下的堆棧段。

本實例仍作了大量的簡化處理。如:沒有建立IDT和LDT等,各特權級均是0。也沒有采用分頁管理機制。

從本實例的GDT中可見,兩個數據段的界限都是根據實際大小而設置的。從源程序代碼段CSEG3可見,在切換到實模式之前,把一個指向似乎沒有用的數據段的描述符Normal的選擇子裝載到DS和ES。這是為什麼呢?

實模
式下
段描
述符
高速
緩沖
寄存
器的
內容 段寄存器 段基地址 段界限(固定) 段屬性(固定) 存在性 特權級 已存取 粒度 擴展方向 可讀性 可寫性 可執行 堆棧大小 一致特權 CS 當前CS*16 0000FFFFH Y 0 Y B U Y Y Y - N SS 當前SS*16 0000FFFFH Y 0 Y B U Y Y N W - DS 當前DS*16 0000FFFFH Y 0 Y B U Y Y N - - ES 當前ES*16 0000FFFFH Y 0 Y B U Y Y N - - FS 當前FS*16 0000FFFFH Y 0 Y B U Y Y N - - GS 當前GS*16 0000FFFFH Y 0 Y B U Y Y N - -

在分段管理機制一文中已介紹過,每個段寄存器都配有段描述符高速緩沖寄存器,這些高速緩沖寄存器在實方式下仍發揮作用,只是內容上與保護模式下有所不同。如上表所示,其中“Y”表示“是”; “N”表示“否”;“B”表示字節;“U”表示向上擴展,“W”表示以字方式操作堆棧。段基地址仍是 32位,其值是相應段寄存器值(段值)乘以16,在把段值裝載到段寄存器時刷新。由於其值是16位段值乘上16,所以在實模式下基地址實際上有效位只有20位。每個段的32位段界限都固定為0FFFFH,段屬性的許多位也是固定的。所謂固定是指在實方式下不可設置這些屬性值,只能繼續沿用保護方式下所設置的值。因此,在准備結束保護模式回到實模式之前,要通過加載一個合適的描述符選擇子到有關段寄存器,以使得對應段描述符高速緩沖寄存器中含有合適的段界限和屬性。本實例GDT中的描述符Normal就是這樣一個描述符,在返回實模式之前把對應選擇子Normal_Sel加載到DS和ES就是此目的。由於SS段描述符中的內容已符合實模式的需要,所以盡管也改變了SS,但不需要重新加載SS(本實例中重新加載了SS,這除了稍增加運行時間外,並沒有什麼壞處)。16位代碼段描述符中的內容也符合實模式的需要,所以在通過16位代碼段返回實模式時,CS段描述符中的內容也符合實模式的要求。需要注意的是,不能從32位代碼段返回實模式,這是因為無法實現從32位代碼段返回時CS高速緩沖寄存器中的屬性符合實模式的要求(實模式不能改變段屬性)。順便說以下,實例一中的描述符都是符合實模式要求的。段描述符高速緩沖寄存器中含有合適的段界限

4.關於32位代碼段程序設計的說明

在32位代碼段中,缺省的操作數大小是32位,缺省的存儲單元地址大小是32位。由於串操作指令使用的指針寄存器是ESI和EDI,LOOP指令使用的計數器是ECX,所以,在代碼段CSEG2中,為了使用串操作指令,對ESI和EDI等寄存器賦初值。請比較代碼段CSEG3中的相關片段和實例一中的相關片段,它們是16位代碼段。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved