ブートで遊ぼっ!(3)

86系のPCをブートさせて遊んでみよう、というお話の3回目になります。テキストは『OSを書く:初歩から一歩ずつ』です。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。
前回はMakefileを作ったところで気力が尽きちゃったんだけど、今回はどうなるかなぁ(不安)
まぁ、最低でも文字を表示するところは終わらせるつもりですけど…。

スクリーンにプリントする

画面に文字を表示します。定番と言うか、「またお前か!」というか、何と言うか。
Hello World
です。
細かい説明は(不十分かもしれませんが)テキストにありますので、まずはそちらを参考にしてください。ざっくり言うと、表示したい文字列をメモリ上に定義して、その最初のアドレスから文字列が終わるまで1文字ずつ表示しています。
ていうか、これくらいしか言うこと無くない?これ以上何を言えと?
とか思ったり思わなかったり。
ともあれ、ソースを作成します。

; boot.asm
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov si, msg
mov ah, 0x0E

print_character_loop:
lodsb

or al, al
jz hang

int 0x10

jmp print_character_loop

msg:
db 'Hello, World!', 13, 10, 0

hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.2.1

アセンブル & 実行します。makeさん大活躍です。

make qemu

『Hello World!』が表示されました!

めでたしめでたし!
おしまい。

とか言いたいところなんですが、もし私が読み手としてこの記事をみたら
「ふざけんな!ボケぇ」
とか言いたくなるような気がするので、少しだけ追加です。

ahとal

ふと。
ahとalってaxレジスタのハイとロー、上位バイトと下位バイトじゃなかったっけ?
なら

mov ah, 0x0
mov al, 0x3

って

mov ax, 0x0003

とかにしても動くんじゃない?
というわけでソース修正。
こんな感じ。5行目が修正箇所ですね。

; boot.asm
mov ax, 0x07c0
mov ds, ax

mov ax, 0x0003
int 0x10

mov si, msg
mov ah, 0x0E

print_character_loop:
lodsb

or al, al
jz hang

int 0x10

jmp print_character_loop

msg:
db 'Hello, World!', 13, 10, 0

hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.2.2

で、実行。

無事に動きました。

or al, al の謎

まぁ、謎っていうほどのモノじゃないんですが、最初にこれを見た時に非常に悩みました。だってね

or X, Y

ってXとYのorをとって結果をXに設定する、という命令なわけで。
普通のプログラム言語っぽく書くなら

X = X or Y

なわけじゃないですか。
今回はXもYもalなので

al = al or al

になります。で、

同じ値のorをとると、元の値になります。
なので

al = al

になるという。ということはコードとしては

mov al, al

と同等になるという。
意味あるの?

ここでアセンブラを使ってるととても重要な機能と意味を持つフラグレジスタのお話がちょろっと出ます。
アセンブラの場合、全ての命令ではないのですが命令の実行結果によってフラグレジスタが更新されます。雑な言い方で申し訳ないんですが、アセンブラのコーディングでは
命令の実行
フラグの確認
といったスタイルが多くなります。

or al, al
jz hang

がこのパターンですね。
jz命令はゼロフラグが立っていたら(言い換えれば結果がゼロだったら)指定先に飛べという動作です。
表にするとこんな感じ。

lodsbで読
んだalの値
or la, alの結果ゼロフラグ文字
 1回目0x480x480H
 2回目0x650x650e
 3回目0x6c0x6c0l
 4回目0x6c0x6c0l
 5回目0x6f0x6f0o
: :: :: :: :: :
 13回目0x210x210!
 14回目0x0d0x0d0CR
 15回目0x0a0x0a0LF
 16回目0x000x001NULL
alの値とゼロフラグ

定義した文字列で一番最後の0x00の時だけ、ゼロフラグが1になります。
なので、最初から文字を1文字ずつ表示して0x00まで来たらhangに飛ぶという処理となり、めでたく『Hello World!』が表示されるというわけです。

蛇足

『alがゼロだったら~』という処理なんだから素直に

cmp al, 0x00
je hang

じゃいけないの?
という向きもあると思います。なので動かしてみましょう。
ソースはこれ。15行目、16行目を修正してます。

; boot.asm
mov ax, 0x07c0
mov ds, ax

mov ah, 0x0
mov al, 0x3
int 0x10

mov si, msg
mov ah, 0x0E

print_character_loop:
lodsb

cmp al, 0x00
je  hang

int 0x10

jmp print_character_loop

msg:
db 'Hello, World!', 13, 10, 0

hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

PlayWithBoot.2.3

ちゃんと動きます。

う~ん、どっちを使うかなんですが、or al, al を使った方がいいことあるんでしょう、きっと。知らないけど。

《2024/4/27 15:25:24》

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です