●今回のTASに利用された主な解析結果
・ディレオブジェクトテク
ついにロックマン1にもディレイの時代がやってきました。
cstrakm氏が、なんかアイスマンステージで水流起きたんだけど?と言ってきたことからこのテクが発覚。
FinalFighterが正確に調査したところ、ディレイステージクリアという強力なテクを発見
この圧倒的な更新のTASの作成に至りました。
ディレイオブジェクトテクとは、特定のマップで特定のオブジェクトを呼び出すことができるテクニックです。
今回のTASでは以下のテクニックが使われました。
1:アイスマンステージ ディレイ水流(Object2F)
アイスマンステージでは、ディレイオブジェクトテクを使うと、
特定のマップで特定のオブジェクトが必ず出現します。
(バンク切り替えで間違った後の読み込み先が固定なため。)
2:ボンバーマンステージ ディレイステージクリア(ObjectF5) 1327665追記
ボンバーマンステージでは、該当の箇所でディレイオブジェクトテクを使うと、
00〜FFまでのすべてのオブジェクトを出現させることができますが、
その中で、ごく稀にオブジェクトF5が呼び出されることがあります。
オブジェクトF5が呼び出されると、ステージクリアのルーチンをいきなり呼び出します。
ボンバーマンステージでは、ディレイオブジェクト呼び出しの際、
$2006+XというPPUが使う特殊なメモリ領域を参照します。
この領域はPPUが使用するため、読み込み、書きこみのたびに激しく変動する領域です。
そのため、エミュレータによってはこのテクニックを再現できないことが発覚。
ディレイオブジェクトテクは通常だと、非常に再現が困難なため、
ディレイオブジェクト状態を意図的に再現するハックロムを作成し、
各種エミュレータでテストしたところ、下記のような結果となった。
Nestopia、Nintendulator(通常モード)、FCUEX2.1.3(OldPPU) :ディレイステージクリア可能
Nintendulator(デバッグモード)、FCEUX2.1.3(NewPPU):ディレイステージクリア不可
この結果は混乱をもたらしましたが、FinalFighterがテスト用にディレイ状態を意図的に再現するハックロムを作成し、
Inzult氏が協力してくれて、PowerPak(SDカードからROMを読み込めるカートリッジ)を使って実機でテストしたところ、
FCEUX(OldPPU)の結果と一致、ディレイステージクリアはボンバーマンステージでも可能ということになりました。
事前にドロップアイテムを増やし、ディレイ量が「1」が出やすい状態に調整し、
BOTをひたすら実行、成功例を得た。
(また、敵が多いとオブジェクト10や20ばかり出て成功しないので敵を減らしている)
オブジェクト別出現数、出現率グラフ (ダウンロード)。オブジェクトF5はほとんど出ないことがわかる。
3:ワイリーステージ1面 ディレイステージクリア(Object75) 1298595追記
このステージでは該当の箇所で、
00〜FFまでのすべてのオブジェクトを出現させることができます。
ドロップアイテムが出ているときに限り、オブジェクト75が出ることがあります。
オブジェクト75が呼び出されると、オブジェクトF5と同様に、ステージクリアのルーチンをいきなり呼び出します。
オブジェクト75を出し、かつソフトウェアリセットが生じるオブジェクトが出現していない状態であれば、成功となります。
Shinryuu氏と共同でBOTをひたすら実行し、成功。
オブジェクト別出現数、出現率グラフ (ダウンロード)。オブジェクト75はそこそこ出るが、リセットが出ないパターンを得るのが難しい。
4:ワイリーステージ2面 ディレイステージクリア(Object5D→75ルーチン実行→ディレイステージクリア) 1467496追記
このステージでは該当の箇所でディレイオブジェクトテクを使うとオブジェクト5Dが出現することがあります。
オブジェクト5Dが出現すると、マグネットビームのルーチンが途中から呼び出されることで、$610〜$6FFあたりのメモリを壊します。
このとき、$608または$618に入っている値が、$6A8にコピーされることがあります。
$6A8は敵のルーチン実行に使われるメモリで、ここに0x75が入ると、ディレイステージクリアのルーチンが実行されます。
つまり、$618が0x75が出やすいように調整($618は9番目の敵のY座標)し、あとはBOTでひたすら実行し、成功例を得ました。
また、オブジェクト5Dの出現直後に、セレクトを押すことで、オブジェクト75のステージクリアルーチンを先に実行させ、
メモリ破壊によるソフトリセットを避けています。
また、オブジェクト5Dによって、$6A0に0xF7が入ってしまうことがあり、
この場合はワイリーステージ3面の冒頭でロックマンが出現時に247フレーム固まってしまいます。
多くのクリア例を得ましたが、こうならない例を出すのが非常に大変でした。
オブジェクト別出現数、出現率グラフ (ダウンロード)。オブジェクト5Dはかなり出るが、座標を合わせてかつ、リセットを避けるのが難しい。
・ディレオブジェクトテクの解説
さて、ディレイオブジェクトテクはなぜ生じるのでしょうか?
調べてみた結果がこの図です。
通常の処理はバンク6に切り替わった後に、敵を読み込みます。
しかし、ChangeBankの直前(1命令前)にNMIが発生すると、バンク5のデータから、敵データを読み込もうとします。
その結果、ディレイオブジェクトテクは生じていたわけです。
ロックマン2だと、9命令分余裕があるので、ボタンを押せば処理量を若干変化させて、
範囲内に収めることはある程度可能なのですが、
ロックマン1の場合は、幅が1命令分しか余裕がない上、
出現するオブジェクトが変動する場所が多いため、
ロックマン2のようなボタンを押す最適化調整法はほとんど効きません。
(※アイスマンステージ冒頭は出現オブジェクトが固定のため効きます)
そのため、現状はある程度BOTに頼って良い例を出すしかないと思います。
ディレイオブジェクトテク解析結果
----------------------
LoadEnemies (敵読み込み処理)
LoadEnemyNumber (敵の番号決定処理)
0001D99A: BD 52 A4
lda EnemyDataPointers+0,x (敵情報を指し示す情報)
X=#$02,$A454=#$8D,A=#$8D
Bank5($A454=#$8D)
and 6($A454=#$29) changed by one frame in this part.
The value of
$A454=#$29 is read usually.
But,the
data of Bank5($A454=#$8D) is uncommonly read (by NMI).
0001D99D: 65 06 adc CurrentRoomPointer
A=A+$06=99 $06=$0C
0001D99F: 85 06 sta CurrentRoomPointer
$06=99 $07=85
Then,CurrentRoomPointer=($06)=#$8599!
(ここで現在の部屋の敵情報が間違う)
-----------------------
LoadEnemies_Forward (前側の敵ロード)
0001D8F0: B1 06 lda (CurrentRoomPointer),y ;load
enemy number
$06=#$8599 Y=#$03
$859C=#$2F
There is #$2F in
$859C in the icemen stage.
0001D8F5: 20 AD D9
jsr SpawnObject
A=#$2F
-----------------------
SpawnObject (オブジェクト出現ルーチン)
0001D9AD: 85 02 sta $02
$02=#$2F
0001D9E5: A5 02 lda $02 ;object
type
0001D9E7: 9D E0 06 sta ObjectType,x
06F1=#$2F
The enemy of
Object2F was generated.
-----------------------
DoEnemyAI (敵のAIが実行される)
$AA29> BD E006: LDA ObjectType,X
AI of Object2F is
executed.
-----------------------
AI_Object2F (オブジェクト2FのAIが実行される)
$BA79> A9 30:
LDA #$30
The water current is
generated by AI of Object2F.
-----------------------
・オブジェクトの敵AIの解説
敵のAIの実行アドレスは通常出現する
0x00〜0x3Aの範囲のオブジェクトに対して設定されている。
敵のAIルーチン実行
↓
オブジェクト番号からジャンプ先を算出
↓
AI実行
といった具合だ。
しかし、ディレイオブジェクトテクを使うと、それ以外の範囲のオブジェクトが出現することがある。
それにより、いろいろと問題が起きるのである。
たとえば、オブジェクトF5が出現すると、
敵のAIルーチン実行
↓
オブジェクト番号F5からジャンプ先が「C0BD」に決定される
↓
C0BDはステージクリア処理の途中のアドレスであり、いきなりステージクリアとなる。
敵のAIルーチン解析結果。
DoEnemyAI (敵ののAI実行ルーチン)
$AA29> BD E006: LDA ObjectType,X
A=#$4C
$AA2D> A8: TAY
Y=#$4C
$AA2E> B9 3BAA: LDA EnemyAIaddr+0,Y
A=AAD1+4C=#$06
$AA31> 85 04: STA $04
$04=#$06
$AA33> B9 3CAA: LDA EnemyAIaddr+1,Y
A=AAD2+4C=#$D0
$AA36> 85 05: STA $05
$05=#$D0
$AA38> 6C 0400: JMP ($0004)
JMP (D006)
オブジェクト別のジャンプ先解析結果とかはこちら↓
http://www.yuko2ch.net/rockman/JumpAddressList.txt
・ディレオブジェクトテク支援LuaScript集
*rm1delay.lua (ダウンロード)
NEXT=1に調整すると、ディレイオブジェクトテクニックが成功します。
このスクリプトでは、ディレイ量が1付近に近付くかどうかテストするのに使うのが主な用途です。
X:X(XSpeed)
Y:Y(YSpeed)
PRE:NMI終了から、ChangeBankまでの処理数
NEXT:ChangeBankからNMI開始までの処理数。この値を1に調整すると、ディレイオブジェクトテクが成功する。
ENMYBNK:テクが成功したときに読み込まれるバンク番号
CRNTBNK:通常時に読み込まれるバンク番号
*rm1delaytest.lua (ダウンロード)
セレクトを2回押すことで、ディレイオブジェクトテクと同様のことが起きたことを再現するLuaScript。
ディレイ量が1に届く箇所なら、このスクリプトで出来たことは、可能ということです。下記のようないろいろな例が起きます。
アイスマンステージで水流
敵が急に増える
いきなりエレキマンが出現する
ソフトウェアリセットがかかる
ロックマンが死んだら、復活地点がボス前になったりする
激しいグラフィックバグがかかる
ロックマンが謎のキャラに変身して壁抜けできるようになる などなど
*rm1delayobject_rev2.lua (ダウンロード)
このLuaScriptは、毎フレーム、バンク番号を5に間違えてみて、
そのフレームでどんな間違ったオブジェクトが出現するかを調べるLuaScriptです。(微妙に不安定)
セーブロードは、Sキー、Lキーを使ってください。
このLuaScriptはそのステージでどんなオブジェクトが出現するかをだいたい把握するのに使われました。
PRE:number of instructions of
"NMIEnds->ChangeBank"
NEXT:number of instructions of "ChangeBank-> NMIStarts"
We should adjust this value to "1"
NOWBANK:Bank number of the frame.
"5" is a frame for the investigation.
"6" is a frame for normal game.
The NOWBANK changes like
5.5.6.5.5.6.5.5.6....
Bank6 frame is only recorded in movie.
[6F0]..:ObjectNumbers. It is high of the possibility
appearing in the same action and the frame.
2F,AF:object of DelayWaterCurrent
5D,DD:objcet of DelayWarpDeatch
75,F5:object of DelayStageClear
*rm1delayobject_rev3.lua(ダウンロード)
rm1delayobject_rev2.luaが重かったので、(1フレーム分実行して結果を得て戻りながら進むため)、
ロックマン1の処理をLuaに移植してみたもののさすがに
ボンバーマンステージの再現に必要な、PPUのI/Oポートのエミュレートは
手間がかかりすぎるので途中で書くのをやめたLuaScript。
(一応ボンバーマンステージ以外ではrev2同等に使えると思います)
*delayclear.lua(ダウンロード)
ボンバーマンステージのディレイステージクリアに使われたLuaScriptBOT。
サンダービームやジャンプの確率調整等が可能になってます。
サンダービーム発射後はなるべく左を向くようになってます。(ボンバーマンステージでは左を向いているときに、ディレイステージクリアが生じるため)
また、出現したオブジェクトの番号の出力機能付き。
* delayclear2.lua(ダウンロード)
ワイリーステージ1のディレイステージクリアに使われたLuaScriptBOT。
サンダービームやジャンプの確率調整等が可能になってます。
右左は気にしなくなってます。
また、出現したオブジェクトの番号の出力機能付き。
*delayclearw2.lua(ダウンロード)
ワイリーステージ2のディレイステージクリアに使われたLuaScriptBOT。
オブジェクト5Dが出現かつ、$618=0x75(9番目の敵のY座標が0x75)でかつ、
リセットがかかるオブジェクトCF、EFが同時出現していないときに停止するようになってます。
あとは、その例が出たら粘ってみるとディレイステージクリアが成功することがあります。
(大半はソフトウェアリセットだったり次ステージで岩男が長時間固まったりするんですけどね。)
また、出現したオブジェクトの番号の出力機能付き。
*rockman_beams.lua(ダウンロード) 要LuaGD
動画作成の際に使われたLuaScript。
マグネットビームの点滅を防いで見やすくする。
・ディレオブジェクトテク支援ハックロム
ディレイオブジェクトテクは実機やデバッガに対応していないエミュで
起こすのはかなりむずいので、TestLuaScriptと同等のセレクト2回で
ディレイオブジェクト状態を再現できるようにしたハックロム。
このハックロムとPowerPakにより、ボンバーマンステージで実機でディレイステージクリアが可能ということが確かめられた。
*ipsパッチ(ダウンロード)
Default Rockman(J):
A9 06 8D 06 C0 85 12 A5 8F F0 03 20 02 DB
lda #$06
sta $C006
sta CurrentBank
lda ZigZagFireStatus
beq +
jsr UpdateZigZagFire
Patch HackROM:
A5 16 A0 06 29 10 F0 02 A0 05 8C 06 C0 EA
lda $16
ldy #$06
and #$04
beq +
ldy #$05
+
sty $C006
nop