アセンブリ言語を C/C++ コードと組み合わせて使用する方法は、次のとおりです。
❏ アセンブルしたコードのモジュールを個別に使用し、それらのモジュールを、コ ンパイルした C/C++ モジュールとリンクします(7.4.1 項「C/C++ コードでのア センブリ言語モジュールの使用方法」(7-19 ページ)を参照)。これは最も汎用性 のある方法です。
❏ インライン・アセンブリ言語を直接 C/C++ ソース内に埋め込んで使用します
(7.4.4 項「インライン・アセンブリ言語の使用方法」(7-25 ページ)を参照)。
❏ C/C++ ソースで組み込み関数を使用して、アセンブリ言語文を直接呼び出します
(7.4.5 項「組み込み関数(intrinsics)を使用してアセンブリ言語文にアクセスす る方法」(7-26 ページ)を参照)。
7.4.1 C/C++ コードでのアセンブリ言語モジュールの使用方法
7.3 節「関数呼び出し規則」(7-13 ページ)で規定された呼び出し規則、および 7.2 節
「レジスタ規則」(7-11 ページ)で規定されたレジスタ規則に従っている場合には、
アセンブリ言語関数と C/C++ とのインターフェイスを取ることは難しくありませ
ん。C/C++ コードからアセンブリ言語で定義された変数や呼び出し関数にアクセス
でき、アセンブリ・コードからも C/C++ 変数にアクセスしたり C/C++ 関数を呼び出 したりできます。
アセンブリ言語と C/C++ をインターフェイスするには、次の指針に従ってください。
❏ C/C++ 言語で記述される関数でもアセンブリ言語で記述される関数でもすべて、
7.2 節「レジスタ規則」(7-11 ページ)で概要を説明している規則に従う必要があ
ります。
❏ 関数によって変更される専用のレジスタは、保存する必要があります。専用のレ ジスタは次のとおりです。
XAR1
アセンブリ言語と C/C++ 言語間のインターフェイス
❏ スタックは関数境界に位置合わせされます。
❏ 割り込みルーチンは、使用するすべてのレジスタを保存しなければなりません
(詳細は、7.5 節「割り込み処理」(7-35 ページ)を参照)。
アセンブリ言語と C/C++ 言語間のインターフェイス
❏ アセンブリ言語から C/C++ 関数を呼び出す場合、7.3.1 項「関数の呼び出し方法」
(7-14 ページ)に記述されているように、引数を指定して特定のレジスタをロー
ドし、残りの引数をスタック上にプッシュします。
C/C++ 関数から渡された引数をアクセスする場合、これらの規則が同様に適用さ れます。
❏ C/C++ 呼び出し規則は RPC を使って、LCR および LRETR 命令から戻り値を格
納するので、アセンブリ関数は同じ規則に従う必要があります。
❏ long および float は、最下位ワードが下位アドレスになるようにメモリに保存さ
れます。
❏ 構造体は、7.3.2 項「呼び出し先の関数の対応方法」(7-15 ページ)に記述されて いるように値を戻さなければなりません。
❏ アセンブリ言語モジュールは、グローバル変数の自動初期化以外の目的に .cinit セクションを使用することができません。boot.asm の C/C++ 始動ルーチンは、
.cinit セクションがすべて初期化テーブルで構成されているものと見なします。
それ以外の情報を .cinit に入れるとテーブルが壊され、予期できない結果が生じ ます。
❏ コンパイラは、すべての識別子の先頭に下線(_)を付けます。アセンブリ言語 モジュールでは、C/C++ からアクセス可能になっているすべてのオブジェクトに 対して先頭に下線を付ける必要があります。たとえば、x という C/C++ オブジェ クトは、アセンブリ言語では _x となります。アセンブリ言語モジュール
(1 つまたは複数)の中でのみ使用される識別子の場合、下線で始まらない名前 は C/C++ 識別子と競合することなく安全に使用することができます。
❏ アセンブリ言語内で宣言し C/C++ からアクセスまたは呼び出しが行われるオブ ジェクトや関数は、すべてアセンブラの中で .global または .def 疑似命令を使用 して宣言しなければなりません。これにより、シンボルが外部シンボルとして定 義され、リンカはそのシンボルへの参照を解決できます。
同様に、アセンブリ言語から C/C++ 関数またはオブジェクトをアクセスするに は、C/C++ オブジェクトを .global または .ref を使用して宣言します。これによ り、リンカが解決できる未定義の外部参照が作成されます。
アセンブリ言語と C/C++ 言語間のインターフェイス
❏ アセンブリで構造体を定義し、extern struct を使って C でアクセスする場合は、
構造体をブロックしてください。コンパイラはDP ロード命令を最適化するため に、構造定義がブロックされているものと想定しています。したがって、定義で はこの前提を尊重してください。.usect または .bss 疑似命令でブロック・フラグ を指定することで、構造体をブロックすることができます。これらの疑似命令の
詳細は、『TMS320C28x アセンブリ言語ツールユーザーズ・マニュアル』を参照
してください。
例 7-1 に、asmfunc というアセンブリ言語関数を呼び出す main という C/C++ 関数を
示します。asmfunc 関数は 1 つの引数を取り、gvar という C/C++ グローバル変数に その引数を加算し、その演算結果を戻します。
例 7-1. アセンブリ言語関数 (a) C++ プログラム
(b) アセンブリ言語プログラム
例 7-1 の C++ プログラムでは、extern “C” 宣言でコンパイラに C 命名規則(すなわ ち、ネーム・マングリングなし)を使用するように指示しています。リンカが .global _asmfunc 参照を解決できる場合、アセンブリ・ファイル内の対応する定義が 一致します。
extern “C”{
extern int asmfunc(int a); /* declare external asm function */
int gvar = 0; /* define global variable */
}
void main() {
int i = 5;
i = asmfunc(i); /* call function normally */
}
.global _gvar .global _asmfunc _asmfunc:
MOVZ DP,#_gvar ADDB AL,#5 MOV @_gvar,AL LRETR
アセンブリ言語と C/C++ 言語間のインターフェイス
7.4.2 アセンブリ言語のグローバル変数にアクセスする方法
アセンブリ言語で定義された変数に C/C++ プログラムからアクセスできると便利な 場合があります。.bss セクションにある初期化されない変数にアクセスするのは、簡 単です。
1) .bss 疑似命令を使用して変数を定義します。
2) .global 疑似命令を使用して、その定義を外部定義にします。
3) 名前の前に下線を付けます。
4) C では、その変数を extern 宣言し、通常の方法でアクセスします。
例 7-2 に、.bss で 定義されている変数をアクセスする方法を示します。
例 7-2. C/C++ から .bss で定義されている変数にアクセスする方法 (a) C/C++ プログラム
(b) アセンブリ言語プログラム
変数が必ず .bss セクションになければならないわけではありません。たとえば一般 的な状況として、アセンブリ言語に定義された照合テーブルを RAM に入れたくな い場合があります。この場合、オブジェクトへのポインタを定義し、間接的に C/C++
からアクセスする必要があります。
extern int var; /* External variable */
. . .
var = 1; /* Use the variable */
* Note the use of underscores in the following lines .bss _var,1 ; Define the variable
.global _var ; Declare it as external
アセンブリ言語と C/C++ 言語間のインターフェイス
例 7-3. .bss 内に定義されていない変数に C からアクセスする方法 (a) C プログラム
(b) アセンブリ言語プログラム
7.4.3 アセンブリ言語定数へのアクセス
.set、.def、.global の各疑似命令を使用することにより、アセンブリ言語の中でグロー
バル定数を定義できます。また、リンカ代入文を使用してリンカ・コマンド・ファ イルの中でグローバル定数を定義することもできます。C/C++ からグローバル定数 にアクセスする唯一の方法は、特別な演算子を使用することです。
C/C++ またはアセンブリ言語で定義した通常の変数の場合、シンボル・テーブルに はその変数の値のアドレスが入っています。しかしアセンブラ定数の場合は、シン ボル・テーブルには定数の値が入っています。コンパイラは、シンボル・テーブル 内のどの項目が値で、どの項目がアドレスであるか判断できません。
アセンブラ(またはリンカ)定数に名前でアクセスしようとした場合、コンパイラ は、シンボル・テーブルの中で表されているアドレスから値を取り出そうとします。
この望ましくない取り出しを防止するには、& 演算子(アドレス演算子)を使用し て値を取り出さなければなりません。つまり x がアセンブリ言語定数の場合、その 値は C/C++ では &x になります。
プログラムの中でこれらのシンボルを使いやすくするため、例 7-4 に示すように、
キャストと #define を使用できます。
extern float sine[]; /* This is the object */
float *sine_p = sine; /* Declare pointer to point to it */
f = sine_p[4]; /* Access sine as normal array */
.global _sine ; Declare variable as external .sect "sine_tab" ; Make a separate section _sine: ; The table starts here .float 0.0
.float 0.015987 .float 0.022145
アセンブリ言語と C/C++ 言語間のインターフェイス
例 7-4. C からアセンブリ言語定数にアクセスする方法 (a) C プログラム
(b) アセンブリ言語プログラム
この場合はシンボル・テーブルに保存されたシンボルの値だけを参照しようとして いるので、シンボルの宣言された型は重要ではありません。例 7-4 では、int が使用 されています。リンカで定義したシンボルも、これとほとんど同じ方法で参照でき ます。
7.4.4 インライン・アセンブリ言語の使用方法
C/C++ プログラムの中で asm 文を使用すると、コンパイラで作成されたアセンブリ
言語ファイルの中に、アセンブリ言語の一文を挿入できます。連続して asm 文を使 用すれば、他のコードを間に入れることなくコンパイラ出力の中にアセンブリ言語 を連続的に挿入できます。
注: asm 文の使用方法
asm 文は、C/C++ から他の方法によってアクセスできないハードウェアの機能をア クセスできるようにするために用意されています。asm 文を使用する場合は、
C/C++ 環境が損なわれないよう特に注意してください。コンパイラは挿入された命 extern int table_size; /*external ref */
#define TABLE_SIZE ((int) (&table_size))
. /* use cast to hide address-of */
. .
for (i=0; i<TABLE_SIZE; ++i)
/* use like normal symbol */
_table_size .set 10000 ; define the constant .global _table_size ; make it global