トラ技BIOSのファンクションコールのしくみ平成20年7月26日 トラ技BIOSを使うにはヘッダファイル trgbios.h をインクルードしますが、このヘッダファイルには次のようなマクロのようなものが定義されています。
このページではこの怪しげなマクロについて解説します。 これは何?これは、関数のような形式をしたC言語のマクロです。 void (*)( )型という関数ポインタをイメージしてください。関数ポインタというのは、C言語から任意の番地をコールするためのしくみです。trg_usbopenというマクロは、0x18C0という数値を、関数ポインタにキャストし、それを呼び出します。 つまり、0x18C0という値を指し示す関数ポインタを作って、それを呼び出しているわけです。 どのように実行されるの?trg_usbopen()を実行すると、call !018C0Hという命令コードが置かれます。 18C0番地に何が書かれているかについては、付録CD-ROM内にあるトラ技BIOSのソースファイル中にある"BIOSstart.asm"の157行目を参照してください。
このコードを解説すると、 ・TRGIOPENはただの名前です。 これをアセンブルすると、絶対番地の18C0に、br命令とnop命令が配置されます。このコードを実行すると、CPUは_usbOpenというラベルで示されたアドレスへジャンプします。 _usbOpenというのはC言語で書いたusbOpen()という関数のアドレスを指します。一般に、C言語で書いたグローバルな名前は、アセンブラでは頭に_つけることでアクセスすることができます。 usbOpen()という関数は、トラ技BIOSの本体のソースコードで定義されています。
このようにして、trg_usbopen()を実行すると、トラ技BIOSの中のusbOpen()関数が呼び出されるわけです。 注目すべき点は、usbOpenという関数がどこのアドレスに配置されるかを知る必要がないという点です。 なんでこんな面倒なことをしているの?ここでは、2つのテクニックを使いました。 @ 固定のアドレスに、サービスコールの入り口を作ったこと。 その理由は、トラ技BIOSのようにROM内に固定されたルーチンと、ユーザアプリのようにRAM上のアプリをリンクするためです。 本来は、78Kマイコンのアプリケーションを開発する場合、ブートコードとアプリケーションを一緒にコンパイルしてリンクします。ブートコードが用意する各種サービス関数のアドレスがわかっているので、アプリケーションからそのまま名前でアクセスできます。もし機能を拡張したりして、サービス関数のアドレスが変わってしまっても一緒にリンクするのですから問題はありません。 しかし、付録基板では、トラ技BIOSを後から機能拡張したりすることもありうるので、各種サービス関数が配置されるアドレスは、変わるかもしれません。そのため、アセンブラを使って固定のアドレスに入り口を作る必要があります。 ところが、78K0のリンカには、関数の呼び出し先アドレスを指定するしくみがありません。 そのため、このような「数値を関数ポインタにキャストする」というトリッキーなことをしています。
引数を取る場合は?trg_puts()を例として紹介します。
このマクロは、void (*)(const char *src)型の関数ポインタをつくり、その関数ポインタが絶対アドレス0x18C8を指すようにキャストし、さらにそれをコールしています。 最後の行の(SRC)というのが、引数にSRCを積んでコールすることを指示しています。 78KのC言語の呼び出し規約については別のページで解説する予定ですが、 @ 引数が1個ならレジスタAXに積まれます。2個以上ならスタックに積まれます。 となります。しかし、それを意識する必要はないようです。
この方法の問題点@ 今回の78Kマイコンでは、仮想アドレスのしくみがないのでうまく動いたという意見もあります。 32bitマイコンとかで同様に記述しても任意のアドレスをコールできるとは限りません。ただし、Borland C++ CompilerでIntel86のコードをコンパイルした場合、下記のようになりました。 ちゃんと動いているようです。
これ以外のCPUではどうなるかわかりません。RISC CPUでは、任意のアドレスから任意のアドレスに常にジャンプできるとは限らないので、この方法で必ずしもうまくいく保証はありません。
A PM+が関数として認識しないので、コードの補完機能が働かない。 コード補完が働かないので、少々面倒です。
|
Copyright(C) 2008 NAITOU Ryuji. All rights reserved. 無断転載を禁ず