アプリ版:「スタンプのみでお礼する」機能のリリースについて

 マイコンのROMに書いてある関数を呼び出すのに、次のような方法を使っています。
 1000番地のsub1と言う関数を呼ぶのにアセンブラで次のように書き、リンカで結合しています。
sub1: EQU 1000

 これをアセンブラを介せずC言語だけで行う方法は無いでしょうか。
イメージ的には次のような感じで1000番地のプログラムを呼びたいのです。
 この方法では上手く行かないのは分かっていますが、それを何とかしたいと思っています。

#define sub1 1000
sub1() ;

 宜しくお願いします。

A 回答 (8件)

定数を関数へのポインタにキャストすれば良い。



#define sub1 1000
main()
{
int i;
int i;
i = ((int (*)(int,int))sub1)(10,20); /*intの引数が2つあってintを返す関数*/
}
    • good
    • 0
この回答へのお礼

 やった! 有難う御座います。

 コンパイルすると、下記の結果になりました。

-------------------------------------------
#define sub1 0x1000
int i;
((void (*)(void ))sub1)( );
i = ((int (*)(int,int))sub1)(0x10,0x20);
-------------------------------------------
JSR @H'01000:24
MOV.W #H'0020:16,E0
MOV.W #H'0010:16,R0
JSR @H'01000:24
------------------------------------------

 更に次のようにすると、普通の関数と変わり無く使えるのが分かりました。

----------------------------------------------
#define sub2((void (*)(void ))0x2000)
sub2();
#define sub3((int (*)(int,int))0x3000)
i = sub3(0x30, 0x40);
---------------------------------------------
JSR @H'02000:24
MOV.W #H'0040:16,E0
MOV.W #H'0030:16,R0
JSR @H'03000:24
---------------------------------------------

 このような複雑な書き方はI/Oポートの構造体とか、C言語のクイズのような物で見るだけで、解説本には見かけないと思います。

 どうも有難う御座いました!

お礼日時:2009/05/18 23:39

蛇足ですが。



>更に次のようにすると、普通の関数と変わり無く使えるのが分かりました。
>#define sub2 ((void (*)(void ))0x2000)
>#define sub3 ((int (*)(int,int))0x3000)

ROMの内容は不変だと思いますので、ROMの関数のアドレスも不変だと思います。

なので、これらのROM関数の定義を以下のようにヘッダーファイル rom.h に書いて、いつもそれを使うと良いでしょう。

---rom.h---
#define InitSerialPort ((void (*)(void))0x1200)
#define ResetSerialPort ((void (*)(void))0x1258)
#define ReadSerialPort ((char (*)(void))0x12A2)
#define WriteSerialPort ((void (*)(char))0x12E4)
(以下略)
※関数名とアドレスは架空の物です。実在の関数名とアドレスとは一切関係ありません。

名前も、sub1とかsub2ではなく、上記のように「意味がある名前」にしましょう。

そうすれば、実際に使う場合、以下のように書くだけで良く、毎回#defineを書かずに済みます。

#include <rom.h>
void main(void)
{
  int r;
  char readbuf[10];
  InitSerialPort();
  ResetSerialPort();
  WriteSerialPort('R');
  WriteSerialPort('0');
  WriteSerialPort('1');
  WriteSerialPort('S');
  readbuf[0] = ReadSerialPort();
  readbuf[1] = ReadSerialPort();
  readbuf[2] = ReadSerialPort();
(以下略)
    • good
    • 0
この回答へのお礼

 分かりました。
 色々と有難う御座いました。

 また何か有りましたら宜しくお願いします。

お礼日時:2009/05/20 04:42

>別にアセンブリしたりサブ・コマンドファイルの変更などが有り。


ここら辺は工程をバッチファイル化したり、ROM側のリンク時にシンボルファイルを書き出してJavascript/VBScriptなどで加工してアセンブラソースに加工するってのが定番です。


C言語だけでやると問題としては、
1.関数にパラメータがあるとキャストがめんどくさい。
2.リンカだけで解決するよりコードがわずかだが肥大化する。
3.関数ポインタの初期化を忘れてもエラーが出ない。リンクならエラーが出るからすぐ分かる。
    • good
    • 0
この回答へのお礼

 再度回答ありがとう御座います。

 今までの方法が、最良だったという事ですね。

お礼日時:2009/05/17 15:02

>#1さん



確かに関数ポインタを使えばよいですが、だからといって

>void(*sub1)(void) = 1000;

これはないでしょう。

# 本当に専門家?
    • good
    • 0
この回答へのお礼

 回答有難う御座います。

 名前はパラグアイの首都アスンシオンですか?

 20年前にアスンシオンの友人の家に数ヶ月滞在していたので、懐かしいなと思って。

お礼日時:2009/05/17 13:25

私もjactaさんと同じでリンカでの解決が良いと思います。


アセンブラによるのですが、
_sub1:: EQU 1000
とか外部シンボルになる定義を行ってください。
※ この場合::が外部シンボルの意味となる。そのほかexternと書くものや色々アセンブラによって違います。アンダーバーが必要かもC言語コンパイラの仕様によって変わります。

C言語側では、
extern void sub1(void);
と宣言して、
sub1();
と呼び出すだけです。
    • good
    • 0
この回答へのお礼

 早速の回答ありがとう御座います。

 秋月電商のH8キットでプログラムを作っているのですが、printf 関数は大きくてRAMに入りません。
 それでROMに書き込んで呼び出しています。

 現在zwiさんの回答と同じ方法でリンカで解決しているのですが、C言語の機能を引き出してC言語で解決できる道を探しています。

お礼日時:2009/05/17 13:09

C言語の枠を外れる話ですが、組み込み系のCコンパイラなら、そのコンパイラでどう記述するか機能が提供されていると思うので、コンパイラのマニュアルを見ましょう。


見つからなければ、だめもとで#1の方の書いた方法。
    • good
    • 0
この回答へのお礼

 早速の回答ありがとう御座います。

 秋月電商のH8キットの安いコンパイラを使っています。
 コンパイル時間が短いので気に入っています。

 シンプルな所が気に入っています。
 シンプルだから、基本的な事しか出来ません

お礼日時:2009/05/17 12:59

一番確実なのは、アセンブリ言語のソースで、関数名が外部結合のシンボルになるような擬似命令を記述することです。


関数へのポインタを使えばほとんどの場合動作しますが、規格上保証されているわけではありませんので。
    • good
    • 0
この回答へのお礼

 早速の回答有難う御座います。

 現在はアセンブリ言語の EQU を使って結合しているのですが、C言語だけで行う方法を探っています。

 アセンブリ言語が絡んでくると、別にアセンブリしたりサブ・コマンドファイルの変更などが有り、ミスが増えそうなのでC言語だけで処理出来ないかなと思っています。

お礼日時:2009/05/17 12:46

関数ポインタを使えば可能です。


void(*sub1)(void) = 1000;
sub1();

この回答への補足

 下記のお礼の補足です。

 使っているコンパイラが秋月電商のH8マイコン用です。
 機能が小さくて、C言語機能全体をカバーしていないかも知れません。

補足日時:2009/05/17 12:02
    • good
    • 0
この回答へのお礼

 早速の回答有難う御座います。
 このような回答を待っていました。

 しかし次のプログラムを書いてコンパイルしたのですが、
2129(E) ILLEGAL INITIALIZER TYPE
と言う、エラーメッセージが出てしまいます。

void test(void)
{
void(*sub1)(void) = 1000;
sub1();
}

 次のようにしても同じでした。

void(*sub1)(void) = 1000;
void test(void)
{
sub1();
}

 関数ポインタという機能を調べて、自分でも実験してみます。

お礼日時:2009/05/17 11:57

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!