pmtest6.asm代码详细解释,对分页的形成过程有详细的解释

; ==========================================
; pmtest6.asm
; 编译方法:nasm pmtest6.asm -o pmtest6.com
; ==========================================

%include "pm.inc" ; 常量, 宏, 以及一些说明

PageDirBase  equ 200000h ; 页目录开始地址: 2M
PageTblBase  equ 201000h ; 页表开始地址:  2M + 4K

org 0100h
 jmp LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,       段界限     , 属性
LABEL_GDT:  Descriptor        0,                 0, 0       ; 空描述符
LABEL_DESC_NORMAL: Descriptor        0,            0ffffh, DA_DRW  ; Normal 描述符
LABEL_DESC_PAGE_DIR: Descriptor   PageDirBase,              4095, DA_DRW    ; Page Directory, 4K,PageDirBase=2000000h
LABEL_DESC_PAGE_TBL: Descriptor   PageTblBase,              1023, DA_DRW  | DA_LIMIT_4K  ; Page Tables, 4M,PageTblBase=201000h
LABEL_DESC_CODE32: Descriptor        0,  SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor        0,            0ffffh, DA_C  ; 非一致代码段, 16
LABEL_DESC_DATA: Descriptor        0, DataLen - 1, DA_DRW  ; Data
LABEL_DESC_STACK: Descriptor        0,        TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位
LABEL_DESC_VIDEO: Descriptor  0B8000h,            0ffffh, DA_DRW  ; 显存首地址
; GDT 结束

GdtLen  equ $ - LABEL_GDT ; GDT长度
GdtPtr  dw GdtLen - 1 ; GDT界限
  dd 0  ; GDT基地址

; GDT 选择子
SelectorNormal  equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorPageDir  equ LABEL_DESC_PAGE_DIR - LABEL_GDT
SelectorPageTbl  equ LABEL_DESC_PAGE_TBL - LABEL_GDT
SelectorCode32  equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16  equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData  equ LABEL_DESC_DATA  - LABEL_GDT
SelectorStack  equ LABEL_DESC_STACK - LABEL_GDT
SelectorVideo  equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1]  ; 数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
; 字符串
PMMessage:  db "In Protect Mode now. ^-^", 0 ; 进入保护模式后显示此字符串
OffsetPMMessage  equ PMMessage - $$
DataLen   equ $ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
 times 512 db 0

TopOfStack equ $ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, 0100h

 mov [LABEL_GO_BACK_TO_REAL+3], ax
 mov [SPValueInRealMode], sp

 ; 初始化 16 位代码段描述符
 mov ax, cs
 movzx eax, ax
 shl eax, 4
 add eax, LABEL_SEG_CODE16
 mov word [LABEL_DESC_CODE16 + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_CODE16 + 4], al
 mov byte [LABEL_DESC_CODE16 + 7], ah

 ; 初始化 32 位代码段描述符
 xor eax, eax
 mov ax, cs
 shl eax, 4
 add eax, LABEL_SEG_CODE32
 mov word [LABEL_DESC_CODE32 + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_CODE32 + 4], al
 mov byte [LABEL_DESC_CODE32 + 7], ah

 ; 初始化数据段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_DATA
 mov word [LABEL_DESC_DATA + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_DATA + 4], al
 mov byte [LABEL_DESC_DATA + 7], ah

 ; 初始化堆栈段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_STACK
 mov word [LABEL_DESC_STACK + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_STACK + 4], al
 mov byte [LABEL_DESC_STACK + 7], ah

 ; 为加载 GDTR 作准备
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_GDT  ; eax <- gdt 基地址
 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址

 ; 加载 GDTR
 lgdt [GdtPtr]

 ; 关中断
 cli

 ; 打开地址线A20
 in al, 92h
 or al, 00000010b
 out 92h, al

 ; 准备切换到保护模式
 mov eax, cr0
 or eax, 1
 mov cr0, eax

 ; 真正进入保护模式
 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:  ; 从保护模式跳回到实模式就到了这里
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax

 mov sp, [SPValueInRealMode]

 in al, 92h  ; ┓
 and al, 11111101b ; ┣ 关闭 A20 地址线
 out 92h, al  ; ┛

 sti   ; 开中断

 mov ax, 4c00h ; ┓
 int 21h  ; ┛回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]

LABEL_SEG_CODE32:
 call SetupPaging                                    //启动分页机制

 mov ax, SelectorData
 mov ds, ax   ; 数据段选择子
 mov ax, SelectorVideo
 mov gs, ax   ; 视频段选择子

 mov ax, SelectorStack
 mov ss, ax   ; 堆栈段选择子

 mov esp, TopOfStack


 ; 下面显示一个字符串
 mov ah, 0Ch   ; 0000: 黑底    1100: 红字
 xor esi, esi
 xor edi, edi
 mov esi, OffsetPMMessage ; 源数据偏移
 mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
 cld
.1:
 lodsb
 test al, al
 jz .2
 mov [gs:edi], ax
 add edi, 2
 jmp .1
.2: ; 显示完毕

 ; 到此停止
 jmp SelectorCode16:0

; 启动分页机制 --------------------------------------------------------------
SetupPaging:
 ; 为简化处理, 所有线性地址对应相等的物理地址.

 ; 首先初始化页目录
 mov ax, SelectorPageDir ; 此段首地址为 PageDirBase    //PageDirBase=200000h
 mov es, ax                                                              //es指向PageDirBase,页目录基址
 mov ecx, 1024  ; 共 1K 个表项
 xor edi, edi
 xor eax, eax
 mov eax, PageTblBase | PG_P  | PG_USU | PG_RWW     //PageTblBase=201000h,将PageTblBase加属性的结果赋给eax。eax值通过stosd放到
.1:                                                                            // es:edi指向的地址处,即页目录基址处。
 stosd                                                                       //stosd的作用是将累加器EAX内容存储到由ES:E(DI)寻址的内存地址,d表示是32位
 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.      //eax中的PageTblBase的基址加4096,并通过循环不断加4096.
 loop .1

 ; 再初始化所有页表 (1K 个, 4M 内存空间)
 mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase 
 mov es, ax
 mov ecx, 1024 * 1024 ; 共 1M 个页表项, 也即有 1M 个页
 xor edi, edi
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add eax, 4096  ; 每一页指向 4K 的空间
 loop .2

 mov eax, PageDirBase                                               //将页目录基址赋给eax,再由eax赋给cr3
 mov cr3, eax                                                           //把PageDirBase页目录基地址赋给cr3
 mov eax, cr0
 or eax, 80000000h
 mov cr0, eax
 jmp short .3
.3:
 nop

 ret
; 分页机制启动完毕 ----------------------------------------------------------

SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
 ; 跳回实模式:
 mov ax, SelectorNormal
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov gs, ax
 mov ss, ax

 mov eax, cr0
 and al, 11111110b
 mov cr0, eax                          // PE=0, PG=0关闭分页,进入实模式

LABEL_GO_BACK_TO_REAL:
 jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

----------------------------------------------------------------------------------------

启动分页部分程序的结果为:

   1、 在物理内存页目录基址200000H处 存放了1024个PDE的结合

  • PDE1(PageTblBase 201000H+0000H )---->这是一个PDE的一部分,因为PTE的基址就放在一个PDE
  • PDE2(                   201000H+1000H)
  • PDE3(                   201000H+2000H)
  • 。。。。。。。。
  • PDE1024(            201000H+3FF000H)以上一共1024个PDE ,一个PDE大小为4字节(4Bety),一共为4K,这样所有PDE就初始化好了。

     页目录构建好之后,正好到物理内存的201000H处,这也是第一个页表的基址  

     2、在物理内存页表基址201000H处 存放了1024*1024个PTE

  • PTE1(PageBase  0000H)     这是第一个页表基址201000H所对应的1024个PTE ,存放一个页表的物理的大小是4K。4Byte*1024=4K字节。
  • PTE2(               0000H+1000H)   每个PTE对应的页的真实物理大小为4K,范围是从0000H---0FFFH,所以偏移地址要加1000H,一个PTE本身是4个字节,4Bety
  • PTE3(               0000H+2000H)
  • PTE2(               0000H+3000H)
  • PTE2(               0000H+4000H)
  •  ........
  • PTE1024(          0000H+3FF000H) 以上这些PTE一共表示了4M大小的的真实物理内存         0000H~~3FF000H正好是4M

    ----------------------------------------------------------

  •  PTE1025(          0000H+400000H) 这是第二个页表基址201000H+1000H所对应的1024个PTE,大小为4k
  •  PTE1026(          0000H+401000H)
  •  PTE1027(          0000H+402000H)
  •  .......
  •  PTE2048(            0000H+7FF000H)  

   ----------------------------------------------------------

———————————————————————————————

  • PTE1024*1024-1023(0000H+FFC00000H)这是第1024个页表基址201000H+3FF000H所对应的1024个PTE,大小为4K
  • .......
  • PTE1024*1024          (0000H+FFFFF000H) // FFC00+3FF=FFFFF

    最后一个page base address FFFFF000H对应一个page ,大小为0~FFFh,加上基址正好是FFFFFFFFH,所以一共4G物理内存都被PTE分配了

   ----------------------------------------------------------

以上一共有1024*1024个PTE,一个PTE为4bety,一共是4M。

页表建构好之后201000+4M=601000

所以PDE,PTE都建构好之后,它的地址为200000H到601000H。

posted on 2011-04-26 13:51  wanghj_dz  阅读(1034)  评论(0编辑  收藏  举报

导航