参考书籍为《一个64位操作系统的设计与实现》、《30天自制操作系统》
Boot的作用主要是当BIOS将执行权交给boot后,初始化文件系统,然后寻找Loader文件,如果找到则将其载入内存中并移交执行权
之前在BS_OEMName处使用了自定义的厂商名,发现软盘无法挂载,后来发现此项长度要求为8
Boot引导如下:
org 0x7c00
BaseOfStack equ 0x7c00
BaseOfLoader equ 0x1000 ; loder基地址
OffsetOfLoader equ 0x00 ; loder偏移
RootDirSectors equ 14 ; 根目录所占扇区数
SectorNumOfRootDirStart equ 19 ; 根目录起始扇区号
SectorNumOfFAT1Start equ 1 ; FAT1表的起始扇区号
SectorBalance equ 17 ; 平衡文件\目录的起始簇号与数据区起始簇号的差值
; 簇号对应扇区位置=根目录占用扇区数+根目录起始扇区号+(FAT表项-2)=根目录占用扇区数+FAT表项+SectorBalance
; FAT12文件系统引导扇区结构
jmp short Label_Start
nop ; BS_jmpBoot
BS_OEMName db 'QKKKboot' ; 注意这此项长度为8,否则将导致软盘无法挂载
BPB_BytesPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xf0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db 'boot loader'
BS_FileSysType db 'FAT12 '
Label_Start:
; 初始化寄存器
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
; 清屏
mov ax, 0600h
mov bx, 0700h
mov cx, 0
mov dx, 0184fh
int 10h
; 设置光标位置
mov ax, 0002h
mov bx, 0000h
mov dx, 0000h
; 显示字符串
mov ax, 1301h
mov cx, 25
mov dx, 0000h
mov bx, 0083h
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartBootMessage
int 10h
; 重置软盘
xor ah, ah
xor dl, dl
int 13h
; 文件搜索(loader.bin)
mov word [SectorNo], SectorNumOfRootDirStart ; 根目录起始扇区号
Label_Search_In_Root_Dir_Begin:
cmp word [RootDirSizeForLoop], 0
jz Label_No_LoaderBin
dec word [RootDirSizeForLoop]
mov ax, 00h
mov es, ax
mov bx, 8000h ; 设置缓冲区ES:BX
mov ax, [SectorNo]
mov cl, 1
call Func_ReadOneSector
mov di, 8000h
mov si, LoaderFileName ;设置di和si,方便后面使用lodsb指令
cld ;复位DF(DF=0)
mov dx, 10h ; 每个扇区可容纳得目录项个数
Label_Search_For_LoaderBin:
cmp dx, 0
jz Label_Goto_Next_Sector_In_Root_Dir
dec dx
mov cx, 11 ; 目录项的文件名长度11B
Label_Cmp_FileName:
; 逐个字节比较文件名
cmp cx,0
jz Label_FileName_Found
dec cx
lodsb ; (DS:SI)->(AL),(SI++)
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 ; di后5位置0,该语句将di恢复为8000h
add di, 20h ; 增加一个目录项长度
mov si, LoaderFileName
jmp Label_Search_For_LoaderBin
Label_Goto_Next_Sector_In_Root_Dir:
; 在下一个扇区搜索
add word [SectorNo], 1
jmp Label_Search_In_Root_Dir_Begin
Label_No_LoaderBin:
; 找不到文件则报错
mov ax, 1301h
mov cx, 21
mov dx, 0100h
mov bx, 000ch
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, NoLoaderMessage
int 10h
jmp $ ;无限循环
; 如果找到loader.bin
Label_FileName_Found:
mov ax, RootDirSectors
and di, 0ffe0h ; 后五位清0,表示回到当前目录首地址
add di, 01ah ; 目录项的DIR_FSTCLUS位置,表示起始簇号
mov cx, word [es:di]
push cx
add cx, ax
add cx, SectorBalance ; 这一步得到了簇所在位置(扇区号)
mov ax, BaseOfLoader
mov es, ax
mov bx, OffsetOfLoader ; ES:BX为loader将要载入内存的位置
mov ax, cx
Label_Go_On_Loading_File:
push ax
push bx
mov ah, 0eh
mov al, '.'
mov bl, 0fh
int 10h
pop bx
pop ax
; 以上功能为打印一个“.”可依此观察读了几个扇区数据,即loader占了多少扇区
mov cl, 1
call Func_ReadOneSector
pop ax
call Func_GetFATEntry
cmp ax, 0fffh ; 0FFF是文件最后一个簇的标志
jz Label_File_Loaded
push ax
mov dx, RootDirSectors
add ax, dx
add ax, SectorBalance ; 重新计算扇区号
add bx, [BPB_BytesPerSec] ; bx表示内存偏移
jmp Label_Go_On_Loading_File
Label_File_Loaded:
jmp BaseOfLoader:OffsetOfLoader ; 移交给loader
; 读取软盘功能实现
Func_ReadOneSector:
; int 13h,AH=02h 读取磁盘扇区
; AL=读入的扇区数, CH=磁道号,CL=扇区号,DH=磁头号,DL=驱动器号,ES:BX=数据缓冲区
;保存现场
push bp
mov bp, sp
sub esp, 2
mov byte [bp-2], cl
push bx
; LBA格式转CHS格式
mov bl, [BPB_SecPerTrk]
div bl ; 余数在ah,商在al
inc ah
mov cl, ah
mov dh, al
shr al, 1
mov ch, al
and dh, 1
pop bx
mov dl, [BS_DrvNum]
Label_Go_On_Reading:
mov ah, 2
mov al, byte [bp-2]
int 13h
jc Label_Go_On_Reading ; CF未复位则重新调用中断
add esp, 2
pop bp
ret
Func_GetFATEntry:
;解析FAT表
push es
push bx
push ax
mov ax, 00
mov es, ax ; 初始化es
pop ax
mov byte [Odd], 0
mov bx, 3
mul bx
mov bx, 2 ; ax为FAT表号,扩大1.5倍
div bx
cmp dx, 0 ;判断当前表项是奇数还是偶数,
jz Label_Even
mov byte [Odd], 1 ;奇数置1
Label_Even:
xor dx,dx
mov bx, [BPB_BytesPerSec]
div bx ; 商为FAT表偏移扇区号,余数为FAT表项在扇区的偏移
push dx
mov bx, 8000h ; 设置缓冲区
add ax, SectorNumOfFAT1Start
mov cl, 2
call Func_ReadOneSector ;一次读出两个扇区,防止fat表横跨两个扇区
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [Odd], 1
jnz Label_Even_2
shr ax, 4 ; 上一项为奇数,则下一项为偶数,右移4位是舍弃多余数据
Label_Even_2:
and ax, 0fffh ; 上一项为偶数,下一项是奇数,前四位置0,舍弃多余数据
pop bx
pop es
ret
; 中间变量
RootDirSizeForLoop dw RootDirSectors ; 用于控制循环
SectorNo dw 0 ; 当前扇区号
Odd db 0 ; FAT表项奇偶标志
; 消息类型定义
StartBootMessage: db "Created by Qkk,Start Boot"
NoLoaderMessage: db "ERROR:No LOADER Found"
LoaderFileName: db "LOADER BIN",0
; 填充及引导标志设置
times 510 - ($ - $$) db 0
dw 0xaa55