初代X68000で動くDOOMに続いてフルポリゴンFPS「Quake」をシャープが1987年に発売したパーソナルワークステーション「X68000」に移植してみました。
Github - X68Quake 実行ファイル (X68Quake.zip)

動作環境は68060ないしそれに準じたアクセラレータ、メモリ12MB、HDD必須、エミュレータXM6上で68060 400MHz相当で約13fps、200MHz相当でも6fpsとおそらくX68000史上最も重いゲームです。実機だとPhantomX + Raspberry Pi 4の環境を推奨しておきます。
X68000エミュレータ「XM6」で68060 200MHz相当にしてのプレイ動画
- 実行には製品版Quakeかシェアウェア版Quakeのゲームデータ(.pak)が必要です。SteamやGOGから購入し、中に入っているPAK0.PAK・PAK1.PAKをid1フォルダ内に入れてください(X68QUAKE.Xと同一ディレクトリに入れない)。入れたら後はX68QUAKE.Xを実行するだけでゲームが起動します。
- キーボード・マウス操作可能、実行ファイルと一緒に収録しているautoexec.cfgが入ったid1フォルダをそのまま実行ファイルと同じ場所にコピーすればWASD移動、マウスルックとなります。
- 68060アクセラレータ必須。またFLOAT#.Xは使わず、060turbo.sys内に収録されているFLOATルーチンを使用(/feオプション)してください。FLOAT#.Xを組み込んで実行すると3D画面が一切表示されません。
- 画面表示はCRTCを直接弄って24KHz・解像度320*208にしています。実機ではディスプレイによっては映らないです。
- PCM拡張ボードの「まーきゅりーゆにっと」の制御方法がわからないので音声はなし
- ネットワークプレイ不可
- セーブ&ロードは問題なく行える
- ベンチマークを行う場合は、メニュー(ESCキー)から「OPTIONS」を選択し「GO TO CONSOLE」でコンソールを開いて「TIMEDEMO DEMO#」(#には1~3までの数字)を入力してください。デモが完走すると平均フレームレートが表示されます。
FM TOWNSにQuakeを移植していて、DOOMと同様にソースコードがかなり抽象化されており、極端に言えばフレームバッファへの書き込み・キーの入力・ファイルの読み書きという各ハード固有部分さえどうにかできればすぐ移植できる代物だとわかっていたので、FPU・メモリ12MBフル実装のX68000をターゲットにすればギリ動くのでは、ハード依存部のコードもX68000版DOOM8088STからそのまま流用できるはずなのでコピペレベルでは、と思い立って作業してみましたが、結構苦戦する羽目に。
元々がメインメモリ1~2MB時代のリンカなのでデフォルト設定のヒープ・スタックサイズが数十KBしかなくそれを変更する術を探したり、何度やっても配列参照がありえない部分から読みだされると思ったらエミュレータ自体のFPUコアの不具合を見つけそれを回避するようにしたり、コンパイラオプションで最適化を指定してしまうと最新のGCCと古いXCコンパイラのライブラリが相性悪くて特定の関数(vsprintf等)で暴走しまって該当部分で_attribute_((optimize("O0")))を付けて最適化を無効化したり、コードは全く問題ないのに演算結果が狂って3Dが表示されないと思ったら68040・68060上でX68000標準の浮動小数点演算を行う例外処理プログラムを動かすと正常に動かないとかシステム的な問題だったり、X68000でFPU・大容量メモリを要求するプログラムが想定されていないっていうのが大きいですね。
しかしFM TOWNS版移植時に「PC-98やX68000にはQuakeは移植されていないからFM TOWNSの勝ちだ」と言いましたが、言いだしっぺがこのX68000版Quakeが誕生させたことによって勝負はまた五分五分となりましたね(お前は一体何と戦っているのだ)。
68060 400MHz相当でも約13fpsと、かなりヘビー級なソフトとなってしまいましたが、68040・68060の内蔵FPUはフル実装ではなく一部機能が簡略されていて該当部分がソフトウェア処理、CPUが高速でもグラフィックバス自体は16bitのまま、とかが原因かもしれない。
焼け石に水だと思いますが、最適化も多少なりともやっておいたのでその紹介。
まずはQuake 3エンジンで有名となった「高速逆平方根アルゴリズム(fast inverse square root)」
当時のFPUは除算と平方根(SQRT)を実行するのにそれぞれ何十サイクルも費やさないと実行できない速度で、「1 / SQRT(元の値)」という逆平方根を求めるだけでも負担がかかったので、除算と平方根の代わりに実行速度の短い乗算を駆使して近似値を求めるやり方。近似値なので実際にまじめに計算した際の結果とは若干異なっているが、ゲームで簡単に使われる程度であれば問題なかった。なお現在ではFPU自体が高速化していたり1命令で逆平方根を求める命令まで追加されているのでこのアルゴリズムは過去の遺物となっている。
オリジナルのQuake 3のコードでは

というものが使われており、乗算4回 + αではあるが乗算1回が3~5サイクル・+ αで10サイクルだったとしても十分除算・平方根の組み合わせよりも2倍以上高速となっている。ただしこの求め方だと0.17%程度の誤差。これでも十分なのだが、2010年に更に誤差を縮めるマジックナンバーが発見されており

というコードをベースにすることにした。これだと誤差は0.0652%らしい。
Quake 1では空のスクロールやキャラクターの移動で

というベクトル正規化関数が読みだされていてSQRTと除算が発生するので

とし、「1 / 平方根(元の値)」相当の結果をまた元の値と乗算することで実質平方根値に直すということをしている。乗算5回 + αだが68060の乗算命令が5サイクルだったとしても、除算が40サイクル・平方根が68サイクル程度はかかるのでやはり倍以上の高速化。
ただ、このコードがQuakeで使われている箇所は前述通り空のスクロール・キャラクターの移動程度で頻繁に使われるわけではなくこれを組み込んだからといっても目に見えた違いはないと思われる。
もう一つはクリッピング処理の改善。

というように条件式でクリップするかどうかのビットを立てているが、

比較結果をそのままビットシフトで該当ビットに入れてしまうという処理にしておいた。最大でも数%程度の高速化だろうが、こちらは多少なりとも効果が出てくるはず。
この最適化は今時のコンパイラやCPUでも有効な手法となる。
※追記
VRAM書き込みの最適化を行えたのでそれも追加。ループ命令中に必ず発生する余分な命令を削除できるとはいえ、元々軽い命令をたった1命令削るだけなのでこれも数%の効果でほぼ誤差であるんだけど。QuakeだけでなくX68000版DOOMにも反映
X68000のVRAMアドレスはちょっと特殊でビットマップ画面は16色・256色・65536色モードとあるのだが、どの色数でも1ドット=1ワード(16bit)アドレスの計算となる。つまり16bitあったらかならず16色なら下位4bitのみ・256色なら下位8bitのみ・65536色は全bitが使用される。256色にしたからといって1ワードアクセスしても上位8bitの値は無視され、一度の書き込みで同時に2ドット分書き換えられるということが行えない。
1ワードでのアドレス計算をしやすくするためにこのような設計になっているのだろうが、本来は256色なら1ドット=1バイトで済むので別に計算が難しいわけでもないのに、この仕様で256色の書き込み時に不利になる。
更にC言語でVRAM側が16bitのshort型、グラフィックソース側が8bitのchar型ということで単純に

というコードをGCC 13にて最適化オプションO2を指定してコンパイルしても

レジスタD0にsrc値を入れた後にバイトを型変換のためにAND 0xFFかEXT命令で変換処理が行われてから実際に書き込まれる。だが、X68000の256色VRAMは前述の通り、上位8bitの値がどんなものになっていても無視されるので無駄な処理だ。
そこでunion(共用体)を使って

というように2バイトのchar型配列2バイト目(68000はバイエンディアンなのでこうなる)にsrc値を入れ、その共用のshort型をVRAMに書き込めば

無駄な型変換のための命令が消えてくれる。初期化しないとb[0]側には出鱈目な数字が入っていることになるが、VRAM書き込みでは無視されるので問題がない。