2019年11月9日土曜日

ゲームボーイの上で動くソフトウェアと通信をする

ゲームボーイのカートリッジを自分で作って遊んでいます。いままでカートリッジ側のROM・RAM・MBCをすべてマイコンに実装したものを作って遊んでいましたが、カートリッジの外からRAMにデータを書き込む方法が見つからず難儀していました。

マイコン側CPUの処理を使うとゲームボーイが止まる

このカートリッジでは、ゲームボーイに対するROMデータの出力をマイコンのCPUがまかなっています。

  1. ゲームボーイ側がカートリッジにアドレスを渡す。
  2. マイコンがアドレスを受け取る。
  3. Read/Writeのどちらか、バンク設定があるのかを判断して、返すべきデータを決める。
  4. マイコンがデータを出力する。

1バイトとはいえ、どのデータを返すかが決まるまでにいくつかの条件分岐が入ります。なので、外部通信等で他の処理がCPUを使用すると、このデータを返す処理が間に合わずゲームボーイ側がハングアップします。

外部通信の受信をDMAに任せたらどうか

今回、受信したデータをそのままカートリッジRAMの領域に直接書き込む形にしたら問題が解決しました。DMAのサンプルコードをみると、受信したあとそのデータを確かめるためのコールバック関数があったりしますが、その部分が動くとCPUを使ってしまうため、その部分は絶対に使わないようにします。ゲームボーイのソフト側は、DMAの書き込み先に合わせた特定のアドレスだけを読むようにすれば、カートリッジRAMに書き込んだデータをゲームボーイ内で扱うことができます。

実装例

ゲームボーイのカートリッジRAMにMIDIデータを書き込み、MIDIノートオンのときにゲームボーイ側で音を鳴らす仕組みです。

MIDIは、フォトカプラで受信後はbaudrateが31250のUARTとしてそのまま扱えるので、マイコンのDMAで直接RAMに書き込むことができます。書き込まれる予定のアドレスをゲームボーイ側は常時監視し続け、MIDIノートオンのメッセージを受信したら、ノート番号に合わせた音程で音を鳴らすという仕組みになっています。