ライン

拡大縮小回転機能付きビデオ出力実験

ライン

2014年7月8日

 私が高校を卒業したばかりの頃、ゲームセンターでとんでもないゲームを見つけ、目が釘付けになってしまいました。ナムコの「ASSAULT」(アサルト)というそのゲームは、戦車を操り敵の砲台を破壊していくシューティングゲームだったのですが、地上を上空から見た画面構成となっており、戦車を旋回させると自機の画像が回るのではなく、背景の地上の画像全体が回転したのです。また、なぜか戦車が上空にジャンプでき、地上の画像の縮尺が滑らかに縮小していき表示範囲が拡大する機能もありました。(ゲーム紹介の動画はこちらにあります。)

 私はゲームの内容そのものではなく、この画面の拡大縮小回転機能に衝撃を受け、いったいどんなハードで実現しているのだろうかと、自分なりに考えてロジック回路の回路図を書いてみたりしたのですが、実際に製作することなどできるわけもなく、夢想のままとなってしまいました。
 あれから25年以上たった今、世の中の進歩はめざましく、画面が回転することなど何とも感じなくなってしまいましたが、今回はあの若き頃の夢をマイコンのハードとプログラムで実現しようとたくらんでみました。

ASSAULT
(C) BANDAI NAMCO GAMES Inc.


実験回路

 実験に用いたマイコンは64ピンTQFPタイプのPIC32MX370F512Hです。PIC32MXシリーズ最高のシステムクロック100MHz対応と、512Kバイトフラッシュメモリ、128KバイトSRAM搭載が特徴です。同じ128KバイトSRAM搭載のPIC32MX695F512Hなどと比べると、機能が少ない分、低価格となっています。
 回路としては、PORT EのRE0〜RE4に抵抗でDAコンバーターを形成しているだけの非常にシンプルな構成です。以前PIC32MX695F512Hを用いて実験した「大容量メモリ搭載PIC32MXシリーズによる高解像度ビデオ信号出力実験」からSDカードスロットを除いたものと同様の回路なのですが、大きな違いとしてシステムクロックには発振周波数が以前の4倍の14.31818MHzの水晶発振子を用いています。これについては後述します。
 実験は写真のように、TQFPの変換基板とブレッドボードで行いました。

回路図

システム設計1

 マイコンを用いたカラーコンポジット信号生成の詳細については、以前実験したこちらをご参照ください。
 PIC32MX370F512Hは最大100MHz(=100MIPS)で動作させることが可能です。これまでの実験ではオシレータとしてNTSCのカラーサブキャリア周波数である3.579545MHzの水晶発振子を用い、内部のPLL回路で必要なクロックを作ってきました。しかし、このPLL回路の設定は最大で24倍までしかなく、この方法では約86MHzが限界となります。せっかくの100MHz対応マイコンなのでもう少し高速にしたいと考え、図のようにカラーサブキャリアの4倍である14.31818MHzの水晶を用い、PLL入力前の分周器で3分周してからPLLに入力することにしました。
 PLL回路では20倍の設定にしました。21倍にするとちょうどカラーサブキャリアの28倍となり(4×21÷3)、しかもほぼ100MHzとなり非常に都合がよいのですが、一つ問題点があります。それは、これまでの経験上、ドットを正方形にするには1ドットの横幅をカラーサブキャリアの5分3周期にするとちょうどよい、というところです。1周期あたり28個の命令を実行できるのですが、28命令から5分の3周期を作ることはできません。近似値で17命令で1ドットとしても、プログラムは非常に煩雑になります。
 20倍とした場合、カラーサブキャリアの1周期あたり4×20÷3=3分の80命令と割り切れないのですが、5分の3周期はちょうど16命令クロックとなります。これだと4命令クロックごとに1回信号出力を行えば、1ドットあたり4回信号出力のきれいな波形が作れます。

製作写真


波形


システム設計2

 いよいよ肝心の拡大縮小回転機能の設計です。
 まず、ビデオメモリとして256×256バイトの配列「VRAM」を用意します。1バイトで1ドットを表現するので、64Kバイトが必要となります。色数は256色を同時に表現できます。
 通常のビデオメモリの場合、この領域の先頭アドレスから順次読み出し、カラー信号に変換して出力することで横一行分の映像を表示し、また次のアドレスから次の行を出力とするのですが、拡大縮小回転機能を実現するためには、アドレス順に読み出すのではなく、表示させたい順に読み出し出力する必要があります。
 これを実現するため、次のような変数を用意しました。vscanstartxとvscanstartyは画面出力したときの左上のX、Y座標を表します。vscanv1_xとvscanv1_yは、画面横方向に順次出力するときのVRAM内の読み出し座標のX方向、Y方向それぞれの移動量(ベクトル)を表します。また、vscanv2_xとvscanv2_yは一行分出力後に次の行に移動するときのX方向、Y方向の移動量を表します。これらを読み出し座標を表す内部変数vscanx、vscanyに足していくことで、信号出力すべき座標を順次得ることができます。
 実際にはこれらの変数は16ビットで、上位8ビットが整数部分、下位8ビットが小数部分を表すようにしています。vscanx、vscanyの座標からVRAM上の位置への変更は、単純にそれぞれの上位8ビットを図のようにvscanyが上位となるようにつなげるだけで生成できます。
 画面表示解像度は横256ドット、縦224ドットとしました。勘のいい人ならすぐに気付くことですが、VRAM上の座標が端からはみ出した場合、例えばX座標が255を超えた場合、続きは左端からつながったように表示されることになります。広大なビデオメモリがあればあまり気にならないのかもしれませんが、今回はビデオメモリの大きさと実際の表示解像度がほぼ同じなので、少しでも回転させたりすると、端から反対の画像が表示されます。

ビデオメモリスキャン


映像信号タイミング

 今回はシステムクロックをカラーサブキャリア周波数の3分の80倍、解像度を横256ドットとしたので、各信号のタイミングは下図のようになります。カラーサブキャリアの1周期が命令クロックで割り切れないので、カラーバースト信号が正常に認識されるか少し不安でしたが、特に問題なく認識されるようです。

水平1ラインのタイミング


プログラム製作

 今回作成したプログラムは、ビデオメモリ上の読み出し位置をベクトルで移動させていくこと以外は、これまでに作成してきたものと大きな違いはありません。16命令クロックの間に座標からアドレスを生成し、データを読み出し、カラー信号に変換して4回出力し、座標をベクトルで移動するというのは、なかなか大変で1クロックの隙もありません。
 PIC32MXの少ピンの1xx/2xx番台シリーズ以外は、高速動作を実現するためにプリフェッチキャッシュを搭載しています。以前PIC32MX695F512Hを使用したときにも書いたように、カラービデオ信号生成では1クロックのずれも許されないため、1ライン中のカラーバースト信号の始まりから映像信号の終わりまで、全てアセンブラーで一切ループもジャンプもなく、4000命令以上をひたすらベタに書きました。無駄なようですが、こうしないとどうしても微妙に色がずれることがあるので仕方がありません。
 ところで、これまでPIC用プログラムの開発にはMPLAB 8.xを使ってきましたが、今後最新のチップのサポートは行われないようで、今回使用したPIC32MX370F512Hもサポートされていません。そのため、MPLAB Xを導入しました。またフラッシュへの書き込みにはPICkit2と、PICkit2でPIC32MX対応するpic32progというフリーウェアを使ってきましたが、この組み合わせではどうしてもPIC32MX370F512Hを動作させることができませんでした。そのため、今回PICkit3を導入しました。考えてみれば、これでようやくPIC32MXを使う一般的な環境がそろったのかもしれません。実際、今回はMPLAB XとPICkit3でデバッグ機能を使い、クロックのタイミングを実際に計ることができ、大変有効でした。


使い方

 これまで同様、最初にinit_composite()という関数を呼び出すことで、映像出力が始まります。初期状態ではビデオメモリの先頭から読み出しで、拡大も縮小も回転もありません。
 ビデオメモリに何か描画を行った後、前述した拡大縮小回転用の変数を変更することで、さまざまな表示を行うことができます。
 vscanstartxを256ずつ足していくと、表示開始位置が右にずれるので、左向きの横スクロールとなります(256ずつ足すのは255以下が小数のため)。同様にvscanstartyを足すと上にスクロールします。
 vscanv1_xとvscanv2_yを255以下にすると拡大画面となります。逆に256より大きくすると縮小画面となり、ビデオメモリの端を超えた部分は反対端からの繰り返し表示となります。2つの変数の値を違うものに設定すると、縦横比が変わるため、正方形が長方形になり、円が楕円となります。
 上記2変数に加え、vscanv1_yとvscanv2_xも変更すると、画面がひずんだようになりますが、回転行列を用いて適切に設定すると、画面を回転させることができます。計算式は以下のようになります。


回転の計算式1


 また、vscanstartx、vscanstartyはどこを画面の中央に表示するかで考えるとわかりやすいと思います。

回転の計算式2


 考えればわかることですが、ベクトルの回転方向と実際に表示される画面の回転方向は逆になりますので、ご注意ください。


サンプルプログラム

 これまで作ったシステムの実験でも使ってきたサンプル画面をほとんどそのまま流用しました。ただ、時間ごとに全体が縦や横にスクロールしたり、拡大したり回転したりします。動作は冒頭の紹介動画で見てください。なお、今回のデモプログラムでは、浮動小数点演算を特に工夫することもなく使っているため、コンパイルの最適化レベルを最低でも「1」にしないと処理が間に合わず、ちらつきが発生してしまいます。ビデオ出力システム本体自体は全て整数演算ですので、mainプログラムの書き方次第で高速化は可能です。

<プロジェクトに追加するファイル>
 rotatevideodemo1.c (デモ用mainプログラム)
 rotatevideo1.c (システム本体)
 rotatevideo.h (システム用インクルードファイル)
 graphlib.c (グラフィック関連ライブラリ)
 graphlib.h (ライブラリ用インクルードファイル)

サンプル写真1

サンプル写真2


ダウンロード

 画面出力システムとサンプルプログラムのソース、HEXファイル等の一式を公開します。なお私の環境はMPLAB X v2.10とXC32 v1.31です。

ソースプログラムのダウンロード

バグ修正でRev. 1.1に更新しました。(2014.11.3)


最後に

 10代の頃に考え夢想に終わってしまった夢が、こういう形で実現するとは思っていませんでした。アルゴリズムは当時考えたものと全く同じで、ただロジック回路で組むか、ソフトウェアで実現するかだけの違いです。補間処理など全く行っていないので、もっとガタガタした映像になるのではと想像していましたが、意外ときれいだったので感動しました。この感動を学生の頃に味わえていればさらによかったのでしょうけれど・・・あれから四半世紀以上たってしまいました。
 そんな感慨はともかく、せっかく実現したので何かに応用したいのですが、戦車ゲームを真似るのもちょっといまいちですし、もうしばらく考えてみます。



追記

 本実験の応用で、ゼビウスのような縦スクロールゲームに、回転も取り入れたオリジナルのシューティングゲーム「VELUDDA」(ベルーダ)を作成しました。なかなかの出来ばえだと自負しています。(2014.12.31追記)

Copyright (C) KenKen All Rights Reserved.