基礎知識
戦闘関係
敵ターンにおける行動選択において、使用技判定時に、回復技が選択されたパターンについて、このページで説明する。
$C1/9258で技データ13をロードしてから、反撃専用技ではないという判定後、$C1/9264~が回復技の判定の分岐になる。
;周囲攻撃、反撃専用、回復判定 $C1/9258 LDA $D5000D,x ;Aに[$D5000D,x]をロード $C1/925C BIT #$40 ;Aと$40で論理積(ステータスフラグ変更のみ) $C1/925E BNE $5B [$92BB] ;ゼロフラグが立っていないとき[$92BB]分岐 ;ゼロフラグON 反撃専用技ではない $C1/9260 BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/9262 BEQ $5A [$92BE] ;ゼロフラグが立っているとき[$92BE]分岐 ;ゼロフラグON 回復技である $C1/9264 LDX $AC [$00:03AC] ;Xに[$00:03AC]をロード $C1/9266 LDA $0000,x[$00:1B50] ;Aに[$00:1B50](敵ID)をロード $C1/9269 CMP #$D7 ;Aと$D7を減算比較(ステータスレジスタ変更のみ) $C1/926B BNE $11 [$927E] ;ゼロフラグが立っていないとき[$927E]分岐 ;ゼロフラグON 敵ID$D7はスタビライザー $C1/926D JSR $927E [$C1:927E] ;[$C1:927E]へジャンプ
$C1/9264からが回復技用の処理になるのだが、まずやっているのが、敵IDの判断である。
$C1/9266~$C1/926Bは、敵IDが$D7かどうかの判定である。
敵ID$D7は、SF編ラストバトルに登場するスタビライザーであり、ここでスタビライザーかどうかで専用の分岐が生じることになる。
ここで、大まかな処理手順を見るため、$C1/926B~の処理を追ってみよう。
途中のジャンプ処理などは省略する。
$C1/926B BNE $11 [$927E] ;ゼロフラグが立っていないとき[$927E]分岐 ;ゼロフラグON 敵ID$D7,スタビライザー $C1/926D JSR $927E [$C1:927E] ;[$C1:927E]へジャンプ ;★$C1:927E(回復技使用判定)へのジャンプ1回目 $C1/9270 BEQ $01 [$9273] ;ゼロフラグが立っているとき[$9273]分岐 ;ゼロフラグOFF $C1/9272 RTS ;サブルーチン戻り ;ゼロフラグON $C1/9273 PHX ;Xをスタックにプッシュ $C1/9274 LDX #$1B00 ;Xに$1B00をロード $C1/9277 JSR $927E [$C1:927E] ;[$C1:927E]へジャンプ ;★$C1:927E(回復技使用判定)へのジャンプ2回目 $C1/927A PLX ;Xレジスタにスタックからプル $C1/927B AND #$FF ;Aと$FFで論理積 $C1/927D RTS ;サブルーチン戻り ;ゼロフラグOFF スタビライザー以外の敵 $C1/927E PHY ;Yをスタックへプッシュ $C1/927F LDY $0002,x ;Yに[$0002,x]をロード $C1/9282 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/9284 LDA $7E8F00,x ;Aに[$7E8F00,x]をロード $C1/9288 STA $4204 [$00:4204] ;Aを[$00:4204]に書き込み $C1/928B LDA $0010,y ;Aに[$0010,y]をロード $C1/928E PLY ;Yレジスタにスタックからプル $C1/928F ASL A ;Aを算術左シフト *2 $C1/9290 ASL A ;Aを算術左シフト *2 $C1/9291 ASL A ;Aを算術左シフト *2 $C1/9292 ASL A ;Aを算術左シフト *2 $C1/9293 BNE $03 [$9298] ;ゼロフラグが立っていないとき[$9298]分岐 $C1/9295 LDA #$0001 ;Aに$0001をロード $C1/9298 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/929A STA $4206 [$00:4206] ;Aを[$00:4206]に書き込み $C1/929D JSR $46C8 [$C1:46C8] ;[$C1:46C8]へジャンプ ;($C1/46C8~$C1/46C9 計算時間稼ぎ用ジャンプ) $C1/92A0 LDA $4214 [$00:4214] ;Aに[$00:4214]をロード $C1/92A3 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/92A5 LDA #$1F ;Aに$1Fをロード $C1/92A7 SEC ;キャリーフラグON $C1/92A8 SBC $10 [$00:0310] ;A - [$00:0310] $C1/92AA STA $4202 [$00:4202] ;Aを[$00:4202]に書き込み $C1/92AD JSR $6150 [$C1:6150] ;[$C1:6150]へジャンプ ;戦闘乱数計算 $C1/92B0 STA $4203 [$00:4203] ;Aを[$00:4203]に書き込み $C1/92B3 JSR $46C9 [$C1:46C9] ;[$C1:46C9]へジャンプ ;($C1/46C9 計算時間稼ぎ用ジャンプ) $C1/92B6 LDA $4217 [$00:4217] ;Aに[$00:4217]をロード $C1/92B9 BEQ $03 [$92BE] ;ゼロフラグが立っているとき[$92BE]分岐 ;ゼロフラグOFF $C1/92BB LDA #$00 ;Aに$00をロード $C1/92BD RTS ;サブルーチン戻り ;ゼロフラグON $C1/92BE LDX $AC [$00:03AC] ;Xに[$00:03AC]をロード $C1/92C0 LDA #$FF ;Aに$FFをロード $C1/92C2 RTS ;サブルーチン戻り
途中のジャンプ先の処理は端折ると、スタビライザー以外の敵は、$C1/927E~の処理をして$C1/92B9まで進み、この時点でゼロフラグが立っているかどうかで、サブルーチンの戻り位置が$C1/92BDと$C1/92C2のどちらかになる。
後で処理を見るとわかるが、$C1/92BDだと技使用見送り(Aに$00を入れて戻ってくる)、$C1/92C2だと技使用確定(Aに$FFを入れて戻ってくる)となる。
$C1/927E~$C1/92C2が、回復技使用判定ということである。
ということを踏まえて、$C1/926D~のスタビライザー用処理を見てみると、
$C1/926B BNE $11 [$927E] ;ゼロフラグが立っていないとき[$927E]分岐 ;ゼロフラグON 敵ID$D7,スタビライザー $C1/926D JSR $927E [$C1:927E] ;[$C1:927E]へジャンプ ;★$C1:927E(回復技使用判定)へのジャンプ1回目 $C1/9270 BEQ $01 [$9273] ;ゼロフラグが立っているとき[$9273]分岐 ;ゼロフラグOFF $C1/9272 RTS ;サブルーチン戻り ;ゼロフラグON $C1/9273 PHX ;Xをスタックにプッシュ $C1/9274 LDX #$1B00 ;Xに$1B00をロード(敵番号0、マザーCOMのアドレス呼び出し用) $C1/9277 JSR $927E [$C1:927E] ;[$C1:927E]へジャンプ ;★$C1:927E(回復技使用判定)へのジャンプ2回目 $C1/927A PLX ;Xレジスタにスタックからプル $C1/927B AND #$FF ;Aと$FFで論理積 $C1/927D RTS ;サブルーチン戻り
$C1/926Dで、$C1/927E~$C1/92C2の回復技使用判定に進む。
$C1/9270で戻ってきたらゼロフラグ判定をしており、ゼロフラグOFFは技使用確定でサブルーチンを抜ける。
ゼロフラグONは技使用見送りになるのだが、そうなると$C1/9273に進み、Xに$1B00をロードしてから、$C1/9277で再び$C1/927E~$C1/92C2の回復技使用判定に進んでいるのである。
つまり、スタビライザーだけ、回復技を使用する判定に進んだ時、
というように、使用判定を2回行っていることがわかる。
スタビライザーは使用技1~4がすべて「システムリカバー」だから、上処理は実質、スタビライザーが通常攻撃として「システムリカバー」を使う場合の専用処理である。
スタビライザーとマザーCOMが隣接しているかどうか、といった位置関係は関係なく、必ず2回判定である。
では、$C1/927E~$C1/92C2の回復技使用判定の処理について、詳しく見てみる。
;ゼロフラグOFF $C1/927E PHY ;Yをスタックへプッシュ $C1/927F LDY $0002,x ;Yに[$0002,x]をロード $C1/9282 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/9284 LDA $7E8F00,x ;Aに[$7E:AAn1][$7E:AAn0](現在HP)をロード $C1/9288 STA $4204 [$00:4204] ;Aを[$00:4205][$00:4204]に書き込み $C1/928B LDA $0010,y ;Aに[$00:1A11+$40n][$00:1A10+$40n](最大HP)をロード $C1/928E PLY ;Yレジスタにスタックからプル $C1/928F ASL A ;Aを算術左シフト *2 $C1/9290 ASL A ;Aを算術左シフト *2 $C1/9291 ASL A ;Aを算術左シフト *2 $C1/9292 ASL A ;Aを算術左シフト *2 $C1/9293 BNE $03 [$9298] ;ゼロフラグが立っていないとき[$9298]分岐 ;ゼロフラグON(最大HP * $10が$0000) $C1/9295 LDA #$0001 ;Aに$0001をロード ;ゼロフラグOFF(最大HP * $10が$0000ではない) $C1/9298 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/929A STA $4206 [$00:4206] ;Aを[$00:4206]に書き込み $C1/929D JSR $46C8 [$C1:46C8] ;[$C1:46C8]へジャンプ ; ;符号付16bit / 8bit $C1/46C8 NOP ;計算待ち $C1/46C9 RTS ;サブルーチン戻り ; $C1/92A0 LDA $4214 [$00:4214] ;Aに[$00:4214](商、下1バイト)をロード $C1/92A3 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/92A5 LDA #$1F ;Aに$1F(31)をロード $C1/92A7 SEC ;キャリーフラグON $C1/92A8 SBC $10 [$00:0310] ;A - [$00:0310] $C1/92AA STA $4202 [$00:4202] ;Aを[$00:4202]に書き込み $C1/92AD JSR $6150 [$C1:6150] ;[$C1:6150]へジャンプ ; ;戦闘乱数計算 $C1/6150 PHX ;戦闘乱数計算 ;(省略) $C1/61A4 RTS ;乱数がAと[$00:033B]に入る ; $C1/92B0 STA $4203 [$00:4203] ;Aを[$00:4203]に書き込み $C1/92B3 JSR $46C9 [$C1:46C9] ;[$C1:46C9]へジャンプ ; ;符号付8bit x 8bit $C1/46C9 RTS ;サブルーチン戻り ; $C1/92B6 LDA $4217 [$00:4217] ;Aに[$00:4217](乗算結果上1バイト)をロード $C1/92B9 BEQ $03 [$92BE] ;ゼロフラグが立っているとき[$92BE]分岐 ;ゼロフラグOFF(技使用見送り) $C1/92BB LDA #$00 ;Aに$00をロード $C1/92BD RTS ;サブルーチン戻り ;ゼロフラグON(技使用確定) $C1/92BE LDX $AC [$00:03AC] ;Xに[$00:03AC]をロード $C1/92C0 LDA #$FF ;Aに$FFをロード $C1/92C2 RTS ;サブルーチン戻り
敵の現在HP、最大HP、戦闘乱数から技を使用するかどうかを計算・判定しているのだが、計算式が少々ややこしい。
$C1/9284~$C1/92A0は、符号付16bit / 8bit、つまり割り算である。
$00:4204~$00:4205に被除数2バイト、$00:4206に除数を書き込むと、一定時間後に$00:4214~$00:4215に商2バイト、$00:4216に余りが入る仕組み。
サブルーチンを見ると、
trunc(現在HP / (最大HP * $10の下1バイト))
=trunc(現在HP / ((最大HP * $10) & $00FF))
を計算し、$C1/92A0で[$00:4214](商の下1バイト)をAにロードしている。
ポイントは「最大HP * $10の下1バイト」である。
16進数で最大HPが下1桁が0の時、$10を掛け算すると、2バイト表記の時、下1バイトは必ず$00である。
例えばスタビライザーなら、最大HPは$30なので、2バイトで$0030とした時、$10倍すると$0300である。
「最大HP * $10 & $00FF」が$00になるので、
=trunc(現在HP / ((最大HP * $10) & $00FF))
を計算すると、割り算部分で、
現在HP / $00
いわゆるゼロ除算になってしまう。難しい話は横におくが、「0で割ることはできないので答えが出せない」のである。
(ゼロ除算 - Wikipedia)
ただ、65C816の符号付16bit / 8bitの計算では、こういう場合にエラーが出ないよう、商2バイトは$FFFF、余りは被除数を返すようになっている。
ここから、
16進数で最大HPが下1桁が0の敵は、現在HPに関係なく、「=trunc(現在HP / ((最大HP * $10) & $00FF))」の商として$FFFFが返ってくる
ということがわかる。
よって、$C1/92A0では、[$00:4214]に$FFが入っているから、Aに$FFがロードされる。
さて、敵の最大HPだが、敵キャラデータ04に入っており、数値が$80未満ならそのままHPとするが、$80以上の時は、
実際のHP = (敵キャラデータ04 & $7F) * $10
で計算している。
つまり、HPが$80(10進数128)以上の敵はすべて、16進数で下1桁が0であり、回復技を使う場合、上のゼロ除算のケースが当てはまることになる。
$80未満でも、スタビライザーのように最大HPの下1桁が0の敵なら、もちろん当てはまる。
下1桁が0以外かつ回復技を使う敵ももちろんいる。
回復技「クォ~ン」は、ちびマンモス(HP:$28 = 40)が使うのならHPの下1桁が0ではない。
マンモス♀も「クォ~ン」を使うが、最大HPが480(敵キャラデータ04が$9E)なので、HPの下1桁が0である。
ということで、最大HPの下1桁次第で分岐と見なしてよい。
最大HPの下1桁が0以外なら、$C1/92A0にAにロードされるのは、
=trunc(現在HP / ((最大HP * $10) & $00FF)) & $00FF
最大HPの下1桁が0なら、$FFがロードされる、ということになる。
とりあえずこの値を暫定的にNとしておく。
なお、最大HPの下1桁が0以外の時、Nがとる範囲は$00~$07のはずである。
最大HPが$79以下として、Nが最大になるのは、最大HPの下1桁ができるだけ小さい、1の時になる。
最大HPが$71として考えてみる(ちなみに最大HPが$71の敵はいないのだが、便宜上である)。
HPが満タンの時、
trunc(現在HP / ((最大HP * $10) & $00FF)) & $00FF
= trunc($71 / ($71 * $10) & $00FF ) & $00FF
= ($71 / $10) & $00FF
= $07 & $00FF
= $07
(最大HP * $10) & $00FFの解は$10, $20, ... $90のいずれかであり、最大HPが$00~$71であるから、割り算をするとどうやっても商が$00~$07にしかならない。
よって、Nが取る範囲は、$00~$07または$FFしかない。
先に上げた、ちびマンモス(HP:$28)であれば、
trunc(現在HP / ((最大HP * $10) & $00FF)) & $00FF
= trunc(現在HP($00~$28) / $80) & $00FF
なのだが、trunc(現在HP($00~$28) / $80)は、現在HPが幾つであっても答えは$00で確定である。
現在HPが$80以上の時計算値は1以上の値になるが、最大HPが$28だからありえない。
最大HPの下1桁の値次第で、現在HPは計算には全く関係ない値になってしまうこともある、ということがわかる。
続いて$C1/92A0~だが、$C1/92A8で、キャリーフラグを立てた状態で
$1F - N
を計算している。
先に述べた通り、Nが取る範囲は、$00~$07または$FFである。
よって、
最大HPの下1桁が0以外なら、
$1F - N($00~$07のいずれか)
=$18~$1F
最大HPの下1桁が0なら、
$11F - $FF
=$20
この計算値をMとしておく。
Mと、$C1/92B0でAにロードされた戦闘乱数($00~$FF)を掛け算している(符号付8bit x 8bit)。
$C1/92B6で、乗算結果の上1バイトをロードし、$C1/9270のゼロフラグ判定で、最終的に回復技使用判定となる。
M * 戦闘乱数($00~$FF)
の結果、$0100を越えたら乗算結果の上1バイトは$01以上になりゼロフラグが立たない。
$0100未満なら、ゼロフラグが立つ。
という分岐である。
ここまでの計算で、Mは$18(%0001 1000)~$20(%0010 0000)のいずれかを取ることがわかった。
最大HPの下1桁が0のケースで考えると、
M * 戦闘乱数($00~$FF)
= $20 * 戦闘乱数($00~$FF)
この乗算結果が$0100を越える乱数は$08以上である。
つまり、乱数が$00~$07なら、$C1/9270のゼロフラグ判定でゼロフラグがONになる(技使用確定)。
$08以上なら、ゼロフラグ判定でゼロフラグがOFFになる(技使用見送り)。
ゼロフラグが立つ確率は8/256 = 1/32ということだから、回復技使用確定になる確率はかなり低いことがわかる。
最大HPの下1桁が0以外のケースなら、
M * 戦闘乱数($00~$FF)
= $18~$1F * 戦闘乱数($00~$FF)
Mが$18なら乱数$10以上で、Mが$1Fなら乱数$09以上で、乗算結果が$0100を越える。
要するに、最大HPの下1桁が0以外のケースだと、ゼロフラグが立つ確率は9/256か10/256のどちらか。
結局、最大HPや現在HPがいくつでも、回復技使用確定になる確率は8/256, 9/256, 10/256のいずれかであり、大差ないのである。
計算式からして、HPが減ってきたら(現在HP/最大HPが小さくなってきたら)、回復技使用確率を上げる、という処理のようだが、実際のプレイでどれほど差が出ているかというと謎である。
(差が出にくかったからこそ、スタビライザーは回復技を頻繁に使うように、2回判定にしているのかもしれない)
最後に、$C1/92BDまたは$C1/92C2でサブルーチンを終えた後の続きを見ておく。
$C1/92B9 BEQ $03 [$92BE] ;ゼロフラグが立っているとき[$92BE]分岐 ;ゼロフラグOFF(技使用見送り) $C1/92BB LDA #$00 ;Aに$00をロード $C1/92BD RTS ;サブルーチン戻り ;ゼロフラグON(技使用確定) $C1/92BE LDX $AC [$00:03AC] ;Xに[$00:03AC]をロード $C1/92C0 LDA #$FF ;Aに$FFをロード $C1/92C2 RTS ;サブルーチン戻り $C1/8F23 BEQ $24 [$8F49] ;ゼロフラグが立っているとき[$8F49]分岐 ;ゼロフラグOFF(技使用確定) $C1/8F25 JSR $920C [$C1:920C] ;[$C1:920C]へジャンプ ;ゼロフラグON(技使用見送り、次判定へ) $C1/8F49 LDX $AC [$00:03AC] ;Xに[$00:03AC]をロード
戻り先の$C1/8F23でゼロフラグ判定となり、$C1/92BEの戻りでAに$FFを入れている場合はゼロフラグOFF、回復技使用確定となり、回復技の処理[$C1:920C]にジャンプする。
$C1/92BBの戻りでAに$00を入れている場合はゼロフラグON、攻撃技の時と同じように技使用見送りとなって、次の技判定の$C1/8F49に飛ぶ。
以上が回復技に関する判定である。