🚀 核心目標

Bootloader 的空間非常有限(僅 512 Bytes)。今天的目標是撰寫一個函式,利用 BIOS 中斷去磁碟中讀取更多資料(即未來的 Kernel),並將其加載到記憶體中執行。


1. 理解磁碟定址:CHS 模式

在 16 位元的實模式下,我們使用的是傳統的 CHS (Cylinder-Head-Sector) 定址法來告訴 BIOS 我們要讀取磁碟的哪個位置:


2. 實作:磁碟讀取函式 disk_load

為了保持代碼整潔,將 Day 3 的列印函式移至 print_string.asm,並在主程式中使用 %include

新增加的 print_string.asm

; ---------------------------------------------------------
; 函式: print_string
; 功能: 在 16 位元實模式下使用 BIOS 中斷印出字串
; 參數: SI = 指向以 0 (null) 結尾的字串起始位址
; ---------------------------------------------------------

print_string:
    pusha             ; 將所有通用暫存器壓入堆疊保護起來 (AX, CX, DX, BX, SP, BP, SI, DI)

.loop:
    lodsb             ; 這個強大的指令會:
                      ; 1. 將 [DS:SI] 指向的內容讀入 AL
                      ; 2. 自動增加 SI (指向下一個字元)

    cmp al, 0         ; 檢查字元是否為 0 (字串結束符號)
    je .done          ; 如果是 0,跳轉到結束

    mov ah, 0x0e      ; BIOS 中斷 0x10 的功能號:0x0e (顯示字元)
    int 0x10          ; 呼叫 BIOS 視訊服務
    
    jmp .loop         ; 繼續處理下一個字元

.done:
    popa              ; 從堆疊還原所有暫存器,確保主程式的狀態不被破壞
    ret               ; 返回呼叫者

修改後的 boot.asm

[org 0x7c00]

    mov [BOOT_DRIVE], dl ; BIOS 啟動後會將磁碟代號存入 dl,先存起來備用

    ; 1. 設定堆疊 (Stack)
    mov bp, 0x8000       ; 避開 0x7c00 區域,找一塊安全的空間
    mov sp, bp

    ; 2. 準備讀取磁碟
    mov bx, 0x9000       ; 目標記憶體位址:es:bx = 0x0000:0x9000
    mov dh, 1            ; 設定讀取 1 個磁區
    mov dl, [BOOT_DRIVE] ; 使用剛才存起來的磁碟編號
    call disk_load

    ; 3. 驗證:印出 0x9000 處的資料
    mov si, 0x9000
    call print_string

    jmp $

; 引入模組
%include "print_string.asm"

; --- 磁碟讀取函式 ---
disk_load:
    push dx              ; 備份 dx (包含 dh=讀取數量)
    mov ah, 0x02         ; BIOS 讀取磁碟功能編號
    mov al, dh           ; 要讀取的磁區數量
    mov ch, 0x00         ; 磁柱編號 (0)
    mov dh, 0x00         ; 磁頭編號 (0)
    mov cl, 0x02         ; 從第 2 個磁區開始讀
    
    int 0x13             ; 呼叫 BIOS 中斷
    jc disk_error        ; 若 Carry Flag 為 1,跳轉錯誤處理

    pop dx
    cmp dh, al           ; 檢查實際讀取的數量 (al) 是否等於預期 (dh)
    jne disk_error
    ret

disk_error:
    mov si, DISK_ERR_MSG
    call print_string
    jmp $

; 資料區
BOOT_DRIVE: db 0
DISK_ERR_MSG: db 'Disk read error!', 0

; --- 第 1 磁區結束標誌 ---
times 510-($-$$) db 0
dw 0xaa55

; --- 第 2 磁區內容 (位址將位於 0x9000) ---
test_data:
    db 'Data from Sector 2!', 0
times 512-($-test_data) db 0           ; 填充第 2 磁區,確保檔案大小正確

3. 編譯、運行與驗證

編譯

nasm -f bin boot.asm -o boot.bin

注意: 此時 boot.bin 的檔案大小應該剛好是 1024 Bytes (512 * 2)。你可以使用 ls -l 檢查。