じゅうべえくえすとの敵が出る数の解析

メガトンコインRTAという動画でキノコングという敵が1匹でるべきである場面でそれが2匹でる, その原因は5つもバージョン違いだろう. という考察が書かれていました. 5つは多すぎでおかしいと思ったのでそれを調べました.

バージョン違いはあるが...

5個の根拠として[b]とか[h]とかついているファイル名があると書かれておりましたが、これは dump の手順を間違えているファイルが過去の混乱の遺物として残っているだけなので無視します.

かなり信憑性のある nescartdb は1つ登録があります. そこそこ信憑性のある MAME の hash/nes.xml には2つ登録があります.

この2つを見比べてみましたが、本当にバージョン違いなのか判断には困りました. 命令が増えたり減ったりしているのでそこの差分の部分をアセンブルするから命令とaddressが差分点からずれるていくものが普通のバージョン違いです. これは lda abs (3byte) 命令を jsr abs (3byte) 命令 にしていて ROM の末尾に命令を増やしています.
この手法は第3者がパッチを当てる場合は自分もよくやるんですが、その jsr 命令の中身が何かの計算の補正だったのでプログラマ当人かはわかりませんがたぶん当時の関係者が手をいれたものと思われます. *1

どちらとも敵の数は基本2匹、たまに1匹

両方のバージョンでキノコングが1匹でるはずであろうというところを確認しましたが、基本2匹、たまに1匹出るのでバージョン違いの可能性は低いと思います. (その2つ以外のバージョンがあるならべつの可能性はありそうだけど、1度生産して終わりの販売規模のこのソフトにはちょっと考えられない)

1匹でる条件を調べてみた

エンカウントで trace log を取りました. CPU address $6050 が敵の数なのはわかりました.

F969: lda ($fc), y <- 敵のテーブルのポインタらしい
F96B: lsr a
F96C: lsr a
F96D: lsr a
F96E: lsr a
F96F: lsr a
F970: sta $6050 <- bit7:6 を敵の数にしている

(中略)

8987: lda $6050
898A: clc
898B: adc $6051
898E: adc $6052
8991: rts
8940: cmp #$02
8942: bcc $8986 ;敵の数が2以上か
8944: jsr $f195
F195: lda $038e ;乱数生成
F198: clc
F199: adc #$17
F19B: sta $038e
F19E: lda $21 ;frame カウンタらしい
F1A0: clc
F1A1: adc #$b1
F1A3: adc $5000 ;φ2カウンタ bit7:0
F1A6: sta $21
F1A8: lda $5800 ;φ2カウンタ bit15:8
F1AB: eor $22
F1AD: adc $21
F1AF: eor $1f
F1B1: adc $038e
F1B4: sta $21
F1B6: rts
8947: lsr a
8948: bcs $8986 ;乱数 bit0 == 0 なら減らさないので分岐
894A: lsr a
894B: bcs $8986 ;乱数 bit1 == 0 なら減らさないので分岐
894D: lsr a
894E: and #$03
8950: cmp #$03
8952: beq $8986 ;乱数 bit3:2 == 3 なら分岐
8954: tay
8955: lda $6050, y ;敵を減らすなら $6050 で乱数 bit3:2 == 0 でいてほしい
8958: beq $8986 ;それが 0 なら減らさないので分岐
895A: sec
895B: sbc #$01
895D: sta $6050, y ;敵の数を1つ減らす
8960: bne $8986
8986: rts

乱数生成のサブルーチンはミサイルポッドの確率を出してた動画(じゅうべえくえすとの鉄パイプ乱数を観察)でもでてきたものと同じです. PC (program counter) $8955 の y の値がちょっと気になるのですが変数 $6050 は敵の数で, 変数 $6051, $6052 は未調査ですが敵に関するパラメータだと思います. (別種類の敵の数かも)

このログからの考察ですと、下記のポイントとなります.

  • 敵がでてくるときに乱数を引く
  • 乱数の bit3:0 == 0 の場合は敵の数を1減らす
  • 敵の数が1匹減る条件は 1/16 (6.2%)
  • この命令は通常敵が出るときでも実行する(ボスはやらない)
  • どちらのバージョンもここに違いはない

おまけ:ランダムエンカウントをなくす方法

FA1B: cmp $d2 
FA1D: bcs $fa22 
FA1F: inc $04c9 <= これを nop にするとランダムエンカウントがきえる 
FA22: jmp $fe05

cmp $d2 をユーザー入力にしてスタートボタンを押したときだけ敵が出るようにアレンジするとすごく遊びやすくなると思います.

*1:第3者がやるならエミュレータで動くように無理矢理書き換えるとか、ゲームを有利に進める目的なのでたぶん白