さて、各部を解説していきたい。ついてこれる奴だけついて来い!
・CPU
Microchip PIC16F88/20MHz。メジャーな8ビットマイコンを採用。全てアセンブラで記述。
プログラム領域は2ページ4Kワード、でギリギリ状態。幕間デモとかネームエントリーとか入れる余裕はまったく無くなった。サイズを減らすためにサブルーチン化をやりすぎると、スタック8段をすぐにオーバーして突然動かなくなる。CALLの深さに注意しながらサブルーチン化を行なった。
入出力ピンはポートB0〜7をデータバス用として、その他制御用にポートA0〜6を使用。
水平走査線数を数えるためにタイマー割り込みを使用。
・表示システム
スクリーンは32x32個のキャラクターで構成される。
1キャラクターは8x8ドット3プレーン、1ドット8色。パレットなし。
各キャラクターは1バイトで指定。よってキャラクターの種類は256種類。
RAMのアドレスマップは以下。
| 開始 |
終了 |
用途 |
サイズ(KB) |
| 0x0000 |
0x07FF |
スクリーンデータ/ワーク |
2 |
| 0x0800 |
0x0FFF |
キャラクターデータ(赤) |
2 |
| 0x1000 |
0x17FF |
キャラクターデータ(緑) |
2 |
| 0x1800 |
0x1FFF |
キャラクターデータ(青) |
2 |
| 0x2000 |
0x7FFF |
非接続 |
24 |
キャラクターデータはROMから読み込んでRAMに書き込む。
キャラクターROMは32Kバイト。同時に使える256個のキャラクターデータが5ページ分は入るが、タイトル用とゲーム用の2ページ分しか使用していない。
BMPファイルからROMバイナリを生成するプログラムも作成。こういうのはC#で書くと超楽。
同期信号は、VESA規格に合わせて、640x480@60Hz(VESA
STD) H:31.469kHz V:59.940Hz に近い信号を生成している。
RAMのアクセススピードは55nsで、20MHzの1周期は50nsなので、アクセスに2クロック必要になる。もう少し速いクロックを使用すれば1クロックで間に合って、いろいろ余裕ができてドット比1:1も実現できたかもしれない。
・走査線同期手法
1フレーム内の更新はVブランク期間では間に合わない。
ダブルバッファにするメモリもハードリソースもない。
なので、キャラクターを書き変えるときは水平走査の位置を調べて、書き変え中に走査線をまたがないように、水平走査線の通過待ちをしている。これでティアリングを回避できる。
・CPLDの処理
1、同期信号を生成し、逐次RAMからデータを読み出し出力する。
画面は単純なビットマップ構成ではなく、キャラクター面構成になっているので、
1度目にキャラクター番号を読み、2度目にそのキャラクタービットマップデータを読む必要があるため、RAMのアクセススピード55ns、ドットクロック20MHzでは横320ドット程度が限界だった。640x480の解像度は、RAM容量やRAMアドレッシング用のバス幅の関係で断念した。なので、水平同期信号は31.5kHzのまま、2ライン同じものを表示している。
1キャラクターは8x8ドットで、横8ドットが1バイトのデータとして8バイト連続して並び、それが256キャラクター分並んでいる。そしてその256キャラクター分がRGB3チャンネル分並んでいる。
キャラクターデータをRGB各1バイトRAMから読み出してドットクロックに合わせてRGB1ビットずつ出力する部分が95108に入らなくなったため、9572側にその処理を移した。
95108側はギリギリのところで合成されている。ずーっと入らなくて苦労していた部分である。
9572では色データを再構成する処理の他に、ジョイスティック読み込み、サブCPUコマンド送信、キャラクターROM制御などを行なっている。
2、CPUからのRAM読み書きリクエストに応じる。
CPUはまずCPLDにアドレスを書き込む。これは非同期に行なえる。
次に読み込みの場合、RWを読み込みにしてリクエストフラグを立てる。データがCPU側に出力されたらレディフラグが立つのでデータを読み込む。リクエストフラグを下げる。
書き込みの場合はCPUはデータを出力したままRWを書き込みにしリクエストフラグを立てる。CPLDがデータをRAMに書き込んだらレディフラグが立つ。リクエストフラグを下げる。
CPLD内部ではドットクロックに合わせて逐次RAMから読み出しているスキに、CPUからのリクエスト応じて読み書きしなければならない。ビデオデータの読み出し側が待つわけにはいかない。
CPLDはキャラクターデータを横8ドット分まとめて処理するので、その期間をフェイズに分けるとかろうじて読みか書きのどちらか1回だけ実行可能なスキができる。その空いたフェーズにCPUのリクエストを調べて読み書きの処理を行なっている。
・音声発生システム
サブCPUでは音声を生成している。
ROMからPCMデータをストリーミングしたチャンネルが1つと、矩形波で線形減衰する波形をシーケンス再生するチャンネル2つの計3チャンネルをミックスして出力している。
PCMはモノラルで量子化ビットは符号なし8ビット、サンプリング周波数は22050/11050/8269/5500Hzの4種類。音のタイプによってサンプリング周波数を変えている。低い音ほど低いサンプリング周波数にして容量を稼いでいる。ROM容量は128Kバイト。効果音数は13音で128Kバイトの容量をきっちり使い果たしている。
ポップコーン(BGM)はシーケンスデータから矩形波を生成して鳴らしている。シーケンスデータは自分の耳コピーなのでまったくもって自信がない。つくづく音感がないことに絶望した!出力も8ビットで、ラダー抵抗でD/A変換。その後386で増幅。ボリュームとスピーカに接続されている。
・筐体
今回どうしてもご覧のごついジョイスティックが使いたかったため、ケースを選択する余地が無くなった。ジョイスティックの深さ分の高さを持つケースなどほとんどなかったのである。唯一存在した高さ75mmのケースを採用した。実物を手にしたときは、けっこうでけえとか思ったりした。ケースの加工は、アルミ板をドリル、リーマー、ヤスリでひたすら削る削る削る。ハンドニブラの出番は残念ながら今回もなかった。ここでジョイスティックとスピーカが干渉する設計ミスが発生。スピーカを予定していたものより一回り小さいものにすることで回避した。
・アプリケーションソフトウェア
ゲームはペンゴである。題材としては最適。見た目は違うけど中身は同じものを作るよりは、中身は違うけど見た目は同じものを作ろうと思った。移植する感覚か。なので、まったく同じものではないという意味でもタイトルに「TRIBUTE」を追加した。
以下、オリジナルとの相違点を中心に解説。
オリジナルはスプライトを搭載しているのでその辺のシステム変更は大きい。全てをキャラクター単位で表現しなければならない。
オリジナルのペンゴは縦画面。横画面になってもゲーム性に影響はないだろう。
オリジナル操作は4方向1ボタン。ボタンでアイスブロックを押す、壊す、壁を揺らす。本ゲームでは
ボタンを排除して、ボタン操作を対象物方向への移動操作で代用した。ペンゴの向きはゲーム性に関係ないので、全体のゲーム性は大きく変わってはいないだろう。
デモ画面、リザルト画面、幕間デモ、ネームエントリー画面はCPUプログラム容量の関係で割愛した。
滑るブロックと敵のコリジョンはオリジナルに比べて相当甘い。半キャラずれは当たりになっているから、気持ちよく敵を潰しまくれるようになっている。
敵の卵まわりの使用がオリジナルといろいろ違う。卵の入っているブロックを壊しても卵が割れる絵は出ない。また、卵から生まれている最中でもブロックで潰すことができてしまう。
オリジナルでは最後の1匹は逃げ出すが、本ゲームでは逃げない。
1UPなし。
敵のAIもあまり凝ることができなくて残念な部分ではある。
・まとめ
アセンブラのコーディングは面白い面もありつつ、キツイ部分もあったりして。ゲーム機製作プロジェクトは当分いいわって感じ。次にやるときは順当にスプライトエンジン(ラインバッファ)の実現がテーマになると思われ。また長い構想期間に入るとしよう。
さて、これでようやくモニタをXBOX360に繋ぎなおせるということでもあるわけであります。
久しぶりに火を入れてみようかな。