《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汇编语言程序设计教程》十二 任务状态段控制门和控制转移