分页机制代码详细注释

分页的代码红色标记,其它的在前面“保护模式跳转中”已经解释过。

%include "pm.inc"

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

[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW
LABEL_DESC_CODE32: Descriptor 0,SegCode32Len-1, DA_C + DA_32
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW

LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW;Page Directory
LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;Page Tables
;此时G位置,界限粒度为4K,所以1023表示0-1023的1024个4K

;End od GDT


GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1
dd 0


SelectorNormal equ LABEL_DESC_NORMAL - 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
SelectorPageDir equ LABEL_DESC_PAGE_DIR - LABEL_GDT
SelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDT


[SECTION .data1]
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueRealMode dw 0
PMMessage: db "In Protect Mode now. ^_^",0
OffsetPMMessage equ PMMessage - $$
DataLen equ $ - LABEL_DATA


[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $ - LABEL_STACK - 1




[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 [SPValueRealMode],sp

;初始化16位段描述符
;xor eax,eax
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


xor eax,eax
mov ax,ds
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr + 2],eax

;加载 gdt
lgdt [GdtPtr]

;关中断
cli

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

;set the cr0
mov eax,cr0
or eax,1
mov cr0,eax

jmp dword SelectorCode32:0
;End of [SECTION .16]

LABEL_REAL_ENTRY:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,[SPValueRealMode]

in al,92h
and al,11111101b
out 92h,al

sti
mov ax,4c00h
int 21h



[SECTION .s32]
[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
xor esi,esi
xor edi,edi
mov esi,OffsetPMMessage
mov edi,(80 * 10 + 0) * 2
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 ;
mov es,ax ;将PageDir段首个加载到es,后面的stosd会从es:edi开始。
mov ecx,1024 ;循环控制标量
xor edi,edi
xor eax,eax
mov eax,PageTblBase|PG_P|PG_USU|PG_RWW
;PDE中前20位存放的是页表的地址,第一个PDE放的正好是PageTblBase
;的基址。如果PTE连续放置的话,下面每个PDE对应的地址就是PageTblBase+4K*索引
.1:
stosd ;将eax的内容存储到es:edi的位置后,di=di+4, 所以每次存储后自动跳过正好是下一个
;PDE的位置。而内容是下一个页表的地址,所以只需要将上一个页表地址加4096.
add eax,4096
loop .1 ;每次执行loop指令,cx减1,然后判断cx是否等于0,如果不为0则转移到loop指令后的标号处,
;实现循环;如果为0顺序执行。
;初始化页表

mov ax,SelectorPageTbl ;
mov es,ax ;同上,将页表的首地址加载到es,后面的stosd会从es:edi开始
mov ecx,1024*1024 ;按20位(PTE的前20位)表示的最大值来计算所需要初始化的页表个数。
xor edi,edi
xor eax,eax
mov eax,PG_P|PG_USU|PG_RWW ;这里为了使用线性地址和物理地址做绝对映射,所以第1个PTE的首地址被初始化为物理地址的0位置。
.2:
stosd
add eax,4096 ;作用同上
loop .2 ;

mov eax,PageDirBase
mov cr3,eax ;将PageDirBase的地址加载到cr3
mov eax,cr0
or eax,80000000h
mov cr0,eax ;将cr0的PG位置1使分页生效。
jmp short .3

.3: nop
ret

SegCode32Len equ $ - LABEL_SEG_CODE32

[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 eax, 7FFFFFFEh ; 跳回实模式后一定要将CR0复位。与上面的 "or eax,80000000h; mov cr0,eax" 对应PE=0, PG=0
mov cr0,eax

LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY

Code16Len equ $ - LABEL_SEG_CODE16

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

下面是根据内存动态计算页表个数的代码

%include "pm.inc"
PageDirBaseequ200000h
PageTblBaseequ201000h

org0100h
jmpLABEL_BEGIN

[SECTION .gdt]
;GDT
LABEL_GDT:Descriptor 0, 0,0
LABEL_DESC_NORMAL:Descriptor 0, 0ffffh,DA_DRW
LABEL_DESC_PAGE_DIR:Descriptor PageDirBase, 4095,DA_DRW
LABEL_DESC_PAGE_TBL:Descriptor PageTblBase, 4096 * 8 - 1, DA_DRW; Page Tables,这里只是一个估算
LABEL_DESC_CODE32:Descriptor 0, SegCode32Len-1,DA_C + DA_32
LABEL_DESC_CODE16:Descriptor 0, 0ffffh,DA_C
LABEL_DESC_VIDEO:Descriptor 0B8000h, 0ffffh,DA_DRW
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32; Stack, 32 位
;End od GDT

GdtLenequ$ - LABEL_GDT
GdtPtrdwGdtLen - 1
dd0
SelectorNormalequLABEL_DESC_NORMAL - LABEL_GDT
SelectorPageDirequLABEL_DESC_PAGE_DIR- LABEL_GDT
SelectorPageTblequLABEL_DESC_PAGE_TBL- LABEL_GDT
SelectorCode32equLABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16equLABEL_DESC_CODE16 - LABEL_GDT
SelectorVideoequLABEL_DESC_VIDEO - LABEL_GDT
SelectorDataequLABEL_DESC_DATA- LABEL_GDT
SelectorStackequLABEL_DESC_STACK- LABEL_GDT


[SECTION .data1] ; 数据段
ALIGN32
[BITS32]
LABEL_DATA:

;字符串定义
_szPMMessage:db"In Protect Mode now. ^-^", 0Ah, 0Ah, 0; 进入保护模式后显示此字符串
_szMemChkTitle:db"BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0; 进入保护模式后显示此字符串
_szRAMSizedb"RAM size:",0
_szReturndb0Ah,0

;变量,这些变量是在实模式下调用中断15时用到的,使用的是物理地址。

_wSPValueInRealModedw0
_dwMCRNumber:dd0
_dwDispPos:dd(80 * 6 + 0) * 2; 屏幕第 6 行, 第 0 列。
_dwMemSize:dd0
_ARDStruct:;Address Range Descriptor Strucure
_dwBaseAddrLow:dd0
_dwBaseAddrHigh:dd0
_dwLengthLow:dd0
_dwLengthHigh:dd0
_dwType:dd0

_MemChkBuf:times256db0

; 当上面的变量转换到保护模式后,所以要把变量的绝对地址换算成偏移地址。即物理
; 地址减速去段基址。这是段基址$$的一个非常好的应用。
; 保护模式下使用这些符号

szPMMessageequ_szPMMessage- $$
szMemChkTitleequ_szMemChkTitle- $$
szRAMSizeequ_szRAMSize- $$
szReturnequ_szReturn- $$
dwDispPosequ_dwDispPos- $$
dwMemSizeequ_dwMemSize- $$
dwMCRNumberequ_dwMCRNumber- $$
ARDStructequ_ARDStruct- $$
dwBaseAddrLowequ_dwBaseAddrLow- $$
dwBaseAddrHighequ_dwBaseAddrHigh- $$
dwLengthLowequ_dwLengthLow- $$
dwLengthHighequ_dwLengthHigh- $$
dwTypeequ_dwType- $$
MemChkBufequ_MemChkBuf- $$

DataLenequ$ - LABEL_DATA

[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStackequ$ - LABEL_STACK - 1


[SECTION .s16]
[BITS 16]

LABEL_BEGIN:
mov ax,cs
movds,ax
moves,ax
movss,ax
movsp,0100h
mov[LABEL_GO_BACK_TO_REAL + 3],ax
mov[_wSPValueInRealMode],sp

;利用int 15获取内存信息时,每次调用时要填充以下信息:
;eax:0E820h, 固定的int 15的获取内存信息的功能号
;ebx:是下一个内存块地址描述符的后续标记,第一次开始时要求为0.
; 然后每次调用前将上次返回值原样放入ebx(即不要修改ebx),只到
; ebx的值为0时说明且CF没有置1, 说明是最后一次的内存描述符。
; (如果ebx为0而CF=1,有可能是第一次调用时就出错)
;es:di:指向地址范围的描述描述符结构,调用且BIOS会将内存信息的填充到这个结构中。
;ecx:为es:di指向的地址范围描述符结构的大小。即使es:di指向的结构大于这个数,BIOS
; 也只填充ecx指定大小的字节,而有些BIOS只填充20个字节,所以一般ecx指定20
;edx:0534D4150h,固定的字符标记('SMAP')
;调用后:
;CF=0,正常。CF=1,错误。
;eax:0534D4150h
;es:di 返回传入的地址范围的描述符结构
;ecx:返回填充到上述结构中的字节数
;ebx:下一个地址描述符所需要的后续标记,如果为0且CF=0则当前为最后一个。如果不为0则
;在继续调用下一次中断时把此值填充,即不要修改上一次返回的值。

movebx, 0;首次为0,以后由 BIOS填充并保持给下一次调用,程序不要修改。
movdi, _MemChkBuf;准备由BIOS返回的地址范围描述符填充的结构首地址,每次调用后增加ecx指定的大小供下一次填充。
.loop:
moveax, 0E820h;
movecx, 20;
movedx, 0534D4150h;
int15h;上以四行按int 15的0534D4150号功能要求填入参数。
jcLABEL_MEM_CHK_FAIL;如果CF被置位,由跳转到LABEL_MEM_CHK_FAIL
adddi, 20;地址步进20为下一个结构地址。
incdword [_dwMCRNumber];每调用一次将_dwMCRNumber加1作为循环计数。
cmpebx, 0;
jne.loop;如果ebx为0则跳出循环。
jmpLABEL_MEM_CHK_OK;
LABEL_MEM_CHK_FAIL:
movdword [_dwMCRNumber], 0;如果是CF=1跳入这里,说明调用错,将_dwMCRNumber置0
LABEL_MEM_CHK_OK:
;调用完成后,_MemChkBuf中保存了_dwMCRNumber个20字节大小的结构,所以_MemChkBuf大小有可能不够用。要有一定的预估。

;初始化16位段描述符
;xoreax,eax
movax,cs
movzxeax,ax
shleax,4
addeax,LABEL_SEG_CODE16
movword [LABEL_DESC_CODE16 + 2],ax
shreax,16
movbyte [LABEL_DESC_CODE16 + 4],al
movbyte [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

; 初始化数据段描述符
xoreax, eax
movax, ds
shleax, 4
addeax, LABEL_DATA
movword [LABEL_DESC_DATA + 2], ax
shreax, 16
movbyte [LABEL_DESC_DATA + 4], al
movbyte [LABEL_DESC_DATA + 7], ah

; 初始化堆栈段描述符
xoreax, eax
movax, ds
shleax, 4
addeax, LABEL_STACK
movword [LABEL_DESC_STACK + 2], ax
shreax, 16
movbyte [LABEL_DESC_STACK + 4], al
movbyte [LABEL_DESC_STACK + 7], ah


xoreax,eax
movax,ds
shleax,4
addeax,LABEL_GDT
movdword [GdtPtr + 2],eax

;加载 gdt
lgdt[GdtPtr]

;关中断
cli

;打开A20地址线
inal,92h
oral,00000010b
out92h,al

;set the cr0
moveax,cr0
oreax,1
movcr0,eax

jmpdword SelectorCode32:0
;End of [SECTION .16]

LABEL_REAL_ENTRY:
movax,cs
movds,ax
moves,ax
movss,ax
movsp,[_wSPValueInRealMode]

inal,92h
andal,11111101b
out92h,al

sti
movax,4c00h
int21h

[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32:

mov ax,SelectorData
movds,ax
moves, ax;SelectorData传给es,是因为在下面的串操作中需要使用es,而为了访问在同一段内访问串,所以将es的值ds的值相等,

;指象同一段描述符
movax,SelectorVideo ;
movgs,ax
movax,SelectorStack
movss,ax
movesp,TopOfStack

; 下面显示一个字符串
pushszPMMessage
callDispStr
addesp, 4

pushszMemChkTitle
callDispStr
addesp, 4

callDispMemSize; 显示内存信息
callSetupPaging; 启动分页机制
jmpSelectorCode16:0

; 启动分页机制 --------------------------------------------------------------
SetupPaging:
; 根据内存大小计算应初始化多少PDE以及多少页表
xoredx, edx;div指令一定要清空edx,否则极容易产生非法操作.
moveax, [dwMemSize]
movebx, 400000h; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
divebx; div的商返回到eax,余数返回到edx
movecx, eax; 此时 ecx 为页表的个数,也即 PDE 应该的个数
testedx, edx
jz.no_remainder
incecx; 如果余数不为 0 就需增加一个页表
.no_remainder:
pushecx; 暂存页表个数

; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.

; 首先初始化页目录
movax, SelectorPageDir; 此段首地址为 PageDirBase
moves, ax
xoredi, edi
xoreax, eax
moveax, PageTblBase | PG_P | PG_USU | PG_RWW
.1:
stosd
addeax, 4096; 为了简化, 所有页表在内存中是连续的.
loop.1

; 再初始化所有页表
movax, SelectorPageTbl; 此段首地址为 PageTblBase
moves, ax
popeax; 页表个数
movebx, 1024; 每个页表 1024 个 PTE
mulebx
movecx, eax; PTE个数 = 页表个数 * 1024
xoredi, edi
xoreax, eax
moveax, PG_P | PG_USU | PG_RWW
.2:
stosd
addeax, 4096; 每一页指向 4K 的空间
loop.2

moveax, PageDirBase
movcr3, eax
moveax, cr0
oreax, 80000000h
movcr0, eax
jmpshort .3
.3:
nop

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

DispMemSize:
pushesi
pushedi
pushecx

movesi, MemChkBuf
movecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++)//每次得到一个ARDS
.loop: ;{
movedx, 5 ; for(int j=0;j<5;j++) //每次得到一个ARDS中的成员
movedi, ARDStruct ; {//依次显示BaseAddrLow,BaseAddrHigh,LengthLow,
.1: ; LengthHigh,Type
pushdword [esi] ;
callDispInt ; DispInt(MemChkBuf[j*4]); //显示一个成员
popeax ;
stosd ; ARDStruct[j*4] = MemChkBuf[j*4];
addesi, 4 ;
decedx ;
cmpedx, 0 ;
jnz.1 ; }
callDispReturn ; printf("\n");
cmpdword [dwType], 1 ; if(Type == AddressRangeMemory)
jne.2 ; {
moveax, [dwBaseAddrLow];
addeax, [dwLengthLow];
cmpeax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize)
jb.2 ;
mov[dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow;
.2: ; }
loop.loop ;}
;
callDispReturn ;printf("\n");
pushszRAMSize ;
callDispStr ;printf("RAM size:");
addesp, 4 ;
;
pushdword [dwMemSize] ;
callDispInt ;DispInt(MemSize);
addesp, 4 ;

popecx
popedi
popesi
ret

%include"lib.inc"; 库函数
SegCode32Lenequ$ - LABEL_SEG_CODE32

[SECTION .s16code]
ALIGN32
[BITS 16]
LABEL_SEG_CODE16:
movax,SelectorNormal
movds,ax
moves,ax
movfs,ax
movgs,ax
movss,ax

moveax,cr0
andeax, 7FFFFFFEh; PE=0, PG=0andal,11111110b
movcr0,eax

LABEL_GO_BACK_TO_REAL:
jmp0:LABEL_REAL_ENTRY

Code16Len equ $ - LABEL_SEG_CODE16

你可能感兴趣的:(数据结构,J#,Go)