undefinedundefinedbokuweb.me2020-04-14Rustから目覚めるぼくらのゲームボーイ!Conway氏についてですが、公式にアナウンスがでたようです。ご冥福をお祈り申し上げます。www.math.princeton.edu2003年に発売された「Linuxから目覚めるぼくらのゲームボーイ!」というC言語でゲームボーイアドバンスで動作する自作ゲームを作成していく書籍があります。ゲームボーイアドバンスはARM7TDMIというコアを使用しており、Rustで自作ゲームを作ることも可能となっています。この記事では「Linuxから目覚めるぼくらのゲームボーイ!」のステップをRustで実施するための準備としてライフゲームが動くまでを書いてみます。動機は今作っているWasmインタープリタをGBAで動かすことができないかの調査です。(たとえLチカレベルでも)AssemblyScriptとかでGBAのゲームかけたら面白くないですか。2020環境のセットアップRustはARMに対応しています。Coretexシリーズなど比較的新しめのアーキテクチャのみの対応となっており、古いARM7TDMIには対応していません。幸いなことにLLVMはこのCPUに対応しているので直接LLVMにターゲット設定してやることでARM7TDMIに対応したバイナリを出力することが可能となります。DockerRustRust現時点での最新のnightlyを使用しています。組み込みでRustを使用する場合原則nightlyとなるようです。2020-04-09)また、クロスビルド用にcargo-xbuildもインストールします。cargo-xbuildToolchainARM7TDMI用のToolchainを用意します。ターゲットはarm-none-eabi。Worldのようです。crt0.Sではmain.rsから書けるかと言うとそうではありません。main関数を呼ぶためのスタートアップルーチンをアセンブラで書く必要があります。いっても最低限でよければ以下を書くだけです。mainを呼び出しているだけです。割り込みを使用する際にはここでいくつか設定が必要になると思いますがとりあえずは不要です。詳しくは「Linuxから目覚めるぼくらのゲームボーイ!」に記載があります。r0linker.ld今回のようなケースでは、どのようなメモリ構成になっていて、どこにどのセクションをロードするのか。教えてあげる必要があります。そのためにリンカースクリプトを用意します。0xff}「プログラムを内蔵ROM(0x8000000)に配置する」よう記述していますmain.rsようやくmain.rsにとりかかります。以下は液晶ディスプレイの中央に青いドットを一つ表示するサンプルです。0}#![no_std]Rustの標準ライブラリはOSの機能を利用しているため今回のようなベアメタルなケースでは使用できません。そのため#![no_std]をつけてstdクレートではなくcoreクレートをリンクするよう示します。(アロケータも使用できなくなるため、この時点ではVecやStringも使用不可能となります。30x400_0000番地はLCD制御を行うレジスタで0x0403を書き込むことで複数枚ある背景のなかから2枚目、描画モード3を指定しています。GBAでもファミコンやゲームボーイのように8x8のスプライトを敷き詰めてゲームを描画するのが基本的な使用方法になるようなのですが、ピクセル単位で描画することができるモードがあり、モード3はそのうちの一つです。スプライトの用意や設定をせずに直接描画できるので楽です。write_volatileは最適化により省略されたり並べ替えられたりしないことを保証するメソッドです。組み込みなどでは「レジスタのあるビットが変化するまでloopの中で待ち続ける」といった処理を書くことがあるかと思いますが、その場合volatileがないと同じ番地にリードを繰り返す無意味な処理とみなされ省略されるケースがあると思います。xxxx_volatileを使用することでこれを防げるという認識です。120にオフセットすることで真ん中にドットを打つことができますできます。ここでは0x001Fを書き込んでいますが、これは赤色になります。GBAでこのモードの場合各ドット色は15bitで表現されます。bit0~4が赤、bit5~9が緑、bit10~14が青となっています。そのためこのサンプルは赤いドットになります。用のバイナリを吐くようになります。このjsonはいくつかのサンプルを参考にしており、その際data-layoutについて、初めて見たのでびっくりしたのですが、以下を見ると読み解くことができそうです。llvm.orgどうやらターゲットのエンディアンがリトルエンディアンであることやポインタを32bitアライメントでレイアウトすること、64bit整数を64bitアライメントでレイアウトする等指定しているようです。makefile自分は以下のようなmakefileを用意しました。arm-none-eabi-asでcrt0.Sからcrt.oを作成しtarget配下に格納。xbuildでビルドした後、arm-none-eabi-objcopyでelfをbinに変換しています。game.gbaビルドここまででようやくビルドできそうです。makeでうまくいけばgame.gbaが生成されると思います。実行エミュレータで動作させてみます。自分はvisualboyadvance-mを使用しました。game.gbaうまく行けば以下のように赤いドットが表示されると思います。World!が終了です。Lifeもう少し動きのあるものを動かしてみたいので以前Wasmの勉強用に作ったライフゲームを移植してみたいと思います。github.comAllocator前述したように現時点ではVecやStringが使用できません。上記のサンプルはVecを使用しています。組み込みなどでは動的メモリ確保を禁止するルールなども多く、Vecを利用しないよう書き換えることも可能ですがせっかくなのでVecを使えるようにします。linnker.ldの修正linker.ldを修正し、data,bssのセクションを追加し、各開始と終了アドレス、またwramの終了アドレスを取れるようにしました。;}main.rsの修正main.rsを修正します。MutexとHeapを作成します。これはCoretex-M用のアロケータであるrust-embedded/alloc-cortex-mから移植してきています。{}rust-embedded/alloc-cortex-mの中身を見たところ、linked_list_allocatorクレートのHeapをMutexで包んだものを使用していました。no_stdなのでMutexも用意する必要がありますが、リソース取得前に割り込みを禁止、取得後に割り込みを再設定をしてやれば良さそうです。以下の箇所が該当の箇所です。上記で作成したHeapをglobal_allocatorとして設定し初期化することでヒープが使用できるようになりVecが使用可能となります。以下がヒープの初期化コードです。liner.ldの設定を参照し、ヒープの開始アドレスとサイズを求め設定しています。}}Vecを使用したmainあとはinit_heapを呼べば、ヒープを使用できるようになります。trueにすることで成功するようになりました。詳細は不明なのですが、リンクする必要のないものを探しにいき失敗していたところ、最適化により回避できるようになったように見えます。今回は4x4を1lifeとして作成しました。当初1pixel/lifeで実装しようとしたのですが、あっという間にヒープが枯渇したためです。RAM領域は32KiBなので当たり前感がありますね。メモリを節約するよう書き換えれば1pixel/lifeでもいけそうでしたが、あまりゲーム側に手を加えたくなかったので今回はこのようにしています。wait_for_vsyncはVBlankまでブロックし待つ関数です。VRAMを更新するのは基本的にはVBlankの間に行うことになっています。VBlankというのは描画を行っていない期間です。これは描画中にVRAMを変更すると意図しない描画になる可能性があるためです。VBlank中であるかどうかは割り込みにて知ることも可能ですが、REG_VCOUNTすなわち0x0400_0006をリードすることで知ることもできます。REG_VCOUNTを読むことで現在描画中のy座標を知ることができるのでこの値が160以上であればVBlankということになります。あとは特に特筆することはなく、lifeを計算し描画していくだけなので割愛します。}}正しく動作すれば以下のようにライフゲームが動作します。「Linuxから目覚めるぼくらのゲームボーイ!」にはブートケーブルが付属しており、これを使うことで実機で動作させることが可能になります。linker.ldの修正ブートケーブル経由で動作させる場合リンカースクリプトの修正が必要になります。これはブートモード時はROM領域ではなく0x0200_0000の外部RAM領域から起動するためです。romの箇所をbootに変更します。;}optusbのセットアップブートケーブル経由で転送するためにoptusbというツールを使います。optusbに必要なlibusbをセットアップするのですが、optusbが使用しているAPIが古いためlibusb-compatが必要となります。macであればbrewで入るようです。6thで試しています。/proc/bus/usb/devices次にoptusbのコードをダウンロード、展開します。その後Makefileを書き換えビルドすることでセットアップは完了です。*.omake以下のエントリが参考になりました。本体の電源を投入後、ケーブルを接続、以下のコマンドで転送ができます。game.gba動作すれば冒頭のtweetように動作します。動機であったWasmインタプリタを動かせるか、ですが、インタプリタ側をno_stdに対応させれば動くのではないかと思っていますが、あとはメモリ容量との勝負になりそう。できたとしてもドットをちょっと動かす程度のものにはなると思いますが、もし成功したらまた記事にします。2018の21日目です。12引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。