FAT12格式的引導區實現

org 07c00h


;================================================
jmp short START        
nop                ; 這個 nop 不可少

;這個結構將要被寫在軟盤的第一個扇區,至關於格式化軟盤爲FAT12格式
BS_OEMName    DB 'PAVKOOOO'    ; OEM String, 必須 8 個字節
BPB_BytsPerSec    DW 512        ; 每扇區512字節
BPB_SecPerClus    DB 1        ; 每簇1扇區   簇的定義是爲了操做系統可以更加快速的去硬盤尋址,快速定位,而不僅是使用扇區號
BPB_RsvdSecCnt    DW 1        ; 引導區使用1個扇區,不能有其餘值,由於BIOS啓動以後就會去取軟盤的第一個扇區全部數據到內存,而後將控制權交給這個引導區
BPB_NumFATs    DB 2        ; 共有2 FAT 表 ;FAT格式(12,16,32)幾乎都是2個表,第二個表用來備份。因此內容和第一個表徹底同樣
BPB_RootEntCnt    DW 224        ; 最多能存儲224個文件??不清楚爲何是這個值
BPB_TotSec16    DW 2880        ; 邏輯扇區總數:FAT12的扇區個數=2個磁頭*18個磁道*80個扇區 =2880
BPB_Media    DB 0xF0        ; 媒體描述符 ??
BPB_FATSz16    DW 9        ; 每FAT扇9個
BPB_SecPerTrk    DW 18        ; 每磁道18個扇區
BPB_NumHeads    DW 2        ; 磁頭數(面數)
BPB_HiddSec    DD 0        ; 隱藏扇區數
BPB_TotSec32    DD 0        ; 總扇區數,若是前面16位已經記錄,這個就爲0
BS_DrvNum    DB 0        ; 中斷 13 的驅動器號
BS_Reserved1    DB 0        ; 未使用
BS_BootSig    DB 29h        ; 擴展引導標記 (29h)
BS_VolID    DD 0        ; 卷序列號
BS_VolLab    DB 'PAVKOOOOOOO'; 卷標, 必須 11 個字節
BS_FileSysType    DB 'FAT12   '    ; 文件系統類型, 必須 8個字節  

BaseofStack equ 07c00h
FileEntryBegin equ 19
CsOfLoader equ 010000h    ;在實模式下,將loader.bin加載的內存地址應該爲 07c00h+引導程序(512=200h)=07e00h以後
ipofLoader equ 0100h      ;確保程序加載的內容在實模式尋址空間以內:1MB 
FileEntryCount equ 14     ;文件目錄區所佔用的扇區數
;=============引導程序開始執行====================
START:
xor eax,eax
mov ax,cs
mov ds,ax      ;數據段,視頻段,附加段都指向當前段
mov ss,ax
mov sp,BaseofStack    ;棧頂ss:sp ==> 07C00h 也就是程序開始的位置 ,棧往低地址生長,這裏沒有作堆棧溢出檢查

;復位軟驅
xor ah,ah
xor dl,dl
int 13h
;在軟盤中讀取1個扇區到內存
;由於loader.bin存在於根目錄區,根目錄區在扇區號19(0---引導扇區,1~9---FAT表1,10~18---FAT表2),因此須要將19扇區加載到內存
;整個目錄區佔用了多少個扇區? 224(個文件)*32(每一個文件佔用32個字節)/512(每一個扇區512個字節) = 14 佔用了14個扇區。
mov LoopSecNo,FileEntryBegin
BEGINREADSEC:
cmp LoopOfSec,0
jz NOLOADER
dec LoopOfSec
mov ax,CsOfLoader
mov es,ax
mov ax,ipofLoader
mov bx,ax     ;將數據讀到es:bx指向的緩存中
mov ax,LoopSecNo
mov cl,1    ;只讀1個扇區
call    ReadSector

;讀完以後,內存裏就有512字節的內容了,如今仍是尋找loader.bin
mov    si, LoaderFileName    ; ds:si -> "LOADER  BIN"
mov    di, ipofLoader
cld
mov    dx, 10h
    ;一個扇區512 / 32有16(10H)個目錄項
BEGINLOOPFILENAME:    
    ;開始一個個項目的比較
    cmp    dx, 0
    jz NEXTSECTOR   ;到下一個扇區尋找,因此須要從新加載一個扇區    
    dec    dx
    
    ;11個字母的文件名                                            ; ┛就跳到下一個 Sector
    mov    cx, 11
    COMPARENAME:
        cmp    cx, 0
        jz FILEFOUND ;文件找到了
            dec    cx
            lodsb                ; ds:si -> al
            cmp    al, byte [es:di]
            jz NEXTCHAR 
            jmp NEXTFILE
        NEXTCHAR:
        inc di        
        jmp COMPARENAME
    NEXTFILE:
    and    di, 0FFE0h   loadsb會改變di,因此須要將di指定到文件條目的首地址
    add    di, 20h   ;每一個文件條目佔有32個字符=20H
    jmp BEGINLOOPFILENAME    

NEXTSECTOR:
    add LoopSecNo,1
    jmp BEGINREADSEC

NOLOADER:
    jmp $    ;找不到的話,就卡死到這裏
;若是文件找到了,就要把文件從數據區讀取到內存
;目錄區以後就是數據區,因此數據區的第一個扇區號是19+14=33!
FILEFOUND:
and    di, 0FFE0h        ; di -> 當前條目的開始
add    di, 01Ah ;01Ah = 26 也就是文件條目地址偏移26的位置:DRI_FSTCLUS

;文件所在的數據區扇區號爲:dir.DIR_FileSize -2 + 33   ==> [es:di]
mov word ax,[es:di];
push ax   ;保存序號,getfatentry的時候須要使用
sub ax 2
add ax 33
LABEL_GOON_LOADING_FILE:
    mov cl,1
    ReadSec

    ;loader.bin可能大於512字節,就是說可能超過1個扇區(不能超過兩個,由於如今在實模式,實模式的最大尋址爲1M)
    ;因此須要讀取fat表,查看是否還有其餘的扇區

    pop ax
    call GetFATEntry
    cmp    ax, 0FFFh
        jz    FILELOADED
        ;讀取下一個未完的扇區
        sub ax 2
        add ax 33        
        ;將數據讀到es:bx指向的緩存中
        add    bx, [BPB_BytsPerSec]     ;    讀取的內存指針也應該後移512個字節(1個扇區)
    jmp    LABEL_GOON_LOADING_FILE


FILELOADED:
; **********************
; 這一句正式跳轉到已加載到內
; 存中的 LOADER.BIN 的開始處,
; 開始執行 LOADER.BIN 的代碼。
; Boot Sector 的使命到此結束。
jmp    CsofLoader:ipofLoader    
; **********************

;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 做用:
;    找到序號DRI_FSTCLUS爲 ax 的 Sector 在 FAT 中的條目, 結果放在 ax 中
;    須要注意的是, 中間須要讀 FAT 的扇區到 es:bx 處, 因此函數一開始保存了 es 和 bx

GetFATEntry:
    push    es
    push    bx
    push    ax
    mov    ax, CsOfLoader; `.
    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    cl, 2
    call    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


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;如何函數使用INT 13讀取軟盤
;http://en.wikipedia.org/wiki/INT_13H
;傳入扇區號到ax
;讀取扇區數目到cl
;將數據讀到es:bx指向的緩存中
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;在函數裏面最好使用bp而不是使用SP
ReadSec:
push bp
    mov bp,sp
    sub sp,2
        mov bype [bp-2],cl
        push bx
            mov bl,[BPB_SecPerTrk]    
            div bl   ;扇區號/18; ah=餘數,al=商
            inc ah   ;扇區號從0開始,因此須要加1
            mov cl,ah
            mov dh,al   ;al表示磁道號
            shr al,1    ;al / 2 的值爲磁道號
            mov ch,al
            and dh,1    ;dh磁頭號
            mov dl=[BS_DrvNum]
        pop bx
        ;int 13所須要的值都已經附了
        LoopWhenFeild:
            mov ah,2
            mov cl,[bp-2]
            int 13
        jc LoopWhenFeild
    add sp,2
pop bp
ret



;-.- -.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.-!
;變量定義區
LoaderFileName        db    "LOADER  BIN", 0    ; LOADER.BIN 之文件名
LoopOfSec dw FileEntryCount;
LoopSecNo dw 0;
bOdd            db    0        ; 奇數仍是偶數
;----------------------------------------------------------------------------

times     510-($-$$)    db    0    ; 填充剩下的空間,使生成的二進制代碼剛好爲512字節
dw     0xaa55                ; 結束標誌
相關文章
相關標籤/搜索