「ブートで遊ぼっ!」カテゴリーアーカイブ

ブートで遊ぼっ!(4)

86系のPCをブートさせて遊んでみよう、というお話の4回目になります。テキストは『OSを書く:初歩から一歩ずつ』です。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。
一応前回まででテキストの本文(?)については触れたので、今回からは練習問題について書いてみようかと思います。
まずは練習問題の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, x86!', 13, 10, 0

hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

実行すると、こう。

文字列を追加してみる

さすがに前節で「練習問題クリア!」とかいうのは何なので、文字列を追加して2つの文字列を表示してみます。これも難しくないですね。
ドカンとコピペして該当箇所をちょこっと変えます。
こんな感じ。

; 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 print_2nd_character

int 0x10

jmp print_character_loop

print_2nd_character:
mov si, msg2
mov ah, 0x0E

print_character_loop2:
lodsb

or al, al
jz hang

int 0x10

jmp print_character_loop2

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

msg2:
db 'Hello, x86!', 13, 10, 0

hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

22行目から34行までがコピーした部分です。設定する文字列のアドレスだけ変更してます。39行目と40行目は追加した文字列の定義ですね。
実行するとこう。

サブルーチンにしてみる

一応、練習問題としてはクリアなのかもしれませんが、ソフトウェアを作る立場としては、ほとんど同じで少しだけ違うコードって気持ちが悪いわけです。
こういう場合、どんな言語でも処理を共通化して、呼び出す形にします。
今回もそうしてみましょう。axに表示したい文字列のアドレスを指定して、サブルーチンを呼び出します。
こんな感じ。

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

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

mov si, msg
call print_character

mov si, msg2
call print_character

jmp hang

; sub routine
;
print_character:

mov ah, 0x0E

print_character_loop:
lodsb

or al, al
jz print_character_end

int 0x10

jmp print_character_loop

print_character_end:

ret

; define messages
;
msg:
db 'Hello, World!', 13, 10, 0

msg2:
db 'Hello, x86!', 13, 10, 0

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

うん。すっきりしました。
実行するとこう。前と変わらないですね^^;
(というか変わったら困ります。それはバグです。)

数値を表示してみる

さて、今までは文字を表示していたんですが、『別のもの』を『文字列でないもの』と解釈することもできるわけです。実際『レジスタの中身を見たい』とか、数値を表示したくなるわけで。例えば数値の1を表示したいとか。
なにも考えずに書いたソースがこれ。

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

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

mov ah, 0x0E
mov al, 0x01
int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

実行すると、こうなります。

あらまぁ、望んだ結果にはなりませんでした。
表示したいのは1で、表示できるのは”1″なんですよね。
文字の”1″を指定してあげないと1は表示されません。
結論から言うと文字の”1″は文字コード(これは数値です)の0x31になります。
なのでソースはこうなります。

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

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

mov ah, 0x0E
mov al, 0x01

add al, 0x30

int 0x10

; end of proccess
;
hang:
jmp hang

times 510-($-$$) db 0

db 0x55
db 0xAA

実行してみます。

望み通り1が表示されました。

ここまでやれば、練習問題1はクリアしたと言っていいんじゃないかな。
なので、今回はここまでです。

《2024/5/3 17:07:24》

ブートで遊ぼっ!(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

アセンブル & 実行します。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

で、実行。

無事に動きました。

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

ちゃんと動きます。

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

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

ブートで遊ぼっ!(2)

86系のPCをブートさせて遊んでみよう、というお話の2回目です。テキストは『OSを書く:初歩から一歩ずつ』です。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。
今回はMakefileを作るのと、余裕があれば文字を表示するところまでかな。届くかな?

ビルドプロセスを短縮する

Makefileを作ってビルド…というかアセンブルから実行までを簡単にしましょう、というお話。
『今時make?』という気もしなくはないんですが、アセンブラでboot周りのことをやるのなら『やっぱmakeよね!』とも思います。
やっていることというか、やりたいことは凄く単純で

と、2回コマンドを打っていたのを

で済まそうということ。
いちいちだらだらタイプしたくないし、ヒストリから拾うのも面倒だし、という怠け者の発想ですね。怠けるためにメンドクサイ仕込みをするとか、世の中よくあることです。
makeの使い方とかMakefileの書式とかを知りたい方は、ネットで調べてもらえば沢山記事があると思いますので、そちらに任せます。

テキスト通りMakefileというファイルを作成します。


実行してみます。



ん?
なにこれ?

って、どういうこと?

ちょっと調べてみたら、Makefileではコマンドの前にタブがないといけないみたいです。
何て言うか…『年寄りはこれだから困る』とか言いたくなってみたり?
気を取り直してMakefileを修正します。

2行目、5行目、8行目の先頭にタブを入れます。
で、改めて実行します。

ちゃんと動きました。めでたしめでたし。

ここで気力が尽きました。文字列の表示は次回だなぁ…。

《2024/4/19 11:14:24》

ブートで遊ぼっ!

x86系のPCをブートさせて遊んでみようと思います。
OSを作ろうとか、大それたことは言いません。技術力も知識も能力も全く足りないですから。
そもそもOSを作るのって、とっても

テキストは『OSを書く:初歩から一歩ずつ』です。ざっと目を通してみたんですが、それほどボリュームないし、解説もそれなりにあるし、これなら1日あれば確認できるかなと。チュートリアルだしねw

前提条件

テキストがDebianを前提にしているので、それに乗っかります。
今回のわたしの環境は

Intel Core i7
Windows10
VMwera Workstation 17 Plyer
Debian 12.4

です。Debianはこのために新規で入れました。ほとんど、まっさらです。

テキストで『nasm』『build-essential』『qemu』が必要だというのでインストールします。
Debianを起動してターミナルエミュレーターを実行し、以下を入力します。

む!

とか返ってきた。
どうしよう、どうする?

テキストは2016年とか2017年に書かれたものだし、今は2024年だし、8年もあれば生まれた子供も小学校3年生?になるし、パッケージ構成が変わっててもおかしくはないなぁ。
ともあれ、これでお終いにするってのは、あまりに情けなさすぎる。どうしたものか。

少し調べてみたら、色々な情報がありました。余計なものをインストールしても邪魔にならなければいいんだけれど、できるなら最小限の構成にしたいなぁ。

なので、これでどうだっ!

インストールは成功。
ではソースを書きましょう。
ファイル名は『boot.asm』です。

コードの解説はテキストに任せて、書かれたとおりにオペレーションします。

問題ないですね。

動きました。こんな感じです。


これで前提条件が終了です。

なんか、思ったより先が長そう…

《2024/4/17 0:04:09》