在 16 位元實模式(Real Mode)下,學習如何利用 BIOS 中斷 結合 迴圈 (Loop) 與 指標 (Pointer),建立一個可重複使用的字串列印函式。
在 Day 2 中,我們印出 "Hello" 是透過手寫五次 int 0x10。如果今天要印出一整段系統日誌,這種做法顯然不可行。我們需要建立一個通用函式,只要給它一個記憶體位址,它就能幫我們把字串印完。
在編寫代碼前,需要掌握這三個關鍵:
SI (Source Index):在 x86 組合語言中,我們習慣用它作為讀取資料的「指針」。lodsb:這是一個極其高效的指令。它會執行兩件事:
[SI] 指向的位址讀取 1 Byte 到 AL。SI 加 1,指向下一個字元。pusha / popa):為了防止函式內部的操作弄亂主程式正在使用的暫存器數值,我們先把所有狀態「壓入堆疊」儲存,執行完再「彈出」還原。boot.asm[org 0x7c00]
; --- 主程式區段 ---
mov si, HELLO_MSG ; 將字串的首位址交給 SI
call print_string ; 呼叫函式
mov si, BYE_MSG ; 切換到另一個字串
call print_string
jmp $ ; 無限循環,卡住 CPU
; --- 函式:print_string ---
; 目的:印出以 0 結尾的字串
print_string:
pusha ; 保護暫存器現場
.loop:
lodsb ; 從 SI 讀取到 AL,SI 自動遞增
cmp al, 0 ; 判斷是否為字串結尾 (Null Terminator)
je .done ; 如果是 0,跳轉到結束標籤
mov ah, 0x0e ; BIOS 電傳打字模式
int 0x10 ; 呼叫中斷顯示字元
jmp .loop ; 繼續循環處理下一個字元
.done:
popa ; 還原暫存器現場
ret ; 返回主程式
; --- 資料區段 ---
HELLO_MSG:
db 'Hello, OS World!', 0x0d, 0x0a, 0 ; 0x0d, 0x0a 是換行與回車
BYE_MSG:
db 'Successfully printed string!', 0x0d, 0x0a, 0
; --- 引導扇區填充分區 ---
times 510-($-$$) db 0
dw 0xaa55
同樣地,在 WSL2 中執行編譯與模擬:
`# 編譯成二進位檔 nasm -f bin boot.asm -o boot.bin
qemu-system-i386 -drive format=raw,file=boot.bin`
你會看到 QEMU 視窗整齊地出現了兩行文字: