マイコンのROMに書いてある関数を呼び出すのに、次のような方法を使っています。
1000番地のsub1と言う関数を呼ぶのにアセンブラで次のように書き、リンカで結合しています。
sub1: EQU 1000
これをアセンブラを介せずC言語だけで行う方法は無いでしょうか。
イメージ的には次のような感じで1000番地のプログラムを呼びたいのです。
この方法では上手く行かないのは分かっていますが、それを何とかしたいと思っています。
#define sub1 1000
sub1() ;
宜しくお願いします。
No.7ベストアンサー
- 回答日時:
定数を関数へのポインタにキャストすれば良い。
#define sub1 1000
main()
{
int i;
int i;
i = ((int (*)(int,int))sub1)(10,20); /*intの引数が2つあってintを返す関数*/
}
やった! 有難う御座います。
コンパイルすると、下記の結果になりました。
-------------------------------------------
#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言語のクイズのような物で見るだけで、解説本には見かけないと思います。
どうも有難う御座いました!
No.8
- 回答日時:
蛇足ですが。
>更に次のようにすると、普通の関数と変わり無く使えるのが分かりました。
>#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();
(以下略)
No.6
- 回答日時:
>別にアセンブリしたりサブ・コマンドファイルの変更などが有り。
ここら辺は工程をバッチファイル化したり、ROM側のリンク時にシンボルファイルを書き出してJavascript/VBScriptなどで加工してアセンブラソースに加工するってのが定番です。
C言語だけでやると問題としては、
1.関数にパラメータがあるとキャストがめんどくさい。
2.リンカだけで解決するよりコードがわずかだが肥大化する。
3.関数ポインタの初期化を忘れてもエラーが出ない。リンクならエラーが出るからすぐ分かる。
No.5
- 回答日時:
>#1さん
確かに関数ポインタを使えばよいですが、だからといって
>void(*sub1)(void) = 1000;
これはないでしょう。
# 本当に専門家?
回答有難う御座います。
名前はパラグアイの首都アスンシオンですか?
20年前にアスンシオンの友人の家に数ヶ月滞在していたので、懐かしいなと思って。
No.4
- 回答日時:
私もjactaさんと同じでリンカでの解決が良いと思います。
アセンブラによるのですが、
_sub1:: EQU 1000
とか外部シンボルになる定義を行ってください。
※ この場合::が外部シンボルの意味となる。そのほかexternと書くものや色々アセンブラによって違います。アンダーバーが必要かもC言語コンパイラの仕様によって変わります。
C言語側では、
extern void sub1(void);
と宣言して、
sub1();
と呼び出すだけです。
早速の回答ありがとう御座います。
秋月電商のH8キットでプログラムを作っているのですが、printf 関数は大きくてRAMに入りません。
それでROMに書き込んで呼び出しています。
現在zwiさんの回答と同じ方法でリンカで解決しているのですが、C言語の機能を引き出してC言語で解決できる道を探しています。
No.2
- 回答日時:
一番確実なのは、アセンブリ言語のソースで、関数名が外部結合のシンボルになるような擬似命令を記述することです。
関数へのポインタを使えばほとんどの場合動作しますが、規格上保証されているわけではありませんので。
早速の回答有難う御座います。
現在はアセンブリ言語の EQU を使って結合しているのですが、C言語だけで行う方法を探っています。
アセンブリ言語が絡んでくると、別にアセンブリしたりサブ・コマンドファイルの変更などが有り、ミスが増えそうなのでC言語だけで処理出来ないかなと思っています。
No.1
- 回答日時:
関数ポインタを使えば可能です。
void(*sub1)(void) = 1000;
sub1();
この回答への補足
下記のお礼の補足です。
使っているコンパイラが秋月電商のH8マイコン用です。
機能が小さくて、C言語機能全体をカバーしていないかも知れません。
早速の回答有難う御座います。
このような回答を待っていました。
しかし次のプログラムを書いてコンパイルしたのですが、
2129(E) ILLEGAL INITIALIZER TYPE
と言う、エラーメッセージが出てしまいます。
void test(void)
{
void(*sub1)(void) = 1000;
sub1();
}
次のようにしても同じでした。
void(*sub1)(void) = 1000;
void test(void)
{
sub1();
}
関数ポインタという機能を調べて、自分でも実験してみます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# スタックフレームの消滅 6 2023/05/20 12:33
- PHP isset — 変数が宣言されていること、そして null とは異なることを検査 1 2022/03/27 17:34
- Excel(エクセル) excel関数について 3 2022/06/06 21:58
- Excel(エクセル) Excel表示形式 2 2022/09/09 09:57
- Excel(エクセル) excelで検索した商品の画像(ネットワーク上の)を表示させたい。 3 2023/06/28 00:32
- Excel(エクセル) 【再度】Excelの関数について教えてください。 4 2023/07/28 13:06
- Excel(エクセル) EXCEL 行内のデータを2行に分けて、表を作り直したいのです。教えてください。 5 2023/06/25 14:00
- その他(プログラミング・Web制作) Pythonの作業環境・作業フォルダの迅速な設定・指定方法 3 2022/04/01 07:55
- Excel(エクセル) エクセルでエラーを無視して一番左側のセルの値を返したい 2 2023/07/27 13:06
- Excel(エクセル) Excelの関数について教えてください。 5 2023/07/28 11:27
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
既定のコンストラクタがありま...
-
_beginthread()の使用について
-
【gcc・cygwin】multiple defin...
-
Notepad++の関数リスト表示の変...
-
C++にてtemplateで受け取った任...
-
Arduinoでの圧電スピーカーとタ...
-
マルチメディアタイマー
-
戻り値を返す関数の前に(void)...
-
ArduinoでMouse関数を使用して...
-
マルチメディアタイマーの使用方法
-
LEDで電光掲示板に「A B C D E...
-
多重定義が起きている?--lnk20...
-
Opengl+jpeglibでC3867エラー
-
静的でないメンバ関数の呼び出...
-
int main()、void main()、void...
-
合格か否かを表示するプログラ...
-
C# Controls.Addで動的に配置し...
-
C++でオーバーロードに関するバ...
-
const_castのつかいどころを教...
-
void*型の配列について
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
【gcc・cygwin】multiple defin...
-
int main()、void main()、void...
-
戻り値を返す関数の前に(void)...
-
既定のコンストラクタがありま...
-
Notepad++の関数リスト表示の変...
-
ArduinoでMouse関数を使用して...
-
多重定義が起きている?--lnk20...
-
C++にてtemplateで受け取った任...
-
静的でないメンバ関数の呼び出...
-
C# Controls.Addで動的に配置し...
-
const_castのつかいどころを教...
-
(void)0 はどんな意味ですか
-
C# KeyDownイベントでショート...
-
gcc: incompatible pointer type
-
C#でラジオボタンを設定に記録...
-
VC++でGetKeyboardStateがうま...
-
C言語 ① 5秒間 1秒間隔で点滅を...
-
void*型の配列について
-
GDI+の使用方法について
-
DirectInput でのエラー
おすすめ情報