- 締切済み
メモリ上にプログラムをロードして実行したい!!
C言語とマイコンで遊んでる者です。 タイトルの通りSDカードやCF等の記録媒体からマイコンのRAM上にプログラムをロードして実行させたいのですが方法がよく解りません。 説明しにくいのですが、PCで言うところのBIOSみたいな物を予めマイコンに書き込んでおいて、そのプログラムが外部のストレージの中に有る実行ファイルのようになってるプログラムデータをメモリ上にコピーして、処理をそのプログラムのエントリーポイントに受け渡すようなOSモドキみたいな物を作りたいのです。 アセンブラならLDとかMOVとかでメモリにコピー出来るかもしれませんが、C言語ではどのようにしてコピーするのでしょう? 仮にメモリ上にコピー出来てもmain()関数が重複してしまい呼び出す方法が解りません。 こちらもアセンブラならばプログラムカウンタをロードした位置にしてやればいいのでしょうけれど。 C言語ではできないでしょうか? マイコンはAVRかSH2Aで考えています。 何方かご教授願います。
- みんなの回答 (4)
- 専門家の回答
みんなの回答
- haniriito
- ベストアンサー率57% (12/21)
deltatledさん、こんにちは。 No.3さんがおっしゃってるようにリロケータブルな実行コードを扱うのはちょっと大変ですが、 特定のメモリに置かれることを前提としたプログラムをSDカードなどからロードして実行するだけなら そうでもありません。 まず貴方のいう「BIOSみたいなもの(=ブートローダ)」はSH2Aなどでは ベクタテーブルとともに0000000h番地以降に配置されますよね。たいていの場合は内蔵FlashROMになると思います。 で、ロードしたいプログラムが実行されるアドレスは00000000h番地ではなくて、 例えば2000000h番地以降となるようにリンクしてやればよいです。 あとはブートローダがプログラムをSDカードから20000000h番地以降に読み出し&コピーして、 その20000000h番地にジャンプします。 C言語で書くならば、 typedef void (*FN_PROGRAM)(void); FN_PROGRAM pg = (FN_PROGRAM)0x20000000; (*pg)(); とか書いておけば、0x20000000h番地に飛んでくれます。 飛んだあとどうなるかは、その「プログラム」次第です。 Cランタイムの初期化とか、ロードされるプログラムの構成を無視してかなり乱暴な書き方をしましたが、 要は上のようなコードを書けばブートローダ部分からロードされたプログラム上の任意のアドレスにある”関数”を 呼び出すことは可能だということです。 ご健闘をお祈りします。
- zwi
- ベストアンサー率56% (730/1282)
難しい話になりますがリンク後に出来上がる機械語プログラムが絶対アドレスを含まないリロケータブルなものなのかによって話が変わります。 「リロケータブル - Wikipedia」 http://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%AD%E3%82%B1%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB リロケータブルならば任意のアドレスに読み込んで実行できます。 絶対アドレスを含む場合はリンク時に実行するアドレスを決めてやる必要があり、読み込み先や実行開始もそのアドレスにしないといけません。 ちなみにスタートアップルーチンはリンク時にプログラムの先頭になるようにしてやる必要があります。 それと呼び出し方を書き忘れていましたが、#1の方の言うとおり関数ポインタとして読み込み先頭アドレスを入れて関数として呼び出すだけです。 コメントを見るかぎりかなり知識が不足されていますので、「ブートローダ」で検索されると良いかも知れません。 こんな本もあります。 「起動プログラム ブート・ローダ入門」 http://www.cqpub.co.jp/hanbai/books/49/49911.htm AVR用のブートローダ。お望みのものそのだと思います。HEXからの展開機能付き。 「自作電子小物/TIPS/SDカードブートローダ/AVR」 http://www.suzume-syako.jp/personal/Tips/peji/SDcard_BootLoader_for_AVR.html
お礼
大変遅くなりました!! 回答有難うございます。 >難しい話になりますがリンク後に出来上がる機械語プログラムが絶対アドレスを含まないリロケータブルなものなのかによって話が変わります。 リロケータブルバイナリって実行順序とかジャンプ先が相対的なアドレスになってるやつの事ですよね? うまく言えませんが 0x0000 0x0001 0x0002 とかなってるのが 0x0000 +0x1 +0x1 みたいに基準をきめてそこからの差を利用してアドレスを決めるようになってるって事ですよね? 理屈は解るのですが利用する方法とかは解りません。 処理系依存なのでしょうか?どのようにしたら絶対値参照しないようにできるのでしょうか? >コメントを見るかぎりかなり知識が不足されていますので、「ブートローダ」で検索されると良いかも知れません。 了解です!! ブートローダで調べてみます!!
- zwi
- ベストアンサー率56% (730/1282)
>アセンブラならLDとかMOVとかでメモリにコピー出来るかもしれませんが、C言語ではどのようにしてコピーするのでしょう? ポインタでmemcpy()関数を使うとかが一般的です。 >仮にメモリ上にコピー出来てもmain()関数が重複してしまい呼び出す方法が解りません。 main関数など関数名はリンク時に消えてしまう名前に過ぎません。コンパイル・リンク後はアセンブル・リンク後と同様の機械語です。なのでリンク単位が違えば気にする必要はまったく有りません。 C言語のスタートアップルーチン(アセンブラ)や、リンカでのアドレス配置などを勉強されると良いでしょう。あと実行ファイルのファイルの形式の違いを理解する必要があります。
お礼
回答ありがとうございます!! >ポインタでmemcpy()関数を使うとかが一般的です。 見覚えのある関数です!!strcpy()とかそんな感じの関数とかと一緒に本で見たことあります!! ですが使った事無いので調べてみます。 >main関数など関数名はリンク時に消えてしまう名前に過ぎません。コンパイル・リンク後はアセンブル・リンク後と同様の機械語です。なのでリンク単位が違えば気にする必要はまったく有りません。 機械語になるとよく解らないのです。 機械語になると先頭のアドレスがプログラムのスタート位置と思って間違いないのでしょうか? 必ずしもmain()関数の処理ではなく、スタートアップとかの処理も有るのかも知れませんが先頭アドレスにジャンプさせれば実行できるのでしょうか? ジャンプさせる方法ですがNo1の回答者さんの案のように関数ポインタを利用するのでしょうか? >C言語のスタートアップルーチン(アセンブラ)や、リンカでのアドレス配置などを勉強されると良いでしょう。あと実行ファイルのファイルの形式の違いを理解する必要があります。 スタートアップとリンカ、勉強してみます!! 実行ファイルのファイルの形式の違いっていうのはどういうことでしょうか? コンパイル、リンク後に吐き出されるHEXとかMOTとかの事で良いでしょうか?
- hanabutako
- ベストアンサー率54% (492/895)
マイコンってRAMにあるプログラムを実行出来ましたか? プログラム領域とRAM領域が明確に分かれているとしても、プログラム領域の空いているところにプログラムを書きこんで、そこを呼び出すことで動きそうな予感はします。 (実行中のプログラムからプログラム領域への書き込みができるか知りませんが) マイコンでやったことはありませんが、その手のことをC言語でやるには、プログラムのロードは単なるメモリーのコピーなのでストレージから普通にメモリーにデータを読みだして、そのアドレスにジャンプするという流れになると思います。ジャンプの方法は呼び出される側で準備をした上で、関数ポインターに飛ばしたい先のアドレスを代入して呼び出しても良いですし、そこだけインラインアセンブラにしても良いかもしれません。 プログラム領域にあるプログラムしか実行できないなら、予めプログラム領域に無駄に配列などを確保して、そこにプログラムを書きこんでジャンプという流れになるでしょうか。 技術的にできそうなことはわかりますが、やったことは無いので、あとは頑張って。
お礼
おぉ、回答ありがとうございます!! >マイコンってRAMにあるプログラムを実行出来ましたか? いわれてみればAVRはハーバードですから、データとプログラムとが別になってたら難しいですね。 SH2Aなら結構本格的な感じですし、メモリの領域がフラットっぽいのでAVRよりは出来そうな感じです。 >ジャンプの方法は呼び出される側で準備をした上で、関数ポインターに飛ばしたい先のアドレスを代入して呼び出しても良いですし、そこだけインラインアセンブラにしても良いかもしれません。 別のプログラムのエントリーポイントのアドレスってどうやって取得するのでしょうか? ファイルの先頭=エントリーポイントと考えてよいのでしょうか? >プログラム領域にあるプログラムしか実行できないなら、予めプログラム領域に無駄に配列などを確保して、そこにプログラムを書きこんでジャンプという流れになるでしょうか。 確かにできそうです!! しかし配列内のどこにジャンプさせたらよいのでしょう? 上にも書いたみたいにファイルの先頭ですと配列の先頭のアドレスにジャンプしたらいいのでしょうか? 配列のアドレスを関数ポインターに代入して呼び出しみたいな感じですか。 書き込んでいて思いつきましたが、gotoとかも指定したアドレスにジャンプとか言う用途に使えるのでしょうか?(goto使ったこと無いのでよく解りません)
補足
回答有難うございます!! >C言語で書くならば、 へぇ~!!こんな使い方が出来たとは!! 関数ポインタは関数名でしか使った事無いので新鮮です。 でも改めて考えると当たり前なのかも知れませんね。scanf()の実態を知ったときみたいな感じです。 そういえばHEWの設定にメモリマップか何かの設定があったような気がしますね。SとかBとか(←違うかも)そんな感じのアルファベットをアドレスの横に書くようなやつ。 ちょっと調べてみます。 この方法ならすぐ出来そうな気がします!!