Open1

MS-DOSのアセンブラでCOM形式の実行ファイルを作成する

カゴRPカゴRP

MS-DOSのサンプルコード

Hello, World!を表示するプログラム

hello.asm
	ASSUME	CS:CODE,DS:CODE,SS:CODE,ES:CODE
CODE	SEGMENT
	
	org	100h

START:
	;文字列出力
	mov	ah, 09h
	mov	dx, offset MSG
	int	21h
	;終了
	mov	ah,4ch
	mov	al,00h
	int	21h

MSG	db	'Hello, World!',0dh,0ah,'$'

CODE	ENDS

END

サンプルプログラムは全てのセグメントを同一セグメントに収めるCOM形式のプログラムです。COM形式のバイナリファイルには余計なコードが入りません。バイナリエディタで見たときにわかりやすい構造になっています。

COM形式ファイルのアセンブル&リンク

JWASMR -bin -Fo hello.com hello.asm

バイナリエディタで確認

アドレス
00000000 B4 09 BA 0D 01 CD 21 B4 4C B0 00 CD 21 48 65 6C
00000010 6C 6F 2C 20 57 6F 72 6C 64 21 0D 0A 24

オペコードマップとアスキーコード表があればほぼアセンブラに置き換えられます。

デバッガで確認

アドレス バイナリ オペコード オペランド オペランド
0100 B4 09 MOV AH 09
0102 BA 0D 01 MOV DX 101D
0105 CD 21 INT 21h
0107 B4 4C MOV AH 4C
0109 B0 00 MOV AL 00
010B CD 21 INT 21
	mov	dx, offset MSG

の欄をを見るとバイナリがBA 0D 01、逆アセンブルの結果がMOV DX,101Dとなっています。offset MSGの部分が010DなのはMSGのオフセットアドレスがセットされた結果ですが、バイナリが 0D01なのはx86プロセッサが、メモリに展開する際のデータの並び順がリトルエンディアンだからです。

データ

H e l l o , W o r l d ! Cr Lf $
48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0D 0A 24

COM形式の実行ファイルの中には命令コードとデータ以外に付加情報がない事が分かります。
最後が$で終わっているのはファンクションコールの文字列表示の命令が$を終端文字として処理しているからです。要するに、文章の途中に$マークがあると文字列表示のファンクションコールは途中までしか表示できないという事です。
コードの終了とデータの開始の間に境界線がありません。
終了処理の際に、AHとALにセットする値を分けて書いていますが、まとめて書けば1バイト分だけファイルサイズが小さくなります。

終了をAH=4C,AL=00からAX=4C00に変えてみた

hello.asm
	ASSUME	CS:CODE,DS:CODE,SS:CODE,ES:CODE
CODE	SEGMENT
	
	org	100h

START:
	;文字列出力
	mov	ah, 09h
	mov	dx, offset MSG
	int	21h
	;終了
	mov	ax,4c00h
	int	21h

MSG	db	'Hello, World!',0dh,0ah,'$'

CODE	ENDS

END
アドレス
00000000 B4 09 BA 0C 01 CD 21 B8 00 4C CD 21 48 65 6C 6C
00000010 6F 2C 20 57 6F 72 6C 64 21 0D 0A 24

データ宣言を先頭に書いてみる。

エラーになるhello.asm
	ASSUME	CS:CODE,DS:CODE,SS:CODE,ES:CODE
CODE	SEGMENT
	
	org	100h

MSG	db	'Hello, World!',0dh,0ah,'$'

START:
	;文字列出力
	mov	ah, 09h
	mov	dx, offset MSG
	int	21h
	;終了
	mov	ax,4c00h
	int	21h

CODE	ENDS

END

一見、うまく動いてる様に見える事も有りますが、その後に影響し動作不良を起こすと思います。

アドレス
00000000 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0D 0A 24
00000010 B4 09 BA 00 01 CD 21 B8 00 4C CD 21

データの部分がそのまま命令コードとして実行される事になります。

データ宣言を先頭に書いて正しく動作させる方法

hello.asm
	ASSUME	CS:CODE,DS:CODE,SS:CODE,ES:CODE
CODE	SEGMENT

	
	org	100h
	jmp	START
MSG	db	'Hello, World!',0dh,0ah,'$'

START:
	;文字列出力
	mov	ah, 09h
	mov	dx, offset MSG
	int	21h
	;終了
	mov	ax,4c00h
	int	21h

CODE	ENDS

END

無条件ジャンプで回避しています。

アドレス
00000000 EB 10 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0D
00000010 0A 24 B4 09 BA 02 01 CD 21 B8 00 4C CD 21

簡略化セグメントで記述

hello.asm
	.model	tiny
	.data

MSG	db	"Hello, World!",0dh,0ah,'$'

	.code
	.startup
	;文字列出力
	mov	ah, 09h
	mov	dx, offset MSG
	int	21h
	;終了
	.exit	00h

END

他言語のライブラリとリンクするならこれを利用する方が楽

アドレス
00000000 B4 09 BA 0C 01 CD 21 B8 00 4C CD 21 48 65 6C 6C
00000010 6F 2C 20 57 6F 72 6C 64 21 0D 0A 24

データの定義が上に有りますが、バイナリでは最初のサンプルソースと同じ様にコードの後ろにデータが付いています。自動的に後ろに並び替えてくれるようです。
Linuxのnasmに書き方はこれに近いので学習するならこの書き方が良い気がします。