起動するまでの長い道のり IPL編(4) 続アセンブラ解説の巻

 前回まででセグメントを設定するところまでは説明した。いよいよメッセージ表示の部分について説明する。

メッセージ表示関数コール

    # show message
    movw    $BOOT_MSG, %si
    call    print

# 中略

BOOT_MSG: .string "Hello,World!\r\n"

 ここではメッセージ表示のための関数printを呼び出している。C言語での関数呼び出しは、上記コードに出てくるcall命令で行われる。普通は、呼び出した関数での処理が終わった後に元の場所へ戻り、次の命令が実行される。ここではprintラベルの位置までジャンプし、処理が終わった後(ret命令に達した時)に元の場所へ戻る。

 callの前のmovw命令では、BOOT_MSGのアドレスをsiレジスタに代入している。これはprint関数の「引数」で、見たとおり表示するメッセージを指定している。関数本体でsiレジスタの内容が読まれ、メッセージが表示される。

 最後のBOOT_MSGは、メッセージ文字列のデータだ。.stringという型を指定すると、文字列データの最後にヌル文字(C言語の文字列におけるヌル文字と同じ)が付加される。

メッセージ表示関数本体

# print string
#   params:
#       si = string address
print:
    xorw    %bx, %bx
    movb    $0x0e, %ah
print_char:
    lodsb
    orb     %al, %al
    jz      print_end
    int     $0x10
    jmp     print_char
print_end:
    ret

 で、これが関数本体だ。ややこしい……。ただ、「関数」といっても何か特別なデータ構造とかディレクティブが必要なのではなく、単にラベルを貼って処理の終りでretを実行すれば良いだけだ。後はcallすれば実際に関数として動作する。

 文字表示のために、ここではBIOSファンクションというものを利用している。以前に少し書いたが、BIOSには基本的な入出力機能が実装されていて、プログラムから呼び出せるようになっている。各レジスタにファンクションの引数を設定し、「int」という命令を実効することでBIOSファンクションを呼び出している。

 ここで使っているBIOSファンクションは、alレジスタに設定されているASCII文字を画面に表示する。1度に1文字ずつ表示されるので、文字列を表示したい場合はalレジスタの内容を変えながら何度も呼び出さなければならない。で、文字列の末尾に達したらちゃんと終了しなければならない。そういう処理をここでは行っている。

print:
    xorw    %bx, %bx
    movb    $0x0e, %ah

 最初のprintラベルからprint_charラベルまでは、BIOSファンクションの引数のうち呼び出しごとに変化しない部分(文字色等)を設定している部分だ。

print_char:
    lodsb
    orb     %al, %al
    jz      print_end
    int     $0x10
    jmp     print_char
print_end:

 print_charからprint_endまででは、文字の読み出し・終端(ヌル文字)かどうかのチェック・BIOSファンクション呼び出し(int)・繰り返しを行っている。

 lodsb(Load String Byte)という命令は、siレジスタが指しているメモリから1バイト読み、alレジスタにコピーし、さらにsiレジスタを1進める。これが前回の最初の方に出てきたストリング命令と呼ばれるものだ。

 lodsbの後にあるorb命令は、バイトでのOR演算を行う。xorwと同じ形式の命令だ。どうしてここでOR演算なんかやっているのかというと、alレジスタ(=次に表示する文字)がゼロ(=ヌル文字=文字列の終端)かどうか調べるためだ。どうしてOR演算で値がゼロかどうか調べられるのかというと、演算結果の状態を保持するフラグレジスタというものがあって、そこに演算結果がゼロかどうかが保持されるからだ。そのフラグレジスタの値に従って次のjz命令(Jump if zero)が実行され、ゼロだった場合は指定されたラベル位置までジャンプする。

print_end:
    ret

 ヌル文字に到達してjzによりprint_endにジャンプした後は、ret命令が実行される。これでcallされた元の場所に戻る。

    # infinite loop
end:
    jmp end

 print後は無限ループに入る。


 一応これで文字表示するコードは一通り解説したことになると思う。ただ、やはりCPUの仕組みとか構成が分かっていないとこの先大変そうだ。資料として挙げたIntelのリファレンスマニュアルや「はじめて読む486―32ビットコンピュータをやさしく語る」を読むのが本当は一番良いのだけど、一応次回辺りにx86系CPUの基本的な部分について解説しておこうと思う。