📄 loader.asm
字号:
ret
;--------------------------------------------------------------------
; 函数名: GetFATEntry
;--------------------------------------------------------------------
; 作用:
; 找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中需要注意的是,
; 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:
push es
push bx
push ax
mov ax, BaseOfKernelFile ; ┓
sub ax, 0100h ; ┣ 在 BaseOfKernelFile 后面留出 4K
mov es, ax ; ┛ 空间用于存放 FAT
pop ax
mov byte [bOdd], 0
mov bx, 3
mul bx ; dx:ax = ax * 3
mov bx, 2
div bx ; dx:ax / 2 ==> ax <- 商, dx <- 余数
cmp dx, 0
jz LABEL_NEXT
mov byte [bOdd], 1
LABEL_NEXT: ;偶数
xor dx, dx ; 现在 ax 中是 FATEntry 在 FAT 中的偏
mov bx, [BPB_BytsPerSec] ;移量. 下面来计算 FATEntry 在哪个扇区中
;(FAT占用不止一个扇区)
div bx ; dx:ax / BPB_BytsPerSec ==> ax <- 商
;(FATEntry 所在的扇区相对于 FAT 来说的扇区号)
; dx <- 余数 (FATEntry 在扇区内的偏移)。
push dx ;保存在扇区内的偏移
mov bx, 0 ; bx <- 0 于是,
;es:bx = (BaseOfKernelFile - 100):00 = (BaseOfKernelFile - 100) * 10h
add ax, SectorNoOfFAT1 ; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
mov cl, 2
call ReadSector ; 读取 FATEntry 所在的扇区, 一次读两个,
pop dx ;避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
add bx, dx
mov ax, [es:bx]
cmp byte [bOdd], 1
jnz LABEL_EVEN
shr ax, 4
LABEL_EVEN:
and ax, 0FFFh
LABEL_GET_FAT_ENRY_OK:
pop bx
pop es
ret
;------------------------------------------------------------------
;------------------------------------------------------------------
; 函数名: KillMotor
;------------------------------------------------------------------
; 作用:
; 关闭软驱马达
KillMotor:
push dx
mov dx, 03F2h
mov al, 0
out dx, al
pop dx
ret
;---------------------------------------------------------------
;---------------------------------------------------------------
[section .data1]
align 32
LABEL_STACK:
stackspace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $
;--------------------------------------------------------------
[section .s32]
align 32
[bits 32]
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, TopOfStack
;显示一个字符'P',表示进入保护模式
mov ah, 0Fh
mov al, 'P'
mov [gs:((80 * 0 + 39) * 2)], ax
push szMemChkTitle
call DispStr
add esp, 4
call DispMemInfo
call SetupPaging
call InitKernel
jmp SelectorFlatC:KernelEntryPointPhyAddr
; 显示内存信息 --------------------------------------
DispMemInfo:
push esi
push edi
push ecx
mov esi, MemChkBuf
mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++)
.loop: ;{// 每次得到一个ARDS结构
mov edx, 5 ; for(int j=0;j<5;j++)
mov edi, ARDStruct ; { // 每次得到一个ARDS中的成员,共5个成员
.1: ;// 依次显示:BaseAddrLow,BaseAddrHigh,
push dword [esi] ; LengthLow,LengthHigh,Type
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)
jne .2 ; {// AddressRangeMemory : 1,
mov eax, [dwBaseAddrLow] ; AddressRangeReserved : 2
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
; 启动分页机制 --------------------------------------------------------------
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:
push ecx ; 暂存页表个数
; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.
; 首先初始化页目录
mov ax, SelectorFlatRW ; 此段首地址为 PageDirBase
mov es, ax
mov edi, PageDirBase
xor eax, eax
mov eax, PageTblBase | PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 为了简化, 所有页表在内存中是连续的.
loop .1
; 再初始化所有页表
pop eax ; 页表个数
mov ebx, 1024 ; 每个页表 1024 个 PTE
mul ebx
mov ecx, eax ; PTE个数 = 页表个数 * 1024
mov edi, PageTblBase
xor eax, eax
mov eax, PG_P | PG_USU | PG_RWW
.2:
stosd
add eax, 4096 ; 每一个页表项指向 4K 的空间
loop .2
mov eax, PageDirBase
mov cr3, eax
mov eax, cr0
or eax, 80000000h
mov cr0, eax
jmp short .3
.3:
nop
ret
; 分页机制启动完毕 ----------------------------------------------------------
;---------------------------------------------------------------------------
InitKernel:
xor esi, esi
mov cx, word [BaseOfKernelFilePhyAddr + 2Ch] ;获取节头表的数目
movzx ecx, cx
mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ;获取头节表的偏移
add esi, BaseOfKernelFilePhyAddr ;获取头节表的位置
.Begin:
mov eax, [esi + 0]
cmp eax, 0
jz .NoAction
push dword [esi + 010h] ;把段在文件中的长度入栈
mov eax, [esi + 04h] ;获取段在文件中的偏移
add eax, BaseOfKernelFilePhyAddr ;获取段在内存中的位置
push eax ;把源地址入栈
push dword [esi + 08h] ;把目的地址入栈
call MemCpy
add esp, 12
.NoAction:
add esi, 020h ;指向下一个头节表项
dec ecx
jnz .Begin
ret
%include "lib.inc" ; 库函数
;*****************************************************
[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 + BaseOfLoaderPhyAddr
szMemChkTitle equ _szMemChkTitle + BaseOfLoaderPhyAddr
szRAMSize equ _szRAMSize + BaseOfLoaderPhyAddr
szReturn equ _szReturn + BaseOfLoaderPhyAddr
dwDispPos equ _dwDispPos + BaseOfLoaderPhyAddr
dwMemSize equ _dwMemSize + BaseOfLoaderPhyAddr
dwMCRNumber equ _dwMCRNumber + BaseOfLoaderPhyAddr
ARDStruct equ _ARDStruct + BaseOfLoaderPhyAddr
dwBaseAddrLow equ _dwBaseAddrLow + BaseOfLoaderPhyAddr
dwBaseAddrHigh equ _dwBaseAddrHigh + BaseOfLoaderPhyAddr
dwLengthLow equ _dwLengthLow + BaseOfLoaderPhyAddr
dwLengthHigh equ _dwLengthHigh + BaseOfLoaderPhyAddr
dwType equ _dwType + BaseOfLoaderPhyAddr
MemChkBuf equ _MemChkBuf + BaseOfLoaderPhyAddr
PageTableNumber equ _PageTableNumber + BaseOfLoaderPhyAddr
DataLen equ $ - LABEL_DATA
; END of [SECTION .data1]
;***************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -