org 07c00h
; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
BaseOfStack
equ
07c00h
; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
BaseOfLoader
equ
09000h
; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader
equ
0100h
; LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors
equ
14
; 根目录占用空间
SectorNoOfRootDirectory
equ
19
; Root Directory 的第一个扇区号
SectorNoOfFAT1
equ
1
; FAT1 的第一个扇区号 = BPB_RsvdSecCnt
DeltaSectorNo
equ
17
; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo
;================================================================================================
jmp short LABEL_START
; Start to boot.
nop
; 这个 nop 不可少
BP_OEMName
DB 'FXSTAR1 '
BPB_BytsPerSec
DW 512
; 每扇区字节数
BPB_SecPerClus
DB 1
; 每簇多少扇区
BPB_RsvdSecCnt
DW 1
; Boot 记录占用多少扇区
BPB_NumFATs
DB 2
; 共有多少 FAT 表
BPB_RootEntCnt
DW 224
; 根目录文件数最大值
BPB_TotSec16
DW 2880
; 逻辑扇区总数
BPB_Media
DB 0xF0
; 媒体描述符
BPB_FATSz16
DW 9
; 每FAT扇区数
BPB_SecPerTrk
DW 18
; 每磁道扇区数
BPB_NumHeads
DW 2
; 磁头数(面数)
BPB_HiddSec
DD 0
; 隐藏扇区数
BPB_TotSec32
DD 0
; 如果 wTotalSectorCount 是 0 由这个值记录扇区数
BS_DrvNum
DB 0
; 中断 13 的驱动器号
BS_Reserved1
DB 0
; 未使用
BS_BootSig
DB 29h
; 扩展引导标记 (29h)
BS_VolID
DD 0
; 卷序列号
BS_VolLab
DB 'AREA OS v01'; 卷标, 必须 11 个字节
BS_FileSysType
DB 'FAT12 '
; 文件系统类型, 必须 8个字节
LABEL_START:
mov
ax, cs
mov
ds, ax
mov
es, ax
mov
ss, ax
mov
sp, BaseOfStack
call
os_cls
call
os_searchfile
; *****************************************************************************************************
jmp
BaseOfLoader:OffsetOfLoader
; 这一句正式跳转到已加载到内
; 存中的 LOADER.BIN 的开始处,
; 开始执行 LOADER.BIN 的代码。
; Boot Sector 的使命到此结束。
; *****************************************************************************************************
;----------------------------------------------------------------------------
;AREA OS FUNCTION
os_cls:
mov
ax, 0600h
mov
bx, 0700h
mov
cx, 0000h
mov
dx, 184fh
int
10h
mov ah, 02h
mov bh, 00h
mov
dx, 00h
int 10h
mov
ax, 0
mov
[ds:position_x], al
mov
[ds:position_y], al
ret
os_message:
;输入参数:ax-数据起始地址
mov
bx, ax
add
ax, 2
mov bp, ax ; ES:BP = 串地址
mov cx, [es:bx]
; CX = 串长度
mov
ax, 0
mov ax, 1301h
; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov
dl, [ds:position_x]
; 列
mov
dh, [ds:position_y]
; 行
int 10h ; int 10h
mov
bl, [ds:position_y]
add
bx, 1
mov
ax, cx
mov
dl, 80
div
dl
add
bx, ax
mov
[ds:position_y],bl
ret
os_getfatentry:
push
es
push
bx
push
ax
mov
ax, BaseOfLoader
; `.
sub
ax, 0100h
; | 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT
mov
es, ax
; /
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_EVEN
mov
byte [bOdd], 1
LABEL_EVEN:
;偶数
; 现在 ax 中是 FATEntry 在 FAT 中的偏移量,下面来
; 计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
xor
dx, dx
mov
bx, [BPB_BytsPerSec]
div
bx
; dx:ax / BPB_BytsPerSec
; ax <- 商 (FATEntry 所在的扇区相对于 FAT 的扇区号)
; dx <- 余数 (FATEntry 在扇区内的偏移)。
push
dx
mov
bx, 0
; bx <- 0 于是, es:bx = (BaseOfLoader - 100):00
add
ax, SectorNoOfFAT1
; 此句之后的 ax 就是 FATEntry 所在的扇区号
mov
cx, 2
call
os_readsector
; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界
; 发生错误, 因为一个 FATEntry 可能跨越两个扇区
pop
dx
add
bx, dx
mov
ax, [es:bx]
cmp
byte [bOdd], 1
jnz
LABEL_EVEN_2
shr
ax, 4
LABEL_EVEN_2:
and
ax, 0FFFh
LABEL_GET_FAT_ENRY_OK:
pop
bx
pop
es
ret
os_readsector:
;输入参数:ax-起始扇区位置 es:bx-传输缓冲区地址 cx-传输扇区数
mov
[Data+8],ax
mov
[Data+6],es
mov
[Data+4],bx
mov
[Data+2],cx
.GoOnReading:
mov si,Data
mov dl,80h
mov ah,42h
int 13h
jc .GoOnReading ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止
ret
os_searchfile:
mov
word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp
word [wRootDirSizeForLoop], 0
; ┓
jz
LABEL_NO_LOADERBIN
; ┣ 判断根目录区是不是已经读完
dec
word [wRootDirSizeForLoop]
; ┛ 如果读完表示没有找到 LOADER.BIN
mov
ax, BaseOfLoader
mov
es, ax
; es <- BaseOfLoader
mov
bx, OffsetOfLoader
; bx <- OffsetOfLoader
于是, es:bx = BaseOfLoader:OffsetOfLoader
mov
ax, [wSectorNo]
; ax <- Root Directory 中的某 Sector 号
mov
cx, 1
call
os_readsector
mov
si, LoaderFileName
; ds:si -> "LOADER BIN"
mov
di, OffsetOfLoader
; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100
cld
mov
dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
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
; ds:si -> al
cmp
al, byte [es:di]
jz
LABEL_GO_ON
jmp
LABEL_DIFFERENT
; 只要发现不一样的字符就表明本 DirectoryEntry 不是
; 我们要找的 LOADER.BIN
LABEL_GO_ON:
inc
di
jmp
LABEL_CMP_FILENAME
;
继续循环
LABEL_DIFFERENT:
and
di, 0FFE0h
; else ┓
di &= E0 为了让它指向本条目开头
add
di, 20h
; ┃
mov
si, LoaderFileName
; ┣ di += 20h 下一个目录条目
jmp
LABEL_SEARCH_FOR_LOADERBIN
; ┛
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add
word [wSectorNo], 1
jmp
LABEL_SEARCH_IN_ROOT_DIR_BEGIN
LABEL_NO_LOADERBIN:
mov
ax,0
mov
es,ax
mov
ax,Flag
call
os_message
jmp
$
; 没有找到 LOADER.BIN, 死循环在这里
LABEL_FILENAME_FOUND:
; 找到 LOADER.BIN 后便来到这里继续
mov
ax, RootDirSectors
and
di, 0FFE0h
; di -> 当前条目的开始
add
di, 01Ah
; di -> 首 Sector
mov
cx, word [es:di]
push
cx
; 保存此 Sector 在 FAT 中的序号
add
cx, ax
add
cx, DeltaSectorNo
; cl <- LOADER.BIN的起始扇区号(0-based)
mov
ax, BaseOfLoader
mov
es, ax
; es <- BaseOfLoader
mov
bx, OffsetOfLoader
; bx <- OffsetOfLoader
mov
ax, cx
; ax <- Sector 号
LABEL_GOON_LOADING_FILE:
mov
cx, 1
call
os_readsector
pop
ax
; 取出此 Sector 在 FAT 中的序号
call
os_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:
ret
;----------------------------------------------------------------------------
;BOOT DATA
LoaderFileName db "LOADER BIN", 0 ; LOADER.BIN 之文件名
BootMessage:
db
1,0
db
"Boot"
LoadMessage:
db
1,0
db
"Load"
Flag:
db
5,0
db
"Flag!"
position_x:
db
0h
position_y:
db
0h
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
Data:
db
10h
db
0
dw
01h
dd
00000100h
dd
0
dd
0
;----------------------------------------------------------------------------
times
510-($-$$)
db
0
; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw
0xaa55
; 结束标志