アセンブラ ルーチンとのリンク

Light C で作成したコードをアセンブラ ルーチンとリンクする場合は、以下の点に注意してください。なお、-fa または -fl を指定してアセンブラ コードを生成し、必要な箇所だけを変更すれば、容易に整合性を保つことができます。

スタートアップ ルーチン

プログラムのエントリ ポイントをアセンブラで作成し、アセンブラ ルーチンから C 関数を呼び出す場合は、あらかじめスタートアップ ルーチンを実行しておく必要があります。

セグメント名

セグメント定義とグループの整合性に注意してください。

near コード呼び出しと far コード呼び出し

アセンブラ ルーチンから C 関数を呼び出す場合、RETN (near リターン) 命令で帰還する関数は near CALL で、RETF (far リターン) 命令で帰還する関数は far CALL で呼び出す必要があります。RETF 命令は、small/compact モデルで far を指定した関数と、medium/large モデルで near を指定しなかった関数で使用されます。

//---- C コード ----
void near nfunc() { }
void far ffunc() { }
;---- アセンブラ コード ----
call near ptr nfunc		; OK
call near ptr ffunc		; Error
call far ptr ffunc		; Error
call far ptr ffunc		; OK

C 関数からアセンブラ ルーチンを呼び出す場合も、同様に near と far を合わせる必要があります。

;---- アセンブラ コード ----
_nfunc	proc	near
	ret			; RETN コードが生成される
_nfunc	endp
_ffunc	proc	far
	ret			; RETF コードが生成される
_ffunc	endp
//---- C コード ----
void near nfunc( void );	// near 関数として宣言
void far ffunc( void ); 	// far 関数として宣言
void main( void ) {
	nfunc();		// OK (near CALL コードが生成される)
	ffunc();		// OK (far CALL コードが生成される)
}

引数の受け渡し

Light C が生成した関数は、すべての引数をスタック経由で受け取ります。通常、引数は後ろから順にスタックに積まれますが、この順番は変更できます。 char 型の引数は、常に int 型に変換されます。

関数から戻ったら、スタックに積んだ引数のサイズに合わせて、スタック ポインタ sp の値を増加する必要があります。この操作を「スタックから引数を取り除く」と表現する場合があります。

//---- C コード ----
void func( short, long );
;---- アセンブラ コード ----

push	word ptr [arg2+2]	; long 値の上位半分
push	word ptr [arg2]	; long 値の下位半分

push	word ptr [arg1]	; short 値
call	_func
add	sp, 2+4		; スタックを復元

なお、アセンブラ側では、PROC 文、INVOKE 文など利用して引数の定義を簡略化できます (LASM の場合)。

戻り値の受け渡し

戻り値は、サイズに応じて次の方法で返されます。

バイト数方法
1 AL レジスタ
2 AX レジスタ
3 スタック
4 DX:AX レジスタ ペア (DX が上位)
5 バイト以上 スタック

構造体や double 値を返すときなど、戻り値が 5 バイト以上または 3 バイトになる場合があります。この場合は、戻り値を受け取るデータ域のアドレスを最後にスタックします。

//---- C コード ----
double func( short, long );
;---- アセンブラ コード ----
push	word ptr [arg2+2]	; long 値の上位半分
push	word ptr [arg2]	; long 値の下位半分

push	word ptr [arg1]	; short 値
push	bx			; 戻り値を受け取る領域のアドレスを積む
call	_func
add	sp, 2+2+4		; スタックを復元

この例では、ds:bx で示されるメモリに 8 バイトの double 値が書き込まれます。

戻り値を受け取る領域のアドレスは、メモリ モデルにかかわりなく、常に ds (DGROUP) からの相対オフセットで指定します。

レジスタの保存

C 関数から呼び出されるアセンブラ ルーチンでは、SI、DI など、特定のレジスタの値を不変に保つ必要があります。

レジスタ、フラグ渡される値値の保存が必要か
AX、DX 不定 不要 (必要に応じて戻り値を設定する)
BX、CX 不定 不要
SI、DI 不定 必要
BP 呼び出し側のフレーム 必要
DS near データ セグメント群のグループ アドレス (DGROUP) 必要
ES 不定 不要
SS スタック セグメント 必要

値を保存する必要があるレジスタを使用する場合は、値をいったんスタックなどに退避して、呼び出し側に戻る前に復元してください。

逆に、アセンブラ ルーチンから C 関数を呼び出す場合は、変更不可のレジスタの値が維持されることを前提としたコードを記述できます。

データ形式

参照