05 印出在 memory 中的 character

程式要在 memory 裡才能執行,那麼我們寫在 boot sector 裡的程式,也是被 BIOS load 到 memory 中的某個地方才得以執行的。

這個位置在 0x7c00,也就是 BIOS 會將 boot sector 的內容 load 到 memory 0x7c00 位置的地方。

以下是開機後的 memory layout:

接下來希望可以印出放在 memory 內的資料。

我們用些方式來嘗試印印看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mov ah, 0x0e

; first attempt
mov al, the_secret
int 0x10

; second attempt
mov al, [the_secret]
int 0x10

; third attempt
mov bx, the_secret
add bx, 0x7c00
mov al, [bx]
int 0x10

jmp $ ; infinite loop

the_secret:
db "X"

; padding
times 510-($-$$) db 0
dw 0xaa55

label 表示從 code 一開始到該位置的 offset,the_secret 的值即是從 code 最開始到該位置的 offset。

[bx] 是是去 bx register 內容所代表的的 memory address 拿資料,例如 bx 的值是 0x1e,則 [bx] 就會是 memory address 0x1e 位置上的值,而非這段 code 被 load 到的 memory address 再加上 offset。

最後只有第三種方式可以成功印出 X,這印證了 boot sector 確實被 load 到 memory 0x7c00 的位置。

既然 BIOS 會把 boot sector load 到 0x7c00,這表示對每個 label 我們都要自己加上 0x7c00 才能得到正確的 address。每次要在 offset 加上 0x7c00 也太麻煩,所以可以在最開頭加上以下指令,就能表示「這段 code 預期會被 load 到 memory 哪個位置」,也就不用自己手動加:

1
[org 0x7c00]

加上這行後,就會是第二種方式印出 X 了。

Define strings

定義 string 需要知道關於 string 的兩件事:

  1. string 所在 memory address
  2. string 長度

前者可以用 label,如下所示:

1
2
my_string:
db 'Hello World'

my_string 就是 string Hello World 的 memory address。指令 db 是 declare byte(s) of data 的意思,也就是直接將這幾個 byte 寫進 binary output file,而不要把它們當作 instruction。

那麼如何知道 string 的長度呢?

一種 convention 是定義 null-terminating string,也就是在 string 結尾加上一個 byte,其值為 0,像這樣:

1
2
my_string:
db 'Hello World',0

這樣在處理 string 時,只要遇到值為 0 的 byte 就知道 string 結束了。