フレーム境界以外でのリセットによるSRAM書き換え

タイトルが長ったらしいですね^^;これは何か名前付いてないんでしょうか?

この手法自体はクロノトリガーTAS(YouTube)のいんいち氏が既に発表されていますが、その後あまり応用例を見かけないので、問題提起の意味も込めてFC版FF3でやってみました。どうやら最初の大岩を回避してそのまま浮遊大陸を脱出することは可能なようです(ただし、現在のエミュレータではムービーとして記録することはできません)*1
D
上記動画を再現するための手順を書いておきます(FCEUX推奨)。まず、シドの飛空艇を入手して画像の位置へ移動し:

着陸してから再度離陸してセーブ。次に画像の位置へ着陸し:

デバッガで $640A への書き込みにブレークを仕掛け、画像の位置まで歩いていってセーブ:

ブレークが発生したらソフトリセットしてそのまま続行(Run)。するとセーブデータが残っているので、それをロードすれば飛空艇に乗った状態で大岩の上へワープできます。

私はFF3についてはほとんど解析していないのですが、何が起こっているかだけ簡単に説明してみます。まず、セーブデータの先頭は以下のような構造になっています:

0x6400: 01 53 36 00 00 00 00 00 - 00 4A 2F 00 00 00 00 05

$6400: 飛空艇状態(非0で存在)
$6401: 飛空艇x座標(フィールドx座標+7, 着陸成功時に更新)
$6402: 飛空艇y座標(フィールドy座標+7, 着陸成功時に更新)
$6408: フィールド種別
       0:浮遊大陸
       2:地上世界(復活前)
       3:地上世界(復活後)
$6409: フィールドx座標
$640A: フィールドy座標
$640F: 乗り物
       0:徒歩
       1:チョコボ
       2:カヌー
       3:船
       4:エンタープライズ
       5:シドの飛空艇
       6:ノーチラス
       7:インビンシブル
その他のアドレスは未解析

よって、セーブ時に $6409 まで書き込まれた時点でリセットすれば、y座標を変化させることなくx座標のみを書き換えることができます。
ただし、FF3はセーブデータにチェックサムが付いており、これが不正だと起動時にセーブデータが消去されてしまいます。よって、複数の値を書き換えることで辻褄を合わせてやる必要があります。上記動画では、飛空艇x座標を+1, フィールドx座標を-1することでチェックサムを合わせています。

この後の展開としては、地上世界を復活させるところまでは無事に進められることを確認したので、その後同様のSRAM書き換えを行って

という手順が実行できれば、かなりの短縮になるのではないかと思います(可能かどうかは未確認ですが、前者については事前に飛空艇に乗った状態のデータを別スロットにセーブしておき、そこからSRAM内容を書き写して「飛空艇に乗っている」($640F=5)ことにすればいけるような気がします*2。後者は単なる座標合わせなので多分何とかなりそうです)。
チートで適当に検証してみたところ、シルクスのカギ(アイテム変化技で作れる)があればクリスタルタワー最上階には到達できます。ただしそのままだと最上階イベントのアムルのシーンでフリーズするため、アムルでふゆうそうのくつを取得しておく必要があります。その後は普通に進めていけば無事EDまで到達できるようです(EDではなぜかノーチラスのグラフィックがシドの飛空艇になっており、また最初の大岩を破壊していないために途中で引っ掛かりますが、そのまま待っていれば次のシーンへ進むので問題なし)。

この手法はFF3に限らず、セーブができるほとんどのゲームで使える(本来両立しない状態を両立させられる)*3ので、もしエミュレータ側で対応されれば色々と面白いことになるのではないかと思います。特にFFシリーズなど「セーブを別のセーブから上書きできる」ゲームでは、セーブデータを「合体させる」ことが可能となるため、応用の幅が広がりそうです。

ただし、この手法はあまりに汎用的すぎるが故に賛否両論あるところだと思います。これを認めるとゲーム固有の知識があまり必要なくなり、面白くないと感じる人も相当数いるのではないかと思います(正直言って私も何か違和感を覚えます)。とりあえず、リセットによってセーブデータを書き換えるTASは通常のTASとは別の括りで扱う、というのは最低限合意できるところではないかと思います。

ところで、上記動画ではCPU命令境界でのリセットを行っていますが、「命令の途中でリセット」というのは技術的にアリなのでしょうか?私はハードウェアには詳しくないのでよくわからないのですが…*4 *5

TASVideos Forum内の関係ありそうなトピック:

Dragon Warrior 1(海外版DQ1)は1Fに80回以上入力を読み取ることがあり、その都度入力できればより良い運操作が可能になる、といった書き込みもありました。

*1:Luaスクリプトで memory.registerexec() や memory.registerwrite() を駆使すれば現状でも擬似的に再生可能と思われます。FinalFighterさんよりご指摘頂きました。

*2:ただしこれだと $6408 も書き換わってしまうため、さらに別のセーブデータを用意して $6408 までを上書きする必要がありそうです。

*3:これを防ぐプログラミングも可能だと思います(例えば、存在フラグをセーブデータ末尾に付加し、セーブ時は存在フラグをオフにしてから改めてセーブデータ全体を書き換えるなど)が、そこまでしているソフトは少ないと思います。

*4:NESの場合、NMI, IRQ割り込みは命令境界で検知されるので、RESET割り込みも同じだと考えるのが妥当そうな気がします。つまり、命令途中でリセットしても命令実行直後にリセットしたのと同じになるのではないかと思います。FinalFighterさん情報提供thx!

*5:DMA転送やブロック転送命令の途中でリセットするとどうなるのか、FDSのクイックディスクやPCEなどの外部記憶はどう扱うべきなのか、といった辺りも少し気になるところ。