オレ仕様のゲーム機を作ろう(その14)
前回は,音源と CPU を載せた,「CPU ボード」を作りました。
オレ仕様のゲーム機を作ろう(その13)
今回は,画面背景を表示する「ビデオボードその1」を作っていきます。
複数枚の BG 画面
機能アップを説明した回で,複数の BG 画面にすることを決めましたが,過去にいくつか作ったことがあるので,そんなに大きな問題はないと思います。
回路は,BG 1 枚単位で作り,その出力を合成すれば,何枚でも重ねて表示することができます。
また,1 枚単位で考えると,この BG というものは,表示するキャラクターパターンが格子状に敷き詰められていて,ディスプレイモニターの走査する信号に合わせて,ピクセルデータを出力するので,回路自体はそれほど複雑ではありません。
BG の情報が格納されたビデオメモリから一定間隔で情報を取ってきて,それをキャラクターパターンのアドレスにして,キャラクターパターンメモリからピクセルを出力することを繰り返します。
後で,スプライト画面との合成後に,パレットによる色変換を行うので,この時点では,ディスプレイモニターに出力する RGB カラーデータではなく,パレット番号+ピクセルデータになっています。
画面スクロール
画面の解像度(ディスプレイモニターに表示される領域の大きさ)は,横 256 ドット×縦 224 ドットです。
BG 1 枚の全体の大きさは,512×512 ドットで,この内の 256×224 ドット分が画面に表示されることになります。
おおよそ,4 画面分の広さがありますが,このうちのどの部分を表示するかをスクロールレジスタに指定します。
縦方向,横方向それぞれに,0〜511 の値を指定することができ,はみ出した部分は,逆方向に繋がります。(ファミコン版ドラクエのマップのような感じ…と言えばわかりやすいんですかね?)
このスクロールレジスタですが,数バイト設定すれば画面全体が動くので,とても便利ですが,ディスプレイモニターが画面を走査している時に設定値を変更すると,そこを堺に画面がずれます。
ですので,通常は,ディスプレイモニターが画面を走査していない時,すなわちブランキング期間に設定を行います。
普通の全画面スクロールは,垂直ブランキング期間にレジスタ設定をするのですが,これを水平ブランキング期間に行うと,ラスタースクロールと言われる効果を表現することができます。
ゆらゆら効果とか,遠近表現,画面分割とかによく使われますよね。
同じ回路を2つ載せる
今回,BG は,2 面にしますので,1 面分の回路 2 つを 1 枚の基板に載せます。
同じ回路の基板を 2 枚作って繋いだほうが楽なのですが,前回の「CPU ボード」が結構な大きさで,それに大きさを合わせることを考えると,スカスカになりそうなので,1 枚の基板に,2 面分の回路を入れました。
同じ回路なので,機能も同じです。
パターンの大きさと色数
1 つのパターンの大きさは,横 8 ドット×縦 8 ドットです。
画面には,横 32 パターン,縦 28 パターン表示されることになります。
1 ドットに付き,2 ビットのデータとしているので,4 色表示できますが,他の BG や,スプライトとの重ね合わせがありますので,そのうちの 1 色は,透明色になります。(実質 3 色)
部品数を増やせば,この色数は増やせますが,それは次回作で挑戦したいと思います。(単に面倒なだけだったりします…)
また,パターンごとに縦方向,横方向それぞれで上下フリップ,左右フリップさせることができます。(反転表示機能ですね)
これは,ゲーム機にはよくある機能ですが,反転したパターンデータを別で用意しなくても良いので,パターンの節約になります。
CPU からのアクセス
BG を表示するには,パターンデータを格納するメモリと,そのパターンをどの位置に表示するかを指定するネームテーブルというメモリが必要になります。
これらのメモリをどこにマッピングするか…ですが,CPU のアドレス空間にマッピングする方法と,アドレスレジスタとデータレジスタを用意して,そのレジスタに値を設定して,読み書きする方法が考えらます。
CPU のアドレス空間にマッピングする方法は,普通のメモリのように CPU 命令でアクセスできるので,かなり直感的に扱うことができます。
キャラクターパターンデータなどは,圧縮していることが多いので,それを展開する時に,直接展開することもできます。
デメリットとして,ただでさえ狭い Z80 のメモリ空間の一部に割り当てる必要があることですね。
もう一つの,レジスタを介してデータを読み書きする方法ですが,シーケンシャルにアクセスする場合は,こちらの方が便利そうです。
データを設定後に,アドレスを自動インクリメントさせるようにすると,プログラムも単純になりそうです。
古いゲーム機はこの方法が多かったような気がしますね。ファミコンとか,セガのゲーム機とか…,MSX もそうですよね。
デメリットとしては,圧縮されたデータを展開する場合,シーケンシャルに展開できれば良いのですが,展開済みのデータを使ってさらに展開…などランダムアクセスする場合は,一旦 RAM に展開してから,まとめて転送する必要があったりします。
まぁ,一長一短ですが,今回は,前者の CPU のアドレス空間にマッピングする方法を取ることにしました。
Z80 アドレス空間の $8000〜$BFFF の 16KB の空間に,キャラクターパターンとネームテーブルを,バンク切り替えで割り当てることにします。
キャラクターパターンは,2bit x 8 x 8 x 1024 で,ちょうど 16KB になります。
ネームテーブルは,1byte x 64 x 64 で,4KB ですが,対になるアトリビュートテーブルが必要なので,プラス 4KB で,合計 8KB です。
なので,16KB の空間に,2 面分のネームテーブルとアトリビュートテーブルを入れてしまいます。
ネームテーブルとアトリビュートテーブルは,動的に書き換え可能にしないといけませんが,パターンデータへの読み書きは,BG の表示をオフにしている間のみ可能としました。
負け仕様っぽいですが,シーンの切替時にごっそり書き換えすれば,概ね問題ないような気がします。
作成した基板
こちらが,作成した BG 基板です(クリックで拡大します)
意外と密度は低いです。…が,やっぱり配線は大変でした(笑)。
74 ロジックと,SRAM のみで構成されています。
真ん中を堺に,左右で同じ部品が配置されています。
同じ業者に注文したのですが,「CPU ボード」に比べて,妙に明るい色合いですね…。
やっぱり一発では動かない
お約束ですね(笑)。
基板裏側で修正した結果がこちらです。
これまた,ひどいですが,前回よりちょっとマシですかね?
BG の表示回路が SRAM から情報を取り出す間は,その SRAM に CPU のバスを接続できません。
また,その間は,CPU にウェイト信号を送って,待ってもらいます。
その後,ウェイトを解除すると CPU のバスに切り替わるのですが,このタイミングが早すぎたため,CPU バス側のデータを拾ってしまい,ゴミが出る不具合がありました。
真ん中にある IC 2 個は,その修正です。
また,この基板は,ディスプレイモニターに出力する信号は生成していませんので,ブレッドボードで,これを簡易的に作ってみました。
この状態で,FPGA で事前にテストしたプログラムを実行させてみます。
すみません。途中でピントがずれちゃってますね。
パレットがないので,ピクセルデータがそのまま RGB 信号になっています。
でも,それ以外は,FPGA で動作させたときと同じ動きになっていますね。
オレ仕様のゲーム機を作ろう(その12)
不具合を調査,解決するのが結構大変でしたが,今回も,なんとか想定通りの動きになりました!
次回は,キャラクターを自由に動かすことができる「スプライト」を表示する「ビデオボードその2」を作成します。
それでは,次回に続きます…。