トップに戻る エキサイトバイクのメインページに戻る

VS.エキサイトバイクにおけるエクステンドのバグについて

ディスクシステム版でのエクステンドのバグについて

ディスクシステム版のデータを解析してみたところ、以下の要因で4機増殖することが判明しました。

エクステンド用のデータテーブルが18バイト分用意されております。 10, 15, 20, 25, … 95、という具合に、 5万点おきにエクステンドするようにデータが並んでおります。

判定するときは、点数の10万の位の下位4ビットと1万の位を1バイトの変数(Aレジスタ)に入れて、 現在のエクステンドする点数と比較しています。(例えば15万点だったら、16進数で15が変数に入ります。)

そしてエクステンドしたら次のエクステンドする点数へ参照先が進む…のですが、 95万点に達して18回目のエクステンドしたら最初の10万点に戻す処理が、 誤って16進数で18(10進法だと24)と指定されており、 エクステンド用のデータテーブルからはみ出した所も参照してしまっておりました。

その為、95万点を過ぎた後は本来とは無関係な値でエクステンドしていき、 24回目である320万n+20万点で本来のエクステンドテーブルに戻ることが判明しました。

24回目のエクステンドで最初の位置に戻り、 10を超えてるかと判定したら超えていたので次の15を判定、 それも超えているので次の20も判定、それも超えていたので 次の25では超えていないのでそこで処理が終わり、4回エクステンドした、という流れでした。

ディスクシステム版でのデータの並び

ディスクシステム版では大体以下のような並びになっておりました。

0 1 2 3 4 5 6 7 8 9 A B C D E F
$6FF0 -- -- -- -- -- -- -- -- -- -- -- -- -- 10 15 20
$7000 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 A5
$7010 19 4A B0 06 20 -- -- -- -- -- -- -- -- -- -- --

エクステンドの値:緑

他のデータ:黒

95を超えたら10に戻らず次に行ってしまい、A5,19,4A,B0,06,20という値を見てしまっておりました。

アセンブラで書いていると思われるので、配列の外に出てしまってもエラーは出ないのです。

アーケード版でのエクステンドのバグの予想

ディスクシステム版と同じ(というかこっちが元)、 5万エブリの場合は0x18回エクステンドしたら最初に戻る処理をしているものと思われます。

アーケード版ではディップスイッチの設定によって、 5万エブリ、10万エブリ、10万のみ、エクステンドなしに切り替えられるので、 5万エブリ用のデータテーブルの隣に、10万エブリ用のデータテーブルが用意 されているものと思われます。

下記の様なデータの並びの為、 95万点超えたら170万点から10万点おきにエクステンドしていき、 160万n+60万点に達すると1回エクステンドした後、 エクステンドテーブルの最初の位置に戻ると10~60まで全部超えているので、 合計12回エクステンドする処理をしてしまうものと思われます。

これが10万エブリだと、90万点を超えると160万n+10万点で再び10万点置きに増えるので、 5万エブリと違って12機増えるようなことはありません。

10万エブリ時のエクステンドテーブルの上限判定は0x09と指定されているものと思われます。

これらは私の予想なので、実際は違っているかもしれません。

アーケード版でのデータの並び予想

0 1 2 3 4 5 6 7 8 9 A B C D E F
$F000(仮) 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85
$F010(仮) 90 95 10 20 30 40 50 60 70 80 90 -- -- -- -- --

5万エブリの値:緑

10万エブリの値:オレンジ

他の値:黒

データの並びは予想図で、なおかつアドレスは仮アドレスです。

95を超えたら最初の10に戻らず次に行ってしまい、10,20,30,40,50,60という値を見てしまっているものと思われます。

スコア上限である2560万点に達した後もエクステンドの参照先が10万エブリ用の60の部分を見てしまっているので、 そのままプレイを続行すると60万点で12機増えるようになってしまっております。

10万エブリ設定の場合は9回目のエクステンドで最初の10万点に戻るので、5万エブリに比べると変な増え方はしません。

アーケード版のエクステンド処理をC言語で書くと、恐らく大体こんな感じ

      void extend()
      {
        int8_t a = score[0] << 4;  // 10万の位の下位4ビットをaの上位4ビットに入れる
        a |= score[1];  // 1万の位をaに追加。1万の位はバグらなければ0a以上にはならない。

        // 次のエクステンド超えてないときは何もしないでエクステンド処理終了
        if (every5 && a < extTable5[extIndex]) return;
        if (every10 && a < extTable10[extIndex]) return;

        bikeLeft++; // 残機を増やす
        extIndex++; // 次のエクステンド点数へ

        // 5万エブリでの判定、16進数で18と指定してしまっている
        if (every5 && extIndex < 0x18) return;

        // 10万エブリでの判定、こっちはちゃんと(?)動く
        if (every10 && extIndex < 0x09) return; 
        extIndex = 0; // 次のエクステンド点数を最初の10万点へ戻す
      }
    

余談 アセンブラでのファミコンやVSシステムにおける条件判断について

ファミコンやVSシステムに使われているCPU、リコー・2A03(元となったMOSテクノロジー・6502も同じ)では、 メモリの値を比較するときは、お互いの値を引き算をして、 0x00~0x7F(127)は正の数、0x80(128)~0xFF(255)は負の数と扱う処理があります。 (CMPで比較した後に分岐させる、BMI,BPLが該当)

その為、ディスクシステム版で105万点(0xA5)でエクステンドした後、次に参照する「0x19」は符号なし状態では超えているのですが、 0xA5-0x19=0x8Cとなって負の値と判断して「超えてない」と判断し、エクステンド処理がされません。

比較判定では、正負を扱わないものもあります。(CMPで比較した後に分岐させる、BCS,BCCが該当)

こっちを使えばいいのではと思うかもしれませんが、こっちを使うと無限ループになる可能性もあるので、正負を扱うことにしたのだと思います。

FAQ

Q1:なんで解析しようと思ったの

A1:妙な増え方に出くわしたので、気になって6502アセンブラを勉強してから解析してみました。こういう事調べる人って自分以外に居なさそうだし。

Q2:その前になんでこのゲームをカンストまでやろうとしたの

A2:まず、COVID-19で外出自粛の影響でヒマでした。それと、やれるんじゃないかなと思ったのでやれるだけやってみました。