pmtest8.asm

; ==========================================
;实现虚拟地址的切换
;在任务切换时通过改变cr3的值来切换页目录表,从而改变地址映射关系
;
; pmtest8.asm
; 编译方法:nasm pmtest8.asm -o pmtest8.com
; ==========================================

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

;两套页目录表和页表。用于实现cr3的切换(任务切换)
PageDirBase0  equ 200000h ; 页目录开始地址: 2M
PageTblBase0  equ 201000h ; 页表开始地址:  2M +  4K
PageDirBase1  equ 210000h ; 页目录开始地址: 2M + 64K
PageTblBase1  equ 211000h ; 页表开始地址:  2M + 64K + 4K

LinearAddrDemo equ 00401000h
ProcFoo  equ 00401000h
ProcBar  equ 00501000h

ProcPagingDemo equ 00301000h

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_FLAT_C: Descriptor             0,           0fffffh, DA_CR | DA_32 | DA_LIMIT_4K; 0 ~ 4G
LABEL_DESC_FLAT_RW: Descriptor             0,           0fffffh, DA_DRW | DA_LIMIT_4K ; 0 ~ 4G

LABEL_DESC_CODE32: Descriptor        0,  SegCode32Len - 1, DA_CR | 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
SelectorFlatC  equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW  equ LABEL_DESC_FLAT_RW - 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:
; 实模式下使用这些符号
; 字符串
_szPMMessage:   db "In Protect Mode now. ^-^", 0Ah, 0Ah, 0 ; 进入保护模式后显示此字符串
_szMemChkTitle:   db "BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0 ; 进入保护模式后显示此字符串
_szRAMSize   db "RAM size:", 0
_szReturn   db 0Ah, 0
; 变量
_wSPValueInRealMode  dw 0
_dwMCRNumber:   dd 0 ; Memory Check Result
_dwDispPos:   dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。
_dwMemSize:   dd 0
_ARDStruct:   ; Address Range Descriptor Structure
 _dwBaseAddrLow:  dd 0
 _dwBaseAddrHigh: dd 0
 _dwLengthLow:  dd 0
 _dwLengthHigh:  dd 0
 _dwType:  dd 0
_PageTableNumber  dd 0

_MemChkBuf: times 256 db 0

; 保护模式下使用这些符号
szPMMessage  equ _szPMMessage - $$
szMemChkTitle  equ _szMemChkTitle - $$
szRAMSize  equ _szRAMSize - $$
szReturn   equ _szReturn - $$
dwDispPos  equ _dwDispPos - $$
dwMemSize  equ _dwMemSize - $$
dwMCRNumber  equ _dwMCRNumber - $$
ARDStruct  equ _ARDStruct - $$
 dwBaseAddrLow equ _dwBaseAddrLow - $$
 dwBaseAddrHigh equ _dwBaseAddrHigh - $$
 dwLengthLow equ _dwLengthLow - $$
 dwLengthHigh equ _dwLengthHigh - $$
 dwType  equ _dwType  - $$
MemChkBuf  equ _MemChkBuf - $$
PageTableNumber  equ _PageTableNumber- $$

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

 ; 得到内存数
 mov ebx, 0
 mov di, _MemChkBuf
.loop:
 mov eax, 0E820h
 mov ecx, 20
 mov edx, 0534D4150h
 int 15h
 jc LABEL_MEM_CHK_FAIL
 add di, 20
 inc dword [_dwMCRNumber]
 cmp ebx, 0
 jne .loop
 jmp LABEL_MEM_CHK_OK
LABEL_MEM_CHK_FAIL:
 mov dword [_dwMCRNumber], 0


LABEL_MEM_CHK_OK:

 ; 初始化 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, [_wSPValueInRealMode]

 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:
 mov ax, SelectorData
 mov ds, ax   ; 数据段选择子
 mov es, ax

 mov ax, SelectorVideo
 mov gs, ax   ; 视频段选择子

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

 mov esp, TopOfStack


 ; 下面显示一个字符串
 push szPMMessage
 call DispStr
 add esp, 4

 push szMemChkTitle
 call DispStr
 add esp, 4

 call DispMemSize  ; 显示内存信息

 call PagingDemo  ; 演示改变页目录的效果

 ; 到此停止
 jmp SelectorCode16:0

; 启动分页机制 --------------------------------------------------------------
SetupPaging:
 ; 根据内存大小计算应初始化多少PDE以及多少页表
 xor edx, edx
 mov eax, [dwMemSize]
 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
 div ebx
 mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
 test edx, edx
 jz .no_remainder
 inc ecx  ; 如果余数不为 0 就需增加一个页表
.no_remainder:
 mov [PageTableNumber], ecx ; 暂存页表个数,因为有两个页目录表,初始化时用的着

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

 ; 首先初始化页目录
 mov ax, SelectorFlatRW
 mov es, ax
 mov edi, PageDirBase0 ; 此段首地址为 PageDirBase0
 xor eax, eax
 mov eax, PageTblBase0 | PG_P  | PG_USU | PG_RWW
.1:
 stosd
 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.
 loop .1

 ; 再初始化所有页表
 mov eax, [PageTableNumber] ; 页表个数
 mov ebx, 1024  ; 每个页表 1024 个 PTE
 mul ebx
 mov ecx, eax  ; PTE个数 = 页表个数 * 1024
 mov edi, PageTblBase0 ; 此段首地址为 PageTblBase0
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add eax, 4096  ; 每一页指向 4K 的空间
 loop .2

 mov eax, PageDirBase0
 mov cr3, eax
 mov eax, cr0
 or eax, 80000000h
 mov cr0, eax
 jmp short .3
.3:
 nop

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


; 测试分页机制 --------------------------------------------------------------
PagingDemo:
 mov ax, cs
 mov ds, ax
 mov ax, SelectorFlatRW
 mov es, ax

 push LenFoo
 push OffsetFoo
 push ProcFoo
 call MemCpy
 add esp, 12

 push LenBar
 push OffsetBar
 push ProcBar
 call MemCpy
 add esp, 12

 push LenPagingDemoAll
 push OffsetPagingDemoProc
 push ProcPagingDemo
 call MemCpy
 add esp, 12

 mov ax, SelectorData
 mov ds, ax   ; 数据段选择子
 mov es, ax

 call SetupPaging  ; 启动分页

 call SelectorFlatC:ProcPagingDemo
 call PSwitch   ; 切换页目录,改变地址映射关系
 call SelectorFlatC:ProcPagingDemo

 ret
; ---------------------------------------------------------------------------


; 切换页表 ------------------------------------------------------------------
PSwitch:
 ; 初始化页目录
 mov ax, SelectorFlatRW
 mov es, ax
 mov edi, PageDirBase1 ; 此段首地址为 PageDirBase1
 xor eax, eax
 mov eax, PageTblBase1 | PG_P  | PG_USU | PG_RWW
 mov ecx, [PageTableNumber]
.1:
 stosd
 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.
 loop .1

 ; 再初始化所有页表
 mov eax, [PageTableNumber] ; 页表个数
 mov ebx, 1024  ; 每个页表 1024 个 PTE
 mul ebx
 mov ecx, eax  ; PTE个数 = 页表个数 * 1024
 mov edi, PageTblBase1 ; 此段首地址为 PageTblBase1
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add eax, 4096  ; 每一页指向 4K 的空间
 loop .2

 ; 在此假设内存是大于 8M 的
 mov eax, LinearAddrDemo
 shr eax, 22
 mov ebx, 4096
 mul ebx
 mov ecx, eax
 mov eax, LinearAddrDemo
 shr eax, 12
 and eax, 03FFh ; 1111111111b (10 bits)
 mov ebx, 4
 mul ebx
 add eax, ecx
 add eax, PageTblBase1
 mov dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW

 mov eax, PageDirBase1
 mov cr3, eax
 jmp short .3
.3:
 nop

 ret
; ---------------------------------------------------------------------------


; PagingDemoProc ------------------------------------------------------------
PagingDemoProc:
OffsetPagingDemoProc equ PagingDemoProc - $$
 mov eax, LinearAddrDemo
 call eax
 retf
; ---------------------------------------------------------------------------
LenPagingDemoAll equ $ - PagingDemoProc
; ---------------------------------------------------------------------------


; foo -----------------------------------------------------------------------
foo:
OffsetFoo equ foo - $$
 mov ah, 0Ch   ; 0000: 黑底    1100: 红字
 mov al, 'F'
 mov [gs:((80 * 17 + 0) * 2)], ax ; 屏幕第 17 行, 第 0 列。
 mov al, 'o'
 mov [gs:((80 * 17 + 1) * 2)], ax ; 屏幕第 17 行, 第 1 列。
 mov [gs:((80 * 17 + 2) * 2)], ax ; 屏幕第 17 行, 第 2 列。
 ret
LenFoo equ $ - foo
; ---------------------------------------------------------------------------


; bar -----------------------------------------------------------------------
bar:
OffsetBar equ bar - $$
 mov ah, 0Ch   ; 0000: 黑底    1100: 红字
 mov al, 'B'
 mov [gs:((80 * 18 + 0) * 2)], ax ; 屏幕第 18 行, 第 0 列。
 mov al, 'a'
 mov [gs:((80 * 18 + 1) * 2)], ax ; 屏幕第 18 行, 第 1 列。
 mov al, 'r'
 mov [gs:((80 * 18 + 2) * 2)], ax ; 屏幕第 18 行, 第 2 列。
 ret
LenBar equ $ - bar
; ---------------------------------------------------------------------------


; 显示内存信息 --------------------------------------------------------------
DispMemSize:
 push esi
 push edi
 push ecx

 mov esi, MemChkBuf
 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构
.loop:     ;{
 mov edx, 5   ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员,共5个成员
 mov edi, ARDStruct  ; {   // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1:     ;
 push dword [esi]  ;
 call DispInt   ;  DispInt(MemChkBuf[j*4]); // 显示一个成员
 pop eax   ;
 stosd    ;  ARDStruct[j*4] = MemChkBuf[j*4];
 add esi, 4   ;
 dec edx   ;
 cmp edx, 0   ;
 jnz .1   ; }
 call DispReturn  ; printf("/n");
 cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
 jne .2   ; {
 mov eax, [dwBaseAddrLow] ;
 add eax, [dwLengthLow] ;
 cmp eax, [dwMemSize] ;  if(BaseAddrLow + LengthLow > MemSize)
 jb .2   ;
 mov [dwMemSize], eax ;   MemSize = BaseAddrLow + LengthLow;
.2:     ; }
 loop .loop   ;}
     ;
 call DispReturn  ;printf("/n");
 push szRAMSize  ;
 call DispStr   ;printf("RAM size:");
 add esp, 4   ;
     ;
 push dword [dwMemSize] ;
 call DispInt   ;DispInt(MemSize);
 add esp, 4   ;

 pop ecx
 pop edi
 pop esi
 ret
; ---------------------------------------------------------------------------

%include "lib.inc" ; 库函数

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

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

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值