FM TOWNSプログラミングにおける0.5ドットスクロール & アスペクト比変更テクニック

全国2~3人くらい(自分含む)の現役FM TOWNSプログラマーの皆様、お待たせいたしました。
令和初というかインターネットにあるデータでは初かもしれない富士通が1989年に発売したパソコン「FM TOWNS」におけるCRTCを直接操作してのグラフィックプログラミングテクニックの一例、0.5ドットスクロールやアスペクト比の変更方法をご紹介いたします。これでTOWNSでゲームが作れるぞ!(作るとは言ってない)


なんでこんな需要ゼロな記事を書くかというと、FM TOWNS用横スクロールシューティング「ライザンバー」(データウエスト社発売)が当時「0.5ドットスクロール」という摩訶不思議な宣伝文句を言っていて、あれはスプライト面と背景面で解像度が違うからなんだとかいう話が某所であったものの、そもそもFM TOWNSはスプライト画面を表示できる画面モードは解像度256*256(240)のみに制限されてしまうという制約があり、スプライト有効時はビットマップ面側は横512ドット表示ができないので倍表示論なんて無理でしょ、検索して調べた限りCRTCを直接操作すると細かい解像度単位で位置を指定できるようになるっぽいからそれでは?と思ってたんですけど、その操作方法もわからずあやふやなまま知ったかぶっても自称TOWNSユーザーの名が廃る、あと不適切な解説が後世までそれが正史かのように残り続けるのは歯がゆい、ということで調べてみました。この記事自体に誤った情報が載っているかもしれませんが、その時は許して。
今までHigh Cコンパイラ付属の関数解説に載っていたTBIOS(TownsOSの各API)経由でのグラフィック画面設定しかやっていなくてCRTCの直接操作の方法がわからなかったが、当時のフリーソフトの説明ファイルを調べていたら「参考:Oh! FM 1991年1月号 君にもできる0.5ドットスクロール」という文を見つけたのでこれだと思い、ちょうどタイミングよくヤフオクにその該当記事が載っているOh! FM 1991年1月号が出品していたので入手。これで勝つる!!!

※追記:後で確認したところフリコレ4にもテキストファイルで該当記事の大まかな説明とサンプルプログラムが収録されていることを確認。

f:id:BCC:20220201203229j:plain

はてさて0.5ドットスクロールなんていう細かいスクロールを実現した方法とはいかに!?ここで解説している内容は上記の「Oh! FM 1991年1月号 君にもできる0.5ドットスクロール」(p.150)を参考としているが、そのまま記事内容を引用してもわかりにくいと思うので自分が読んだ際にわかりやすいと思う文にまとめておきました。

 

いきなり本題に入る前にFM TOWNSのスプライト機能について簡単に解説。
FM TOWNSのスプライト機能は解像度256*256のフレームバッファ上に16*16サイズのスプライトを最大1024枚表示するもので、スプライトは32768色中16色か32768色ダイレクト指定・上下左右反転と90度回転・縦横独立で1/2に縮小表示する機能を持っている。一般的なラインバッファ方式ではないフレームバッファ方式ということで、当時は疑似スプライトと揶揄されていたが、当時のセガ製アーケード基板(TOWNSローンチタイトルだったアフターバーナー2のXボードもフレームバッファ方式)やセガサターンでも同じ方式が採用されたスプライトの定義(画面上に独立したキャラクタを高速に表示させる)に沿ったものだ。俺たちのセガはまがい物を出していたってのか!?

ただ、90度なんていう中途半端な回転やこれまた中途半端に8ドットサイズのみにしか縮小できないとか、自由な変形機能も実現可能とするフレームバッファ方式のスプライトの利点が出ておらず、転送速度の問題で60fpsを維持して表示できるスプライトの枚数は初代機で約220枚・後期の白TOWNSでも約280枚程度という制約がある。
この60fpsを維持して表示できる枚数制限なんだけど、High C コンパイラ付属の説明書のスプライトの項目やTownsエミュレータ津軽」の情報から読み解くと、初代機においてはスプライトを表示しているレイヤを全消去するのにVSync開始時から約32マイクロ秒後に、スプライト1個を表示するのに約75(白TOWNSは57)マイクロ秒かかるようなので、32 + (75 * 220) = で約16.6ミリ秒、つまり1フレームあたりの時間ってことで導き出せる。1024個まで表示するスプライトを指定できるものの、転送が1フレーム時間を超す場合は次以降のフレームも使って残りのスプライトを転送し続ける。指定された枚数分のスプライトを転送中かどうかはスプライトコントローラにあるレジスタのフラグが立つので、これでスプライトの描画が終わったかどうかソフトウェア側から確認できる。

※追記:その後入手したoh! FM TOWNS 1991年12月号 p.134「謎のスプライト個数制限を追う」を参照すると、垂直同期周期が変わる関係で画面モードによっても微妙に転送枚数が異なってくるようだ。

何から何まで中途半端な仕様だが、それでも最大128枚表示(なおソフトウェアの改良で抜かれる)のX68000よりは表示枚数も多く、グラフィックパターンの定義数も論理上は最大896枚(16色の場合。スプライト表示数やパレット数を削って増やす方法も実はあるが今回は省略)、32768色中16色指定のパレット数も256個と他のスプライト機能を持つコンピュータよりは表示性能はよさそうなもの。

問題なのはここから。スプライトを表示する転送先のフレームバッファはレイヤ2枚だけある表示画面を丸々1枚を消費、もう一枚の画面は4096色中16色かダイレクト32768色指定のビットマップ面しか指定できないというとんでもない仕様。FM TOWNSには640*480 1667万色中256色という画面モードがあるが、これを指定してしまうとこの256色 レイヤ1枚しか表示できなくなる。ここがFM TOWNSアーケードゲーム作りに向いていない部分の一つとなる。背景面が1枚しかないので多重スクロールするにはスプライトも併用しないといけなくなるし、背景面を描画するにしても16色のビットマップは色数が少なくて地味な画面となるか、32768色だとスプライト面と同じ色数・解像度で馴染むものの書き換えに必要なデータ量が多くてあまり動きが激しい描画が行えない。

表示の優先順位もドット・ライン毎に変更といったこともできず、必ずビットマップ面・スプライト面のどちらかが全域上になるので枠のないステータス表示とかもしたかったらこれらもスプライトで描かないといけない。

 

スプライト専用のフレームバッファRAMを搭載して独立したレイヤ表示が行えたり、VRAMを全SRAM化してスプライトの転送可能枚数を増やしセガYボードやNEOGEOのようにスプライトをBGのように扱えればよかったものの、当時既に30~40万円近くしていたFM TOWNSをさらにコストアップするような仕様強化は行えなかったのだろう。せめて、ビットマップ面に仮想画面512*512の1667色中256色画面を作ることができなかったのか。

 

※追記:スプライト表示のデモ・ベンチマークソフトを作成してみた

実行ファイルとソースコード

FM TOWNSのスプライト最大表示数の1024枚までのボールを画面上に表示させ、表示枚数を1~1024枚まで自由に変更と画面モードの切り替えが行え、現在のフレームレートを表示させる。60fpsを維持できるスプライト数やフレームレートを低下させてそれ以上の枚数だと実際にどのように画面が動かせたか実際の動きで確認できる。

FM TOWNSエミュレータ津軽」及び実機FM TOWNS マーティーで動作確認。エミュレータ「うんづ」では正常に動作しないが、マーティーで正常に動くので津軽の方で試すのが正解。

 

 

  • 0.5ドットスクロールの正体

さて、またまた本題の0.5ドットスクロールに入る前に一つ質問。そもそもFM TOWNSで横1ドットスクロールってどうやれば実現できるんでしょう?常識的に考えてできないと思います。
何を頓珍漢なことを言っているんだ。VRAMの表示位置指定を1ドット単位で指定できないのか、と突っ込まれそうですがそうです、できないんです。
というのもレイヤ0・レイヤ1とそれぞれの画面左上表示開始のVRAMアドレスを指定するレジスタFA0(レジスタ番号17)・FA1(レジスタ番号21)は65536(16ビット)の値範囲、たいして仮想画面の大きさは最大で16色2画面・256色1画面時に1024*512ドットの524288、最小の32768色2画面時でも256*512ドットの131072となっており、524288/65536=8・131072/65536=2となるのでレジスタを最小の1づつ増減させても2~8ドットづつ横に移動していくだけのスクロールしか実現できない(縦スクロールは横ドット/2~8分増減させれば1ドットづつスクロールできるので問題ない)。TBIOSでやってもそう。

ならもう0.5ドットスクロールは何かアブナイクスリをキメた際の幻覚だったのでは?と一瞬悩んでしまうが、実は水平アジェストレジスタHAJ0(レジスタ番号18)・HAJ1(レジスタ番号22)の値を1減算するとドットクロック分表示が右にズレる、逆に1増算すると左にズレて表示されるのです。そしてこのドットクロック分の表示ズレはハイスキャン(31.47KHz)表示の画面モードで0.5ドット、ロースキャン(15.73KHz)表示の画面モードで0.25ドット、インターレース表示の画面モードで0.2ドットとなる。
偶数分のスクロール位置はFAで指定してVRAMの表示位置を決め、奇数・小数点のスクロール位置はHAJで表示そのものをズラして実現する、これこそが本来TOWNSでは無理なはずの1ドットスクロールも、そして大半の他機種でも実現が難しい「0.5ドットスクロール」をも実現させる正体なのだ。

はい正直、ドットクロック分ズラして表示させるより普通に1ドット単位でVRAM表示位置指定できるようになっててくれたほうがメンドクサくなくて嬉しいです。
宣伝文句ではあったけど、厳密には画面のスクロールを表現するための用途ではないものまで使わないとスムーススクロールが行えないCRTCの不自由な制約をどうにかして解決するための苦肉の策でしかなかったんですね。


なお、横スクロールするゲームを作るうえでかなり重要なテクニックなのに、FM TOWNSのハードウェアレジスタに関する細かい内容を記載している「FM TOWNSテクニカルデータブック」(通称赤本)にはこのことは一切かかれていないようだし、当時の一部ソフトメーカーの認識を確認したところTOWNSでは頑張って書き換えないとスクロールは無理と思っていた会社があった模様。
なんでデータウエストはこの仕様を知ってたんだよ・・・。

 

FM TOWNSのスプライト機能の解説で解像度256*256と記載したが、このまま表示するとドット縦横1:1のアスペクトとなる正方形の画面のまま表示される。ドット比率的には間違っていないのだが、当時の4:3アスペクトのディスプレイでは何も表示されない領域ができてしまうのでこれを引き延ばしたい。
方法は二つ。ハイスキャン表示の画面モードではCRTCには25.175MHzのクロックが与えられているが、CRTCのレジスタを操作することで21.0525MHzのクロックに変えられ、こうすると表示が横長となるのだ。もう一つの方法は、ロースキャン・インターレース表示の画面モードではCRTCの機能としてある画面を縦横独立して整数倍に拡大表示する機能が標準では横4倍・縦1倍となっているのでこれを横5倍に変えるだけでいい。元表示解像度は256*240なので横が1.25倍(4→5)伸びて320*240相当、つまりちょうど画面と同じ4:3比率となる。

 

というわけで該当記事内やHigh C付属のサンプルプログラムを元にしてドットクロック分ズラすことによる左右横スクロールとTBIOSの画面モード5(256*256 32768色)でCRTCレジスタを弄ってアスペクト比4:3と正方形を切り替えて表示するプログラムを組んでみました。ライザンバーはスプライト面画面モード5、背景面は仮想画面に1画面分横領域がある画面モード10を使用しているようだが、スプライト・背景が同じ画面モードでも独立して0.5ドットスクロールできることを証明するためこの設定にしています。

Googleドライブ - FM TOWNS 0.5ドットスクロール & アスペクト比変更デモプログラム

ソースコードCRTC.CがCRTCレジスタ操作やスプライトの使える画面モード5~8をレイヤ0,1両方に設定およびアスペクト比の切り替えを行う関数が記述、SCROLL.C内のScroll関数でスクロール操作を行っています。

表示する画像をSCROLL.EXPと同じディレクトリ内にSCROLL.TIFという名前のTIFF画像を256*240 32768色形式(解像度や色数が違うと動かないようになっています)で入れてEXPを実行、画像をパッドの左右でスクロール、上下で1ドットクロック分少しづつスクロール、Aボタンでアスペクト比変更、キーボードのどれかキーを押すことで終了する。

FM TOWNSの実行環境がない、デモを動かす気力がわかないという人のために動画も用意しときました。

なんとなく横スクロールといったら「グラディウス」だろって用意してみたけど、デモに使うにはわかりにくい画像だった。でも1ピクセルしか表示されないはずの星々があるのでいいか。

エミュレータ津軽」では正方形表示時にドットクロック分ズレても1ドット単位でのズレで表示されてしまうようだが、4:3時なら小数点以下の表現ができているようで1ピクセルの星々が画面右端に差し掛かった際にいきなり消えず何単位かに分かれて消えていくのがわかるだろう。