DOSのアセンブラのHello World

第16回は、DOSのアセンブラです。第5回では、Linux/FreeBSD/Solarisのシステムコールを利用したアセンブラを取り上げましたが、今回はDOS上で、*.comファイルの形式で動作するアセンブラプログラムを作成してみましょう。

DOSのシステムコール(文字列出力)を使う

文字列出力を行うDOSのシステムコールを使ったHello Worldのプログラムはリスト1のとおりです。このプログラムは、gccのアセンブラの文法で記述しています。DOS上で使用されているアセンブラとは書式が違う場合があるため、注意してください。

DOSのプログラムは、通常の32ビットモードではなく、16ビットモードで動作します。16ビットモードと32ビットモードでは、CPUの命令コードが異なるため、「.code16」というアセンブラの疑似命令を記述し、以下のコードが16ビットモードであることを指定します。

DOSのシステムコールは、ahレジスタにシステムコール番号を入れて、ソフトウェア割り込みの「int $0x21」を実行すればOKです。DOSのシステムコール番号9番は文字列の出力で、終端に「$」が付けられた文字列の「$」の直前までが出力されます。文字列には、CR+LFの改行コードとして、「\r\n」を付けておきます。

文字列の出力後は、プログラムを正常に終了するために、16進で0x4c番のシステムコールを呼び出します。ここで、alレジスタには終了コードとして、正常終了を表す0の値を入れておきます。

リスト1 DOSのシステムコールで文字列出力(as_dos.s)

このプログラムはDOS上で実行しますが、アセンブルには、gccを使い、x86系CPU版のLinuxまたはFreeBSD上でアセンブルを行います。具体的なコマンドラインは実行例1のとおりです。

コマンドラインの各オプションを順に説明します。「-nostdlib」は、libcなどの標準ライブラリとリンクしないことの指定です。「-Ttext=0x100」は、プログラムの先頭アドレスを、DOSのプログラムがロードされるメモリアドレスである、0x100に指定するものです。「-Wl,--oformat=binary」は、出力ファイルのフォーマットを、(ELFとかではなく)そのままのバイナリイメージにするものです。DOSの*.com形式のファイルは、命令コードのバイナリが直接書き込まれたファイルになっています。

実行例1 DOS用アセンブラをgccでアセンブル

ここで作成されたas_dos.comファイルは、DOS上、またはWindowsのコマンドプロンプト上で実行できます。このファイルをFTPまたはSambaなどで転送し、実行している様子を実行例2に示します。

実行例2 DOS上でas_dos.comファイルを実行

CONに書き込む方法

DOSの9番のシステムコールを利用する方法では、表示文字列中に「$」があると、そこで文字列の終了とみなされてしまうため、表示文字列に「$」を含ませることができません。そこで、ほかの方法も考えてみましょう。

ファイルへの書き込みを行うDOSのシステムコールを用い、その書き込み先に特殊ファイルの「CON」を指定することにより、画面に文字列を表示できます(リスト2)。「CON」はコンソールを表す特殊ファイルで、UNIXでの/dev/ttyに相当するものと考えられます。

ここでは、まずファイルオープン(0x3d)のシステムコールで「CON」のファイル記述子番号を取得し、そのファイル記述子に対してファイル書き込み(0x40)のシステムコールを実行しています。最後にプログラム終了(0x4c)のシステムコールを実行している点は前項と同じです。また、プログラムのアセンブル方法、実行方法も同じです。

リスト2 DOSのシステムコールでCONに書き込む(as_dos2.s)

標準出力に書き込む方法

DOSにも標準入出力の概念はあります。通常、標準出力はファイル記述子番号1番として、オープン済みの状態でプログラムが呼び出されます。そこで、このファイル記述子1番に書き込めば、「CON」をオープンする必要がなくなります(リスト3)。

なお、前項の「CON」をオープンする方法では、コマンドの出力を「as_dos2 > file」のようにファイルにリダイレクトしても、メッセージは画面に表示されていたはずです。しかし、標準出力に書き込む方法なら、「as_dos3 > file」と実行すると、メッセージはファイルに書き込まれます。

リスト3 DOSのシステムコールで標準出力に書き込む(as_dos3.s)

DOSのシステムコール(1文字出力)を使う

文字列の出力ではなく、1文字の出力を行うDOSのシステムコールを、プログラムのループで繰返し呼び出す方法も考えられます(リスト4)。

ここでは、bxレジスタをポインタとして用い、文字列から1文字ずつdlレジスタに代入しながら、1文字の出力を行なう2番のシステムコールを呼び出しています。文字列の終了は、文字コード「\0」によって判定し、dlレジスタの値が0になったのを見て、ループを終了します。

リスト4 DOSの1文字出力のシステムコールを利用(as_dos4.s)