PIC32MXでビデオゲームマシン作成(VGA出力編)

昨日はPIC32MXでLEDチカチカさせました。


次のステップとしては、いったんFAMIRGBからは離れて、PIC32MXでVGA信号を出力し、ビデオゲーム機っぽい映像をパソコンのモニタに表示させたいと思います。
まずは映像出力系から固めていくことで、作ってる感をわかりやすい形で醸し出す作戦です。
ディスプレイになんかしらの絵が出てしまえば、工作意欲も湧きやすいわけですね。

VGA出力

さて、PIC32MXでVGA出力ですが、なるべくゲーム用途に向いた方式で出力したいと思います。
今回使用するデバイスのPIC32MX250F128Bは、データメモリ(要するにRAM)が32キロバイトです。
これを多いと見るか少ないと見るかは、見方によって変わってきます。


最近のゲーム機はたぶん全てのものが、フレームバッファ方式でVRAMを持っていると思います。
要するに1画面分の画面データが1ドットごとそのままVRAMに入っているわけです。


フレームバッファを32キロバイトのメモリで持たせようとすると、かなり厳しいです。
横320ドット、縦240ドット、1ドットあたり1バイトのフレームバッファは、75キロバイトにもなります。
これだとそもそもメモリがたりませんね。
フレームバッファ方式にするとしたら、1ドットの色数をぐっと減らして、1ドット1ビットぐらいにしないと無理です。
横320ドット、縦240ドット、1ドットあたり1ビットのフレームバッファは、9600バイトです。
モノクロでも10キロバイト弱も使うわけです。


ゲーム用途ではモノクロ表示はさびしいですね。
そこで、ラインバッファ方式で映像出力することを考えます。
VRAMとして持つのは横1ライン分のメモリだけです(実際はダブルバッファリングするので、横2ライン分持ちますが)。
これなら、横320ドットで1ドット1バイトなら、320バイトのメモリで済むわけです。
ダブルバッファにしても、640バイトで済みますね。
1ドット1バイトということは、固定256色表示が可能です。
MSX2のSCREEN8相当といえば、伝わる人には伝わりやすいかと思います。


ラインバッファは2本用意します。
ひとつめのラインバッファからDMA転送によってIOポートへRGB値を出力している間に、もう一方のラインバッファに対して、次の水平ラインに表示する内容をCPUによって展開していきます。
これを2水平ラインごとに交互に切り替えながら処理するわけです。
2ラインを同じ内容で出力することで、縦240ドット表示にします。


タイミング

VGA(640x480 60Hz)のタイミングで、映像を出力するためのタイミングを検討します。
H-SYNCの周期は31.778マイクロ秒とのことなので、この周期でタイマ割り込みをかけます。
この割り込み処理で、H-SYNCをアクティブにして、DMA転送を開始して映像出力します。
その裏でCPUが次のラインの画面イメージを展開していきます(ラインバッファ展開)。
これを480回繰り返します。
横方向のタイミングをちゃんとやっておけば、あとは適当でもたぶんモニタに映るのではないかと思います。

スプライト

PIC32MX250F128Bは、プログラムメモリ(要するにROM)が128キロバイトなので、プログラムとキャラクタデータをそのサイズに収める必要があります。
128キロバイトは、1メガビットです。
ゲームのデータ容量として、現在の感覚としてはかなり少ないです。
ファミコンドラクエIIが1メガビットROMだったらしいので、感覚的にはあれぐらいの容量です。

この容量に収めるためには、ゲームのキャラクタ画像はビットマップの一枚絵で持つことは無理です。
スプライトやPCGのような形式で持つ必要があります。
とりあえずスプライトのみを扱えるようにすることを考えます。
スプライトは1ドットあたり4色(2ビットカラー)で、16x16サイズ固定とします。
画面表示自体は256色なので、256色中から4色選ぶパレットカラー方式です。
パレットを4つ定義できるようにして、スプライトごとに4つのパレットから1つ使用するものを選べるようにします。
プログラムメモリの128キロバイトの半分を画像データで使ってよいことにすると、1ドットあたり2ビットなので、64キロバイトだと262144ドット分の画像データが入ります。
262144の√は512なので、縦512ドット、横512ドット分のキャラクタパターンを定義できます。


PIC32MX250F128Bのメモリでゲーム機を作ろうと思うと、ファミコンと同レベルであまり余裕がありませんが、CPUパワーという観点でみるとMIPS32の40MHz動作なのでかなり余裕があると思います。
CPUの計算能力を生かして、スプライトパターンを圧縮して持っておき、ラインバッファ展開時に解凍するのもいいかもしれません(まあ、圧縮アルゴリズムは単純なランレングス法とかしか出来ないと思いますが)。


ひとまずこんな感じで実装してみます。
つづく。