Z80のC言語クロスコンパイル(SDCC)(4)

  • 投稿日:
  • 更新日:2015/03/08
  • by
  • カテゴリ:

前へ 第1回 第2回 第3回 第4回 第5回 次へ

インラインアセンブルコード

sdccのCコンパイラの出力コードの効率があまりりよくないことはお話しました。 そのため、インラインアセンブルを使うことも多いでしょう。 インラインアセンブルの利用は、他のコンパイラ同様、以下のような利点があります。

  • 特定の部分のみ高速化できます。
  • Cプリプロセッサが働くので、#defineの定義が利用できます。

インラインアセンブルコードは、関数内に__asm~__endasm;で囲んで書きます。例えば、次のようになります。

static char *message;

void func() {
  // 他のコード...

__asm
	ld	hl, #_message
	call	_show_message
_endasm;

  // 他のコード...
}

この例では、アセンブルコードからCコードのmessageを参照しています。 ローカル変数の参照も出来ないことはないのですが、第2回で説明したように、スタック上の位置が宣言の順番に依存するので、避けた方がいいと思います。

警告の抑制

インラインアセンブラを使ってコードを書くと、

myprog.c:10: warning 85: in function myfunc unreferenced function argument : 'arg'

という警告が出てしまうことがあります。これは、関数の引数をアセンブルコード内のみで利用し、Cコード内で参照しないためです。sdccは__asm~__endasm;で囲まれた部分はそのままアセンブラに渡し、一切検査しません。

この警告が気になるなら、#pragmaで抑制することができます。

#pragma save
#pragma disable_warning 85
void func(char* arg) {
__asm
  // アセンブルコード...
__endasm;
}
#pragma restore

#pragma save

でこれまでのpragma情報をいったん保存し、

#pragma disable_warning 85

でwarning 85の出力を抑制します。そのままだとこれ以降ずっとwarning 85が出なくなってしまうので、関数終了時に

#pragma restore

でpragma情報を元に戻します。

naked関数

アセンブルコードで関数本体を書いても、第2回で説明したよ うなixのバックアップ~リストアは出力されます。場合によってはこれが不都合なこともあります。そのため、__nakedという予約語があります。この 予約語がついた関数は、一切のプリアンブルコードが出力されません。retすらしません。そのため、全て自分で書く必要があります。

void func(char* arg) __naked {
__asm
  // アセンブルコード...
  // retも自分ですること
__endasm;
} 

インラインアセンブラTips: 関数ポインタの定数化

sdccでは、コードセグメントにおける関数ポインタの定数化ができません。 たとえば、以下のようなコードがあったとします。

char func1(int x) { ... }
char func2(int x) { ... }
char func3(int x) { ... }

const char (*functions[])(int) = {
  func1, func2, func3
};

ここでfunctionsは定数ですので、コードセグメント内に配置して欲しいところなのですが、 sdccはfunctionsをデータエリアに置き、メンバの初期化をGSINIT内に出力します。そのため、非常に効率の悪いコードになるだけでなく、 本来の意味での定数ではなくなっています。

これを、naked関数を使って解決する手段があります。 次のように書きます。

char func1(int x) { ... }
char func2(int x) { ... }
char func3(int x) { ... }

extern char (*functions[])(int);
static unsigned char* __functions() __naked {
__asm
_functions::
	.dw	#_func1
	.dw	#_func2
	.dw	#_func3
__endasm;
}

お分かりでしょうか。C言語からはfunctions外部変数を参照するようにしておいて、実はnaked関数内で定義しています。 なお、スコープをstaticにすることも可能です。その場合は、
_functions:
と、コロンをひとつにします。

上記の場合、メンバとなるfunc1, func2, func3の定義がプロトタイプ宣言と異なっていてもコンパイルエラーは出ません。これは良し悪しで、わざと違う定義にすることもあるかもしれません。た とえば、呼び出し側は常に引数を渡すようにしているが、呼び出された側はその引数を必要としないので、シグネチャに入れない、などということも考えられま す。

これを柔軟性と見るか、悪い設計だと見るかは、アプリケーションに依存するでしょうね。

前へ 第1回 第2回 第3回 第4回 第5回 次へ

こちらもよく読まれています