SHの4バイト境界で例外発生の落とし穴
SHマイコン(SH7144F)のプログラムを組んでいて、はまったことがあります。
配列があって、その20番目~23の要素にunsigned long 型の値を格納したいとします。
そこで、こんなふうにプログラムを書きました。
void hogehoge(unsigned long val)
{
unsigned char tmp[32];
・・・
・・・
*(unsigned long *)&tmp[20] = val;
・・・
}
なんでもない普通のCの記述です。ポインタのキャストがちょっと気になりますが。
ところが、これを実行すると例外が発生して吹っ飛ぶことがあるようです。
この構文をアセンブラにすると、こんな感じになります。
mov r14,r1 ← ここでr1にtmpの先頭アドレスが入る
add #20,r1 ← ここでtmp[20]のアドレスが計算される
mov.b r8,@r1 ← アドレスエラー発生
SHは、4バイトの(ロングワード)データをメモリとやりとりする場合、
ロングワード境界をまだいでしまうと、アドレスエラーという例外が発生してしまいます。
つまり、例えば0x0e番地から4バイトのデータを書き込む場合には、
2回のワードアクセスにわけてやらなければなりません。
普通はlong型やint型の配列を作れば、コンパイラは自動的に各要素がロングワード境界に並ぶようにしてくれるとは思うのですが、今回のケースではchar型の自動変数で配列を作ったため、ロングワード境界をまたぐことになってしまい、このようなエラーになったのでしょう。
コンパイルするたびにアドレスが微妙に変わるから、原因を発見しにくかったです。
SH系のプログラムを組むときにはアドレスエラー例外を必ず捕捉して、ブザーを鳴らすとか赤ランプ点滅とか、とか警告を出さないと、いけませんね。アドレスエラー例外を無視して何もルーチンをつくらないでおくと、ブレークポイントも仕掛けられないですし。
GCCを使っていたら何気ないプログラムでこんな例外にはまってしまいました。
ちなみに、今回使ったコンパイラは、gcc3.3です。
ご参考までに。
| 固定リンク


コメント
はじめまして。
次のように共用体を使うのはどうでしょう。
typedef union {
unsigned char uc[32];
unsigned long ul[8];
} ALIGNED_BUFF;
int main(int argc, char *argv[])
{
int i;
unsigned long val;
ALIGNED_BUFF buff;
val = 100;
buff.ul[20 / sizeof(unsigned long)] = val;
for (i = 0; i < 4; i++) {
printf("%02X ", buff.uc[20+i]);
}
putchar('\n');
}
投稿: moriyama | 2007.11.30 01:18