8、能夠讀 FAT12 了——BootSector 完工

    引導扇區只有 512 字節,過小,基本啥也幹不了,咱們只能利用引倒扇區把咱們的操做系統內核載入內存。可是一會兒把內核所有載入進來也不靠譜,還有不少事情沒作呢——起碼就還沒轉到安全模式,內核大的話,實模式可裝不下。因此正確的姿式是:引導扇區載入一個裝載程序,裝載程序負責作好準備工做後,再載入真正的內核。
安全

    那麼引導扇區的任務就很明確了:找到裝載程序(Loader.bin),而後把它載入內存。第十天初識FAT12時提到了:經過根目錄表項找到文件的第 1 個扇區,再從 FAT 表中找到其餘的扇區。模塊化

    今天的代碼不是徹底照書抄了,是按照個人理解重寫的,代碼裏我很詳細的註釋了個人思路,並且結構也很清晰,子函數功能明確,基本是按照模塊化設計的——我是深受 C 語言影響。引導扇區去掉填空的 0,大小隻有 340 字節,仍是比較滿意的。若是有人有心在看的話,但願能提出意見。函數

; Constant.inc
; 常量
; 四彩
; 2015-11-12

%ifndef _CONSTANT_INC
%define _CONSTANT_INC

; ========================================================================================
; 內存中 0x0500 ~ 0x7BFF(29.75 KB) 段和 0x7E00 ~ 0xFFFF(32.5KB)段、
;        0x10000 ~ 0x9FBFF(575 KB)段可自由使用。引導扇區段在加載完 Loader 也可以使用。
;
SEGMENTBASEOFTEMP       equ 0x7E0       ; 臨時數據被加載到內存的段地址(最多 2 個扇區)
SEGMENTBASEOFLOADER     equ 0x4000      ; Loader.SYS 被加載到內存的段地址

STACKSIZE               equ 0x400       ; Loader 的堆棧大小
; ****************************************************************************************

%endif
; FAT12.inc
; FAT12 文件系統常量及宏定義
; 四彩
; 2015-11-08

%ifndef _FAT12_INC
%define _FAT12_INC

; ========================================================================================
BYTESPERSECTOR              equ 512 ; 每扇區字節數

IFATFIRSTSECTOR             equ 1   ; FAT 表的起始邏輯扇區號
IROOTDIRECTORYFIRSTSECTOR   equ 19  ; 根目錄區的起始邏輯扇區號
IDATAFIRSTSECTOR            equ 33  ; 數據區的起始邏輯扇區號
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系統的引導扇區頭部格式宏
; 調用格式:FAT12Head  Label_RealEntry, OEMName, VolLab
;           Label_RealEntry : 程序入口標籤
;           OEMName         : 廠商名稱(8 字節長,不夠的填空格)
;           VolLab          : 卷標(11 字節長,不夠的填空格)
%macro FAT12Head 3
;   名稱                              偏移 長度 說明                      3.5英寸軟盤內容
    jmp %1                          ; 0x00  3   跳轉指令,指向程序入口     jmp RealEntry
    nop
    BS_OEMName      db %2           ; 0x03  8   廠商名稱                   自行定義
    BPB_BytsPerSec  dw 512          ; 0x0B  2   每扇區字節數               512
    BPB_SecPerClus  db 1            ; 0x0D  1   每簇扇區數                 1
    BPB_RsvdSecCnt  dw 1            ; 0x0E  2   保留扇區數                 1
    BPB_NumFATs     db 2            ; 0x10  1   FAT表份數                  2
    BPB_RootEntCnt  dw 224          ; 0x11  2   根目錄中最多容納的文件數   224
    BPB_TotSec16    dw 2880         ; 0x13  2   扇區總數 (FAT十二、16)     2880
    BPB_Media       db 0xF0         ; 0x15  1   介質描述符                 0xF0
    BPB_FATSz16     dw 9            ; 0x16  2   每一個FAT表所佔的扇區數      9
    BPB_SecPerTrk   dw 18           ; 0x18  2   每磁道扇區數               18
    BPB_NumHeads    dw 2            ; 0x1A  2   磁頭數                     2
    BPB_HiddSec     dd 0            ; 0x1C  4   隱藏扇區數                 0
    BPB_TotSec32    dd 2880         ; 0x20  4   扇區總數(FAT32)          2880
    BS_DrvNum       db 0            ; 0x24  1   磁盤驅動器號               0
    BS_Reserved1    db 0            ; 0x25  1   保留(供NT使用)           0
    BS_BootSig      db 0x29         ; 0x26  1   擴展引導標記               0x29
    BS_VolD         dd 0            ; 0x27  4   卷標序列號                 0
    BS_VolLab       db %3           ; 0x2B  11  卷標                       自行定義
    BS_FileSysType  db 'FAT12'      ; 0x36  8   文件系統類型名             FAT12
                                    ; 0x3E  448 引導代碼及其餘填充字符
                                    ; 0x1FE 2   結束標誌                   0xAA55
;
; BPB:BIOS Parameter Block,BIOS 參數塊
; BS:Boot Sector,引導扇區
%endmacro
; ****************************************************************************************


; ========================================================================================
; 目錄表項結構
struc DirectoryItem
;    字段名                           偏移 長度 說明
    .DIR_Name       resb    11      ; 0x00  11  文件名 8 + 3(大寫,不夠長度末尾填空格)
    .DIR_Attr       resb    1       ; 0x0B  1   文件屬性
                    resb    10      ; 0x0C  10  保留
    .DIR_WrtTime    resw    1       ; 0x16  2   最後修改時間
    .DIR_WrtDate    resw    1       ; 0x18  2   最後修改日期
    .DIR_FstClus    resw    1       ; 0x1A  2   此條目對應的開始簇號(即 FAT 表項序號)
    .DIR_FileSize   resd    1       ; 0x1C  4   文件大小
endstruc
; ****************************************************************************************

%endif
; BootSector.asm
; 引導扇區
; 四彩
; 2015-11-12

; ========================================================================================
; 電腦的啓動過程:
; 一、80x86 CPU 啓動後(加電或復位),CS : IP 被設置爲 0xFFFF : 0x0,CPU 今後處讀取指令
;    開始執行。該單元在基本輸入輸出系統(Basic Input/Output System,BIOS)的地址範圍內,
;    這裏是一條跳轉到 BIOS 中真正啓動代碼處的指令。
; 二、BIOS 首先進行加電自檢(Power-On Self-Test,POST),而後進行更完整的硬件檢測,並加載
;    相關設備。
; 三、接下來按啓動順序(Boot Sequence)讀取第一個設備的第一個扇區,若是該扇區最後兩個字節
;    是 0x55 和 0xAA,代表這個設備能夠用於引導;若是不是,代表這個設備不能用於引導,BIOS
;    繼續讀取啓動順序中的下一個設備……直到找到啓動設備。BIOS 把第一個啓動設備的第一個扇區
;    讀到內存 0x7C00 處,而後把控制權交給該處。
; 四、操做系統經過改寫啓動設備的第一個扇區,被讀入內存後,從內存 0x7C00 處開始接管電腦。
; ****************************************************************************************


; ========================================================================================
; 頭文件及常量定義
; ----------------------------------------------------------------------------------------
%include "./INC/Constant.inc"
%include "./INC/FAT12.inc"
; ----------------------------------------------------------------------------------------
    org 0x7C00
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系統引導扇區的頭部(前 62 字節)
    FAT12Head _main, "NASM+GCC", "TestX_v0.01"
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系統引導扇區的引導代碼(從第 62 字節開始)
; ----------------------------------------------------------------------------------------
; 程序入口
_main:
    cli
    cld
    xor eax, eax

    ; 初始化寄存器
    mov ax, cs
    mov ds, ax
    mov ss, ax
    mov ax, 0x7C00
    mov bp, ax
    mov sp, ax

    mov si, strBootMsg
    call PrintStr

    ; 尋找 Loader
    mov si, LoaderFileName
    call SearchFile

    ; 加載 Loader
    push SEGMENTBASEOFLOADER
    pop es
    mov bx, STACKSIZE
    call LoadFile

    ; 控制權交給已加載到內存的 loader
    jmp SEGMENTBASEOFLOADER : STACKSIZE


; 如下定義子函數
; ----------------------------------------------------------------------------------------
; 函數功能:尋找文件的起始位置
; 入口參數:ds : si = 文件名的存放地址
; 出口參數:ax = loader 文件的起始 FAT 表項序號
SearchFile:
    push bp
    mov bp, sp
    sub sp , 2 * 2                          ; 爲局部變量分配空間

    push di
    push si
    push dx
    push cx
    push bx

    ; 待讀取的根目錄區邏輯扇區號
    mov word[bp - 2], IROOTDIRECTORYFIRSTSECTOR
    ; 待查找的根目錄區扇區數
    mov word[bp - 2 * 2], IDATAFIRSTSECTOR - IROOTDIRECTORYFIRSTSECTOR
    mov di, si

    ; 逐個扇區尋找
    push SEGMENTBASEOFTEMP                  ; Read1Sector 要用到 es
    pop es
.Search_NextSector:
    mov ax, [bp - 2]
    xor bx, bx
    call Read1Sector
                                            ; cx 統計一個扇區內未匹配的表項數
    mov cx, 16                              ; = [BPB_BytsPerSec] / DirectoryItem_size
.Search_ThisSector:
    ; 匹配文件名
    mov si, di
    mov dx, 11                              ; dx 統計未匹配的文件名字符數
.Match_FileName:
    lodsb
    cmp al, byte[es : bx]
    jnz .Match_NextItem
    dec dx
    jz .Found
    inc bx
    jmp .Match_FileName

.Match_NextItem:
    and bx, 0b1111111111100000              ; 回當前表項的開始處
    add bx, 32                              ; 指向下一個表項(一個表項 32 字節,佔用 5 位)
    loop .Search_ThisSector

    ; 判斷是否讀完根目錄區全部扇區:讀完說明沒找到,沒讀完就繼續下一個
    dec word[bp - 2 * 2]
    jz .NotFound
    inc word[bp - 2]
    jmp .Search_NextSector

.NotFound:
    mov si, strNotFoundFile
    call PrintStr
    jmp $

.Found:
    mov ax, word[es : bx + 0x1A - 11 + 1]   ; 指向當前表項中的 .DIR_FstClus

    pop bx
    pop cx
    pop dx
    pop si
    pop di

    mov sp, bp
    pop bp
    ret

; ----------------------------------------------------------------------------------------
; 函數功能:從軟盤裝載文件到內存
; 入口參數:ax = 該文件的起始 FAT 表項序號
;           es : bx = 存放數據的內存緩衝區地址
; 出口參數:無
LoadFile:
    push bp
    mov bp, sp

    push dx
    push cx
    push bx
    push ax

.Load:
    push bx
    push ax

    add ax, IDATAFIRSTSECTOR - 2            ; FAT 表項序號轉換爲邏輯扇區號
    call Read1Sector

    pop ax
    call GetEntryValue
    pop bx
    cmp ax, 0xFF8                           ; FAT 表項的值大於等於 0xFF8,表示文件結束
    jae .Return                             ; 未檢查壞扇區 —— 虛擬的不會壞的

    add bx, BYTESPERSECTOR
    jmp .Load

.Return:
    POP ax
    pop bx
    pop cx
    pop dx

    mov sp, bp
    pop bp
    ret

; ----------------------------------------------------------------------------------------
; 函數功能:取得 FAT 表中指定序號表項的值
; 入口參數:ax = FAT 表項序號
; 出口參數:ax = 對應的 FAT 表項值(即下一個扇區的 FAT 表項序號)
GetEntryValue:
    push bp
    mov bp, sp

    push es                                 ; 讀取 FAT 表時要使用 es 暫存數據
    push dx
    push cx
    push bx

    ; 計算該表項序號所在的邏輯扇區號和在該扇區的偏移量
    xor dx, dx                              ; 字節號(ax * 12 / 8)
    mov bx, 3
    mul bx
    mov bx, 2
    div bx

    mov cx, dx                              ; 保存字節號的奇偶性(0 = 偶數,1 = 奇數)

    xor dx, dx
    mov bx, BYTESPERSECTOR
    div bx
    add ax, IFATFIRSTSECTOR                 ; 邏輯扇區號
    push dx                                 ; 保存在該扇區的偏移量

    ; 讀取連續 2 個扇區(表項可能跨扇區)
    push cx                                 ; Read1Sector 函數改變了 cx、ax
    push ax

    push SEGMENTBASEOFTEMP
    pop es
    xor bx, bx
    call Read1Sector
    pop ax
    inc ax
    mov bx, BYTESPERSECTOR
    call Read1Sector
    pop cx

    ; 讀出 16 位,奇數項取高 12 位、偶數項取低 12 位(低低高高存放原則),獲得項值
    pop bx                                  ; 偏移量(上面壓進去的 dx 值)
    mov ax, [es : bx]
    jcxz .Even
    shr ax, 4
.Even:
    and ax, 0b0000111111111111              ; 奇數項高 4 位已爲 0 執行此操做值也不變

    pop bx
    pop cx
    pop dx
    pop es

    mov sp, bp
    pop bp
    ret

; ----------------------------------------------------------------------------------------
; 函數功能:從軟盤讀取 1 個邏輯扇區
; 入口參數:ax = 邏輯扇區號
;           es : bx = 存放數據的內存緩衝區地址
; 出口參數:同 ah = 二、int 0x13
Read1Sector:
    push bp
    mov bp, sp

    push dx
    push cx

    ; 由 LBA 計算 CHS
    mov dl, 18
    div dl
    mov ch, al
    mov dh, al
    mov cl, ah
    shr ch, 1
    inc cl
    and dh, 1

    ; 讀一個扇區
    mov ax, 0x0201
    xor dl, dl
    int 0x13

;    cmp ah, 0                               ; 虛擬軟盤不會出錯
;    jz .Return

;    call PrintMsg
;    db "Error to read Floppy Disk !", `\r\n`, 0
;    jmp $

.Return:
    pop cx
    pop dx

    mov sp, bp
    pop bp
    ret

; ----------------------------------------------------------------------------------------
; 函數功能:顯示字符串
; 入口參數:ds : si = 字符串地址
; 出口參數:無
PrintStr:
    push bp
    mov bp, sp

    push si
    push ax

    mov ah, 0x0E                            ; 功能號,0x0E:顯示一個字符,光標跟隨字符移動
.Print:
    lodsb
    cmp al, 0                               ; 字符串以 0 結尾
    je .Return
    int 0x10
    jmp .Print

.Return:
    pop ax
    pop si

    mov sp, bp
    pop bp
    ret
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系統引導扇區引導數據部分(字符串)
    strNotFoundFile db "Error 404", `\r\n`, 0
    strBootMsg      db "TestX is booting ...", `\r\n`, 0
    LoaderFileName  db "LOADER  SYS", 0, 0  ; loader 文件名(8 + 3格式,長度不夠的填空格)
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系統引導扇區引導代碼的剩餘部分用 0 填滿,最後兩個字節置結束標誌(0xAA55)
    times 510 - ($ - $$) db 0
    dw 0xAA55
; ****************************************************************************************
; Loader.asm
; 加載程序
; 四彩
; 2015-11-12

[SECTION .text]
; ========================================================================================
; 常量定義及其餘頭文件
; ----------------------------------------------------------------------------------------
%include "./INC/Constant.inc"

; ----------------------------------------------------------------------------------------
    org STACKSIZE
; ****************************************************************************************


; 程序入口
; ========================================================================================
_main:
   ; 初始化寄存器
    mov ax, cs
    mov ds, ax
    mov ss, ax
    mov bp, STACKSIZE
    mov sp, STACKSIZE

    call PrintMsg
    db "Loader is loaded ...", `\r\n`, 0

    mov si, strHelloWorld
    mov cl, 0b00000010
    mov dx, 0x0510
    call ShowStr
    jmp $

    strHelloWorld db "Hello World !", 0

; ----------------------------------------------------------------------------------------
; 函數功能:顯示緊跟在調用指令後定義的字符串
; 入口參數:無
; 出口參數:無
; 注意:本函數改變了寄存器 ax、si 的值,若有必要,父函數應在調用前自行保存
PrintMsg:
    pop si                                  ; si = ip

    mov ah, 0x0E                            ; 功能號,0x0E:顯示一個字符,光標跟隨字符移動
.Loop:
    lodsb
    cmp al, 0                               ; 字符串以 0 結尾
    je .Return

    int 0x10
    jmp .Loop

.Return:
    push si                                 ; 恢復 ip
    ret


; ----------------------------------------------------------------------------------------
; 函數功能:直接寫顯存顯示字符串
; 入口參數:cl = 顏色屬性
;           dh、dl = 屏幕行(0 ~ 24)、列座標(0 ~ 79)
;           ds : si = 待顯示字符串地址
; 出口參數:無
; 80 * 25 彩色字模式的顯存第一頁(共 4 頁)在內存中的地址爲 B8000H ~ B8F9FH,向該地址寫入
; 內容將當即顯示在屏幕上,共可顯示 25 行、80 列,屏幕左上角爲原點(0,0)。
; 每一個字符在顯存中佔兩個字節,第一個字節是 ASCII 碼,第二字節是顏色屬性(共 256 種):
;  位:   7     6 5 4     3     2 1 0
; 含義:  BL    R G B     I     R G B
;        閃爍  背景顏色  高亮  前景顏色
ShowStr:
    push bp
    mov bp, sp

    push es
    push di
    push si
    push dx
    push cx
    push ax

    mov ax, 0x0B800
    mov es, ax

    ; 由行列座標計算顯存偏移量
    mov al, 160
    mul dh
    mov di, ax
    mov al, 2
    mul dl
    add di, ax

.Loop:
    mov al, [ds : si]
    cmp al, 0
    jz .Return
    mov [es : di], al
    mov [es : di + 1], cl
    inc si
    add di, 2
    jmp .Loop

.Return:
    pop ax
    pop cx
    pop dx
    pop si
    pop di
    pop es

    mov sp, bp
    pop bp
    ret

; ****************************************************************************************

   看下運行效果,還不錯,今天的任務結束!oop


相關文章
相關標籤/搜索