- 论坛徽章:
- 0
|
本帖最后由 lmarsin 于 2010-04-19 15:12 编辑
<五>演示分页机制的实例(实例十)
下面给出一个演示如何启用分页管理机制的实例。该实例的逻辑功能是,在屏幕上显示一条表示已启用分页管理机制的提示信息。该实例演示内容包括:初始化页目录表和部分页表;启用分页管理机制;关闭分页管理机制等。该实例假设系统至少有4M字节物理内存。
1.演示步骤和源程序清单
为了简单化,实例只有一个任务,并且没有局部描述符表和中断描述符表,不允许中断,也不考虑发生异常,甚至没有使用堆栈。实例执行步骤如下:
(1)在实模式下为进入保护模式作初始化;
(2)切换到保护模式后进入临时代码段,把部分演示代码传送到预定的内存,然后转演示代码段;
(3)建立页目录表;
(4)建立页表;
(5)启用分页管理机制;
(6)演示在分页管理机制启用后的程序执行和数据存取;
(7)关闭分页管理机制;
(退出保护模式,结束。
实例十源程序清单如下:
;名称:ASM10.ASM
;功能:演示使用分页管理机制
;编译:TASM ASM10.ASM
;连接:TLINK ASM10.OBJ
;============================================================================
INCLUDE 386SCD.INC
;============================================================================
PDT_AD = 200000h ;页目录表所在物理页的地址
PT0_AD = 202000h ;页表0所在物理页的地址
PT1_AD = 201000h ;页表1所在物理页的地址
PhVB_AD = 0b8000h ;物理视频缓冲区地址
LoVB_AD = 0f0000h ;程序使用的逻辑视频缓冲区地址
MPVB_AD = 301000h ;线性地址0B8000H所映射的物理地址
PhSC_AD = 303000h ;部分演示代码所在内存的物理地址
LoSC_AD = 402000h ;部分演示代码的逻辑地址
;============================================================================
GDTSeg SEGMENT PARA USE16 ;全局描述符表数据段(16位)
;----------------------------------------------------------------------------
;全局描述符表GDT
GDT LABEL BYTE
;空描述符
DUMMY Desc <>
;规范段描述符及选择子
Normal Desc <0ffffh,,,ATDW,,>
Normal_Sel = Normal-GDT
;页目录表所在段描述符(在保护方式下初始化时用)及选择子
PDT Desc <0fffh,PDT_AD AND 0ffffh,PDT_AD SHR 16,ATDW,,>
PDT_Sel = PDT-GDT
;页表0所在段描述符(在保护方式下初始化时用)及选择子
PT0 Desc <0fffh,PT0_AD AND 0ffffh,PT0_AD SHR 16,ATDW,,>
PT0_Sel = PT0-GDT
;页表1所在段描述符(在保护方式下初始化时用)及选择子
PT1 Desc <0fffh,PT1_AD AND 0ffffh,PT1_AD SHR 16,ATDW,,>
PT1_Sel = PT1-GDT
;逻辑视频缓冲区段描述符及选择子
LoVideo Desc <3999,LoVB_AD AND 0ffffh,LoVB_AD SHR 16,ATDW,,>
LoVideo_Sel = LoVideo-GDT
;逻辑上的部分演示代码段的描述符及选择子
LoCode Desc <SCodeLen-1,LoSC_AD AND 0ffffh,LoSC_AD SHR 16,ATCE,,>
LoCode_Sel = LoCode-GDT
;预定内存区域(用于部分演示代码)的段描述符及选择子
TPSCode Desc <SCodeLen-1,PhSC_AD AND 0ffffh,PhSC_AD SHR 16,ATDW,,>
TPSCode_Sel = TPSCode-GDT
;----------------------------------------------------------------------------
;以下是需额外初始化的描述符
EFFGDT LABEL BYTE
;临时代码段描述符及选择子
TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,>
TempCode_Sel = TempCode-GDT
;演示代码段描述符及选择子
DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE,,>
DemoCode_Sel = DemoCode-GDT
;演示任务数据段描述符及选择子
DemoData Desc <DemoDataLen-1,DemoDataSeg,,ATDW,,>
DemoData_Sel = DemoData-GDT
;初始化时要移动的代码段描述符及选择子(移动时作为数据对待)
SCode Desc <SCodeLen-1,SCodeSeg,,ATDR,,>
SCode_Sel = SCode-GDT
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表长度
GDNum = ($-EFFGDT)/(SIZE Desc) ;需特殊处理的描述符数
;----------------------------------------------------------------------------
GDTSeg ENDS ;全局描述符表段定义结束
;============================================================================
;这部分代码在初始化时被复制到预定的内存区域,其功能是在屏幕上显示提示信息
;----------------------------------------------------------------------------
SCodeSeg SEGMENT PARA USE16
ASSUME CS:SCodeSeg,DSemoDataSeg
;----------------------------------------------------------------------------
SBegin PROC FAR
mov ax,LoVideo_Sel
mov es,ax
mov di,0
mov ah,17h
mov cx,MessLen
S1: lodsb
stosw
loop S1
JUMP16 DemoCode_Sel,Demo3
SBegin ENDP
;----------------------------------------------------------------------------
MLen = $-SBegin
SCodeLen = $
SCodeSeg ENDS
;============================================================================
DemoDataSeg SEGMENT PARA USE16 ;演示任务数据段
Mess DB 'Page is OK!'
MessLen = $-Mess
DemoDataLen = $
DemoDataSeg ENDS
;============================================================================
DemoCodeSeg SEGMENT PARA USE16 ;演示任务代码段
ASSUME CSemoCodeSeg
;----------------------------------------------------------------------------
DemoBegin PROC FAR
mov ax,PDT_Sel
mov es,ax
xor di,di
mov cx,1024
xor eax,eax ;先把全部表项置成无效
rep stosd ;再置表项0和表项1
mov DWORD PTR es:[0],PT0_AD OR (USU+RWW+PL)
mov DWORD PTR es:[4],PT1_AD OR (USU+RWW+PL)
mov ax,PT0_Sel ;初始化页表0
mov es,ax
xor di,di
mov cx,1024
xor eax,eax
or eax,USU+RWW+PL
Demo1: stosd
add eax,1000h ;先全部置成对应等地址的
loop Demo1 ;物理页,再特别设置两广表项
mov di,(PhVB_AD SHR 12)*4
mov DWORD PTR es:[di],MPVB_AD+USS+RWW+PL
mov di,(LoVB_AD SHR 12)*4
mov DWORD PTR es:[di],PhVB_AD+USU+RWR+PL
mov ax,PT1_Sel ;初始化页表1
mov es,ax
xor di,di
mov cx,1024
mov eax,400000h
Demo2: stosd ;先把全部表项设置为无效
add eax,1000h
loop Demo2 ;再特别设置1项
mov di,((LoSC_AD SHR 12)AND 3ffh)*4
mov DWORD PTR es:[di],PhSC_AD+USU+RWR+PL
mov eax,PDT_AD
mov cr3,eax
mov eax,cr0
or eax,80000000h
mov cr0,eax
jmp SHORT PageE
PageE: mov ax,DemoData_Sel
mov ds,ax
mov si,OFFSET Mess
JUMP16 LoCode_Sel,SBegin
Demo3: mov eax,cr0
and eax,7fffffffh ;关闭分页机制
mov cr0,eax
jmp SHORT PageD
PageD: mov ax,Normal_Sel
JUMP16 TempCode_Sel,ToDOS
DemoBegin ENDP
;----------------------------------------------------------------------------
DemoCodeLen = $
DemoCodeSeg ENDS
;============================================================================
TempCodeSeg SEGMENT PARA USE16 ;临时任务的代码段
ASSUME CS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual PROC FAR
cld ;为演示在启用分页机制后执
mov ax,SCode_Sel ;行位于较高线性地址空间中
mov ds,ax ;的代码作准备
mov ax,TPSCode_Sel
mov es,ax
mov si,OFFSET SBegin
mov di,si
mov cx,MLen ;把分页演示代码复制到预定
rep movsb ;内存
JUMP16 DemoCode_Sel,DemoBegin
ToDOS: mov ds,ax
mov es,ax
mov eax,cr0 ;准备返回实模式
and al,11111110b
mov cr0,eax
JUMP16 <SEG Real>,<OFFSET Real>
Virtual ENDP
;----------------------------------------------------------------------------
TempCodeSeg ENDS
;============================================================================
RCodeSeg SEGMENT PARA USE16 ;实方式的初始化代码和数据
ASSUME CS:RCodeSeg,DS:RCodeSeg
;----------------------------------------------------------------------------
VGDTR PDesc <GDTLen-1,>
;----------------------------------------------------------------------------
Start PROC
push cs
pop ds
cld
call InitGDT ;初始化全局描述符表GDT
EnableA20
lgdt QWORD PTR VGDTR ;装载GDTR
cli ;关中断
mov eax,cr0
or al,1
mov cr0,eax
JUMP16 <TempCode_Sel>,<OFFSET Virtual>
Real: DisableA20
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
InitGDT PROC
push ds
mov ax,GDTSeg
mov ds,ax
mov cx,GDNum
mov si,OFFSET EFFGDT
InitG: mov ax,[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR [si].BaseL,ax
mov BYTE PTR [si].BaseM,dl
mov BYTE PTR [si].BaseH,dh
add si,SIZE Desc
loop InitG
pop ds
mov bx,16
mov ax,GDTSeg
mul bx
mov WORD PTR VGDTR.Base,ax
mov WORD PTR VGDTR.Base+2,dx
ret
InitGDT ENDP
;----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
2.关于实例十的说明
上述演示程序的许多内容与其它实例相同,下面仅就演示分页管理机制方面的内容作些说明:
(1)部分演示代码的移动
为了充分说明分页机制所实现的线性地址到物理地址的转换,在初始化时把部分演示代码移动到预定的内存区域。预定的内存区域从00303000H开始,即页码为00303H的物理页。该部分演示代码的功能是显示指定的字符串。在进入保护模式后做此初始化工作的原因是预定的内存区域在扩展内存中,注意初始化时还没有启用分页机制。
(2)页映射表的初始化
页目录表安排在页码为00200H的物理页中,页表0安排在页码为00202H的物理页中,页表1安排在页码为00201H的物理页中。演示程序涉及的线性地址空间不超过007FFFFFH,所以只使用两张页表,为此页目录表中的其它项被置为无效(P=0)。
页表0把线性地址空间中的00000000H—003FFFFFH映射到物理地址空间中。实例在初始化页表0时,使该线性地址空间直接映射到相同地址的物理地址空间,除线性地址空间中页码为000B8H和000F0H这两页以外。000B8H页被映射到页码为00301H的物理页,而000F0H页被映射到页码为000B8H的物理页。
页表1把线性地址空间中的00400000H—007FFFFFH映射到物理地址空间中。实例在初始化页表1时,似乎使该线性地址空间直接映射到相同地址的物理地址空间,但是处理对应线性地址空间中00402H的表项被另外设置外,其它表项中的P位为0,也即表示对应物理页不存在。初始化后,页表1的第2项把线性地址空间中的00402H页映射到页码为00303H的物理页,也就是存放部分演示代码的指定内存区域。
(3)启动分页管理机制
在建立好页映射表后,启用分页机制所要做的操作是简单的,只要把控制寄存器CR0中的最高位,也就是PG位置1。具体指令如下:
mov eax,cr0
or eax,80000000h
mov cr0,eax
jmp SHORT PageE
PageE: ...
在启用分页机制前,线性地址就是物理地址;在启用分页机制后,线性地址要通过分页机制的转换,才成为物理地址。尽管使用一条转移指令,可清除预取队列,但随后在取指令时使用的线性地址就要经过分页机制转换才成为物理地址。为了顺利过渡,在启用分页机制之后的过渡代码段,仍要维持线性地址等同于物理地址。为了作到这一点,在建立也映射表时,必须使实现过渡的代码所在的线性地址空间页映射到具有相同地址的物理地址空间页。实例中页表0就做到了这一点。
(4)关闭分页管理机制
只要把控制寄存器CR0中的PG位清0,便关闭了分页机制。在这一过渡阶段,也要保持地址转换前后的一致。
(5)地址转换的演示
在启用分页机制之后,就转移到位于线性地址空间中00402000H处开始的代码,该部分代码的功能是显示提示信息"age is OK!"。实际上这部分代码存放在从物理地址00303000H开始的物理内存区域中,是在初始化时被移到此区域的。
在显示提示信息时,要把显示的ASCII字符和显示属性填到线性地址空间中000F0000H开始的区域中,而不是000B8000H开始的区域。从初始化时建立的映射表可见,线性地址空间中的000F0H页,被映射到物理地址空间中的000B8H页。所以,向线性地址空间中的000F0H页写,实际上是向物理地址空间中的000B8H页写,也就是真正显示。(6)页级保护的说明在进入保护模式之后,特权级一直是0,所以,无论系统级和用户级页,无论只能读/执行,还是读/执行/写,总是可进行各种形式的访问。
参考资料 | 书 名 | 出 版 社 | 作 者 | 《保护方式下的80386及其编程》 | 清华大学出版社 | 周明德主编 | 《80X86汇编语言程序设计教程》 | 清华大学出版社 | 扬季文主编 |
|
|