《80X86汇编语言程序设计教程》二十三 分页管理机制实例

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《80X86汇编语言程序设计教程》二十三 分页管理机制实例相关的知识,希望对你有一定的参考价值。

1、  理论知识参考"《80X86汇编语言程序设计教程》二十二 分页管理机制与虚拟8086模式"。演示分页机制实例:初始化页目录表和部分页表;启用分页管理机制;关闭分页管理机制等。逻辑功能:在屏幕上显示一条表示已启用分页管理机制的提示信息。大体步骤是:在实模式下拷贝显示串程序的代码到预定义区域,转保护模式,初始化页目录和2个页表,开启分页机制,转入预定义区执行显示代码,然后关闭分页机制,重新回到实模式,程序终止。

 

 

2、  源代码

  “386scd.asm”不再贴上来。参考"《80X86汇编语言程序设计教程》十三 任务内无特权级变换转移实例",演示代码如下:

 

  1 ;DosTest.Asm
  2 ;演示分页机制实例:初始化页目录表和部分页表;启用分页管理机制;关闭分页管理机制等。
  3 ;逻辑功能:在屏幕上显示一条表示已启用分页管理机制的提示信息
  4 
  5 
  6     include    386scd.asm                    ;文件386.scd含有有关结构、宏指令和符号常量定义
  7     .386p
  8 
  9 ;------------------------------------------
 10 ;常量值
 11 PL         =    001B         ;存在属性位P值
 12 RWR        =    000B         ;R/W属性位值,读/执行
 13 RWW        =    010B         ;R/W属性位值,读/写/执行
 14 USS        =    000B         ;U/S属性位值,系统级
 15 USU        =    100B         ;U/S属性位值,用户级
 16 ;
 17 PDT_AD     =    00200000h    ;页目录表所在物理页地址
 18 PT0_AD     =    00202000h    ;页表0所在物理页的地址
 19 PT1_AD     =    00201000h    ;页表1所在物理页的地址
 20 ;
 21 PhVB_AD    =    000b8000h    ;对应视频缓冲区物理地址
 22 LoVB_AD    =    000f0000h    ;程序使用的视频缓冲区逻辑地址
 23 MpVB_AD    =    00301000h    ;线性地址0b8000h所映射的物理地址
 24 PhSC_AD    =    00303000h    ;部分演示代码所在内存的物理地址
 25 LoSC_AD    =    00402000h    ;部分演示代码的逻辑地址
 26 
 27 
 28 ;------------------------------------------
 29 ;全局描述符表GDT
 30 GDTSeg    segment    para    use16
 31     GDT        label    byte
 32     ;空描述符
 33     dummy    DESCRIPTOR<>
 34     
 35     ;规范数据段描述符,存在的可读写数据段
 36     Normal     DESCRIPTOR<0ffffh,0,0,ATDW,0>
 37     Normal_Sel = Normal - GDT
 38     
 39     ;页目录表所在段描述符(在保护方式下初始化用)
 40     PDTable DESCRIPTOR<0fffh,PDT_AD and 0ffffh,PDT_AD shr 16,ATDW,0>
 41     PDT_Sel = PDTable - GDT
 42     
 43     ;页表0所在段描述符(在保护方式下初始化用)
 44     PTable0 DESCRIPTOR<0fffh,PT0_AD and 0ffffh,PT0_AD shr 16,ATDW,0>
 45     PT0_Sel = PTable0 - GDT
 46     
 47     ;页表1所在段描述符(在保护方式下初始化用)
 48     PTable1 DESCRIPTOR<0fffh,PT1_AD and 0ffffh,PT1_AD shr 16,ATDW,0>
 49     PT1_Sel = PTable1 - GDT
 50     
 51     ;逻辑上的显示缓冲区所在段描述符
 52     LoVideoB     DESCRIPTOR<3999,LoVB_AD and 0ffffh,LoVB_AD shr 16,ATDW,0>
 53     LoVideoB_Sel = LoVideoB - GDT
 54     
 55     ;逻辑上的部分演示代码所在段描述符
 56     LoCode     DESCRIPTOR<SCodeLen - 1,LoSC_AD and 0ffffh,LoSC_AD shr 16,ATCE,0>
 57     LoCode_Sel = LoCode - GDT
 58     
 59     ;预定内存区域(用于部分演示代码)所在段的描述符
 60     TPSCode     DESCRIPTOR<SCodeLen - 1,PhSC_AD and 0ffffh,PhSC_AD shr 16,ATDW,>
 61     TPSCode_Sel = TPSCode - GDT
 62     
 63     ;以下描述符需要动态初始化
 64     EFFGDT    label    byte
 65     
 66     ;临时代码段描述符(16位段,DPL = 0,RPL = 0)
 67     TempCode     DESCRIPTOR<0ffffh,TempCodeSeg,,ATCE,>
 68     TempCode_Sel = TempCode - GDT
 69     
 70     ;演示任务代码段描述符
 71     DemoCode     DESCRIPTOR<DemoCodeLen - 1,DemoCodeSeg,,ATCE,>
 72     DemoCode_Sel = DemoCode - GDT
 73     
 74     ;演示任务数据段描述符
 75     DemoData     DESCRIPTOR<DemoDataLen - 1,DemoDataSeg,,ATDW,>
 76     DemoData_Sel = DemoData - GDT
 77     
 78     ;在初始化时要移动的代码段描述符(移动时作为只读数据对待)
 79     SCode     DESCRIPTOR<SCodeLen - 1,SCodeSeg,,ATDR,>
 80     SCode_Sel = SCode - GDT
 81     
 82     ;GDT中需要初始化基地址的描述符个数
 83     GDTNum = ($ - EFFGDT)/(size DESCRIPTOR)
 84     
 85     ;GDT段长度
 86     GDTLen = $ - GDT
 87 GDTSeg    ends
 88 
 89 ;------------------------------------------
 90 ;这部分代码在初始化时被复制到预定内存区域
 91 ;其功能是在屏幕上显示提示信息
 92 SCodeSeg    segment    para    use16
 93     assume    cs:SCodeSeg,ds:DemoDataSeg
 94 SBegin:
 95     mov        al,LoVideoB_Sel
 96     mov        es,ax
 97     mov        di,0
 98     mov        ah,17h
 99     mov        cx,MessLen
100 s1:
101     lodsb
102     stosw
103     loop       s1
104     JUMP16     DemoCode_Sel,<offset Demo3>
105     MLen = $ - SBegin
106     SCodeLen = $ - SCodeSeg
107 SCodeSeg    ends
108 
109 ;------------------------------------------
110 ;演示任务数据段
111 DemoDataSeg    segment    para    use16
112     Mess    db    Page is ok!
113     MessLen = $ - Mess
114     DemoDataLen = $ - DemoDataSeg
115 DemoDataSeg    ends
116 
117 ;------------------------------------------
118 ;演示任务的代码段
119 DemoCodeSeg    segment    para    use16
120     assume    cs:DemoCodeSeg
121 DemoBegin:
122     ;初始化页目录表,保护模式未开启分页机制情况下
123     ;先把全部表项置为无效
124     mov        ax,PDT_Sel        
125     mov        es,ax
126     xor        di,di
127     mov        cx,1024           ;1024个表项
128     xor        eax,eax            
129     rep        stosd             ;每项为4B
130     ;再置表项0和表项1
131     ;用户级可读/写/执行存在页            
132     mov        dword ptr es:[0],PT0_AD or (USU + RWW + PL)
133     mov        dword ptr es:[4],PT1_AD or (USU + RWW + PL)
134     ;
135     mov        ax,PT0_Sel        ;初始化页表0
136     mov        es,ax
137     xor        di,di             ;无效表项值
138     mov        cx,1024
139     xor        eax,eax           ;物理地址从0开始
140     ;系统级可读/写/执行存在页
141     or         eax,USS + RWW + PL
142     ;先全部置成直接对应等地址的物理页
143 Demo1:
144     stosd                    
145     add        eax,1000h         ;物理地址增加1000h        
146     loop       Demo1
147     ;再特别设置两个表项
148     ;线性地址PhVB_AD(对应视频缓冲区物理地址)转换为物理地址Mp_VB            
149     mov        di,(PhVB_AD shr 12) * 4
150     mov        dword ptr es:[di],MpVB_AD + USS + RWW + PL
151     ;线性地址LoVB_AD转换为物理地址PhVB_AD(对应视频缓冲区物理地址)    
152     mov        di,(LoVB_AD shr 12) * 4
153     mov        dword ptr es:[di],PhVB_AD + USU + RWR + PL
154     ;
155     mov        ax,PT1_Sel        ;初始化页表1
156     mov        es,ax
157     xor        di,di             ;无效表项值
158     mov        cx,1024
159     mov        eax,400000h       ;物理地址从400000h开始
160 Demo2:
161     stosd                        ;先把全部表项置成无效
162     add        eax,1000h
163     loop       Demo2
164     ;再特别设置1项
165     ;线性地址LoSC_AD转换为物理地址PhSC_AD(对应视部分演示代码物理地址)            
166     mov        di,((LoSC_AD shr 12) and 3ffh) * 4
167     mov        dword ptr es:[di],PhSC_AD + USU + RWR + PL
168     ;
169     ;将页目录表所在物理页地址装入CR3
170     mov        eax,PDT_AD
171     mov        cr3,eax
172     ;启用分页机制
173     mov        eax,cr0
174     or         eax,80000000h
175     mov        cr0,eax
176     ;此部分代码段线性地址等于物理地址            
177     jmp        short PageE
178 PageE:
179     mov        ax,DemoData_Sel
180     mov        ds,ax
181     mov        si,offset Mess
182     ;转位于较大线性地址处的代码执行
183     JUMP16     LoCode_Sel,<offset SBegin>
184 Demo3:
185     mov        eax,cr0
186     and        eax,7fffffffh    ;关闭分页机制
187     mov        cr0,eax
188     jmp        short PageD
189 PageD:
190     JUMP16    TempCode_Sel,ToDOS
191     DemoCodeLen = $ - DemoCodeSeg
192 DemoCodeSeg    ends
193 
194 ;------------------------------------------
195 ;临时代码段
196 TempCodeSeg    segment    para    use16
197     assume    cs:TempCodeSeg
198 Virtual:
199     ;在保护模式下
200     cld                        ;为演示在启用分页机制后执行位于
201     mov        ax,SCode_Sel    ;较高线性地址空间中的代码作准备
202     mov        ds,ax
203     mov        ax,TPSCode_Sel
204     mov        es,ax
205     mov        si,offset SBegin
206     mov        di,si
207     mov        cx,MLen         ;把部分演示代码复制到预定的内存
208     rep        movsb
209     ;转演示代码段
210     JUMP16     DemoCode_Sel,DemoBegin
211 ToDOS:
212     ;演示结束后准备返回实模式
213     mov        ax,Normal_Sel
214     mov        ds,ax
215     mov        es,ax
216     mov        eax,cr0
217     and        eax,0fffffffeh
218     mov        cr0,eax
219     JUMP16     <seg Real>,<offset Real>
220 TempCodeSeg    ends
221 
222 ;------------------------------------------
223 ;实模式下的初始化代码和数据
224 RCodeSeg    segment    para    use16
225     VGDTR     PDESC<GDTLen - 1,>
226     assume    cs:RCodeSeg,ds:RCodeSeg
227 start:
228     push      cs
229     pop        ds
230     cld
231     ;初始化GDT
232     call      INIT_GDT
233     call      ENABLEA20
234     ;装载GDTR和切换到保护模式
235     lgdt      fword ptr VGDTR
236     cli
237     mov        eax,cr0
238     or         eax,1
239     mov        cr0,eax
240     JUMP16     <TempCode_Sel>,<offset Virtual>
241 Real:
242     ;又回到实模式
243     call      DISABLEA20
244     sti
245     mov        ax,4c00h
246     int        21h
247 ;------------------------------------------
248 ;初始化全局描述符表的子程序
249 ;(1)把定义时预置的段值转换成32位段基地址并置入描述符内相应字段
250 ;(2)初始化为GDTR准备的伪描述符
251 INIT_GDT    proc    near
252     push      ds
253     mov        ax,GDTSeg
254     mov        ds,ax
255     mov        cx,GDTNum                    ;初始化描述符的个数
256     mov        si,offset EFFGDT             ;开始偏移
257     assume     si:ptr DESCRIPTOR
258 INITG:
259     mov        ax,[si].BaseL                ;取出预置的段值
260     movzx      eax,ax                       ;扩展到32位
261     shl        eax,4
262     shld       edx,eax,16                   ;分解到2个16位寄存器
263     mov        [si].BaseL,ax                ;置入描述符相应字段
264     mov        [si].BaseM,dl
265     mov        [si].BaseH,dh
266     add        si,size DESCRIPTOR           ;调整到下一个描述符
267     loop      INITG
268     assume     si:nothing
269     pop        ds
270     ;
271     mov        bx,16                        ;初始化为GDTR准备的伪描述符
272     mov        ax,GDTSeg
273     mul        bx
274     mov        word ptr VGDTR.Base,ax
275     mov        word ptr VGDTR.Base + 2,dx
276     ret
277 INIT_GDT endp
278 
279 ;打开地址线A20号
280 ENABLEA20    proc
281     push      ax
282     in        al,92h
283     or        al,2
284     out       92h,al
285     pop       ax
286     ret
287 ENABLEA20 endp
288 
289 ;关闭地址线A20号    
290 DISABLEA20    proc
291     push      ax
292     in        al,92h
293     and       al,0fdh
294     out       92h,al
295     pop       ax
296     ret
297 DISABLEA20 endp    
298 
299 RCodeSeg    ends
300     end        start    

 

 

3、  关于源代码的几处说明

  1)  计算段长度时保持惯有错误

  2)  USS和USU两个常量定义的值刚好写反

  3)  开关A20地址线之前在模式切换读取高端内存时使用过,之后一直没用,因为代码全部使用的低端内存

 

 

4、  测试效果

技术分享图片 

 

 

5、   测试说明

  1)  整体说明

    页目录被置物理页码00200H,页表有2张,页表0被置物理页码00202H而页表1被置物理页码00201。页目录初始化时所有页表项全部置为无效,然后初始化索引为0和1的页表项,使分别对应页表0和页表1。在页表0中存有线性地址00000000H~003FFFFFH到物理地址的映射关系,而页表1中存有线性地址00400000H~007FFFFFH到物理地址的映射关系。页表0和页表1初始化时,所有页表项对应线性地址被映射为相同的物理地址,页表1有两个页表项例外,及000B8H线性地址页码映射的不再是物理页码000B8H,为了测试逻辑地址与线性地址的映射,特意将线性地址000B8H映射到了物理页码00301H,而物理地址页码000B8H使用逻辑页码000F0H访问,它就是实际访问视频输出缓存区物理地址(0B8000H)时所使用到的线性地址页码。页表1同样如此,只有一个页表项意外,就是线性地址页码00402H的页,它被映射到了物理地址页00303H,这个页就是部分演示代码所在内存的物理页,在跳转切入来显示信息时,使用的线性地址页就是00402H,它实际上访问的物理页是00303H。

  2)  部分演示代码的移动

    为了说明地址转换机制,特意指定了一个物理内存区域,在映射表中分配它的映射关系,从而在代码中实现使用逻辑地址对它成功进行访问。预定义区域在00303000H(物理地址)开始的内存中,由于使用了高端内存,所以须开启地址线A20,并在初始化时移动代码到预定义区域,这里采用了别名技术。

  3)  映射表的初始化

    映射表初始化必须在保护模式下,开启分页机制之前进行。这里使用了1张目录表,2张页表,共12KB。在初始化时,地址计算“mov         di,(PhVB_AD shr 12) * 4”中PhVB_AD右移12位得到线性地址页的页码,右移以后取低10位作为页表项索引,乘以4得到页表字节偏移。这里的页码为000b8H,高10位为0,所以没有做取低10位的操作,直接乘以4。而“mov      di,((LoSC_AD shr 12) and 3ffh) * 4”做了取低10位的操作。

  4)  切换分页管理机制

    将CR0的PG位(31位)置1或清0来实现切换,须注意,在开启分页管理机制时,线性地址和物理地址没有必然联系,而在关闭分页管理机制时,线性地址就等于物理地址。在切换前后,一个连续的代码段在不同的机制下要求被联系执行,那么这部分代码段必须得保证在保护模式下线性地址也是物理地址。当然,这里其实采用像模式切换时那样的转移来清预取指令同样是可行的。

  5)  页级保护的演示

    页目录表中对页表0和页表1的表项使用了“用户级可读/写/执行存在”页属性,在页表0中,对线性地址空间页PhVB_AD使用了“系统级可读/写/执行存在”页属性,所以组合属性是“系统级可读/写/执行存在”。由于整个程序在CPL = 0上运行,属于系统级,这些属性限制实际上是没有作用的,但是采用CPL = 3的代码去访问这个页,必然造成页故障。

 

以上是关于《80X86汇编语言程序设计教程》二十三 分页管理机制实例的主要内容,如果未能解决你的问题,请参考以下文章

《80X86汇编语言程序设计教程》二十五 结语(读后感:这本书怎么样)

《80X86汇编语言程序设计教程》十二 任务状态段控制门和控制转移

《80X86汇编语言程序设计教程》十五 任务切换实例

操作系统学习80x86保护模式内存管理

《80X86汇编语言程序设计教程》十 实模式与保护模式的切换实例

《80X86汇编语言程序设计教程》十九 操作系统类指令与输入输出保护