Light C | ← → 目次 索引 |
Light C で作成したコードをアセンブラ ルーチンとリンクする場合は、以下の点に注意してください。なお、-fa または -fl を指定してアセンブラ コードを生成し、必要な箇所だけを変更すれば、容易に整合性を保つことができます。
プログラムのエントリ ポイントをアセンブラで作成し、アセンブラ ルーチンから C 関数を呼び出す場合は、あらかじめスタートアップ ルーチンを実行しておく必要があります。
セグメント定義とグループの整合性に注意してください。
アセンブラ ルーチンから 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 関数を呼び出す場合は、変更不可のレジスタの値が維持されることを前提としたコードを記述できます。
参照
データ形式
Copyright © Tama Software Ltd, 1999-2012. | ← → 目次 索引 |