1. 程式人生 > >真實模式、保護模式的區別 真實模式保護模式切換方法

真實模式、保護模式的區別 真實模式保護模式切換方法

真實模式保護模式區別

      從80386開始,cpu有三種工作方式:真實模式,保護模式和虛擬8086模式。只有在剛剛啟動的時候是real-mode,等到linux作業系統執行起來以後就執行在保護模式。
 

       真實模式只能訪問地址在1M以下的記憶體稱為常規記憶體,我們把地址在1M 以上的記憶體稱為擴充套件記憶體。
 

       在保護模式下,全部32條地址線有效,可定址高達4G位元組的實體地址空間;
擴充的儲存器分段管理機制和可選的儲存器分頁管理機制,不僅為儲存器共享和保護提供了硬體支援,而且為實現虛擬儲存器提供了硬體支援;
 

      支援多工,能夠快速地進行任務切換和保護任務環境;
 

      4個特權級和完善的特權檢查機制,既能實現資源共享又能保證程式碼和資料的安全和保密及任務的隔離;
 

       支援虛擬8086方式,便於執行8086程式。

1.虛擬8086模式是執行在保護模式中的真實模式,為了在32位保護模式下執行純16位程式。它不是一個真正的CPU模式,還屬於保護模式。

2.保護模式同真實模式的根本區別是程序記憶體受保護與否 。可定址空間的區別只是這一原因的果。
       真實模式將整個實體記憶體看成分段的區域,程式程式碼和資料位於不同區域,系統程式和使用者程式沒有區別對待,而且每一個指標都是指向"實在"的實體地址。這樣一來,使用者程式的一個指標如果指向了系統程式區域或其他使用者程式區域,並改變了值,那麼對於這個被修改的系統程式或使用者程式,其後果就很可能是災難性的。為了克服這種低劣的記憶體管理方式,處理器廠商開發出保護模式。這樣,實體記憶體地址不能直接被程式訪問,程式內部的地址(虛擬地址)要由作業系統轉化為實體地址去訪問,程式對此一無所知。 至此,程序(這時我們可以稱程式為程序了)有了嚴格的邊界,任何其他程序根本沒有辦法訪問不屬於自己的實體記憶體區域,甚至在自己的虛擬地址範圍內也不是可以任意訪問的,因為有一些虛擬區域已經被放進一些公共系統執行庫。這些區域也不能隨便修改,若修改就會有: SIGSEGV(linux 段錯誤);非法記憶體訪問對話方塊(windows 對話方塊)。

CPU啟動環境為16位真實模式,之後可以切換到保護模式。但從保護模式無法切換回真實模式

3.事實上,現在的64位奔騰4處理器,擁有三種基本模式和一種擴充套件模式,
a)基本模式:
****保護模式:純32位保護執行環境。
****真實模式:純16位無保護執行環境。
****系統管理模式:當SMI引腳為有效進入系統管理模式,首先儲存當前的CPU上下文。它有獨立的地址空間,用來執行電源管理或系統安全方面的指令。
b)擴充套件模式:****IA-32e模式,64位作業系統執行在該模式。該模式有兩種子模式:
 

1)**相容模式:該模式下,64位作業系統執行在32位相容環境,能正常執行16,32位應用程式就像基本的保護模式一樣,訪問32位地址空間,但不能執行純16位真實模式程式(就是不能執行虛擬86模式程式了)。
 

2)**64位模式:在該模式下,處理器完全執行64位指令,使用64位地址空間和64運算元,執行16,32位程式必須切換到相容模式。
IA-32e子模式的切換完全基於程式碼段暫存器。這樣一來,執行在IA-32e模式中(64位)的OS完全可以無縫的執行所有16,32,64為應用程式,通過設定32位後的CS。

真實模式保護模式切換方法

        例項一的邏輯功能是,以十六進位制數的形式顯示從記憶體地址110000H開始的256個位元組的值。本例項指定該記憶體區域的目的僅僅是想說明切

換到保護模式的必要性,因為在真實模式下不能該指定記憶體區域,只有在保護模式下才能到該指定區域

本例項的具體實現步驟是:作切換到保護方式的準備;切換到保護方式;把指定記憶體區域的內容傳送到位於常規記憶體的緩衝區中;切換回真實模式;顯示緩衝區內容。

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位偏移的段間直接轉移指令的巨集定義

JUMP16 MACRO Selector,Offset

DB 0eah ;操作碼

DW Offset ;16位偏移量

DW Selector ;段值或段選擇子

ENDM

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位偏移的段間呼叫指令的巨集定義

CALL16 MACRO Selector,Offset

DB 9ah ;操作碼

DW Offset ;16位偏移量

DW Selector ;段值或段選擇子

ENDM

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 ;段界限

BaseL DW 0 ;段基地址

BaseM DB 0 ;段基地址

Attributes DB 0 ;段屬性

LimitH DB 0 ;段界限

BaseH DB 0 ;段基地址

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 ;除錯陷阱標誌

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

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

;功能:演示實方式和保護方式切換

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 ;緩衝區

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”,在此包含檔案中定義了保護模式程式設計要用到的一些結構、巨集及常量。下面對各實現步驟作些說明。

切換到保護方式的準備工作

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

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

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

LGDT QWORD PTR VGDTR

       該指令的功能是把儲存器中的偽描述符VGDTR裝入到全域性描述符表暫存器GDTR中。偽描述符VGDTR的結構如前所述結構型別PDESC所示,低字是以位元組位單位的全域性描述符表段的界限,高雙字為描述符表段的線性基地址。本例項中未涉及到區域性描述符表及中斷描述符表,後面的文章將作詳細說明。

由真實模式切換到保護模式

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

mov eax,cr0

or eax,1

mov cr0,eax

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

JUMP16 Code_Sel , OFFSET Virtual

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

由保護模式切換到真實模式

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

保護模式下的傳送

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

顯示緩衝區中的內容

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

4.記憶體映象

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

5.特別說明

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

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

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

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

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

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

6.開啟和關閉地址線A20

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

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

二演示32位程式碼段和16位程式碼段切換的例項

       例項二的邏輯功能是,以十六進位制數和ASCII字元兩種形式顯示從記憶體地址100000H開始的16個位元組的內容。

       從功能上看,本例項類似於例項一,但在實現方法上卻有了改變,它更能反映出真實模式和保護模式切換的情況。具體實現步驟是:作切換到保護方式的準備;切換到保護方式的一個32位程式碼段;把指定記憶體區域的內容以位元組為單位,轉換成對應的十六進位制數的ASCII碼,並直接填入顯示緩衝區實現顯示;再變換到保護方式下的一個16位程式碼段;把指定記憶體區域的內容直接作為ASCII碼填入顯示緩衝區中實現顯示;切換回真實模式。

1.例項二源程式

例項二的源程式如下所示:

名稱:ASM2.ASM

功能:演示實方式和保護方式切換

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 ;堆疊段描述符選擇子

DtaLen = 16

DSEG ENDS ;段定義結束

StackSeg SEGMENT PARA STACK USE16

StackLen = 256

DB StackLen DUP

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.關於實現步驟的註釋
切換到保護模式的準備工作

       建立全域性描述符表,這裡的全域性描述符表含有兩個16位段的描述符、一個16位程式碼段的描述符和一個16位的堆疊段描述符。此外,GDT中還有一個32位的程式碼段描述符,描述32位程式碼段,該描述符的屬性欄位中的D位為1。

由真實模式切換到保護模式

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

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

JUMP32 Code16_Sel , OFFSET SPM16

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

顯示指定記憶體區域的內容

      在本例項中,採用直接寫顯示緩衝區的方法實現顯示。假設顯示緩衝區的開始實體地址是0B8000H, 3號文字顯示模式,在螢幕的第一行進行顯示。

3.特別說明

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

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