2020年3月8日 : してログ

再現できたガル・バキュラ
再現できたガル・バキュラ

デモプレイに画面が切り替わる瞬間にクレジットを投入した際、ソルバルウなどが画面に残った状態で PUSH START BUTTON の画面になることが、このバグが成立した状態になります。ただし、状況によっては目立った変化が無い動画もあります、何れにしてもこれが成立していれば、スタート直後に横一列にバキュラが並んだ状態で出現します。こちらのページにそのリプレイファイルがあったので、今回はこのバグが成立する条件を探ってみました。

まず、大量のバキュラが出る原因はすぐに分かりました。バキュラのキャラクターコードが 0x01 で、これが地上物、空中物、バキュラ、スパリオなどのワークメモリすべて埋め尽くされるために起きています。これは総攻撃などと同じ原理で、どのワークに入れても指定したコードのキャラクタが出現するのと一緒です。そのため、空中物に入れられたバキュラは破壊可能で、得点テーブルも 0x01 で埋められているので対応した得点が加算されます。

問題はなぜこのような現象(0x01 で埋め尽くされる)が起きるかですが、やはりメインとサブの CPU が関係していました。画面モードが切り替わった直後にメイン CPU がワークメモリを 0x00 でクリアしています。原因はそのゼロクリアのし方が下記のようになっているためです。

該当箇所のコード(XVI4 の先頭から)
  1. クリアする先頭アドレスに 0x00 を書き込む
  2. ブロック転送命令で1バイト前のアドレスの値を指定回数コピーする
ld   hl,$7900
ld   de,$7901
ld   bc,$06FF
ld   (hl),$00
ldir

このコードはシングル CPU であれば問題は起こりませんが、このブロック転送の最中にサブ CPU が介入してキャラクタの状態を表すワーク領域に 0x01 を書き込むと、意図しない値がコピーされてしまいます。具体的には、ひとつ前のアドレスをメイン CPU が読み出す直前に、サブ CPU がそのアドレスに 0x01 を書き込んだ場合です。本来は先頭に書き込んだ 0x00 がバケツリレーのようにコピーされるはずが、途中から 0x01 に差し替えられてしまい、以降の領域のすべてが 0x01 で埋め尽くされることによります。なお、画面にスプライトが残る残らないの違いは、どの場所をクリアできてどの場所がクリアできなかったかの違いで起きていると思われます。

この条件が成立するのはかなりシビアであり、狙って出すのはかなり難しいと考えます。それはデモ画面に切り替わった直後『サブ CPU がキャラクタの状態のワーク領域に 0x01 を書き込む直前』にクレジットを投入する、というタイミングと思われますが、クロック単位のシビアなタイミングが予想されます。

状況の再現

ロジックが分かったので再現して確かめてみました。前述のコードの先頭に書き込む 0x00 を 0x01 に変更しただけで再現できました。ただし、強制的にやっている関係でミスしても元に戻りません。

ld   hl,$7900
ld   de,$7901
ld   bc,$06FF
ld   (hl),$01
ldir

ゼビウスの他のバグと根っこは同じ

ゼビウスってこれ系のバグが多いですよね。エリア繰り上げワープ然り、森の地上物さん然り、CPU 間でメモリを共有しているにも関わらず、タイミングが取れてないから意図しないデータ参照や書き込みが起きてしまう。もしかしたらまだまだレアな怪奇現象が起こるかも知れません。

古い MAME をビルドする必要があったので調べた結果を共有しておきます。最近のバージョンはデバッガが使える状態になっていますが、古いバージョンはデバッガをオンにしてビルドしないと使えないためその必要がありました。ただ、あまりに古いのでビルドツールを見つけるのに苦労して探し回った結果、下記の掲示板にまとめがありましたのでリンクを張っておきます。

今回は 0.93 をビルドしたいので "MinGW-3.2.0-rc-3.exe + mingw-over-092.zip (0.92 - 0.104u1)" のリンクより2つダウンロードしました。作業用の WindowsXP にそれらをコピーしインストールします(Windows10 でビルドできるかは試していません)。

ビルド手順

※バックスラッシュは小文字の¥マークです

  1. MinGW-3.2.0-rc-3.exe を実行し解凍先を指定します(今回は C:\mame\MinGW にしました)
  2. mingw-over-092.zip を解凍して C:\mame\MinGW に上書きコピーします(ディレクトリ構成そのままコピーすればOK)
  3. mame のソースをダウンロードして C:\mame\mame093s に解凍しておきます
  4. ファイル名を指定して実行で cmd を入力し OK ボタン(コマンドプロンプトが開きます)
  5. カレントディレクトリを変更します
    cd \mame\mame093s
  6. ビルドツールへのパスを通します(何度も使う場合はバッチファイルにします)
    set PATH=C:\mame\MinGW\bin;C:\mame\MinGW\mingw32\bin;%PATH%
  7. ビルドを実行します
    make

デバッガをオンにしてビルドする

MAME のソースコードのルートにある makefile をテキストエディタで開き、DEBUG=1 のコメントアウトを取って(行頭の # を取る)ビルドするだけです。ビルドが完了すると mame.exe と別に mamed.exe ができ、こちらがデバッガ内臓 MAME になっています。デバッガをオンにして起動するには、下記のようにオプションを指定します。

mamed.exe -window -debug [ROMNAME]

試したバージョン(0.93)ではエラーで落ちる

試したバージョン MAME0.93 ではエラーで落ちる事象が見られました。エミュレーション動作中にデバッガのコマンドを実行すると ACCESS VIOLATION で落ちます。このバージョンだけなのか、その他何が原因なのかは分かりません。

ただし、起動直後の停止状態のときであれば、デバッガのコマンド設定が通るのでそこでブレークポイント等を設定してなんとか使えています。最新の MAME で適当なブレークポイントを探しておき、0.93 の開始直後に設定して止まったところで、ステップ実行その他のコマンドを実行するという、非常に面倒な状況で使うしかありません。一応止まった状態からは、trace、dasm など使っても落ちません。