ロックマン1のディレイエンディングTASについて
閉じる
閉じる

新しい記事を投稿しました。シェアして読者に伝えましょう

×

ロックマン1のディレイエンディングTASについて

2014-05-13 05:45
  • 44
  • 29

今回のTASについてざっくり説明

1:ロックマンの位置やドロップアイテムの出現時間を調整したりする

2:バグを引き起こす

3:エンディングが呼ばれるよう調整してある

このようなTASはACE(任意コード実行)というジャンル名で呼ばれています。
ACEは最近急に発展してきたため、普通にクリアしていくTASとは違ったジャンルになり発展しつつあります。

大百科:任意コード実行

今回はロックマン1でACEをするととても速くエンディングを見られるのでは?というTASの方向性の1つを形にしました。

新TASに至った経緯

2010/11/28
前回のTASの完成

2011/01/21
ディレイFFオブジェクトテクを見つけ、アイスマンステージでオブジェクト00~FFが出現することを発見

2014/4/29
FinalFighterが、ピロ彦先生のスーパーマリオブラザーズ3のTASについて簡単に解説を読む
SMB3の任意コード実行の機構を知る

2014/4/30

「ディレイオブジェクト」のうちいくつかのオブジェクトが$700以下のRAMアドレスを実行していたことに気づく
以前はリセットがかかるので使えないオブジェクトとしてチェックしていなかったが、

SMB3TASの任意コード実行の機構を知ったので、メモリの事前調整によってロックマン1でもできるのではないかと思い出す。
ピロ彦先生と共同で任意コードをどう書けばうまくいくかを調査する
ディレイオブジェクトFFテクで、オブジェクト55の出現が可能なことをBOTで確認。
オブジェクト55の呼び出し先アドレスが$600で合っていることを確認。

$600(Y座標)からジャンプさせて$650(アイテムが出現すると増加し消すと止まるカウンタ:ObjectFireDelay)を調整するとうまくいくことを発見

ピロ彦氏曰く<もしロックマン1で任意コードが実行されるようになったとしたら、それはSMB3TASに影響を受けた結果であり、巡り巡ってkentora12の影響であることは確定的に明らか。

2014/5/1
FinalFighterがディレィオブジェクトがどのメモリから読み込まれたか確認できるLuaScriptの作成。それにより、アイスマンステージのディレイオブジェクトFFテクでアドレス:$0023の内容を0x55にするとオブジェクト55が出現することを確認、ディレイFFオブジェクトテクがロックバスター+4ドロップアイテム程度の重さで実行できることをBOTで確認
アイスマンステージでディレイエンディングが完結できることをピロ彦先生がメモリ操作で確認

2014/5/3

新TAS作成開始
ピロ彦先生が事前のメモリ調整担当に。

2つに分離するロボットがオブジェクト20(パンチ)をやたら出すためそのたびにスロットが破壊されるので調整が非常に大変なようであった。

2014/5/8

ロックマン2でもFinalFighterとピロ彦氏がガッツタンク戦で新技を発見
ロックマン2のやり直しが確定した(´・ω・`)<バブルマンステージまで進んでたのに…

2014/5/11

スロットを空けていてもなかなかうまく狙ったスロットに納まってくれないという問題が多発していたため、FinalFighterがオブジェクト出現予測Luaを作成しオブジェクトの出現条件及び原因を突き止める。

2014/5/12
FinalFighterがオブジェクト1のObjectFireDelayの変動する原因を突き止め。これにより最適な任意コードが確定。

2014/5/13
80F程待てばもっと重い音楽のFがあるためディレイエンディング再現は余裕だったのだが、
われわれは妥協したくなかった。$23=0x55となる最速のチャンスでどうしても成功させたかった。BOTが待てども待てども結果を出さないため、

ピロ彦先生は、$8C=6かつ処理量がNMIが割り込む位置が合う条件をロックマン2のディレイスクロールのように支援LuaScriptを使って手作業で狙い始めた。$8C=4ではすぐにいい例が出た。

これで$8C=6でもうまくいけばいいのだがなかなか処理量がうまく調整できない。
処理量がオーバーしてしまう例がでたので解は近くにきっとあるはずなのだが…

しかし、どうしてもうまくいかず、あきらめて80Fほど待つかな…と思った瞬間、
ピロ彦先生が処理量が多い例としてアップしていたfm2を元にFinalFighterが作成しまわしていたBOTが解を導きだす。

最後はピロ彦先生が$0018のコントローラの入力をしてキタ―(゚∀゚)― !!と発言。
ついにわれわれは感動のエンディングを見ることができたのだ。
2010年以来久々の更新がついに完成である!


ディレイオブジェクトFFテク

ロックマンでは特定のマップのある地点を通過すると「0xFF」という番号の見えないオブジェクトが出現することがあります。
例としてアイスマンステージの下記の場所があります。


  

このオブジェクトはPPUのパターンテーブルのリマップ処理を実行します。
おそらくグラフィックの再構築などに使われているオブジェクトと推定されます。
  
この処理は通常以下のような手順で動いています。

敵のロード処理

  1. オブジェクトFF出現(図の下記の場所で見えないオブジェクトとして出現)
  2. オブジェクトFFの処理(バンク2からデータ読み込み)
  3. バンク6に切り替え
  4. 敵番号の読み込み処理

     
    $A454から読み込まれている値はバンク6の「29 A5」であることに注目
    (※敵番号アドレスはアイスマンステージの図の場所のものの一例)

  5. 正しいオブジェクトが出現条件を達成していたら出現する

1フレームの処理量を重くする行動で調整しNMIを敵番号の読み込み処理に割り込ませると下記のような異常な処理となります。

敵のロード処理

  1. オブジェクトFF出現(図の下記の場所で見えないオブジェクトとして出現)
  2. オブジェクトFFの処理(バンク2からデータ読み込み)


  3. バンク6に切り替え


  4. NMIが発生しバンク2に切り替え ここでバンクが通常と違う状態となる。

    ※NMIを発生させる必要がある場所は敵番号読み込み処理の直前となる。

     この個所以外ではNMIは終了するときにバンク番号を正しく復旧するが、

     この個所に限ってはバンク番号を間違えて復旧する。


    NMIは一定処理数おきに必ず割り込んで実行される処理
    1フレーム内の行動量やオブジェクトの量を多くするなどすると、
    特定の処理の前に持ってくることができる。詳しくは前回参照

  5. 敵番号の読み込み処理(バンク2から敵番号アドレスデータ読み込み)

    (※敵番号アドレスはアイスマンステージの図の場所のものの一例)

    読み込まれているアドレスは同じ$A454でもデータは
    下記の例では、正しいバンク6の「29 A5」とは異なり、
    バンク2の「00 00」を読み込んでしまうことに注目。
    それにより、敵番号データを読み込むアドレスが、
    通常の「A541」と比べると大きく狂って、「0023」から読み込もうとしている。
    0023は1フレごとに+1されるカウンタに使われているRAMアドレスであり、
    これにより、通常出現しないオブジェクトが00~FFの範囲で出現したりする。





  6. 誤ったオブジェクトの出現処理

アイスマンステージの画像の箇所では、「ディレイオブジェクトFFテク」を実行すると
従来の「ディレイオブジェクトテク」(バンク5を参照する)では「オブジェクト1、オブジェクト1F」しか出現しなかった場所で、
別の種類のオブジェクトが出現するようになり、0x00~0xFFの全てのオブジェクトが出現するようになります。

オブジェクトのAIのジャンプ先異常

この処理は通常ロックマン1に存在するオブジェクト(00~4Aのオブジェクト)で以下のような手順で動いています。

オブジェクト(00~4A)のAIの処理

  1. オブジェクトのAI番号取得
  2. $AA3B+2*(オブジェクト番号)からオブジェクトのAI処理を開始するアドレスを取得
    (X=4Aまでは正常なアドレスが入っている)
  3. 取得したアドレスへジャンプしオブジェクトのAIの処理を実行する







※0x80以上のオブジェクトの場合はオブジェクト番号-0x80した結果と同様となる。

ところが、ディレイオブジェクトテクやディレイオブジェクトFFテクを使うと通常ロックマン1に存在しない
異常な4B~7F,CB~FEのオブジェクトが発生するため下記のような間違いが生じます。




0x00~0x4Aのオブジェクトは青い色の領域のうちの2byte
0x4B~0x7Fのオブジェクトは濃い緑色の領域のうちの2byteを参照する。
※0x80以上のオブジェクトの場合はオブジェクト番号-0x80した結果と同様となる。

青い色の領域は2byteごとにAIへのアドレスとなっている。
緑色の領域は、「ブンビーヘリ」という敵のAIの処理となっている。

例として以前使っていた「ディレイステージクリア(Object75)」の例を見てみよう

オブジェクト75(ディレイステージクリア)のAIの処理

  1. ディレイオブジェクトテクで「Object75」が出現
  2. オブジェクトのAI番号=「75」取得
  3. $AA3B+2*(オブジェクト番号75)=AB25
  4. $AB25からAI処理のジャンプ先アドレスとして「C0BD」を取得


    正しいアドレスリストが配置されている青い領域をはみ出して、
    間違ったメモリからジャンプ先のアドレスを取得しているのが判る。

  5. $C0BDというアドレスへジャンプして処理を実行する
  6. $C0BDはステージクリア処理の途中のアドレス(下記の図参照)のため、
    ステージクリア処理が実行されステージクリアとなる。


今回のTASで用いた「オブジェクト55(Object55)」の例を見てみよう

オブジェクト55のAIの処理

  1. ディレイオブジェクトFFテクで「Object55」が出現
  2. オブジェクトのAI番号=「55」取得
  3. $AA3B+2*(オブジェクト番号55)=AAE5
  4. $AAE5からジャンプ先アドレスとして「0600」を取得


  5. $600というアドレスへジャンプして処理を実行する




    ※$600の近くは各種Y座標が入るようになっている。


  6. $600、$601、$602…と順にメモリを読み込みファミコンの処理として実行する
    たとえば上記のメモリ値の場合は下記の処理が実行される。


    0600 58 CLI
    0601 F8 SED
    0602 50 54 BVC $0658


  つまり、$600が実行されるタイミングにあわせてメモリ内容を上手に変化させておくことでファミコンの処理を実行できるのだ。
  詳しくは今回のTASの実行手順を参照してほしい。



今回のTASの実行手順

今回のTASの実行手順

  1. $23の値をもとに実行フレームを決定する

    $23の値は毎フレーム増加増加するカウンタで、この値が55になるときにアイスマンステージで、ディレイオブジェクトFFテクを実行するとObject55が出現する可能性がある。調整の結果計算すると1929Fが最速のタイミングとなった。
  2. $650付近の値(ObjectFireDelay、$650)が1929Fに下記になるように調整しておく

    $650 = XX AD 22 XX 6C 18


    ObjectFireDelayは敵を倒したタイミングから出現したドロップアイテムによって1Fごとに1ずつ増加していくので
    1929Fにピッタリ上記の値に合うように調整する。

  3. 敵を倒しながらオブジェクトのスロット0、3、6を0xF8に調整する($610+X)

    これにより、ディレイFFを実行したとき出現する3つのオブジェクトがF8(空きスロット0、3、6)に収まる計算となる
    $610 = F8 3C 2E F8 5A 29 F8

  4. ロックバスター1発目と2発目のY座標で下記に調整する。
    $602=50
    $603=4D

  5. 1フレームの処理量を調整することでNMIを割り込ませ、オブジェクトFFを図の位置で出現させる


  6. 1929FにディレイオブジェクトFFテクを実行する。

    このときObject1,3,55が出現するパターンを満たすため下記の条件を満たしておく

    $8C = 6 , $20 < $1B=#0x04 , $21 < $1A , $23=0x55(毎フレームまわるカウンタ)
    特に$23が55となるタイミングでオブジェクト55が出現するのでこのタイミングにObjectFireDelayがぴったりあうよう調整している。

    また$8C=6のときオブジェクト1,3,($23から読み込まれた番号のオブジェクト)が出現することを確認している。

    またこのディレイオブジェクトFFを実行するフレームに音楽の変化のタイミングを重ねる必要がある(1フレームの処理量が多い)ため、この場所に来る前にラグフレームを増やしたり、セレクトでメモリの数値調整を行ったりといった操作を行っている。


  7. オブジェクトFFの処理
    1. (バンク2からデータ読み込み)
    2. バンク6に切り替える処理が走る



    3. NMIが発生しバンク2に切り替える処理が走る



    4. 敵番号の読み込み処理
      $A454(CurrentRoomPointer)付近がバンク2になることにより敵のオブジェクトが読み込まれるメモリのポインタが狂う。
      この結果、$1Bからオブジェクト番号3が、$1Fからオブジェクト番号1が、$23からオブジェクト番号55が読み込まれる

  8. オブジェクト3,1,55が出現し、$650+Xを破壊することなく空きスロット0,3,6に収まる


    ObjectFireDelayは$A40C(バンク2)+オブジェクト番号で決まる。

    オブジェクト3(スロット0)
    バンク狂っているときのオブジェクト3のObjectFireDelayの初期値は3C

    オブジェクト1(スロット3)
    バンク狂っているときのオブジェクト1のObjectFireDelayの初期値は00だが
    オブジェクト1はメットールという敵であり、メットールのAIが実行されると変化する。
    $0450+スロット番号 AND 0xF0 が0ならObjectFireDelayの初期値は20
    であり、オブジェクト55のAIが実行されるときの値は1フレーム後なので「1F」となる。
    参考にそれ以外の場合は、下記の値となる。
    スロット:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
    出現値 :A0 00 A9 18 20 AC C5 A6 2F A5 04 9D 60 06 A5 05

    オブジェクト55(スロット6)
    バンク狂っているときのObjectFireDelayの初期値は00


    この出現により$650からメモリが下記の値となる

    $650 = 3C AD 22 1F 6C 18 00



  9. オブジェクト55のAIが実行される

    $AA3B+2*(オブジェクト番号55)=AAE5
    $AAE5からオブジェクト55のAI実行先アドレス「$600」が取得される





  10. コントローラを押す
    1930Fに1コンの「左下」+2コンの「左右セレクト」を押す。
    この調整により$0018(インプットを記録するメモリ)に「60 C4」の値が入る

  11. 以上の調整により上記のメモリ値の下記の処理が実行される

    0600:54 F8 NOP F8,X(何もしない)
    ロックマンのY座標=54で調整

    0602
    :50 4D BVC $0651 ($651にジャンプ)
    ロックバスター1番目と2番目のY座標=50と4Dで調整

    0650
    :3C UNDEFINED (ここにはこない)
    ディレイFFにより出現したオブジェクト3のObjectFireDelay=3Cで調整

    0651
    :AD 22 1F LDA $1F22 = #$0A (0x0Aが入っている$1F22をロードしAレジスタにセット)
    普通の敵を倒したときにでるドロップアイテムのObjectFireDelay=AD,22で調整
    ディレイFFにより出現したオブジェクト1のObjectFireDelay=1Fで調整

    0654
    :6C 18 00 JMP ($0018) = $C460 ($C460:ステージクリア処理の途中にジャンプ)
    普通の敵を倒したときにでるドロップアイテムのObjectFireDelay=6C,18で調整
    ディレイFFにより出現したオブジェクト55のObjectFireDelay=00で調整
    ($0018)の値は1コンの「左下」+2コンの「左右セレクト」=60,C4で調整




    以下の処理のステージ番号が0Aになっている状態でステージクリアの途中の処理が実行される

    C45E: A9 00 lda #$00

    C460: 85 31 sta $31(CurrentStage) ←ここにジャンプしてきて割り込む
    C462: 4C 0C C1 jmp $C10C


    以上の調整により
    エンディング処理が実行される

  

 ロックマンはアイスマンステージへ行ってる途中で飽きて帰ってきたようですw
 今回のTASのエンディング後にスタートボタンを押すと…
 
ロックマンがさぼっていたのがバレるので試してみてください。


LuaScript集

*rm1delay.lua

ディレイオブジェクトテクを支援するLuaScriptです。

NEXTの値を1に調整するとディレイオブジェクトテクが成功します。

オブジェクトが出現した場合オブジェクト番号と、オブジェクト番号が読み込まれたアドレス、

出現したフレームが表示されます。




*rm1delayff.lua

ディレイオブジェクトFFテクを支援するLuaScriptです。

オブジェクトFFが出現するところでのみ、各種値が変動します。

NEXTの値を1~33に調整するとディレイオブジェクトFFテクが成功します。

オブジェクトが出現した場合オブジェクト番号と、

オブジェクト番号が読み込まれたアドレス、出現したフレームが表示されます。




* rm1delayobject_rev2.lua

ディレイオブジェクトテクを支援するLuaScriptです。

そのマップでディレイ量が十分足りたときその行動でオブジェクト番号が、

どの種類のものが出現するか推定します。

また、オブジェクトが出現すると予測される場合のオブジェクト番号と、

オブジェクト番号が読み込まれたアドレス、出現したフレームが表示されます。

ディレイオブジェクトテクでどんなオブジェクトが出現するかの事前調査に使います。




* rm1delayffobject.lua

ディレイオブジェクトFFテクを支援するLuaScriptです。

オブジェクトFFが出現するところでのみ、各種値が変動します。

そのマップでディレイ量が十分足りたときその行動でオブジェクト番号が、

どの種類のものが出現するか推定します。

また、オブジェクトが出現すると予測される場合のオブジェクト番号と、

オブジェクト番号が読み込まれたアドレス、出現したフレームが表示されます。

ディレイオブジェクトFFテクでどんなオブジェクトが出現するかの事前調査に使います。

モード1と2を変更することができ、モード1では敵アドレスを指すポインタの両方、

モード2では敵アドレスを指すポインタの下位ポインタを変更します。




* rm1delayobject_revFF.lua

ディレイオブジェクトFFテクの調査に使われた調査スクリプトです

SpawnObject オブジェクト番号[X=X座標,Y=Y座標] [ObjectFireDelay][ObjectScreenNumberを読み込んだアドレス:ObjectScreenNumber < $1B]を出力します。



下記のような結果を調査しました。

$8C = 4 : No Object

$8C = 5 , $20 < $1B=#0x04 , $21 < $1A : SpawnObject0,3,1,(0x23)

$8C = 6 , $20 < $1B=#0x04 , $21 < $1A : SpawnObject3,1,(0x23)

01のObjectFireDelayは00

03のObjectFireDelayは3C

55のObjectFireDelayは00



* delayff_20.lua

ディレイオブジェクトFFテクに使われたBOTです。

下記のようなテキストをログとして出力します。

BOTを動作させながらディレイオブジェクトFFテクで、

どのアドレスを調整すべきかを調査することができます。

[Rerecord数]->出現したオブジェクト番号 :オブジェクト番号が読み込まれたアドレス

[238075]->83 :A538

[238695]->20 :85AC

[242295]->20 :85AC

[242295]->83 :A538

[243493]->20 :85AC

[243493]->1 :1F

[243493]->55 :23




P.S.

今回のような任意コードを実行するTASをTASVideosでは「ACE(arbitrary code execution,任意コード実行)」と呼んでいます。最近はバグからACEへつなぎエンディングへ飛ぶTASが急増しており、マリオとかでは以下の作品などで実行されています。
本家TASVideosでは、ACEを使わない普通にクリアしていくTASもジャンルとして健在であり、最近では別ジャンルとして分離しつつあります。

ファミコンの場合、このLuaをFCEUXに読み込ませてフリーズやリセットが起きるバグを起こしたときにポーズ(RAM領域の0x00~0x1FFのアドレスが実行される例)があったらそれは今回のTASのような「ACE」の実行の可能性がある有望な例なのです。

アクションやRPGでフリーズやリセットされる再現性があるバグを知っている人、ぜひ試してみてください。もしかしたら任意コード実行の穴を見つけるチャンスかもしれません!

思いつくだけでもロックマン3、ロックマン5、FF3、FF4、DQ5ではフリーズやリセットがかかるバグがありますし、ACEVideos祭りはもう始まっているのかもしれませんね。


そして…ロックマン2にも大幅な更新が見つかっています。
次はロックマン2TASの更新に取り掛かる予定です!






広告
他34件のコメントを表示
×
>>32 です。解説用動画見ました!
「あ~そういうことか!」と一気に謎が解けました。ありがとうございます。
もしここの記事を読まず解説用動画だけ見ていても分かっていなかったと思います。この記事と動画両方のおかげでスッキリ出来ました!
27ヶ月前
×
サボり確認の難易度が高過ぎる!

ロックマン2も期待してます、がんばってください。
27ヶ月前
×
地図のない大海を潜ってサルベージしてくるTAS作成者はんには脱帽しますわ。
仕様書に書いておけよってブラックボックスに何か入れたり、手を突っ込んは嘆きぼやいている小生も精進あるのみなんかね?(笑い)
兎に角お疲れ様でした。
27ヶ月前
×
>>30
動画にすると見た目は凄い味気ないのは確かですね。
元々のTASが経路選択とか壁抜けとか多様してたこともあると思います。
ACEはその実行や背景がどのように行われたかを理解して、
面白く感じてくるプログラマチックなジャンルかもしれませんね。
27ヶ月前
×
>>31
この動画から更にいくつかのACEに派生しそうな雰囲気がありますね。
これからもゲームのコントローラでのハッキング…というジャンルはこれはこれで続くでしょう。
ACEも背景とどのように実行したかをうまく可視化できれば面白いジャンルにはなると思いますが、
今後の動画作成の試行錯誤ですねえ。
27ヶ月前
×
>>35
すっきりしたみたいで良かったです。
メモリ内容を座標などを使って変更することで$600に用意しておいて
そこにディレイバグを引き起こすことで$600が実行される感じなのですねー。
少し理解してくださった方がいただけでもこの記事を書いた意味があるというものです^^
27ヶ月前
×
>>36
がんばります。
今度はACEと通常のTASと両方のファイルをつくってデュアル投稿するという野望を抱いています。
どちらのジャンルの視聴者も楽しめる出来になると思うので楽しみにしていてください。
27ヶ月前
×
>>37
「バグの原因をしっかり調べる」というのがやはり重要なことでしょうか。
これまではリセットやクラッシュされるバグをちゃんと調査してなかったのですが、
起きたらちゃんと原因を調べるようになりました。
それがまた発見につながる感じですかね。
27ヶ月前
×
ロックはサボリw
2の新技期待してます!
27ヶ月前
×
妹に押し倒されて、まで読みました(錯乱

ロックマン2の研究も進んでいるとのことで・・・
楽しみにしております!
27ヶ月前
コメントを書く
コメントをするには、
ログインして下さい。