FF2 を調べてみたことを書いてみるその3

たまに書かないとなにをやっているかわからなくなってくるので書いてみる。

stack $01fa-$01ff の破壊原因

前回まで、発生原因は魔法の詠唱だと思っていたものは誤りで単なる攻撃が発生原因だった。前回発生した現象は、魔法の詠唱を選択したものの、先に攻撃の計算を行って、そのときに破壊したと考えるのが無難である。

ポイントは下記。

  • 発生頻度は割と高く、1/2ぐらい。
  • スタックを破壊してもプログラムは正常動作を続ける
  • rts 実行後の復帰 PC address に相当する $01fb,$01fc に書き込まれるデータは闇雲に眺めると $00-$0f,$3a が多い
  • この stack の下を壊すバグを利用することにして言えば、ベクタ $0100 の命令実行は邪魔

trace 調査

実行 trace に SP と scanline の表示をするとかなり理解しやすくなったので、魔法詠唱と攻撃の動作を追ってみた。共に再帰呼び出しをして、サブルーチンの中で同じサブルーチンを呼んで stack を高く積んでいることがわかった。

魔法詠唱の場合、再帰呼び出しをするサブルーチンの中での実行時間が長く、ベクタ命令部の $0100,$0101,$0102 を破壊した時点で、 NMI が発生してプログラムが暴走する。

攻撃の場合は、命令内容が魔法と比べてシンプル。$0100 を突き抜け、 $01fa を破壊するし場合によっては $01f5 まで破壊するが、 NMI 発生前に計算が終わる。 NMI 発生待ちに入る直前に $0100,$0101,$0102 を書き直すのでプログラムが動作し続ける。

攻撃時の再帰呼び出しのたびに stack を積む数が 5 なので、$01fa より先を壊しても、効果はない。これはフロア移動時の stack を積む数も 5 なので、破壊できる部分の先頭は移動時の X 座標になる。*1

ここまではわかったが、調査が雑なのは相変わらずで、攻撃元と対象がよくわかってない。プレイヤーキャラが発生原因ならパラメータを調整しやすいわけだが、敵が発生原因なら敵の選定もディストの洞窟内部だけで絞り込まないといけない。(魔法を唱える敵は暴走要因なので省くべきだし...)

またこの再帰サブルーチンがなにをやっているのがよくわかっていない。自分が攻撃の計算を知らないのと、抽象化されているのがよくわからない。

8F5E:
	pha
	sec
	sbc $02
	tax
	inx
	stx $00
	ldy #$01
	ldx #$00
8F6A:
	txa
	sta ($06),y
	iny
	inx
	cpx $00
	bne $8f6a
	pla
8F74: <= recursive call label
	sta $03
	lda $00
	pha ;-> PC bit15:8
	lda $01
	pha ;-> PC bit7:0
	txa
	pha ;-> floor offset X
	ldx $02
	stx $00
	ldy $03
	sty $01
(中略)
	dec $03
	inc $02
	bcs $8f8c
8FD8:
	ldx $02
	cpy $00
	beq $8fe8
	bcc $8fe8
	lda $00
	sta $02
	tya
	jsr $8f74 ; -> recursive call
8FE8:
	cpx $01
	bcs $8ff3 ; -> resursive end condition
	stx $02
	lda $01
	jsr $8f74 ; -> recursive call
8FF3:
	pla
	tax
	pla
	sta $01
	pla
	sta $00
	rts

*1:さらにもう1周 $0100 を越えて壊せばずれるが、それをやっている途中に別の不具合が起こるだろう