;本引导程序加载从装有fat12文件系统的软盘中加载setup程序到内存,并执行setup程序
bits 16
org 0000h
start:
jmp main
;设置BIOS Parameter Block (BPB)
db 90h ;注意这里必须填充1个字节,由于jmp指令只占2个字节
bpbName DB "xxxxxxxx" ;这里只能是8个字节
bpbBytesPerSector: DW 512
bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1
bpbNumberOfFATs: DB 2
bpbRootEntries: DW 224
bpbTotalSectors: DW 2880
bpbMedia: DB 0xF0
bpbSectorsPerFAT: DW 9
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0
bsDriveNumber: DB 0
bsUnused: DB 0
bsExtBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3
bsVolumeLabel: DB "MOS FLOPPY"
bsFileSystem: DB "FAT12"
;------------------------------------------------------------------
;打印消息,ds:[si]存放显示字符串的起始地址,字符串以0结尾
;使用10h系统调用,功能号为0eh,写字符到光标位置,光标进一
;al=预读字符
;bh=页号
;bl=前景颜色(图形模式)
;------------------------------------------------------------------
PrintMesg:
lodsb
or al, al
jz pmo
;调用bios显示字符
mov ah, 0eh
int 10h
jmp PrintMesg
pmo:
ret
;------------------------------------------------------------------
;引导程序
;------------------------------------------------------------------
main:
;让所有段寄存器的值为7c0h
cli
mov ax, 07c0h
mov ds, ax
mov es, ax
; mov fs, ax
; mov gs, ax
;用于堆栈
mov ax, 0000h
mov ss, ax
mov sp, 0ffffh;
sti
;加载根目录
LoadRoot:
mov si, loadRootD
call PrintMesg
;计算根目录的起始扇区
xor dx, dx
mov ax, word [bpbSectorsPerFAT]
xor cx, cx
mov cl, byte [bpbNumberOfFATs]
mul cx
add ax, word [bpbReservedSectors]
mov word [rootDStartS], ax
;计算根目录占扇区的长度
xor dx, dx
mov ax, 020h
mul word [bpbRootEntries]
div word [bpbBytesPerSector]
or dx, dx
jz StoreRootDlenS
inc ax
StoreRootDlenS:
mov word [rootDLenS], ax
mov ax, word[rootDStartS]
;将根目录读入es:rootDOff内存中
mov cx, word [rootDLenS]
mov dx, ax
add dx, cx
mov word [DataStartS], dx
mov bx, rootDOff
call ReadSectors
;查找setup文件名
mov cx, word [bpbRootEntries]
mov bx, rootDOff
FindSetup:
push cx
mov si, setupName
mov di, bx
mov cx, 11
repe cmpsb
jz GetCluster
add bx, 20h
pop cx
loop FindSetup
;查了整个目录都没找到setup文件
mov si, setupNotFind
call PrintMesg
int 18h
GetCluster:
mov ax, word [bx + 001ah]
mov word [setupCluster], ax
LoadFAT:
mov ax, word [bpbReservedSectors]
mov cx, word [bpbSectorsPerFAT]
mov bx, fatOff
call ReadSectors
loadSetup:
mov bx, setupBase
mov es, bx
mov bx, setupOff
LoadSetupLoop:
mov ax, word [setupCluster]
call ClusterToLba
xor cx, cx
mov cl, byte [bpbSectorsPerCluster]
call ReadSectors
;读取下一个族
mov ax, word [setupCluster]
mov cx, ax
shr cx, 01h
add cx, ax ; 3/2个字节
mov bx, fatOff
add bx, cx
mov dx, word [bx]
test ax, 0001h
jz even
and dx, 0fffh
jmp NextCluster
even:
shr dx, 04h
NextCluster:
mov [setupCluster], dx
cmp dx, 0ff0h
jnb RunSetup
xor ax, ax
mov al, byte [bpbSectorsPerCluster]
mul word [bpbBytesPerSector]
mov bx, setupOff
add bx, ax
jmp LoadSetupLoop
RunSetup:
jmp setupBase:setupOff
;------------------------------------------------------------------
;将族号(从第0个开始)转换为扇区的逻辑号,ax存放要转换的族号,输出ax存放扇区号
;------------------------------------------------------------------
ClusterToLba:
push bx
push dx
sub ax, 0002h
xor dx, dx
mov bl, byte [bpbSectorsPerCluster]
mov bh, 0
mul bx
add ax, word [DataStartS]
pop dx
pop bx
ret
;------------------------------------------------------------------
;将逻辑扇区号转换为柱面/磁头/扇区
;逻辑扇区号存放在ax中
;分别存放在absoluteCylinder, absoluteHead, absoluteSector变量中
;对于1.44M的软盘,总共有两面(磁头号0和1),每面80个磁道(磁道号0-79)
;每个磁道有18个扇区(扇区号1-18)
;计算方法:
; absoluteSector = logicSector % bpbSectorsPerTrack + 1
; absoluteHead = (logicSector / bpbBytesPerSector) % bpbHeadsPerCylinder
; absoluteCylinder = (logicSector / bpbBytesPerSector) / bpbHeadsPerCylinder
;------------------------------------------------------------------
LbaToCHS:
push dx
xor dx, dx
div word [bpbSectorsPerTrack]
inc dx
mov word [absoluteSector], dx
xor dx, dx
div word [bpbHeadsPerCylinder]
mov word [absoluteHead], dx
mov word [absoluteCylinder], ax
pop dx
ret
;------------------------------------------------------------------
;ax为读取扇区的逻辑号的起始位置,cx中存放要读取扇区的个数,es:bx
;存放将扇区读入内存的起始地址
;使用13h系统调用,功能号为2
;dl=驱动器号 dh=低6位是磁头号,高2位为磁道(柱面)号的10、11位
;ch=磁道(柱面)号的0-7位
;cl=低6位是扇区号,高2位为磁道(柱面)号的8、9位
;al=读取的扇区数
;es:bx=数据的存放地址
;------------------------------------------------------------------
ReadSectors:
push dx
push ax
xor ax, ax
int 13h
pop ax
ReadSectorsLoop1:
or cx, cx
jz ReadSectorOut
mov dl, 05h ; 出错尝试的次数
ReadSectorsLoop2:
push ax
push cx
push bx
call LbaToCHS
;判断cx是否超过了一个磁道
mov bx, word [bpbSectorsPerTrack]
sub bx, word [absoluteSector]
inc bx
cmp cl, bl
jbe StartReadSectors
mov cl, bl
StartReadSectors:
mov dl, byte [bsDriveNumber]
mov dh, byte [absoluteHead]
mov ch, byte [absoluteCylinder]
mov al, cl
mov cl, byte [absoluteSector]
mov ah, 02h
pop bx
int 13h
jnc ReadNextSectors
;复位磁盘
xor ax, ax
int 13h
dec dl
jnz ReadSectorsLoop2
mov si, readSectorError
call PrintMesg
int 18h
ReadNextSectors:
xor ah, ah
push ax
mul word [bpbBytesPerSector]
add bx, ax
pop ax
pop cx
pop dx
sub cx, ax
add ax, dx
jmp ReadSectorsLoop1
ReadSectorOut:
pop dx
ret
loadRootD db "Ld Root Dir", 0dh, 0ah, 0
readSectorError db "Rd Sector Error", 0
setupNotFind db "File Not Find", 0
rootDStartS dw 0000h
rootDLenS dw 0000h
rootDOff equ 0200h
fatOff equ 0200h
setupBase equ 0900h
setupOff equ 0000h
setupName db "SETUP"
setupCluster dw 0000h
DataStartS dw 0000h
absoluteSector dw 0000h
absoluteCylinder dw 0000h
absoluteHead dw 0000h
times 510 - ($ - $$) db 0
dw 0aa55h