📄 loader.asm
字号:
org 0100h
jmp LABEL_START ;跳转指令
nop
%include "fat12hdr.inc"
%include "pm.inc"
LABEL_GDT: Descriptor 0, 0, 0 ;定义一个空描述符
LABEL_DESC_FLAT_C: Descriptor 0, 0FFFFFh, DA_CR | DA_32 | DA_LIMIT_4K
LABEL_DESC_FLAT_RW: Descriptor 0, 0FFFFFh, DA_DRW | DA_32 | DA_LIMIT_4K
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0FFFFh, DA_DRW | DA_DPL3
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen
dd BaseOfLoaderPhyAddr + LABEL_GDT ;GDT的基地址和长度
;选择子
SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3
;==========================================================================
;变量及宏
;--------------------------------------------------------------------------
wRootDirSizeForLoop dw 14 ; Root Directory 占用的扇区数
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
dwKernelSize dd 0 ; KERNEL.BIN 文件大小
PageDirBase equ 100000h ; 页目录基址,内存1M处
PageTblBase equ 101000h ; 页表基址,1M + 4K处
BaseOfStack equ 0100h
BaseOfLoader equ 09000h ;Loader.bin被加载的位置:段地址
OffsetOfLoader equ 0100h ;Loader.bin被加载的位置: 偏移地址
;09000:0100h = 090100h = 577K左右
BaseOfKernelFile equ 08000h ;内核被加载的位置---段地址
OffsetOfKernelFile equ 0h ;内核被加载的位置---偏移
;08000:0h = 080000h = 512K
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h
BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h
KernelEntryPointPhyAddr equ 030400h
; 大约193k,注意:1、必须与 MAKEFILE 中参数 -Ttext 的值相等!!
;2、这是个地址而非仅仅是个偏移,如果 -Ttext 的值为 0x400400,
;则它的值也应该是 0x400400。
;======================================================================
;字符串
;----------------------------------------------------------------------
KernelFileName db "KERNEL BIN", 0 ; KERNEL.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength equ 13
LoadMessage: db "Loading "
Message1 db "Loader Ready"
Message2 db "No KERNEL "
;======================================================================
LABEL_START:
;初始化寄存器
mov ax, cs
mov es, ax
mov ds, ax
mov ss, ax
mov sp, BaseOfStack
;显示字符串
mov dh, 0 ;"Loading "
call DispStrRealMode ;
;得到内存数
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:
;软驱复位
xor ah, ah ; ┓
xor dl, dl ; ┣ 软驱复位
int 13h ; ┛
mov word [wSectorNo], SectorNoOfRootDerectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop], 0 ;检查根目录扇区是否读完
jz LABEL_NO_KERNELBIN ;读完则跳转
dec word [wRootDirSizeForLoop] ;计数器减1
;还有扇区没有读完则读取扇区
mov ax, BaseOfKernelFile
mov es, ax
mov bx, OffsetOfKernelFile ;es:bx 准备好缓冲区
mov cl, 1 ;读取一个扇区
mov ax, [wSectorNo] ;从[wSectorNo]开始读
call ReadSector
mov si, KernelFileName ;指向要读取的内核文件名字
mov di, OffsetOfKernelFile
cld
mov dx, 10h ;一个扇区可以存放10h个条目
LABEL_SEARCH_FOR_KERNELBIN:
cmp dx, 0 ; ┓循环次数控制, 如果已
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; ┣ 经读完了一个 Sector,
dec dx ; ┛ 就跳到下一个 Sector
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0 ; ┓
jz LABEL_FILENAME_FOUND ; ┣ 循环次数控制, 如果比较了 11 个
dec cx ; ┛字符都相等, 表示找到
lodsb
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME
LABEL_DIFFERENT:
and di, 0FFE0h
add di, 20h
mov si, KernelFileName
jmp LABEL_SEARCH_FOR_KERNELBIN
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add word [wSectorNo], 1
jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN
LABEL_NO_KERNELBIN:
mov dh, 2
call DispStrRealMode
%ifdef _LOADER_DEBUG_
mov ax, 4C00h
int 21h
%else
jmp $
%endif
LABEL_FILENAME_FOUND:
mov ax, RootDirSectors
and di, 0FFF0h ; di -> 当前条目的开始
push eax
mov eax, [es : di + 01Ch] ; ┓
mov dword [dwKernelSize], eax ; ┛保存 KERNEL.BIN 文件大小
pop eax
add di, 01Ah ; di -> 首 Sector
mov cx, word [es:di]
push cx ; 保存此 Sector 在 FAT 中的序号
add cx, ax
add cx, DeltaSectorNo ; 这时 cl 里面是 LOADER.BIN 的起始扇区号
mov ax, BaseOfKernelFile; (从 0 开始数的序号)
mov es, ax ; es <- BaseOfKernelFile
mov bx, OffsetOfKernelFile ; bx <- OffsetOfKernelFile 于是
; es:bx = BaseOfKernelFile:OffsetOfKernelFile =
;BaseOfKernelFile * 10h + OffsetOfKernelFile
mov ax, cx ; ax <- Sector 号
LABEL_GOON_LOADING_FILE:
push ax ; ┓
push bx ; ┃
mov ah, 0Eh ; ┃ 每读一个扇区就在 "Loading " 后面打一个
mov al, '.' ; ┃ 点, 形成这样的效果:
mov bl, 0Fh ; ┃ Loading ......
int 10h ; ┃
pop bx ; ┃
pop ax ; ┛
mov cl, 1
call ReadSector
pop ax ; 取出此 Sector 在 FAT 中的序号
call GetFATEntry
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
push ax ; 保存 Sector 在 FAT 中的序号
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo
add bx, [BPB_BytsPerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
call KillMotor ; 关闭软驱马达
mov dh, 1 ; "Ready."
call DispStrRealMode ; 显示字符串
;下面准备跳入保护模式
;加载 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 SelectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START)
;------------------------------------------------------------------
; 函数名: DispStrRealMode
;------------------------------------------------------------------
; 作用:
; 显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStrRealMode:
mov ax, MessageLength
mul dh
add ax, LoadMessage ;确定要显示的字符串的偏移
mov bp, ax ; ┓
mov ax, ds ; ┣ ES:BP = 串地址
mov es, ax ; ┛
mov cx, MessageLength ; CX = 串长度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
add dh, 3 ; 从第 3 行往下显示
int 10h ; int 10h
ret
;-------------------------------------------------------------------
; 函数名: ReadSector
;-------------------------------------------------------------------
; 作用:
; 从序号(Directory Entry 中的 Sector 号)为 ax 的的 Sector 开始,
; 将 cl 个 Sector 读入 es:bx 中
ReadSector:
; ---------------------------------------------------------------
; 怎样由扇区号求扇区在磁盘中的位置(扇区号 -> 柱面号, 起始扇区, 磁头号)
; ---------------------------------------------------------------
; 设扇区号为 x
; ┌ 柱面号 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁头号 = y & 1
; 每磁道扇区数 │
; └ 余 z => 起始扇区号 = z + 1
push bp
mov bp, sp
sub esp, 2 ; 辟出两个字节的堆栈区域保存要读的扇区数:
; byte [bp-2]
mov byte [bp-2], cl
push bx ; 保存 bx
mov bl, [BPB_SecPerTrk] ; bl: 除数
div bl ; y 在 al 中, z 在 ah 中
inc ah ; z ++
mov cl, ah ; cl <- 在柱面上的起始扇区号
mov dh, al ; dh <- y
and dh, 1 ; dh & 1 = 磁头号
shr al, 1 ; y >> 1 (其实是 y/BPB_NumHeads,
;这里BPB_NumHeads=2)
mov ch, al ; ch <- 柱面号
pop bx ; 恢复 bx
; 至此, "柱面号, 起始扇区, 磁头号" 全部得到
mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘)
.GoOnReading:
mov ah, 2 ; 读
mov al, byte [bp-2] ; 读 al 个扇区
int 13h
jc .GoOnReading ; 如果读取错误 CF 会被置为 1, 这时就不停地读,
; 直到正确为止
add esp, 2
pop bp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -