F AT12是DOS時(shí)代就開(kāi)始使用的文件系統(File System),直到現在仍然在軟盤(pán)上使用。幾乎所有的文件系統都會(huì )把磁盤(pán)劃分為若干層次以方便管理和組織,這些層次主要包括:
扇區(Sector):磁盤(pán)上的最小數據單元
簇(Cluster):一個(gè)或多個(gè)扇區
分區(Partition):通常是指整個(gè)文件系統
下面是FAT12格式的軟盤(pán)存儲圖:
FAT12引導扇區的格式
名稱(chēng)偏移長(cháng)度內容示例值
BS_jmpBoot03一個(gè)跳轉指令
jmp LABEL_START
nop
BS_OEMNane38廠(chǎng)商名'ForrestY'
BPB_BytePerSec112每扇區字節數0x200
BPB_SecPerClus131每簇扇區數0x1
BPB_RsvdSecCnt142Boot記錄占用多少扇區0x1
BPB_NumFATs161共有多少FAT表0x2
BPB_RootEntCnt172根目錄文件數最大值0xE0
BPB_TotSec16192扇區總數0xB40
BPB_Media211介質(zhì)描述符0xF0
BPB_FATSz16222每FAT扇區數0x9
BPB_SecPerTrk242每磁道扇區數0x12
BPB_Numheads262磁頭數(面數)0x2
BPB_HiddSec284隱藏扇區數0
BPB_TotSec32324如果BPB_TotSec16是0,由這個(gè)值記錄扇區數0
BS_DrvNum361中斷13的驅動(dòng)器號0
BS_Reserved1371未使用0
BS_BootSig381擴展引用標記(29h)0x29
BS_VolID394卷序列號0
BS_VolLab4311卷標'OrangesS0.02'
BS_FileSysType548文件系統類(lèi)型'FAT12'
引導代碼及其他62448引導代碼、數據及其他填充字符等引導代碼(其余為0)
結束標志51020xAA550xAA55
FAT表
FAT表占1-18扇區,共兩個(gè)FAT表,FAT1和FAT2,一般FAT2不用,FAT2僅是FAT1的拷貝,每個(gè)FAT表占9個(gè)分區,即 (512*9=4608)字節,FAT表由FAT項組成,每個(gè)FAT項占12位,共3072個(gè)FAT項,去掉兩個(gè)不用的FAT的項(0和1,這是因為數據區的簇號是從2開(kāi)始的0和1不用,故對應的FAT表中的0和1也不用),故FAT12功能管理3070個(gè)簇區即 (1.499M*BPB_SecPerClus,這里是1.499M)的大小。
要注意的是,由于每個(gè)FAT項占12位,包含一個(gè)字節和另一個(gè)字節的一半。如上圖所示連續的3個(gè)字節表示兩個(gè)FAT項,BYTE1是Fat Entry1的低8位,BYTE2的低4位是Fat Entry1的高4位,BYTE2的高4位是Fat Entry2的低4位,BYTE3是Fat Entry2的高8位。
下面介紹根目錄:
僅跟FAT表的是根目錄,從第19個(gè)分區開(kāi)始,它是由若干個(gè)目錄條目(Directory Entry)組成,條目最多有BPB_RootEntCnt個(gè)。由于根目錄區的大小依賴(lài)于BPB_RootEntCnt的大小,所以長(cháng)度不固定。
根目錄中的每一個(gè)條目占用32字節,它的格式如下表所示:
名稱(chēng) 開(kāi)始字節 長(cháng)度 內容
DIR_Name 0 0xB 文件名8字節,擴展名3字節
DIR_Attr 0xB 1 文件屬性
保留位 0xC 10 保留
DIR_WrtTime 0x16 2 最后一次寫(xiě)入的時(shí)間
DIR_WrtDate 0x18 2 最后一次寫(xiě)入的日期
DIR_FstClus 0x1A 2 此文件在數據區和FAT表中的開(kāi)始簇號
DIR_FileSize 0x1C 4 文件大小
數據區
需要注意的是數據區的第一個(gè)簇號是2,而不是0或者1,(簇號和扇區號在這里是一樣的,因為一簇只有一個(gè)扇區),假設根目錄區共占有RootDirSectors個(gè)扇區,則有:
RootDirSectors =[ (BPB_RootEntCnt*32)+(BPB_BytesPerSec-1)]/BPB_BytsPerSec
只所以分子上要加上(BPB_BytesPerSec-1),是為了保證此公式在根目錄區無(wú)法填滿(mǎn)整數個(gè)扇區時(shí)仍然成立。
DOS可以識別的引導盤(pán)
Nasm代碼
;===============================================================================
jmp short LABEL_START
nop ;必不可少的
;**以下是FAT12的磁盤(pán)頭的定義************
BS_OEMName DB 'SongYang' ;OEM String, 必須 8 個(gè)字節
BSB_BytePerSec DW 512 ;每扇區字節數
BSP_SecPerClus Db 1 ;每簇多少扇區
BPB_RsvdSecCnt DW 1 ;Boot 記錄占用多少扇區
BSP_NumFats DB 2 ;共有多少 FAT 表
BSP_RootEntCnt DW 224 ;根目錄文件數最大值
BSP_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由這個(gè)值記錄
;扇區數
BS_DrvNum DB 0 ;中斷 13 的驅動(dòng)器號
BS_Reserved1 DB 0 ;未使用
BS_BootSig DB 29h ;擴展引導標記 (29h)
BS_VolID DD 0 ;卷序列號
BS_Volab DB 'Tinix0.01 ' ;卷標, 必須 11 個(gè)字節
BS_FileSysType DB 'FAT12 ' ;文件系統類(lèi)型, 必須 8個(gè)字節
LABEL_START:
軟盤(pán)的讀寫(xiě)
要讀寫(xiě)軟盤(pán)的話(huà),這時(shí)就需要BIOS中斷int 13h。用法如下:
中斷號 寄存器 作用
13h
ah=00h
dl=驅動(dòng)器號(0表示A盤(pán)) 復位軟驅
ah=02h
ch=柱面(磁道)號
dh=磁頭號
es:bx->數據緩沖區
al=要讀扇區數
cl=起始扇區號
dl=驅動(dòng)器號(0表示A盤(pán)) 從磁盤(pán)讀數據入es:bx
指向的緩沖區
下面我們編寫(xiě)一個(gè)示例,按上述 FAT12引導扇區的格式 編寫(xiě)一個(gè)引導程序讀出FAT12軟盤(pán)中的一個(gè)文件(test.txt)并將其打印在屏幕上,然后將編寫(xiě)的引導程序寫(xiě)入引導扇區即0號扇區,這樣軟盤(pán)的文件格式就是FAT12了,這時(shí)就可以在
DOS或者其他操作系統下訪(fǎng)問(wèn)軟盤(pán)了,向里面新建一個(gè)文件test.txt并些入一些什么,然后將其作為啟動(dòng)盤(pán)啟動(dòng),就會(huì )自動(dòng)運行引導程序,找到文件test.txt并打印出文件中的東西。下面介紹具體的做法:
1.扇區的讀寫(xiě)
從上表中 我們發(fā)現int 13h的參數不是扇區號 ,而是用磁頭號、柱面和起始扇區表示的,那我們如何知道扇區號來(lái)讀取扇區呢,首先進(jìn)行轉換,公式如下:
扇區號/每磁道扇區數(BPB_SecPerTrk,18) 商Q=>柱面號=Q>>1
磁頭號=Q&1
余數R=> 起起始扇區號=R+1
下面我們實(shí)現一個(gè)函數來(lái)讀取扇區ReadSector():
參數:
ax從第axSector開(kāi)始讀
cl讀取cl個(gè)Sector
es:bx讀到緩沖區es:bx
代碼如下:
Nasm代碼
;----------------------------------------------------------------------------
; 函數名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 從第 ax 個(gè) Sector 開(kāi)始, 將 cl 個(gè) Sector 讀入 es:bx 中
ReadSector:
; -----------------------------------------------------------------------
; 怎樣由扇區號求扇區在磁盤(pán)中的位置 (扇區號 -> 柱面號, 起始扇區, 磁頭號)
; -----------------------------------------------------------------------
; 設扇區號為 x
; ┌ 柱面號 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁頭號 = y & 1
; 每磁道扇區數 │
; └ 余 z => 起始扇區號 = z + 1
push bp
mov bp, sp
sub esp, 2 ; 辟出兩個(gè)字節的堆棧區域保存要讀的扇區數: 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
shr al, 1 ; y >> 1 (其實(shí)是 y/BPB_NumHeads, 這里
BPB_NumHeads=2)
mov ch, al ; ch <- 柱面號
and dh, 1 ; dh & 1 = 磁頭號
pop bx ; 恢復 bx
; 至此, "柱面號, 起始扇區, 磁頭號" 全部得到 ^^
mov dl, [BS_DrvNum] ; 驅動(dòng)器號 (0 表示 A 盤(pán))
.GoOnReading:
mov ah, 2 ; 讀
mov al, byte [bp-2] ; 讀 al 個(gè)扇區
int 13h
jc .GoOnReading ; 如果讀取錯誤 CF 會(huì )被置為 1, 這時(shí)就不停地讀, 直到
正確為止
add esp, 2
pop bp
ret
注意事項:
注意各個(gè)參數的取值范圍的問(wèn)題,并不是隨意取值的;每次讀取扇區只能在同一個(gè)磁道中讀取,也就是說(shuō)一次最多能讀取BPB_SecPerTrk(18)個(gè)扇區,也就是說(shuō)cl的取值小于等于[BPB_SecPerTrk-(ax%BPB_SecPerTrk)].
es:bx的選擇問(wèn)題,如果es:bx 和 cl選取的不當 會(huì )出現【09h = DMA across 64K boundary】這個(gè)錯誤,原因不知道,有知道的可以告訴我。
舉個(gè)例子:讀取9個(gè)扇區到8EE0:0000h不出錯,但是讀到8EE0:0001h就出錯!但是讀取8個(gè)以下的扇區則沒(méi)問(wèn)題!很奇怪的問(wèn)題。以下是我的猜測:
DMA的緩存是64k的大小,8000:0000h-9000:0000h正好是64K,也就是內存按64K劃分,DMA緩沖區對應于每個(gè)64K的內存,而8EE0:0000h-9000:0000h正好是9個(gè)扇區的大小,8EE0:0001h則少了一個(gè)字節,故出錯!這只是本人的猜測,查了很長(cháng)時(shí)間也沒(méi)差到原因,有知道的可以告訴我。
2.獲得文件的第一個(gè)簇號(這里也是區號,二則相等)
要想獲某個(gè)文件的第一個(gè)簇號 需要在根目錄中查找對應的目錄項(這里假設文件都放在了根目錄中個(gè)),步驟如下:
將整個(gè)根目錄區域加載進(jìn)內存,(根目錄不是很大,相對現在的大內存),直接加載進(jìn)內存方便操作。
Nasm代碼
;**將根目錄整個(gè)的都讀到es:bx處
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx =
BaseOfLoader:OffsetOfLoader
mov ax, SectorNoOfRootDirectory ; ax <- Root Directory 中的某 Sector 號
mov cl, RootDirSectors ;將根目錄都讀出來(lái)
call ReadSector
在根目錄中逐條查找直到結束,代碼如下:
這里得到的簇號就是區號SecNo,因為這里一個(gè)簇里面只有一個(gè)扇區,但是數據區的第一個(gè)簇號是從2開(kāi)始的,要計算其對應的扇區號應按下面的公式計算:
SecNo = SecNo + (1+9*2 +RootDirSectors -2 -1)
其1個(gè)引導扇區,2個(gè)FAT表,0個(gè)隱藏扇區,RootDirSectors個(gè)根目錄,由于第一個(gè)簇號事2故要減去2,編號是從0開(kāi)始的故要減去個(gè)1。
Nasm代碼
mov si, LoaderFileName ; ds:si -> "TEST TXT"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 =
BaseOfLoader*10h+100
cld ;設置方向標志
mov cx, [BSP_RootEntCnt]
mov [RootDirectItemNum], cx ;根目錄的條數
LABEL_SEARCH_FOR_LOADERBIN:
cmp word [RootDirectItemNum], 0
jz NOLOADER
dec word [RootDirectItemNum]
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比較了 11 個(gè)字符都相等, 表示找到
dec cx
lodsb ; ds:si -> al
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT ; 只要發(fā)現不一樣的字符就表明本 DirectoryEntry 不是
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME
LABEL_DIFFERENT:
and di, 0FFE0h
add di, 020h
and si, LoaderFileName
jmp LABEL_SEARCH_FOR_LOADERBIN
NOLOADER:
mov dh, 0
call DispStr
jmp $
LABEL_FILENAME_FOUND:
and di, 0FFE0h
mov eax, [es:di+0x1C]
mov [FileLength], eax
add di, 01Ah ; di -> 首 Sector(偏移)
mov cx, word [es:di]
push cx ;cx里面放的是Loader的第一個(gè)扇區號(數據區的從2開(kāi)始)
3.FAT表中的下一個(gè)簇號
同樣的道理先將整個(gè)FAT表讀到內存中:
Nasm代碼
;**讀取Fat表
mov ax, BaseOfLoader
sub ax, FatOffset ;分配5k內存(512*10/1024)
mov es, ax
mov bx, 0 ;將Fat讀到es:bx
mov ax, [BPB_RsvdSecCnt];Fat開(kāi)始扇區 1
mov cl, [BPB_FATSz16]
call ReadSector
然后查找下一個(gè)簇號,由于可能多次調用寫(xiě)成了一個(gè)函數GetFATEntry():
參數ax:簇號
返回值ax:下一個(gè)簇號,需要注意簇號大于等于0xFF8表示文件的最后一個(gè)簇,為0xFF7表示壞簇。
Nasm代碼
;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 作用: ax
;
GetFATEntry:
push bp
mov bp, sp
sub esp, 2
mov word [bp-2], 0
push es
push bx
push dx
mov bx, 3
mul bx ;ax = ax * 3
mov bx, 2
div bx ;商在ax 余數在dx
cmp dx, 0
jz LABEL_EVEN ;偶數
mov word [bp-2], 1 ;奇數
LABEL_EVEN:
mov bx, ax
mov ax, BaseOfLoader
sub ax, FatOffset
mov es, ax
mov word ax, [es:bx]
cmp word [bp-2], 0
jz LABEL_EVEN_2
shr ax, 4
LABEL_EVEN_2:
and ax, 0FFFh ;低十二位
pop dx
pop bx
pop es
add esp, 2
pop bp
ret
最后給出整個(gè)的程序:
Nasm代碼
;*******************************************************************************80
;**boot.asm 軟盤(pán)引導分區
;*******************************************************************************
org 07c00h ;BIOS將把Boot Sector開(kāi)機加載到
;地址0000:7c00 并執行
;==宏===========================================================================
BaseOfStack equ 07c00h ;boot狀態(tài)下的堆?;刂?注意堆棧是向下
;生長(cháng)的)
;Loader address
BaseOfLoader equ 09000h ;TEST.TXT 被加載到的位置 ---- 段地址
OffsetOfLoader equ 0100h ;TEST.TXT 被加載到的位置 ---- 偏移地
;址 (共63K的大小)
;FAT12
SectorNoOfRootDirectory equ 19 ;Root Directory 的第一個(gè)扇區號=
;BPB_RsvdSecCnt + (BPB_NumFATs * FATSz)
RootDirSectors equ 14 ; 根目錄占用空間: RootDirSectors =
;((BPB_RootEntCnt * 32) + (BPB_BytsPerSec
; – 1)) / BPB_BytsPerSec; 但如果按照此公
;式代碼過(guò)長(cháng)
FatOffset equ 120h ;FAT表緩沖偏移
;===============================================================================
jmp short LABEL_START
nop ;必不可少的
;**以下是FAT12的磁盤(pán)頭的定義************
BS_OEMName DB 'SongYang' ;OEM String, 必須 8 個(gè)字節
BSB_BytePerSec DW 512 ;每扇區字節數
BSP_SecPerClus Db 1 ;每簇多少扇區
BPB_RsvdSecCnt DW 1 ;Boot 記錄占用多少扇區
BSP_NumFats DB 2 ;共有多少 FAT 表
BSP_RootEntCnt DW 224 ;根目錄文件數最大值
BSP_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由這個(gè)值記錄
;扇區數
BS_DrvNum DB 0 ;中斷 13 的驅動(dòng)器號
BS_Reserved1 DB 0 ;未使用
BS_BootSig DB 29h ;擴展引導標記 (29h)
BS_VolID DD 0 ;卷序列號
BS_Volab DB 'Tinix0.01 ' ;卷標, 必須 11 個(gè)字節
BS_FileSysType DB 'FAT12 ' ;文件系統類(lèi)型, 必須 8個(gè)字節
LABEL_START:
;**初始化寄存器*************************
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
;**清屏*********************************
mov ax, 0600h ; AH = 6, AL = 0h
mov bx, 0700h ; 黑底白字(BL = 07h)
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0184fh ; 右下角: (80, 50)
int 10h ; int 10h
;**軟驅復位*****************************
xor ah, ah ; ┓
xor dl, dl ; ┣ 軟驅復位
int 13h ; ┛
;**在軟盤(pán)中尋找Loader.bin(FAT12)********
;**將根目錄整個(gè)的都讀到es:bx處
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx =
BaseOfLoader:OffsetOfLoader
mov ax, SectorNoOfRootDirectory ; ax <- Root Directory 中的某 Sector 號
mov cl, RootDirSectors ;將根目錄都讀出來(lái)
call ReadSector
;mov ax, BaseOfLoader
;mov es, ax
;mov ax, cs
;mov ds, ax
mov si, LoaderFileName ; ds:si -> "TEST TXT"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 =
BaseOfLoader*10h+100
cld ;設置方向標志
mov cx, [BSP_RootEntCnt]
mov [RootDirectItemNum], cx ;根目錄的條數
LABEL_SEARCH_FOR_LOADERBIN:
cmp word [RootDirectItemNum], 0
jz NOLOADER
dec word [RootDirectItemNum]
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比較了 11 個(gè)字符都相等, 表示找到
dec cx
lodsb ; ds:si -> al
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT ; 只要發(fā)現不一樣的字符就表明本 DirectoryEntry 不是
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME
LABEL_DIFFERENT:
and di, 0FFE0h
add di, 020h
and si, LoaderFileName
jmp LABEL_SEARCH_FOR_LOADERBIN
NOLOADER:
mov dh, 0
call DispStr
jmp $
LABEL_FILENAME_FOUND:
and di, 0FFE0h
mov eax, [es:di+0x1C]
mov [FileLength], eax
add di, 01Ah ; di -> 首 Sector(偏移)
mov cx, word [es:di]
push cx ;cx里面放的是Loader的第一個(gè)扇區號(數據區的從2開(kāi)始)
;******************************
;**讀取Fat表
mov ax, BaseOfLoader
sub ax, FatOffset ;分配5k內存(512*10/1024)
mov es, ax
mov bx, 0 ;將Fat讀到es:bx
mov ax, [BPB_RsvdSecCnt];Fat開(kāi)始扇區 1
mov cl, [BPB_FATSz16]
call ReadSector
;**讀取Loader到內存*******
mov ax, BaseOfLoader
mov es, ax
mov bx, OffsetOfLoader
pop ax
LABEL_GOON_LOADING_FILE:
push ax ;保存ax
add ax, 17 + 14
mov cl, 1 ;一個(gè)扇區
call ReadSector
;
pop ax ;回復ax
call GetFATEntry;獲得下個(gè)號碼
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
add bx, [BSB_BytePerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
;打印文件內容
mov ax, BaseOfLoader
mov es, ax
mov bp, OffsetOfLoader
mov cx, [FileLength] ; CX = 串長(cháng)度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0006h ; 頁(yè)號為0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
mov dh, 0
int 10h ; int 10h
jmp $
;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName db "TEST TXT", 0 ; LOADER.BIN 之文件名
RootDirectItemNum DW 0
FileLength DW 0
; 為簡(jiǎn)化代碼, 下面每個(gè)字符串的長(cháng)度均為 MessageLength
MessageLength equ 9
BootMessage: db "No FILE " ; 7字節
;============================================================================
;----------------------------------------------------------------------------
; 函數名: DispStr
;----------------------------------------------------------------------------
; 作用:
; 顯示一個(gè)字符串, 函數開(kāi)始時(shí) dh 中應該是字符串序號(0-based)
DispStr:
push ax
push bx
push cx
push dx
push es
mov ax, MessageLength
mul dh
add ax, BootMessage
mov bp, ax ; ┓
mov ax, ds ; ┣ ES:BP = 串地址
mov es, ax ; ┛
mov cx, MessageLength ; CX = 串長(cháng)度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0006h ; 頁(yè)號為0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
int 10h ; int 10h
pop es
pop dx
pop cx
pop bx
pop ax
ret
;----------------------------------------------------------------------------
; 函數名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 從第 ax 個(gè) Sector 開(kāi)始, 將 cl 個(gè) Sector 讀入 es:bx 中
ReadSector:
; -----------------------------------------------------------------------
; 怎樣由扇區號求扇區在磁盤(pán)中的位置 (扇區號 -> 柱面號, 起始扇區, 磁頭號)
; -----------------------------------------------------------------------
; 設扇區號為 x
; ┌ 柱面號 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁頭號 = y & 1
; 每磁道扇區數 │
; └ 余 z => 起始扇區號 = z + 1
push bp
mov bp, sp
sub esp, 2 ; 辟出兩個(gè)字節的堆棧區域保存要讀的扇區數: 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
shr al, 1 ; y >> 1 (其實(shí)是 y/BPB_NumHeads, 這里
BPB_NumHeads=2)
mov ch, al ; ch <- 柱面號
and dh, 1 ; dh & 1 = 磁頭號
pop bx ; 恢復 bx
; 至此, "柱面號, 起始扇區, 磁頭號" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
mov dl, [BS_DrvNum] ; 驅動(dòng)器號 (0 表示 A 盤(pán))
.GoOnReading:
mov ah, 2 ; 讀
mov al, byte [bp-2] ; 讀 al 個(gè)扇區
int 13h
jc .GoOnReading ; 如果讀取錯誤 CF 會(huì )被置為 1, 這時(shí)就不停地讀, 直到
正確為止
add esp, 2
pop bp
ret
;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 作用: ax
;
GetFATEntry:
push bp
mov bp, sp
sub esp, 2
mov word [bp-2], 0
push es
push bx
push dx
mov bx, 3
mul bx ;ax = ax * 3
mov bx, 2
div bx ;商在ax 余數在dx
cmp dx, 0
jz LABEL_EVEN ;偶數
mov word [bp-2], 1 ;奇數
LABEL_EVEN:
mov bx, ax
mov ax, BaseOfLoader
sub ax, FatOffset
mov es, ax
mov word ax, [es:bx]
cmp word [bp-2], 0
jz LABEL_EVEN_2
shr ax, 4
LABEL_EVEN_2:
and ax, 0FFFh ;低十二位
pop dx
pop bx
pop es
add esp, 2
pop bp
ret
times 510-($-$$) db 0 ;填充剩余的空間,是生成的代碼正好為512B
dw 0xaa55 ;結束標記
編譯命令:
Java代碼
nasm -I .\include\ boot1.asm -o boot.bin
WriteFlopy.exe
WriteFlopy.exe的功能是見(jiàn)生成的boot.bin文件寫(xiě)道虛擬軟盤(pán)TINIX.IMG的引導扇區中,然后可以將該虛擬軟盤(pán)掛載,向里面新建一個(gè)文件TEST.TXT然后寫(xiě)一些東西測試。