H8開発TIPS

★H8に関して

★一般的なこと

gccで2進数を使えるようにする

 処理系によると思いますが、gcc-2.95.2では0b101010のような2進数表記ができません。もともとANSIにはないと思うので仕方がないのですが、どうしてもビット操作が多くなりがちな組み込み用途では不便です。そこで、0bXXXXXXを扱えるようにするパッチを作りました。でも、ソース配布時に問題あるかも・・・

% cd gcc-2.95.2
% cat ../gcc-2.95.2-20010926.diff | patch -p1
patching file `gcc/c-lex.c'
後はコンパイル・インストールすればOKです。
% cd build
% su
% make LANGUAGES="c"
% make LANGUAGES="c" install


ためしにプログラムを入力してみます。
 

% cat > bin.c

main()
{
    int a = 0b1010110;
}
CTRL+D
% h8300-hms-gcc -mh -mint32 -S bin.c

エラーが出なければOKです。生成されたbin.sを開いて確認してみてください。



割り込みの制御

 H8マイコンはCCRというレジスタにグローバルな割り込み許可・禁止のフラグを持っています。割り込みを発生させるには割り込みコントローラの割り込み許可フラグを立てるほかに、このCCRレジスタの変更が必要です。割り込みコントローラはメモリマップドI/OなのでC言語で直接アクセスすることができますが、CCRレジスタにはC言語で
直接アクセスすることはできません。そこアセンブラの力を借ります。1つの関数にしてもいいのですが、関数のコールのオーバーヘッドが大きいのでインラインアセンブラを使います。ヘッダファイルに記述しておくといいでしょう。H8/300Hシリーズ共通です。

 
#define STI()   asm volatile ("andc.b #0x7f,ccr")
#define CLI()   asm volatile ("orc.b #0x80,ccr")
次のように使います。
 
main()
{
    <初期化処理>

    STI();    // 割り込み許可

    <割り込みが発生します。>

    CTI(); // 割り込み禁止

    <割り込みが発生しません。>
}
 


割り込みハンドラの作成

 gccでは割り込み処理ルーチンもC言語で作成できます。割り込みハンドラ関数の定義の前に次のような宣言をすればOKです。

 
#pragma interrupt
void int_handler(void)
{
    <割り込み処理ルーチン>
}
"#pragma interrupt"がその宣言です。割り込みハンドラは当然ですが引数なしで、戻り型はvoidでなければなりません。
通常、関数名はリンカスクリプト内で設定されている名前を使わなければなりません。先頭の_(アンスコ)は外してください。
 



printf文(可変個引数)使用時の注意点

 printfに代表されるような変数を可変長にすることができる関数があります。このような関数をH8で利用する場合、必ずプロトタイプ宣言が必要です。printfについてはstdio.hをインクルードするだけで済みますので乱暴に作らなければ大丈夫ですが、ユーザが可変長引数の関数を作成する場合は注意が必要です。例えば、

 
#include <stdarg.h>

int debug(char *fmt, ...)
{
    va_list arg;

    va_start(arg, fmt);
    <省略>
    va_end(arg);

}
 

のようなプログラムを作成しておいて、他の別のソースファイルから呼び出した場合、
 

extern int debug(char *fmt, ...);

main()
{
    debug("%s:%d\n", filename, line);
}
 

上記の例では正しく動作しますが、赤字の部分"extern int debug(char *fmt, ...);"がないと正しく動作しません。これは、コンパイラの引数渡しの方法に原因があります。
 通常のプロセッサでは、引数をスタックに積んでから関数をコールします。呼ばれた関数ではスタックの上の方をみて、親関数からの値を得ます。必ずスタックを使うような設計になっているのですが、H8は違います。
 引数の個数が少ない場合、コンパイラは引数をスタックに積まず、レジスタに直接値をセットして関数をコールします。これにより、メモリアクセスが少なくなり実行が速くなります。H8はCPUの汎用レジスタが多いので引数をレジスタにセットしても、問題になりません。
 今回のケースでは変数が可変なので、上記のdebug関数は引数がスタックに積まれているものとしてコードを生成します。
呼び出す側では、プロトタイプ宣言の定義に従ってレジスタ渡しにするか、スタック渡しにするかが決まります。コンパイラはできるだけ、速いコードになるようにしたいので、レジスタ渡しを優先します。上記の場合プロトタイプ宣言が無いと、引数がレジスタ渡しになってしまい、呼び出し側と受け取る関数側で食い違ってしまいます。これが暴走の原因です。

 ここでは可変引数の関数についてだけ説明していますが、それ以外のタイプの引数についても、プロトタイプ宣言がないとトラブルが発生する可能性がありますので、必ずやっておいた方がいいでしょう。戻り値がintだから宣言しなくても動くというふうに思わないように!!



H8tinyでの引数の問題
 すでにH8メーリングリストで挙がっていた問題です。H8Tinyにおいて、引数を4つ以上(可変個引数も対象)渡すと、呼ばれた側で正しく引数を受け取れないというものでした。これは佐藤 嘉則さんによって解決されています。パッチのあてかたについては開発環境の準備のLinuxをみてください。



プログラム製作のポイント
 




もっとマクロを活用しよう!
 今までプログラム開発をしてて、マクロを使うとプログラムが見やすくなったり、未然にバグを防ぐことができたりできて、非常に有効な手段だと思っています。
でも、うまく活用されていないのが実状のような気がします。マクロ(プリプロセッサ)命令について思い付いたものから掲載します。この項目はH8に限ったことではなくて、C言語の一般的な内容です。
 
  1. 引数を文字列に

  2. ANSIコンパイラは次のようにすると変数名をくっつけたり、文字列として扱うことができるので便利です。これを知ってるとかなりcppを使いこなせます。
     

    #define    DUMP(var)        printf(#var "=%d\n", var)

    main()
    {
            int x, *y;

            x = 12345;
            y = &x;
            DUMP(x);
            DUMP(*y);
    }
     

    上記のプログラムを入力、実行してみてください。ANSI準拠のコンパイラならできるはずです。すると
     

    x=12345
    *y=12345

    のように表示されます。DUMP(*y)とやっただけで、自動的に "*y="を付けてくれるのです。
    もちろん  DUMP(ptr[2]->array[i]->member)  のように複雑なものでも、ちゃんと表示します。DUMP(x+100) とかやってもちゃんと、"x+100=12445" とでます。
     

        printf("x=%d\n", x);
        printf("*y=%d\n", *y);

    となどとやってもいいのですが、普通コピー&ペーストでプログラム組みますから、絶対に変数と文字列の修正を忘れます。10行以上ならかなりの率です。
    コピペに頼るのはよくないです。それで、さっきの方法が有効となってきます。
     
    #define    DUMP(var)        printf(#var "=%d\n", var)

    このマクロの説明をするとい、まずvarが引数になりますが、ここでDUMP(counter)というマクロの展開を考えます。スタートは
     
    DUMP(counter)

    です。次にvarがcounterに置き換わりますから、
     
    printf(#counter, "=%d\n", counter)

    と等価になります。それで#counterをみて?と思うかもしれません。そうです、#という演算子はC言語にありません。でも#演算子はプリプロセッサ命令として存在するのです。#はプリプロセッサ命令についています。#includeにも頭についてるでしょ。
    #counterはどう解釈されるかというと、文字列として扱われるようになります。つまり、変数あるいはラベルとしてのcounterではなく、文字列 "counter"となるのです。
    それを踏まえると、次のようになります。
     
    printf("counter" "=%d\n", counter)

    "AAAAA" "BBBBB"のように文字列を並べて書くとコンパイラがその文字列を連結しますので、
     
    printf("counter=%d\n", counter)

    となり、うまいことcounter=12345のように表示できるようになります。これで、知らない人に教えてあげよう!
    あと ## という演算子もあります。どういうものか調べてみよう!