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位来寻址

过个春节,中间隔了好多天

原文地址:https://www.cnblogs.com/cdwodm/p/2910081.html