pmtest8讲解
稍微写了下注释
1 ; ==========================================
2 ; pmtest8.asm
3 ; 编译方法:nasm pmtest8.asm -o pmtest8.com
4 ; ==========================================
5
6 %include "pm.inc" ; 常量, 宏, 以及一些说明
7
8 PageDirBase0 equ 200000h ; 页目录开始地址: 2M
9 PageTblBase0 equ 201000h ; 页表开始地址: 2M + 4K
10 PageDirBase1 equ 210000h ; 页目录开始地址: 2M + 64K
11 PageTblBase1 equ 211000h ; 页表开始地址: 2M + 64K + 4K
12
13 LinearAddrDemo equ 00401000h
14 ProcFoo equ 00401000h
15 ProcBar equ 00501000h
16
17 ProcPagingDemo equ 00301000h
18
19 org 0100h
20 jmp LABEL_BEGIN
21
22 [SECTION .gdt]
23 ; GDT
24 ; 段基址, 段界限 , 属性
25 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
26 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
27 LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR | DA_32 | DA_LIMIT_4K; 0 ~ 4G,可执行可读代码段
28 LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW | DA_LIMIT_4K ; 0 ~ 4G,可读写数据段
29 LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_CR | DA_32 ; 非一致代码段, 32
30 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16
31 LABEL_DESC_DATA: Descriptor 0, DataLen - 1, DA_DRW ; Data
32 LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA | DA_32 ; Stack, 32 位
33 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
34 ; GDT 结束
35
36 GdtLen equ $ - LABEL_GDT ; GDT长度
37 GdtPtr dw GdtLen - 1 ; GDT界限
38 dd 0 ; GDT基地址
39
40 ; GDT 选择子
41 SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
42 SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
43 SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
44 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
45 SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
46 SelectorData equ LABEL_DESC_DATA - LABEL_GDT
47 SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
48 SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
49 ; END of [SECTION .gdt]
50
51 [SECTION .data1] ; 数据段
52 ALIGN 32
53 [BITS 32]
54 LABEL_DATA:
55 ; 实模式下使用这些符号
56 ; 字符串
57 _szPMMessage: db "In Protect Mode now. ^-^", 0Ah, 0Ah, 0 ; 进入保护模式后显示此字符串
58 _szMemChkTitle: db "BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0 ; 进入保护模式后显示此字符串
59 _szRAMSize db "RAM size:", 0
60 _szReturn db 0Ah, 0
61 ; 变量
62 _wSPValueInRealMode dw 0
63 _dwMCRNumber: dd 0 ; Memory Check Result
64 _dwDispPos: dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。
65 _dwMemSize: dd 0
66 _ARDStruct: ; Address Range Descriptor Structure
67 _dwBaseAddrLow: dd 0
68 _dwBaseAddrHigh: dd 0
69 _dwLengthLow: dd 0
70 _dwLengthHigh: dd 0
71 _dwType: dd 0
72 _PageTableNumber dd 0
73
74 _MemChkBuf: times 256 db 0
75
76 ; 保护模式下使用这些符号
77 szPMMessage equ _szPMMessage - $$
78 szMemChkTitle equ _szMemChkTitle - $$
79 szRAMSize equ _szRAMSize - $$
80 szReturn equ _szReturn - $$
81 dwDispPos equ _dwDispPos - $$
82 dwMemSize equ _dwMemSize - $$
83 dwMCRNumber equ _dwMCRNumber - $$
84 ARDStruct equ _ARDStruct - $$
85 dwBaseAddrLow equ _dwBaseAddrLow - $$
86 dwBaseAddrHigh equ _dwBaseAddrHigh - $$
87 dwLengthLow equ _dwLengthLow - $$
88 dwLengthHigh equ _dwLengthHigh - $$
89 dwType equ _dwType - $$
90 MemChkBuf equ _MemChkBuf - $$
91 PageTableNumber equ _PageTableNumber- $$
92
93 DataLen equ $ - LABEL_DATA
94 ; END of [SECTION .data1]
95
96
97 ; 全局堆栈段
98 [SECTION .gs]
99 ALIGN 32
100 [BITS 32]
101 LABEL_STACK:
102 times 512 db 0
103
104 TopOfStack equ $ - LABEL_STACK - 1
105
106 ; END of [SECTION .gs]
107
108
109 [SECTION .s16]
110 [BITS 16]
111 LABEL_BEGIN:
112 mov ax, cs
113 mov ds, ax
114 mov es, ax
115 mov ss, ax
116 mov sp, 0100h
117
118 mov [LABEL_GO_BACK_TO_REAL+3], ax
119 mov [_wSPValueInRealMode], sp
120
121 ; 得到内存数
122 mov ebx, 0
123 mov di, _MemChkBuf
124 .loop:
125 mov eax, 0E820h
126 mov ecx, 20
127 mov edx, 0534D4150h
128 int 15h
129 jc LABEL_MEM_CHK_FAIL
130 add di, 20
131 inc dword [_dwMCRNumber]
132 cmp ebx, 0
133 jne .loop
134 jmp LABEL_MEM_CHK_OK
135 LABEL_MEM_CHK_FAIL:
136 mov dword [_dwMCRNumber], 0
137 LABEL_MEM_CHK_OK:
138
139 ; 初始化 16 位代码段描述符
140 mov ax, cs
141 movzx eax, ax
142 shl eax, 4
143 add eax, LABEL_SEG_CODE16
144 mov word [LABEL_DESC_CODE16 + 2], ax
145 shr eax, 16
146 mov byte [LABEL_DESC_CODE16 + 4], al
147 mov byte [LABEL_DESC_CODE16 + 7], ah
148
149 ; 初始化 32 位代码段描述符
150 xor eax, eax
151 mov ax, cs
152 shl eax, 4
153 add eax, LABEL_SEG_CODE32
154 mov word [LABEL_DESC_CODE32 + 2], ax
155 shr eax, 16
156 mov byte [LABEL_DESC_CODE32 + 4], al
157 mov byte [LABEL_DESC_CODE32 + 7], ah
158
159 ; 初始化数据段描述符
160 xor eax, eax
161 mov ax, ds
162 shl eax, 4
163 add eax, LABEL_DATA
164 mov word [LABEL_DESC_DATA + 2], ax
165 shr eax, 16
166 mov byte [LABEL_DESC_DATA + 4], al
167 mov byte [LABEL_DESC_DATA + 7], ah
168
169 ; 初始化堆栈段描述符
170 xor eax, eax
171 mov ax, ds
172 shl eax, 4
173 add eax, LABEL_STACK
174 mov word [LABEL_DESC_STACK + 2], ax
175 shr eax, 16
176 mov byte [LABEL_DESC_STACK + 4], al
177 mov byte [LABEL_DESC_STACK + 7], ah
178
179 ; 为加载 GDTR 作准备
180 xor eax, eax
181 mov ax, ds
182 shl eax, 4
183 add eax, LABEL_GDT ; eax <- gdt 基地址
184 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
185
186 ; 加载 GDTR
187 lgdt [GdtPtr]
188
189 ; 关中断
190 cli
191
192 ; 打开地址线A20
193 in al, 92h
194 or al, 00000010b
195 out 92h, al
196
197 ; 准备切换到保护模式
198 mov eax, cr0
199 or eax, 1
200 mov cr0, eax
201
202 ; 真正进入保护模式
203 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
204
205 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
206
207 LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
208 mov ax, cs
209 mov ds, ax
210 mov es, ax
211 mov ss, ax
212
213 mov sp, [_wSPValueInRealMode]
214
215 in al, 92h ; ┓
216 and al, 11111101b ; ┣ 关闭 A20 地址线
217 out 92h, al ; ┛
218
219 sti ; 开中断
220
221 mov ax, 4c00h ; ┓
222 int 21h ; ┛回到 DOS
223 ; END of [SECTION .s16]
224
225
226 [SECTION .s32]; 32 位代码段. 由实模式跳入.
227 [BITS 32]
228
229 LABEL_SEG_CODE32:
230 mov ax, SelectorData
231 mov ds, ax ; 数据段选择子
232 mov es, ax
233 mov ax, SelectorVideo
234 mov gs, ax ; 视频段选择子
235
236 mov ax, SelectorStack
237 mov ss, ax ; 堆栈段选择子
238
239 mov esp, TopOfStack
240
241
242 ; 下面显示一个字符串
243 push szPMMessage
244 call DispStr
245 add esp, 4
246
247 push szMemChkTitle
248 call DispStr
249 add esp, 4
250
251 call DispMemSize ; 显示内存信息
252
253 call PagingDemo ; 演示改变页目录的效果
254
255 ; 到此停止
256 jmp SelectorCode16:0
257
258 ; 启动分页机制 --------------------------------------------------------------
259 SetupPaging:
260 ; 根据内存大小计算应初始化多少PDE以及多少页表
261 xor edx, edx
262 mov eax, [dwMemSize]
263 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
264 div ebx
265 mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
266 test edx, edx
267 jz .no_remainder
268 inc ecx ; 如果余数不为 0 就需增加一个页表
269 .no_remainder:
270 mov [PageTableNumber], ecx ; 暂存页表个数
271
272 ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.
273
274 ; 首先初始化页目录
275 mov ax, SelectorFlatRW
276 mov es, ax
277 mov edi, PageDirBase0 ; 此段首地址为 PageDirBase0
278 xor eax, eax
279 mov eax, PageTblBase0 | PG_P | PG_USU | PG_RWW
280 .1:
281 stosd
282 add eax, 4096 ; 为了简化, 所有页表在内存中是连续的,下一个页表的地址为下一个页目录项中的内容,eax中存放的是内容
283 loop .1
284
285 ; 再初始化所有页表
286 mov eax, [PageTableNumber] ; 页表个数
287 mov ebx, 1024 ; 每个页表 1024 个 PTE
288 mul ebx
289 mov ecx, eax ; PTE个数 = 页表个数 * 1024
290 mov edi, PageTblBase0 ; 此段首地址为 PageTblBase0
291 xor eax, eax
292 mov eax, PG_P | PG_USU | PG_RWW
293 .2:
294 stosd
295 add eax, 4096 ; 每一页指向 4K 的空间
296 loop .2
297
298 mov eax, PageDirBase0
299 mov cr3, eax ;cr3页目录基地址寄存器
300 mov eax, cr0
301 or eax, 80000000h
302 mov cr0, eax
303 jmp short .3
304 .3:
305 nop
306
307 ret
308 ; 分页机制启动完毕 ----------------------------------------------------------
309
310
311 ; 测试分页机制 --------------------------------------------------------------
312 PagingDemo:
313 mov ax, cs
314 mov ds, ax
315 mov ax, SelectorFlatRW
316 mov es, ax
317
318 push LenFoo
319 push OffsetFoo
320 push ProcFoo
321 call MemCpy
322 add esp, 12
323
324 push LenBar
325 push OffsetBar
326 push ProcBar
327 call MemCpy
328 add esp, 12
329
330 push LenPagingDemoAll
331 push OffsetPagingDemoProc
332 push ProcPagingDemo
333 call MemCpy
334 add esp, 12
335
336 mov ax, SelectorData
337 mov ds, ax ; 数据段选择子
338 mov es, ax
339
340 call SetupPaging ; 启动分页
341
342 call SelectorFlatC:ProcPagingDemo
343 call PSwitch ; 切换页目录,改变地址映射关系
344 call SelectorFlatC:ProcPagingDemo
345
346 ret
347 ; ---------------------------------------------------------------------------
348
349
350 ; 切换页表 ------------------------------------------------------------------
351 PSwitch:
352 ; 初始化页目录
353 mov ax, SelectorFlatRW
354 mov es, ax
355 mov edi, PageDirBase1 ; 此段首地址为 PageDirBase1
356 xor eax, eax
357 mov eax, PageTblBase1 | PG_P | PG_USU | PG_RWW
358 mov ecx, [PageTableNumber]
359 .1:
360 stosd
361 add eax, 4096 ; 为了简化, 所有页表在内存中是连续的.
362 loop .1
363
364 ; 再初始化所有页表
365 mov eax, [PageTableNumber] ; 页表个数
366 mov ebx, 1024 ; 每个页表 1024 个 PTE
367 mul ebx
368 mov ecx, eax ; PTE个数 = 页表个数 * 1024
369 mov edi, PageTblBase1 ; 此段首地址为 PageTblBase1
370 xor eax, eax
371 mov eax, PG_P | PG_USU | PG_RWW
372 .2:
373 stosd
374 add eax, 4096 ; 每一页指向 4K 的空间
375 loop .2
376
377 ; 在此假设内存是大于 8M 的
378 mov eax, LinearAddrDemo
379 shr eax, 22
380 mov ebx, 4096
381 mul ebx
382 mov ecx, eax
383 mov eax, LinearAddrDemo
384 shr eax, 12
385 and eax, 03FFh ; 1111111111b (10 bits)
386 mov ebx, 4
387 mul ebx
388 add eax, ecx
389 add eax, PageTblBase1
390 mov dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW
391
392 mov eax, PageDirBase1
393 mov cr3, eax
394 jmp short .3
395 .3:
396 nop
397
398 ret
399 ; ---------------------------------------------------------------------------
400
401
402 ; PagingDemoProc ------------------------------------------------------------
403 PagingDemoProc:
404 OffsetPagingDemoProc equ PagingDemoProc - $$
405 mov eax, LinearAddrDemo
406 call eax
407 retf
408 ; ---------------------------------------------------------------------------
409 LenPagingDemoAll equ $ - PagingDemoProc
410 ; ---------------------------------------------------------------------------
411
412
413 ; foo -----------------------------------------------------------------------
414 foo:
415 OffsetFoo equ foo - $$
416 mov ah, 0Ch ; 0000: 黑底 1100: 红字
417 mov al, 'F'
418 mov [gs:((80 * 17 + 0) * 2)], ax ; 屏幕第 17 行, 第 0 列。
419 mov al, 'o'
420 mov [gs:((80 * 17 + 1) * 2)], ax ; 屏幕第 17 行, 第 1 列。
421 mov [gs:((80 * 17 + 2) * 2)], ax ; 屏幕第 17 行, 第 2 列。
422 ret
423 LenFoo equ $ - foo
424 ; ---------------------------------------------------------------------------
425
426
427 ; bar -----------------------------------------------------------------------
428 bar:
429 OffsetBar equ bar - $$
430 mov ah, 0Ch ; 0000: 黑底 1100: 红字
431 mov al, 'B'
432 mov [gs:((80 * 18 + 0) * 2)], ax ; 屏幕第 18 行, 第 0 列。
433 mov al, 'a'
434 mov [gs:((80 * 18 + 1) * 2)], ax ; 屏幕第 18 行, 第 1 列。
435 mov al, 'r'
436 mov [gs:((80 * 18 + 2) * 2)], ax ; 屏幕第 18 行, 第 2 列。
437 ret
438 LenBar equ $ - bar
439 ; ---------------------------------------------------------------------------
440
441
442 ; 显示内存信息 --------------------------------------------------------------
443 DispMemSize:
444 push esi
445 push edi
446 push ecx
447
448 mov esi, MemChkBuf
449 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构
450 .loop: ;{
451 mov edx, 5 ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员,共5个成员
452 mov edi, ARDStruct ; { // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
453 .1: ;
454 push dword [esi] ;
455 call DispInt ; DispInt(MemChkBuf[j*4]); // 显示一个成员
456 pop eax ;
457 stosd ; ARDStruct[j*4] = MemChkBuf[j*4];
458 add esi, 4 ;
459 dec edx ;
460 cmp edx, 0 ;
461 jnz .1 ; }
462 call DispReturn ; printf("\n");
463 cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
464 jne .2 ; {
465 mov eax, [dwBaseAddrLow] ;
466 add eax, [dwLengthLow] ;
467 cmp eax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize)
468 jb .2 ;
469 mov [dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow;
470 .2: ; }
471 loop .loop ;}
472 ;
473 call DispReturn ;printf("\n");
474 push szRAMSize ;
475 call DispStr ;printf("RAM size:");
476 add esp, 4 ;
477 ;
478 push dword [dwMemSize] ;
479 call DispInt ;DispInt(MemSize);
480 add esp, 4 ;
481
482 pop ecx
483 pop edi
484 pop esi
485 ret
486 ; ---------------------------------------------------------------------------
487
488 %include "lib.inc" ; 库函数
489
490 SegCode32Len equ $ - LABEL_SEG_CODE32
491 ; END of [SECTION .s32]
492
493
494 ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
495 [SECTION .s16code]
496 ALIGN 32
497 [BITS 16]
498 LABEL_SEG_CODE16:
499 ; 跳回实模式:
500 mov ax, SelectorNormal
501 mov ds, ax
502 mov es, ax
503 mov fs, ax
504 mov gs, ax
505 mov ss, ax
506
507 mov eax, cr0
508 and al, 11111110b
509 mov cr0, eax
510
511 LABEL_GO_BACK_TO_REAL:
512 jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
513
514 Code16Len equ $ - LABEL_SEG_CODE16
515
516 ; END of [SECTION .s16code]
关于分页,有一点要注意,如果按照4M-->4k来初始化,则寻址的时候按照10位-->10位-->12位来寻址
过个春节,中间隔了好多天