AT互換機のブートの仕組み
私たちがPCの電源を入れるとしばらく待てばwindowsなどのOSが起動される。まあ当然のことなんだけどね。
OSを一から書き上げる場合、PCの電源を入れてからOSが起動するまでPCの内部でどの様な事が起こっているのかを少し知っていなきゃいけない。
ここではいつも使っているAT互換機のブートの仕組みを解説して。「OSを作ろう」で書いたプログラムがどうのように実行されたのかを理解しよう。
電源を入れてみた
電源を入れるとPCはマザーボード上にあるRAMに記憶されているBIOS(Basic Input/Output System)と呼ばれるプログラムをメモリ上に読み込み実行する。このプログラムは基本的な入出力ができるプログラムで、実行されるとまず周辺機器を初期化するこれはOSの自作には重要ではないので詳しく知る必要はない。
次にBIOSはディスクがつながれているかをチェックする。チェックの順番はBIOSの設定に依存する。大抵はFD>CD>HDDという順番である。
BIOSはディスクを見つけるとそのディスクの先頭から510バイト目に0xAA55という記述があるかを調べる。あればそのディスクの先頭のセクターをメモリ上(0x7C00)に読み込み制御を移す。このセクターをFDではブートセクター、HDDではマスターブートレコードと呼ぶ。
ブートセクターのプログラム
つまり電源を入れてBIOSの仕事が一段落したらブートセクターのプログラムへ制御が移る。このプログラムが「OSを作ろう」で書いたプログラムである。
ではプログラムの解説をしよう。
[BITS 16] jmp 0x07C0:setup
アセンブラでプログラムを書き始める場合かならず上記のような記述から始める。[BITS 16]は16bitのプログラムを作成するという意味である。jmp 0x07C0:setupはメインの処理(setup)までジャンプする命令でC言語のgotoに相当する。setupというラベルのアドレスは相対番地であるので目的の場所へ正しくジャンプするには「0x07C0:」が必要である。
つぎにsetupが実行される。これはセグメントレジスタの設定をしている。
setup: mov ax, cs mov ds, ax
前述の様にjmpした場合csに0x07C0が代入される。これをdsに代入しているのだが、csからdsに直接代入することはできないので一時的にaxに代入している。セグメントレジスタを設定すればもう「0x07C0:」と言った記述は必要ない(なぜかって?「メモリアドレスについて」を参照しよう)。
clean: mov ah, 0 ;setup video mode mov al, 0x10 ;video mode. 0x10=80x25 with 16 colors int 0x10
これはレジスタに適当な値を代入してBIOSの関数を呼び出している。
これは画面を真っ黒にする命令である。
BIOSには便利な命令が用意されていてこれを使うことで簡単にプログラムをかける。BIOSの命令は「(AT)BIOS - OS-Wiki」にまとめてくださっている方がいるので参考にしよう。
putchar: mov ah, 0x0E ;put char mov al, 'A' mov bh, 0 mov bl, 0x0E ;text color. 0x0E = Yellow int 0x10
これも同様にBIOSの関数を呼び出していて、文字を画面に表示する命令である。
end: jmp end
これは無限ループである。CPUはメモリ上のプログラムを次々に読み込んで実行していく。この命令以降のメモリには何もプログラムを読み込んでいないのでメモリ上に何が書いてあるか分からない、これをCPUに読み込ませてしまうと予想外の動作をしてしまうので、ここで無限ループなどで処理を止める必要がある。
bootsign: times 510-($-$$) db 0 dw 0xaa55
timesはいわいるイテレーターで指定された回数分処理を繰り返す。
($-$$)は現在の位置を表す特殊な文字で、510-($-$$)は現在の位置と510バイトの隙間を表す。db 0は直接0を書き込む命令である。
よってtimes 510-($-$$) db 0はプログラムの510バイト目まで0で埋め尽くすという意味である。
そして510バイト目には0xaa55が必要である。
; FDのための記述 name: DB "Name " 〜略〜
説明を抜かしていたがこれはブートセクターの決まりでこのように書かなくてはいけない。そのままコピペすれば問題なく動作するので、ここでは詳しく述べない。