BasicOS - A Minimal Operating System
Hello World!
死循環沒什么意思,我們來嘗試輸出一句 Hello, World!
問題來了,如何在匯編中打印字符?首先,我們要設置要打印哪個字符。我們只需要將字符存儲在 ax 寄存器的低 8 位(也就是 al 寄存器),然后調用 int 0x10 中斷執行打印即可。
對于此時的 x86 CPU 來講,一共有 4 個 16 位通用寄存器,包括 ax、bx、cx 和 dx。有時候我們只需要使用 8 位,因此每個 16 位寄存器可以拆為兩個 8 位寄存器,例如 al 和 ah。
什么是中斷?簡單來講就是給 CPU 正在做的事情按下暫停,然后去執行我們指定的任務。中斷可以執行的任務被存儲在內存最開始的區域,這個區域像一張表格(中斷向量表),每個單元格指向一段指令的地址,也就是 ISR(interrupt service routines)。
為了方便在匯編中調用,BIOS 給這些中斷分配了號碼。例如,int 0x10 就是第 16 個中斷,它指向了一個打印字符的 ISR。
然而 int 0x10 中斷只知道要打印,但并不知道要怎么打印。我們這里將其設置為 TTY(TeleTYpe)模式,讓它接收字符并顯示在屏幕上,然后將光標向后移動。設置 TTY 模式的方法是將 ah 寄存器設置為 0x0e,你可以理解為傳給系統中斷的參數。
why have to load into 0x7c00?
512 字節小小的也很可愛,但顯然滿足不了操作系統龐大的欲望,因此操作系統的絕大部分代碼被放在磁盤的其它地方。這些代碼是如何加載到內存的呢?
在回答如何加載到內存之前,我們先關注另一個更緊迫的問題:加載時應該加載到內存的哪里?
答案是,引導扇區并沒有被加載到內存的 0x0000 處。這是因為內存中還需要存儲一些重要的信息,例如中斷向量表、BIOS 數據區等。這些內容需要占用一部分內存,因此有人規定,引導扇區應當被加載到 0x7c00 處。
更具體地講,開頭這塊的內存布局如下:
| Free |
0x100000 +-----------------------+
| BIOS (256 KB) |
0x0C0000 +-----------------------+
| Video Memory (128 KB) |
0x0A0000 +-----------------------+
|Extended BIOS Data Area|
| (639 KB) |
0x09FC00 +-----------------------+
| Free (638 KB) |
0x007E00 +-----------------------+
| Loaded Boot Sector |
| (512 Bytes) |
0x007C00 +-----------------------+
| |
0x000500 +-----------------------+
| BIOS Data Area |
| (256 Bytes) |
0x000400 +-----------------------+
| Interrupt Vector Table|
| (1 KB) |
0x000000 +-----------------------+
編譯
nasm -f bin boot.asm -o boot.bin
創建磁盤映像
創建一個 1.44MB 的空磁盤映像:
dd if=/dev/zero of=floppy.img bs=512 count=2880
將引導扇區寫入磁盤映像:
dd if=boot.bin of=floppy.img bs=512 count=1 conv=notrunc
測試引導扇區
qemu-system-x86_64 -fda floppy.img
你應該能看到屏幕打印出 Hello, world!,然后程序進入死循環。