ゲーム : してログ

YouTube のほうで「FC版ゼビウス 1万点グロブダーを倒したい」という企画をやってみた結果、破壊には成功しました。詳しくはチャレンジ動画のほうを見ていただくとして、少し考察してみたいと思います。なお、プログラムを解析した訳では無く、あくまで推測レベルの話になります。

アルゴリズムの考察

見た目から分かるアルゴリズムは、下記のような割と単純なものだと思います。この場合、ブラスターの投下地点と着弾時間、グロブダー移動速度の関係で、破壊可能かどうかが決まるはずです。

  1. 停止状態
  2. ブラスター投下中の赤い照準に重なっていれば、高速で下方向に移動開始(等速)
  3. 3キャラ(AC版は2キャラ半)分移動したら、初期状態に戻る

AC版との比較では、グロブダーの移動距離が半キャラ長くなっており、着弾のタイミングはほぼ同じに見えます。照準と自機の距離が違いますが、投下開始を合わせて比較すると同じタイミングで着弾するのが分かります。

移動距離はAC版より長い3キャラ分
移動距離はAC版より長い3キャラ分
着弾時間はAC版とほぼ同じ

論理的には破壊不可能?

このようなアルゴリズムではランダム要素も無く、反応する最下端を狙ってブラスターを落として間に合わないのであれば、破壊することはできないと思われます。そもそもロジック的に「破壊不可能なプログラムがされているのでは無いか」というのが私の見解です。

それにも拘わらず、1万点グロブダーが破壊できてしまうのは、恐らく何らかのバグが関係しているのでは無いかと言う訳です。

AC版の破壊シーンから考察

破壊成功シーンをスローで再生してみると、グロブダーの発進が2~3フレーム遅れているように見えます。いっぽうで失敗シーンでは直ちに発進していて、この数フレームの違いが破壊の成否に繋がっていると思われます。

この数フレームの遅れはどこから来るでしょうか? 非常に疑わしいのはキャラクタの座標が何らかの理由で、微妙にブレているのでは無いかという点です。反応するかギリギリのところにブラスターを落とした場合、グロブダーとの位置関係がフレーム間で1ドット程度ブレていると説明が付きます。

そして最も疑わしいのは、処理する CPU の違いです。スクロールによる位置の計算を担当する CPU と、当たり判定をする CPU が異なる場合、グロブダーと照準のスクロール計算が終わってから当たり判定が行われるパターンと、片方のスクロール計算が終わっていないときに行われるパターンがあると思います。その結果、重なっていると判定されたり、重なっていないと判定されたりするブレが生じるのでは無いでしょうか。

数フレーム遅れて発進している

1万点グロブダーはバグから生まれた?

ギドスパリオのオレンジフィルム(爆発時に赤く四角いパターンが混じるバグ)は「バグと分かっていたが面白いので残した」ということですが、1万点グロブダーもこれに似た状況で生まれたのでは無いでしょうか。

つまり、本来は破壊不可能な高速回避のアルゴリズムだが、バグ(あるいは仕様)により稀に破壊可能なプログラムが偶然できたとします。そうとは知らずに「破壊がかなり難しいから1万点にしよう」という判断をして、今の状態になったというものです。

FC版が正攻法で破壊できない理由

ファミコン版の1万点グロブダーはアーケードと同じ方法では破壊できません。これは、アーケードで存在したバグ(あるいは仕様)がファミコン版には無いためでは無いでしょうか。

敵のアルゴリズムがアーケードを再現しているとすれば、1万点グロブダーのアルゴリズムは破壊不可能なものになって当然です。ファミコン版はシングル CPU で処理されているため、座標計算と判定のタイミングが完全に同期してブレないのだと考えています。

FC版の破壊シーンから考察

チャレンジ動画で破壊に成功してから、レトロフリークのクイックセーブを使って確認したところ、やはり相討ちが鍵のようです。ただし、クイックセーブを使っても何十回~百回繰り返してようやく成功するという、かなりシビアなタイミングでした。

照準ロック位置はグロブダーが反応しない限界(これより2ドットほど上でも反応しません)よりも離れているので、この位置を狙っても反応すらしないはずです。

本来この位置関係では反応しない

恐らく相討ちになって爆発パターンの再生が終わった後、僅かの時間だけ座標計算が停止してしまうのでは無いでしょうか。動画をよく見てみると、爆発パターンが終わった瞬間に照準が消え、数フレーム後に爆発しています。この仮定が正しければ、下記のような推論が成り立ちます。

  1. 自機の爆発パターンが再生し終わると同時に照準が消える(この時点で自機関係のスクロール座標計算が停止)
  2. 自機の存在は消え、ブラスターの存在は内部的に残ったままになる
  3. リスタートに移る前の数フレームの間、グロブダーはスクロール座標計算により下方向に移動する
  4. 照準は消滅しスクロール座標計算が停止しているが、グロブダーはスクロールにより照準位置に入ってくる
  5. ブラスターの着弾までに残った数フレームが処理される
  6. 更新されなくなった照準位置とグロブダーの位置で当たり判定が行われる
  7. この結果、当たったと判定され、グロブダーは破壊される

FC版の失敗シーンから仮説の強化

この仮説を思い至ったのは、チャレンジ動画の中で気になっていた現象があったためです。こちらも発生頻度が20時間中2回と、かなり低いため探し出すのに苦労しました。

成功パターンと同じように相討ちになっていますが、こちらは成功時よりも早いタイミングで自機が爆発しています。このためグロブダーが高速発進し、再スタートで画面が切り替わった後にブラスターが着弾しています。再スタートで自機の存在が復活したことで、照準とブラスターが表示され、残りのフレームが処理されたと考えられます。

ブラスターの処理が残っている証拠

結びに

1万点グロブダーの破壊はゼビウスにおいて、上達した後も挑戦したくなる重要なギミックであり、ファミコン版でも破壊可能な存在として入れて欲しかったと思います。当時の攻略本には「このグロブダーはなんと1万点、余裕があれば挑戦してみよう」みたいに紹介されているのですが、とんでも無かったですね。

実は、子供の頃に破壊できた記憶があったため、このチャレンジ企画もこんなに大変だと思っていませんでした。今思うと、その記憶が正しかったのか疑問です。恐らくは、ファミコン版以外のゼビウスの記憶か、単にアーケードより簡単なのだからそうに違いない、と思い込んでいたのだろうと思います。

理想環境はやっぱり

アーケード基板からのキャプチャだとフレームマイスターが理想環境ということになりましょう。いちど購入を検討したことがあるにはあるのですが、そんなに基板持って無いし、RGB ケーブル一式揃えるといくら掛かるか分かりません。

最小の出費で済ますはずが...

当初の思惑では、激安アプコンのVGA → VGA to HDMI コンバータ → IODATA GV-HDREC というのを考えていました。足りないコンバータだけポチって試してみるとこれが映らない、調べると GV-HDREC が受け付ける解像度が 1920×1080、1280×720 など限られるっぽい。アプコンからは 800×600 などの 4:3 のみで対応する解像度で出力できないので無理でした。

仕方がないのでビデオキャプチャデバイスも追加購入となり、トータル1万8千円ほどの出費となりました。フレームマイスター最初から買っとけっていうのも分かる気がしますね。

4:3ディスプレイは絶滅した?

所有するアーケード基板はほとんどが縦画面です。FlexScan L885 は中古で 1500 円で購入したものですが、こちらは回転して縦置きできるため大変重宝しております。今だともう 4:3 の新製品が無いため、このシリーズがジャンクで出ていたら確保しておいた方がいいです。現在、予備機として少し小ぶりな FlexScan S1721 も所有していて、多少生活空間を圧迫しております。

サイバーガジェットのアーケードスティックが安くなっていたときに購入したのですが、WindowsXP で利用できなかったので原因等を調べてみました。USBのゲームコントローラには DirectInput と XInput の規格があり、XP 時代は DirectInput だったようです。恐らくはこのアケコンが XInput のみに対応したものなのでは無いかと思われます。

WindowsXP 用 XInput ドライバ入れれば良い

調べると DirectInput は DirectX 8 までで、DirectX 9 から XInput が導入されたとあります。であれば WindowsXP の DirectX 9 で使えてもよさそうなものですが事実はそうなっていません。

更に調査してみると XInput は Xbox360 のコントローラと関係があり、そのコントローラ用の XP ドライバを発見しました。以下のサイトにある「Xbox 360 アクセサリ ソフトウェア 1.2」を、試しにインストールしたところ使えるようになりました。ただし、コントロールパネルのゲームコントローラーについては開けない状態です(開いてもエラーも何も表示されません)。

前から欲しかったディスクシステム版のゼビウスをようやくゲットできました。カセット版との違いはたぶんタイトル画面の年号だけだと思われます。ちなみに、裏面はボンバーマンでした。

同時購入でゲームボーイ版のネメシスです。ゲームボーイは持っていなかったので分かりませんが、グラディウスじゃなくネメシスということで購入してみました。レトロフリークだと入力遅延があるみたいですが、実機もこうなのか良くわかりません。

表ゼビウス

アーケード版ゼビウスでは自動難易度設定が導入され、プレイする度に内容が異なるようになっています。これが効果的に機能しているかは微妙ですが、「完全にパターン化できないがある程度調整可能」といった独特のプレイ感覚になっています。詳しい説明は他所に譲るとして、これには 0~127 で定義される攻撃パターンテーブルを指すポインタを増減させることによって実現しています。

先日気づいたことに「偶数番の攻撃コードだけが使用されているのでは無いか」ということがあります。理由は単純で、この増減の幅がひとつを除いて偶数になるためです。奇数になるのは、マップ上の決められたチェックポイント通過時に「出撃1機あたりのスコアが1万点以下(厳密には少し違う)でスコアの千の位が奇数のとき」だけになります。普通にプレイしている限り、この条件に該当するのは無いに等しく、結果として偶数攻撃コードの「表ゼビウス」が進行して行きます。

ポインタの増減

マップの進行による増加と、プレイヤーの行動による減少によってゲームが進行していきます(表は難易度がノーマル設定の場合)。

イベント増減値
空中物出現時(固定以外)2増加
決められたチェックポイントを通過するプレイ内容によって最大16増加
ゾルバクを破壊する2減少
残機を失う16減少

だいたいエリア2序盤付近で2万点を超え、ある程度上達するとミスもしなくなるため、以降のチェックポイントでは毎回最大値(16)増加することになります。また、チェックポイントはエリア1で4箇所、エリア2で0箇所設定されていますが、エリア1の設定箇所は2個ずつ連続していて「奇数判定されても×2で偶数」になってしまいます。ただし、ごく稀に連続したチェックポイントの間でスコアが変動した場合のみ奇数になることはあり得ます。

裏ゼビウスへの入り方

意図的にプレイしてチェックポイントで奇数判定させれば、奇数番の攻撃コードで進行する「裏ゼビウス」に移行できます。エリア1は意図的にこれを行うのは難しく、エリア2は設定されていないため、次に設定されているエリア3のスペシャルフラッグゾーン付近で調整します。

  • エリア3のスペシャルフラッグゾーン時点でのスコアを9、7、5、3、1千点代の何れかに調整する
  • 例えば空中物ノーショットで、エリア1のスペシャルフラッグ、エリア2までのソル全出現で9千点
  • スペシャルフラッグゾーン直後の空中物が出現すれば、奇数判定済みなのでスぺフラも空中物も普通に撃って大丈夫
  • これ以降普通にプレイすれば「裏ゼビウス」のまま維持される

表と裏の違い

ほとんど違いはありませんが、いくつかの攻撃テーブルで敵機の数が1つ多く設定されています。良く分かるのがブラグザカートで、表ゼビウスでは1個、裏ゼビウスでは2個ずつ出現します。また、テラジの最大機数が1機多い5機となっており、総じて裏ゼビウスのほうが難易度が高くなります。詳しくは、以下の攻撃テーブル表で確認してください。

総攻撃 A3 も

奇数コードなので A3 は当初出せないと思いましたが、裏ゼビウスに移行してからエリア12まで行くことで出すことに成功しました。研究され尽くされていると思っていましたが、まだまだ色々とありますね。

追記:ファミコン版でも確認

ファミコン版ゼビウスでも同様のプレイをすることで、出現テーブル上のブラグザカートが2個ずつ出現するのを確認できました。この部分はアーケードの出現テーブルとロジックを忠実に移植しているようです。

またファミコン版はスコアに1000万の桁が存在しますが、チェックポイントでは100万以下の桁を計算に使用しているようです。このため、1000万点の超えた直後のチェックポイントで偶数奇数のレーンチェンジが起こる可能性があり、これが1000万点ごとに繰り返されると思われます。

追記:通常プレイで奇数パターンを確認

偶然に奇数パターンになったプレイの動画が撮れたので YouTube にアップロードしました。条件は推測していたので検証してみたところ、推測どおりの内容を確認できて確信できました。

奇数パターンに移行する可能性があるのはエリア1の2箇所で、ここはチェックポイントが間髪入れずに2個連続しています。スクロール位置がぴったり図の画面になった瞬間に、千の位の偶奇が変化すると奇数パターンに移行します。他の箇所では点数が高くなりすぎて、常に+16(最大値)と判定されるため、意識してプレイしない限り奇数パターンには移行できないと思われます。

動画で移行したと思われるポイント
動画で移行したと思われるポイント
別のチェックポイント連続箇所
別のチェックポイント連続箇所
左と右のスコアでそれぞれチェックポイントの判定が行われたと推定
左と右のスコアでそれぞれチェックポイントの判定が行われたと推定

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (6) からのつづき)

調整方法の早見表

エリア11の攻撃パターンの組み合わせから、エリア12で必要なテーブル調整方法をまとめてみました。なお、通常プレイでは偶数コード進行になるため奇数コードは除いています(奇数コードは参考資料を参照)。

追記:2パターンある場合があったので表を修正しました。

他のエリアでも再現可能

難易度設定によって、ミス時の減少値、空中物出現時の増加値が変わってくるため、HARD や HARDEST では他にも可能なエリアがありました。ただし、エリア3は HARDEST+5機設定でないと難しい(チェックポイント通過時の判定を最大にするために点稼ぎも必要)です。

難易度設定空中物出現時
(固定を除く)
チェックポイント
通過時(最大)
ゾルバク破壊再スタート時
(ミス)
EASY0+16-2-24
NORMAL+2+16-2-16
HARD+6+16-2-8
HARDEST+16+16-20
  • 空中物出現時に128以上かどうか判定されます

仕込み時の進行ポイントの目安

各進行ポイントまで進んでミスした場合、難易度設定毎に攻撃ポインタは下記のように変化します。

エリア進行ポイントEASYNORMALHARDHARDEST
エリア3スペシャルフラッグゾーンまで進行-80+8+16
エリア12斜めに道路が交差するあたりまで進行+8+16+24+32
エリア13埠頭の手前のゾルバク×4まで進行-80+8+16

注意など

ご自分の所有する基板、エミュレータ実装の市販ゲームなどでお試しください。ゲームセンターなどでは迷惑が掛かるのでやってはいけません。

追記:発動確認環境

エミュレータ実装のゼビウスであれば発動可能なので、逆にエミュレータなのか確認に使えると思います。最も移植精度の高いナムクラ、PS 3D/G+ は発動しないので移植作品だと分かります。

  • アーケード基板
  • NAMCO HISTORY VOL.1 (Window95/98)
  • SUPER1500 ゼビウス (Windows95/98)
  • ULTRA2000 ゼビウス (Windows95/98)

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (5) からのつづき)

前回の検証で終わったはずでしたが、なんとプログラムの解析結果から未改造のゼビウスで総攻撃が発動する可能性があることが判明しました。ただし意図されたものでは無く、コード上のバグを利用して攻撃テーブルポインタをオーバーランさせることによって可能になります。

条件が成立するマップを探したところ1箇所だけ合致するポイントを発見しました。それがエリア12の冒頭部分で、ここは都合のいいことにテーブル調整が安定して行える正にうってつけのポイントでした。ほとんど同じ動きを最低5回繰り返す必要があるので、意図せず遭遇した人はいないと思いますが、まったく無いとも言えません。先日検証した巨大バキュラバグよりも、実は再現するのは簡単だと思います。

仕込み方法

再現条件は下記の通りで、普通にプレイしてフラグを立てることが可能です。攻撃パターンのチャートがあれば、テーブルの数値を頭で計算しながら調整できます。

  1. エリア12まで行く(残機はある程度必要です)
  2. エリア12の開始時に、空中物のテーブルポインタを調整しておく(直前のエリア11で、テラジ×3、またはテラジ×4が理想)
  3. エリア12のゾルバクを破壊しないでバーラ4つの右側の道路を降りてくるドモグラムの弾(タイミングの目安なので別のドモグラムのでも良い)に当たってミスする
  4. この手順を5回繰り返す(テラジ以外は後述の回数で)
  5. 6回目はそのまま4本ソルのところまで行くと総攻撃が始まるはずです(失敗するとリセットが掛かるか、何も起こらない)

テーブル調整はテラジが一番分かりやすいのですが、他にも下記のような空中物を目安にすることができます。カッコ内はエリア12でミスをする回数を表しています。

  • ジアラ(6回)
  • カピ(7回)
  • タルケン+ジアラ(8回)

※目安なのでゾルバク調整が必要な場合があります(それぞれ4~6パターン同じ機種の数違いが続くのでポインタの数値を確定できません/上記でだいたい40%~50%の成功率です)

追記:追記のほうに書いた「ザカート>ジアラ」で調整するのが確実です。 仕込み完了後に操作可能なリプレイファイルも用意しました。

具体的な調整方法

これはだいたいの目安なので、ネットで攻撃パターンのチャートを探して(時間が出来たら私も資料作りますが)ください。具体的な手順は、エリア11の攻撃パターンをよく見ておいて、今どこにいるか把握します。エリア11の最後の攻撃が今いるポイントになるので、その数値を基準に下記の計算をしながら調整します。

  • エリア12のバーラ4基(の先の道路が交わる部分が画面に出る)まで進むとプラス32
  • ミスでマイナス16
  • ゾルバク破壊で1基マイナス2
  • 攻撃テーブルによる空中物の出現でプラス2
    (ただし空中物の出現時に128以上かどうかチェックされるので仕込み中は出さないようにしよう)

これをうまく使って、エリア12スタート時に攻撃テーブルの数値を112~113に調整できれば勝ち確定です。この状態で上のプレイを1回行うごとにポインタをオーバーランさせ、6回目にそのまま川まで進むと総攻撃が始まります(調整に失敗した場合はリセット)。ただし、発動できても川の手前のごく僅かの時間だけで、川に差し掛かると通常攻撃に戻ってしまいます。

参考画像
参考画像

エリア12のみでテーブル調整(未検証/理論上可能)

残機があればエリア12の川直前の空中物で調整する方法があります。この空中物の種類がテラジ、カピ、タルケン+ジアラのときミスした場合、上記の回数+1回繰り返すと仕込みのラインに乗ります。もし違う空中物の場合は、ゾルバクを少し倒すなりして調整してください。ジアラを外したのは、攻撃パターンのグループが2つあり特定ができないからです。

何が起きているか

からくりは「攻撃テーブルポインタを2加算>128以上になったら64減算する>ポインタに対応する空中物を出現させる」というロジックになっているためです。つまり元のポインタが128未満であることしか考慮していないことを利用しています。更に、得点とミス回数から16加算する処理のバグ(範囲外になってもチェックしていない)を組み合わせることにより、次の空中物が出るときに総攻撃のパターンコード(162~163)を指すよう調整しています。

エリア12の序盤では、バグのあるこの処理が2回実行されるため、攻撃テーブルポインタが32進みます。ミスしたときは単純に16減算しているのみなので、1回のプレイでプラス16進むことになります。従って、(仕込み5回分で)112+16×5=192、(6回目のプレイで)192+32-64+2=162(総攻撃パターンコード0xA2)となります。

なお、ハードやベリーハードの場合はまだ可能性のあるエリアがあるかも知れませんし、テーブル調整がしやすいかも知れません。こちらはまだ想像の範囲ですが、きちんと調べれば入り口の間口は広がるかも知れません。

とりあえず、総攻撃への入り口はエリア12にありました、というご報告でした。動画を作成していますので、できしだい追記&アップロード致します。

追記1

下記に動画をアップしました。

この動画ではエリア11の攻撃が「ザカート>ジアラ」だったのでポインタが104か105で確定できました。(上の目安で書いたジアラの少し後に出てくる2パターン連続しているジアラと特定)そのため、エリア12のゾルバクで調整を入れて必ず発動するよう調整しています。また、エリア11を繰り上げで抜けているので1回分多く仕込み作業をしています。ここら辺はややこしいので早見表を作っておくと成功率が上がると思います。

また想定外ですが仕込み中、変なところでスぺフラ効果音みたいなのが鳴っています。これ、コード上鳴りそうもないのに、どうしてなのかはまったく分かりません。

エリア12が総攻撃への入り口だった
エリア12が総攻撃への入り口だった

追記2

仕込み完了直後にプレイ可能になるリプレイファイルを作りましたので試せる方はご利用ください。

だんだん慣れて成功率が上がってきました。エリア11の斜めの地上物から2回空中物が来ますが、ここが「ザカート>ジアラ」となった場合は、仕込み×6回、ゾルバク×4個破壊、これで仕込み完了になります。もし、動画やリプレイファイルのように繰り上げでエリア12に来た場合は、仕込み×7回、ゾルバク×4個破壊、になります。

リプレイファイルでは、カウントを間違えて5個破壊していますが、恐らく1回目の仕込みの際に、若干スクロールさせすぎて空中物の+2が入ってしまい帳尻が合ったと思われます。もう少し早めに当たった方が成功率が上がると思います。

追記3

A3 を出そうと思って少し気づいたことを書いときます。この出現テーブルポインタですが、加算も減算も偶数で難易度設定変えてもそれは変わらないと思います。そうだとすると、奇数には絶対にならないため A3 は出せないばかりか、出現テーブルの半分は実際には使われていないっぽいです。

追記の追記:チェックポイントで得点とミス数から出現テーブルポインタの加算値を算出しますが、その際に奇数になることがありました。 ただ、ゲーム開始の早いうちでないと以降ほぼ確実に +16 の判定になるっぽい(後ほど検証します)。 また、奇数になっているのはテーブル上のブラグザカートが ×2 になることで判断できます。

固定の空中物としてコードを直接指定されれば別ですが、それでも多くの使われない攻撃パターンがあると思われます。加算や減算の数値を半分にすれば、出現テーブルを倍使えるので非常にもったいないと思います。

おかげで A3 がちょっと難しそうですが、B2 であれば仕込み回数をもう1回増やすだけなので、後で挑戦してみようと思います追記2のリプレイファイルから仕込みを1回追加すると B2 の攻撃になります(動画も追加しておきました)。

あと、先ほどの偶数奇数の話に戻ると「ザカート>ジアラ」と来た場合は仕込み方法がひとつしかありません。出現テーブル上は、同じジアラの攻撃が2連続定義されていますが、片方が奇数になるためポインタが 104 であることが確定するからです。テラジや他の機体でも編隊の機数によって、仕込み回数とゾルバク破壊個数が確定的に言えそうです。

更に改造のアイディアも。ということはですよ、何らかのトリガーで奇数にレーンチェンジできれば、出現テーブルが2本できますよね。ゼビウスアレンジメントのように、出現テーブルを2つ用意してプレイヤーを苦しめることができますね。

追記4:調整の早見表

攻撃順から起点攻撃コードを特定できるものを早見表にしてみました。繰り上げでエリア12に来た場合は、仕込み回数を+1してください。

エリア11の攻撃パターン起点攻撃コード仕込み回数ゾルバク破壊数
ザカート > ジアラ0x68 (104)64
ジアラ > トーロイド0x6A (106)65
テラジ×2 > テラジ×30x70 (112)50
テラジ×3 > テラジ×40x72 (114)51
テラジ > トーロイド0x74 (116)52
発動後に再チャレンジしたい場合

A2 の発動後はテーブルポインタが 146(0x92)になっているので、ゾルバク1個破壊することでもう一度ラインに乗せることができます。ラインに乗ったら、また仕込み3回行うと再びチャレンジできる状態になります。ただし、川付近まで進んだ場合は仕込みが壊れているので諦めましょう。

また、16進で考えたほうが分かりやすいです。まず、ラインに乗せた状態は16進で1桁目がゼロの場合になりますので、ゾルバクを使って乗せてください。仕込み1回で +0x10 進むので 0x90 → 0xA0 → 0xB0 → 0xC0 と仕込みを繰り返し、出撃の際に 0xC0 になっていれば条件が成立しています。

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (7) につづく)

ROM チェックエラー
ROM チェックエラー

今まで ROM チェックを無視して結果判定のところだけ NG に分岐しないよう改造してたんですけど、それをちゃんとチェックが通るようにしよう、ということで重い腰を上げてみた。

ROM データの最終バイトにチェックサム調整の数値が書き込まれているのは気づいていたので、恐らく総和の下位8ビットがゼロになるよう調整してあるんだろうと察しはついていました。実際に計算してみるとその通りだったので、チェックサムを計算してこの調整バイトに書き込んで終わり、と思ったのになぜかチェックが通らない。調べてみると、チェックサムを計算した計算結果が並ぶメモリがあって、そこの恐らく XVI6 に当たる部分がゼロになっていませんでした。でも、こちらのエクセルの計算結果はゼロになります。

プログラムを追ってようやく分かったのは、なんとサブ CPU にマップされる ROM はチェックサムを計算していない、ということです。データの中でたまたまゼロになっている1バイトのアドレスを、ただ計算結果のメモリにコピーしてるだけ! そんなの有りですか? 時間を返して欲しいです。

だから、プログラム内のたまたまゼロ値であるアドレスは変わりようがないので、XVI5 についてはチェックサムは関係なくてもパスします。しかし、出現テーブルの XVI6 は改変したら大きくずれて参照先アドレスがゼロ値でなくなり、チェックサムが合っていても ROM エラーとなります。しかも、ROM 5 という表示でいかにも XVI5 がエラーというふうなのですが、XVI6 がエラーというトラップもありました。

散々苦労しましたが、最初の ROM チェックを無視するというコードが正解でした。同時にプログラムの ROM を改造する場合は、最終バイトまで心置きなく使っても大丈夫なことが判明しました。たぶん、サブ CPU のほうはメンドクサイからそうしたのか、チェック完了のタイミングが CPU 同士で取るのが大変だから、とかそういう理由でしょうね。

ちなみに、エリア8の中盤で右側道路に3台のドモグラムが下りてくるシーンがあると思いますが、そのうち真ん中の1台のデータがそれです。このドモグラムを弄るか、この前のエリアに何か追加してずれてしまうと ROM エラーになります。

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

デモプレイに画面が切り替わる瞬間にクレジットを投入した際、ソルバルウなどが画面に残った状態で 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 間でメモリを共有しているにも関わらず、タイミングが取れてないから意図しないデータ参照や書き込みが起きてしまう。もしかしたらまだまだレアな怪奇現象が起こるかも知れません。

追記:大量得点の件

破壊可能なバキュラから大量の得点が得られる点については、得点インデックスが3バイトの二進化十進数で定義されているためです。インデックス値としては3の倍数が期待されますが、そこに 0x01 というイレギュラーなデータが入り、1バイトずれた3バイトを参照してしまいます。その結果、本来20点であるはずのデータが20万点として加算されてしまうようです。

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (4) からのつづき)

今回はロマン溢れる記事から一転、現実解的な内容になりますので、よろしくお願いします。インパクトある物量攻撃が起こる原因について少し見て行きたいと思います。

複合攻撃の仕組み

空中物が出現するとワークメモリに、出現機数とキャラクタ出現テーブルの位置がロードされます。例えば弾なしトーロイドを3機出現させる場合「0x03 0x01」になります。同時出現は6機までなので、1バイト目は 0x01~0x06 の範囲、2バイト目は 0x01~0x73 くらいの値を取ります。

キャラクタ出現テーブルは表のように定義されており、先の例だと1番目から6機分を出現させる指定になります。複合攻撃にしたい場合は、キャラクタの切り替わる境界を使うことで実現しています。例えば、弾なしトーロイド×3+弾ありトーロイド×3を出したい場合「0x06 0x04」と指定すれば良いことになります。

おなじみの、ジアラ×2+タルケン×3~4の組み合わせもジアラとタルケンが切り替わる境界のところで、「0x05 0x17」か「0x06 0x17」と指定します。機数の違いのみでタルケンを3機にしたり4機にしたりできる、とても巧妙な作りです。

+0+1+2+3+4+5
0x01トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)トーロイド(弾なし)
0x07トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)トーロイド(弾あり)
0x0Dジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)ジアラ(弾あり)
0x13ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)ジアラ(弾なし)
0x19タルケンタルケンタルケンタルケンタルケンタルケン
出現テーブルの冒頭部分

総攻撃はバッファオーバーランか

出現テーブルに従って機数分の空中物ワークへキャラクタが転送されると、キャラクタに対応するプログラムが紐づいて攻撃が始まります。また同様に、地上物は地上物用ワーク、バキュラはバキュラ用ワークを持っていて、出現テーブルに従ってセットされたりクリアされたりしながらエリアが進行していきます。

これらのワークはメモリ上で隣接しているため、例えばボザログラム(ワークを5個使用する)を11番目以降(地上物のワークは14個まで)に設定してしまうとバキュラ用ワークにはみ出します。それぞれのワーク領域では、自機に当たる・当たらない、ザッパーに当たる・当たらない、ブラスターに当たる・当たらない、の属性になっているので、このボザログラムはバキュラバリアを装備し、かつ自機との当たり判定も持つことになります。

ここで面白いのは、本来とは異なるワーク領域へキャラクタをロードさせてもそのキャラクタのプログラム(動き)はそのままな点です。左記のボザログラムはスクロールに同期して移動し、見た目上は地上物としてふるまいます。

総攻撃に話を戻しましょう。今までの前置きを踏まえると「空中物のワーク領域からオーバーランさせて、地上物やバキュラのワーク領域にロードさせた状態なのでは無いのか」という話が見えてきます。

実際に検証してみます

1番目のトーロイドを起点に実際にオーバーランさせて検証してみたいと思います。敵出現テーブルのメモリにブレークポイント入れて、読みだされた後の動きを追ってみると、XVI5 の 0x04D8~0x054B あたりまでが空中物の組み合わせを定義しているテーブルと分かりました(複合攻撃のところで書いた2バイト単位が羅列されています)。

このテーブルの機数のところを書き換えてオーバーランを起こさせます。しかし、適当に数を増やしてみても6機以上は出て来ず、極端に増やすとリセットが掛かりました。見当が外れたのかと思いましたが、71機以上設定すると地上物のワークを使い始めると判明。トーロイド起点だとカピ(71機目)からブラグザカート(87機目)までが地上物扱いになりました。

これ以降109機くらいまではバキュラのワークに配置され、バキュラバリアが付いた状態になります。

更に増やすとスパリオのワークに行くらしく、幻影弾が出るようになります。これ以上増やすとキャラクタのテーブルも終わるため、リセットや halt してしまいます。

0xA2、0xB2、0xA3 では何が起きているか

理由は分かりませんが上のキャラテーブルの続きではなく、XVI5 の 0x03D8~0x04D7 を参照していました。実は 0x80 以降の攻撃パターンは、0xE1 のジアラ+ギドスパリオぐらいしか使用されておらず、なぜこうなっているのか良くわかっていません。キャラテーブルに空きもあるし入れられると思うですけどね、謎です。

それぞれの参照先で何が定義されているか調べてみると下記の通りでした。

攻撃コード参照先(XVI5)定義(機数・起点キャラ)
0xA20x041C0xA7 0xED
0xB20x043C0xC1 0xC9
0xA30x041E0x52 0x19

うーむ。上2つは想像してたのとぜんぜん違いますね。これだとリセットコースなんですが、さっき書き換えてテストしていた部分に入れてみると A2, B2 と同じ攻撃が出て来ました。この A2 の定義だと範囲外のキャラを167機出現させるという指定であり、リセットが掛かってもおかしくありません。これでよく動いてるな、というのが心証です。

追記:0x80~0xFF までの参照で実際有効なのは 0xE0~0xFF(XVI5 の 0x0498~0x04D7)です。 残念ながらプログラムコードの一部を拾ってしてしまい、バグって総攻撃に至っているというのが事の真相のようです。

残りの A3 については、25番目(0x19)のタルケンを指定して82機出現させると言った意味になります。関連するキャラテーブルを書き出すと下記の通り。

+0+1+2+3+4+5
0x19タルケンタルケンタルケンタルケンタルケンタルケン
0x1Fゾシー(乱)ゾシー(乱)ゾシー(乱)ゾシー(乱)ゾシー(乱)ゾシー(乱)
0x25トーロイド(弾)トーロイド(弾)ギドスパリオギドスパリオギドスパリオギドスパリオ
0x2Bギドスパリオギドスパリオゾシー(追撃)ゾシー(追撃)ゾシー(追撃)ゾシー(追撃)
0x31ゾシー(追撃)バックゾシーバックゾシーバックゾシーバックゾシーザカート(落下)
0x37ザカート(落下)ザカート(落下)ザカート(落下)ザカート(落下)ザカート(落下)ザカート(自狙)
0x3Dザカート(自狙)ザカート(自狙)ザカート(自狙)ザカート(自狙)ザカート(自狙)バックゾシー
0x43バックゾシーバックゾシーカピカピカピカピ
0x49カピカピザカート(自狙)ザカート(自狙)ザカート(自狙)テラジ
0x4Fテラジテラジテラジテラジテラジブラグザカート(自狙)
0x55ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)ブラグザカート(自狙)
0x5Bブラグザカート(自狙)タルケンタルケンギドスパリオギドスパリオギドスパリオ
0x61ギドスパリオジアラ(弾)ジアラ(弾)カピカピギドスパリオ
0x67ギドスパリオギドスパリオギドスパリオテラジテラジザカート(自狙)

青字が通常の空中物として出ているタルケン6機、赤字が地上物扱いで出ているものになります。正直これ以外はメモリのゴミか無関係な参照かも知れませんが、こちらはキャラクタの区切りにきっちり設定してあり、キャラテーブル内の機数で設定され、かつ地上物ワークの範囲内に入れてある、という点を取って意図を感じてしまいます。

追記:地上物ワークまで間が空くのは、<地上物>~<バキュラ>~<スパリオ>~<空中物>~<未使用>の順に配置されているためでした(2バイト単位なので128機(うち有効なのが最大54機)で1周しています)。 細かく書くとバキュラとスパリオの間も、自機や照準など7機分の空きがあるようです。

結びに

果たして、意図したものなのか、単に無意味な参照だったのか、判断はお任せします。個人的にはロマンあるほうを主張しておきたいですね。

最後に、すごくインパクトのある発見で久しぶりにゼビウスで楽しめましたし、ここ最近プレイもして久方ぶりに1000万点を出しました。あと、消失ソルを1バイトで出す方法(今までは2バイトだった)とか、複合攻撃のコントロール方法、その他改造に役立つ知見が得られました。消失ソルの件は時間が出来たら記事にしますので、お楽しみに。

[ゼビウス] AC版ゼビウスに総攻撃がプログラムされていた (6) につづく)