libgba(LGPL v2)を静的リンクしている関係でGPLになります。本人としてはNYSL 0.9982(通称:煮るなり焼くなり好きにしろライセンス)が希望なんですけれど、大人の事情でびみょーなところで落ち着いています。私が作成したコンバータやツール類はNYSLでいいです。他の作者様の作成されたものは、個別に従ってください。
2023/08追記:最新のサンプルコード(No115以降)はlibgbaを動的リンクに変更しました。その結果、私のコード部分はCC0にしています。煮るなり焼くなり好きにしてください。
2023年現在は海外のdiscordが活発です。全て英語ですが一度覗いてみることをオススメします。
libgbaにはマニュアルがないので直接ソースコードを読まなくてはなりません。githubより別途ダウンロードしてください。
ブートケーブル(MultiBoot)か、カードリッジからの起動の違いです。もちろんブートケーブルからはカードリッジを使用していないことを前提にしています。それぞれの電源を入れたときの開始番地は0x2000000(ブートケーブル)、0x8000000(カードリッジ)になります。エミュレータが対応していれば、ブートケーブル用ロムをリネームすると正しく動きます。
本当です。devkitARMでは暗黙の了解としてlibgcc.aで計算をしています。ただし計算は非常に遅いので、BIOS(SWI 6, 7)を使うか、IWRAM領域に自作の関数を作るのがいいようです。
マジコン(カードリッジの内容を書き換えるハードウェア)は、homebrewを目的としたものではなく、あくまで市販ゲームを遊ぶものとして販売されています。このwikiではhackしていく楽しみを目的としていきたいと思っていますので、申し訳ないのですが別のサイトを利用してください。
電源投入時のGBAには、乱数に使えるようなメモリ領域がありません。NDSならRTC(日付や時間)をsrand関数に使うことが可能ですが、GBAにはRTCの対応したカードリッジを使用しなくてはなりません。以下、回避策です。
なおgccに入っているのではなく、メルセンヌ・ツイスタ法を使われることをオススメします。
GBAは起動時、ROMの内容が正しいかどうかCRCチェックを行っています。失敗した場合、ロゴが表示されたところでフリーズしてしまいます。対策としてはgbafixというツールでROMのCRC値を修正してください。実行ファイルはdevkitProに標準でついています。
gbafix.exe test.gba -tTest -cTest
doc.8#h5951034を参照してください。
ConsoleDebug("表示\n");
例でいうところの「表」(ShiftJIS)コードは、0x95 0x5cです。問題なのは2バイト目で、この0x5cというのは1バイト単体だけだと「\」(エンマーク)という特殊文字で解釈されます。この為、「\\n」となるので書き方がおかしくないか警告を出していると思われます。以下のように修正してください。
ConsoleDebug("表\示\n");
推薦図書を作りました。
devkitARM をインストールしたディレクトリから arm-eabi/lib/gba_cart.ld を開いて下さい。以下のような記述が見つかると思います。
__iwram_start = ORIGIN(iwram); __iwram_top = ORIGIN(iwram) + LENGTH(iwram);; __sp_irq = __iwram_top - 0x060; __sp_usr = __sp_irq - 0x0a0; __irq_flags = 0x03007ff8;
このうち __sp_usr と __sp_irq に、各モードにおけるスタックポインタの指すべきアドレスが割り当てられ、crt0.S 内でのスタックポインタ初期化によりスタック領域が確定します。
.iwram __iwram_start (0x03000000) | .bss (不定) | 初期値なしグローバル変数領域 .data (不定) | 初期値ありグローバル変数領域 __iheap_start (不定) | IWRAMヒープ領域 | ユーザースタック領域 __sp_usr (0x03007F00) | IRQスタック領域 __sp_irq (0x03007FA0) | __irq_flags (0x03007FF8) | 割り込みフラグ | 割り込みベクタ __iwram_top (0x03008000)
通常は EWRAM にあるヒープ領域 (__eheap_start) を使用するため問題となることはありませんが、ユーザースタック領域とヒープ領域が重なっている点に注意して下さい。
typedef struct { u16 a; u8 b; u16 c; } ST_TEST;
この構造体をsizeofで見てみると、6バイトになります。コンパイラとしてはソースを機械語に落としたときに、そちらの方が効率的にアクセスできるからという理由です。この状態でmemcmp関数を使用して、初期化されていないスタック変数で比較すると、1バイト不定の為バグったりしますので注意してください。どうしても意図したサイズにしたい場合、以下のマクロを使用するといいと思います。
#define __PACKED __attribute__ ((__packed__)) typedef struct { u16 a; u8 b; u16 c; } __PACKED ST_TEST;
この場合は5バイトになります。
コンパイラのオプションにコードの最適化(-0x)を指定しているときに起こる現象です。たとえば次のような処理があったとします。
u32 flag=0; void wait() { while(flag == 0) { //割り込みにより、処理を抜けます。 } }
while文の処理の内容は、コンパイラ側から見れば余計な処理と解釈されてしまい、機械語に落とす過程で処理自体を省かれてしまいます。こういう時は変数flagにvolatile修飾子をつけることで、この変数は最適化しないでくださいという指示をコンパイラに教えることができます。
volatile u32 flag=0; vu32 flag=0;
先の例はものすごく簡単なものでしたけれど、実際にはもっと複雑です。妙にハマることもしばしばあって、こんなことまでも最適化されてしまうのかと関心してしまうこともあります。なので、コードに問題がなくて、処理が思うように行かない場合は、コンパイラの最適化オプションを弄るか修飾子を付けてみてください。