メガドラ開発:大容量のゲーム開発

 最近はメガドラゲームの開発を趣味と仕事で平行しており、いろいろと知識もたまってきました。SGDKによってメガドラのゲーム開発がC言語で充分な速度が得られるようになり、もっとも手軽なレトロゲーム機の開発環境となったのではないかと考えています。
 一方で簡単なゲーム開発には十分でも規模が大きくなると理解しなければならない項目が増えるものの、古いハードゆえの情報不足や、エミュレータの機能をフル活用しないと問題の特定が難しくなっていきます。HowTo情報はいくつかネット上でも見られますが、この辺の込み入った情報は整理しておく必要がありそうです。今回はメガドラで大容量のゲームを開発する際の情報についてまとめてみたいと思います。まだ、私自身が研究中のところも多いため間違いもあるかもしれません。その辺はご了承ください。

バンク切り替えについて

 レトロハードのゲームプログラムでは、バンク切り替えは一般的な手段で、メガドラでも大容量ゲームを開発する際には必須の技術です。一般的にプログラムの入ったバンクを切り替えて使用することは、様々な問題を抱えます(例えば、関数実行後に戻ってきたらバンクが変わってててバグるなど…)。そのような問題を回避するには面倒な手順が必要ですが、C言語でそこまで考えて作るのは大変です。メガドラのゲームをSGDKで開発する場合、基本的にはプログラムが入ったバンクは切り替えない方が無難でしょう。バンク切り替えはリソースへのアクセスのみに限定した方が良さそうです。

メガドラのメモリマップについて

 まずメガドラのメモリマップとしては0x000000-0x3FFFFFまでの4MByteがユーザープログラム領域となっています。バンク切り替えは512KByte単位で全8か所です。ただし、最初のバンクはヘッダと起動プログラムが入るので固定ですので、変更できるのは7か所となります。ロム容量ですが、通常のメガドラフォーマットでは48Mbit = 6MByte(?)、SSFフォーマットのロムでは64Mbit = 8MByteまで使用可能らしいです。つまりロムの後半へのアクセスはバンク切り替えしないと使用できません。

SGDKのバンク切り替え

 SGDKのバンク切り替えの仕組みは2通りあり、一つはSYS_getFarData()を使用してリソースへの動的アドレスを取得する方法と、SYS_setBank()によって手動でバンクを切り替える方法です。前者はリソースのROM内のアドレスを引数として渡すことで、SGDKが自動的に0x300000-0x37FFFF(又は0x380000-0x3FFFFF)のバンクにセットしたうえで、リソースへの有効なアドレスを返してくれます。恐らく、この関数を3回以上(異なるバンクのデータに対して)実行すると、最初に取得したアドレスは有効ではなくなる可能性があります…(試してないですが)。後者の方法は、文字通り指定バンクエリアを指定番号のバンクに設定する関数です。

SYS_getFarData()の補足

この関数は、引数として渡したアドレスのリソースがすでにアクティブなバンク内であった場合、バンクの状態を変えずにアドレス値を渡してくれます。つまり同じバンク内にある複数のリソースに対しては、バンク切り替えを行わずにアドレスが取得できるわけです。また、0x200000 - 0x2FFFFF辺りもリソースが入っているわけですが、(起動後SYS_setBank()でバンクの状態を変えてなければ)SYS_getFarData()で0x200000 - 0x2FFFFF内のデータを取得するとバンクは変化せずに元のアドレス(0x200000 - 0x2FFFFFの範囲)が返ってきます。当然ながらSYS_getFarData()を使わなくても、そのままのROM内のアドレスでアクセスできます。これは推奨されない使用法ですが、SYS_getFarData()が常に0x300000 - 0x3FFFFFの範囲のアドレスを返すわけではないことがわかります。

実装に際して

 メガドラのプログラムがどのアドレスまで配置できるかは、完全には理解していません。実験した範囲では0x200000を超えた段階でエミュレーターで動作しなくなりました。0x200000以降はバンクとしてリソースを配置することはできても、そのアドレス上のプログラムは実行できないのかもしれません。とはいえ、プログラムは2MByteもあれば十分ですね。
 以上のことから、プログラムは0x1FFFFFまでに収め、残りはリソースとして使用。バンクの切り替えは基本はSYS_getFarData()により0x300000 - 0x3FFFFFの範囲を自動的に管理してもらい、どうしても固定したいバンクがある場合にはSYS_setBank()で0x200000 - 0x2FFFFFを手動で管理。といった運用が良さそうです。
 また、リソースの作成はresフォルダ以下に.resファイルを置くと自動的にrescompが記載された内容をリソースファイルとしてまとめてくれます(rescompの使用方法はSDK内のbin/rescomp.txtを参照)。.resファイルにALIGN 524288と記述することで、512KByteのアライメントとなります。シンプルにいうとそれ以降は別バンクとして扱われる、ということです。それとバンクの番号というのは登録順にのようですので、.resのファイル名にr0_xxxx.resといった感じにしておき、1つのファイルが1バンク分にしておくと良さそうです。

追記:最終バンクは少し空けておく必要があるっぽい?

SSFの場合は0x7FFFFFがロムの終端っぽいのですが、SGDKが終端に何やらデータを入れてるっぽいので4.2KByteほど空けておく必要があるようです。それを超えてしまうとエミュレーターで起動しなくなりました。

symbol.txtの最後です。上記分は空けておく必要がありそうです。

この記事が気に入ったらサポートをしてみませんか?