基礎知識
戦闘関係
アイテム関係の処理については前回のサモに与えるアイテムでも多少触れたが、せっかく近くの場所なので、最終編の勇者の山でキューブのバッテリーを拾う時の処理についても調べてみた。
勇者の山で拾えるキューブのバッテリーは、最終編主人公がキューブ以外の時に拾えるが、最終編主人公によりアイテム名が異なる。
また、アイテムIDも変わる。
| アイテム名 | アイテムID | 対応主人公 |
|---|---|---|
| 鉄の箱 | $FC | 心山拳師範、サンダウン、おぼろ丸 |
| ???? | $FD | ポゴ |
| なんかの部品 | $FE | 高原日勝 |
| バッテリー | $FF | アキラ |
拾える場所は光るエフェクトが付くが、特定のマスを踏んだ時だけ光るようになっている。
おそらく、特定座標と主人公キャラが居る座標が同一だと光るエフェクトを表示する、というような処理が入っているのだろうが、今回はそちらの調査はしていない。
バッテリーを拾う時には、以下の順にイベントが起こる。
セリフやアイテムの違いは、最終編主人公に対応した値で判別し、最終編用のデータ($7F:8E00~)の対応アドレスを呼び出すことで起きる。
キューブのバッテリー関係は[$7F:A389]~[$7F:A3BC]に値が入っている。
まずは、最終編主人公が誰なのかの判定部分を紹介する。
最終編主人公の値は、$00:0A10($7E:0A10)に入っている。
心山拳師範は、ユン・レイ・サモで共通で$03が入る。
| 主人公ID | 主人公 |
|---|---|
| $00 | オルステッド |
| $01 | ポゴ |
| $02 | キューブ |
| $03 | 心山拳師範 |
| $04 | サンダウン |
| $05 | 高原 日勝 |
| $06 | アキラ |
| $07 | おぼろ丸 |
サブルーチンは以下の通り。
このタイミングでは、全キャラ共通でY:158Eである。
$C0/2C45 LDA $000A10[$00:0A10] ;Aに[$00:0A10](=主人公ID)をロード $C0/2C49 BEQ $15 [$2C60] ;ゼロフラグが立っているとき[$2C60]分岐 $C0/2C4B STY $30 [$00:0030] ;インデックスレジスタY(Y:158E)の値を[$00:0030]に書き込み $C0/2C4D DEC A ;Aをデクリメント(A-1) $C0/2C4E ASL A ;Aを算術左シフト(*2) $C0/2C4F REP #$20 ;Mフラグをクリア Aレジスタは16bit幅 $C0/2C51 AND #$00FF ;Aと#$00FFで論理積 $C0/2C54 CLC ;キャリーフラグクリア $C0/2C55 ADC $30 [$00:0030] ;A+[$00:0030](=$158E) $C0/2C57 TAX ;Aレジスタの値をXレジスタに転送 $C0/2C58 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C0/2C5A LDY $8E00,x ;Yに[$8E00,x]をロード $C0/2C5D BRL $EF41 [$1BA1] ;フラグにかかわりなく常に分岐[$1BA1]
2行目の$C0/2C49でゼロフラグの分岐が起きているが、ゼロフラグが立つのは最終編主人公がオルステッドの時のみ(IDが$00)。
未確認だが、この部分は最終編主人公が誰かを判定するための汎用サブルーチンであり、オルステッドルート用の何かしらの処理がBEQ $15で分岐して飛ぶようにしているのだろう。
ただし今回は、キューブのバッテリーを取得する処理のための分岐のため、ゼロフラグが立つことはない。
オルステッド以外の主人公はそのまま処理が続く。
最後から2行目のLDY [$8E00,x]は、Xの値によって呼び出すアドレスが変わる。
Xは$C0/2C57でAレジスタの値をXレジスタに転送している。
ここでAに入っている値は、$C0/2C4D~以降の計算から、
(主人公ID - 1)*2 & $00FF + $158E
である。つまり主人公IDによって、$C0/2C5Aで呼び出す値が変わる。
また、Yに[$8E00,x]をロードすることになるが、この先はYの値によって呼び出す値やジャンプ先アドレスなども変動する。
キャラ毎の[$8E00,x]の値及び、そのアドレスに入っている数値は以下の通り。
| キャラ | [$8E00,x] | 数値 |
|---|---|---|
| ポゴ | [$7F:A38E] | $9C |
| 心山拳師範 | [$7F:A392] | $A3 |
| サンダウン | [$7F:A394] | $A3 |
| 高原 日勝 | [$7F:A396] | $AA |
| アキラ | [$7F:A398] | $B1 |
| おぼろ丸 | [$7F:A39A] | $A3 |
この時点で、心山拳師範、サンダウン、おぼろ丸は同じ値が呼び出されていることがわかる。
他キャラは別の値が呼び出されており、これが最終的にアイテム名の違いになる。
上アドレスと数値から、各アイテム名を呼び出すのが以下サブルーチンである。
$C0/227D LDA $8E00,y ;Aに[$8E00,y]をロード $C0/2280 STA $00013D[$00:013D] ;Aに[$00:013D]を書き込み
ここのLDA $8E00,yだが、呼び出し先にはアイテムIDが入っている。
この時点でのYは、心山拳師範、サンダウン、おぼろ丸だとY:15A6、ポゴはY:159F、高原はY:15AD、アキラはY:15B4とYの値が異なる。
| キャラ | [$8E00,x] | 数値 |
|---|---|---|
| ポゴ | [$7F:A39F] | $FD |
| 心山拳師範 | [$7F:A3A6] | $FC |
| サンダウン | [$7F:A3A6] | $FC |
| 高原 日勝 | [$7F:A3AD] | $FE |
| アキラ | [$7F:A3B4] | $FF |
| おぼろ丸 | [$7F:A3A6] | $FC |
ここで、最初に載せた表をもう一度載せる。
| アイテム名 | アイテムID | 対応主人公 |
|---|---|---|
| 鉄の箱 | $FC | 心山拳師範、サンダウン、おぼろ丸 |
| ???? | $FD | ポゴ |
| なんかの部品 | $FE | 高原日勝 |
| バッテリー | $FF | アキラ |
この通り、対応したアイテムIDが[$00:013D]に入ったことがわかる。
これで取得アイテムが主人公毎に決定した。
ついでに、取得アイテムを持ち物欄の空欄に入るサブルーチンも紹介する。
最終編における持ち物欄のアイテムID一覧は$7E:0B01~$7E:0BFFに、持ち物欄のアイテム個数は$7E:0C01~$7E:0CFFに入っている。
手順としては、持ち物欄のアイテムIDを、$7E:0B01~$7E:0BFF全てのアドレス内の数値と[$00:013D]を比較して、同一アドレスがあるかどうかをループ処理でチェックする。
同一アイテムがあるなら該当アイテムの個数をチェックして、アイテム数を+1する。
同一アイテムがないのなら、アイテム個数$7E:0C01~$7E:0CFFを最初からチェックし、最初に個数が$00の空欄に[$00:013D]のアイテムIDとアイテム個数を書き込む。
今回は、キューブのバッテリーという1個しか手に入らないイベントアイテムであるから、持ち物欄に同一アイテムは存在しない。
よって空欄位置に、該当アイテムを設置するサブルーチンが動くことになる。
おそらく、このサブルーチンが、何かしらのアイテムを入手した時に使われる汎用サブルーチンなのだろう。
$C0/47E8 LDA #$00 ;Aに$00をロード $C0/47EA PHA ;Aレジスタの値をスタックにプッシュ $C0/47EB PLB ;DBレジスタにスタックから値をプル $C0/47EC PHX ;Xレジスタの値をスタックにプッシュ $C0/47ED PHY ;Yレジスタの値をスタックにプッシュ ; ;持ち物欄のアイテムIDチェック ループ処理 X:0B01 Y:0C01から開始 $C0/47EE LDA $00,x ;Aに[$00:0B??]をロード(?? = $01~$FF) $C0/47F0 CMP $013D [$00:013D] ;Aと[$00:013D](アイテムID)を減算比較 $C0/47F3 BEQ $1C [$4811] ;ゼロフラグが立っているとき[$4811]分岐(同一アイテムがある時の分岐) $C0/47F5 INX ;Xをインクリメント(+1) $C0/47F6 INY ;Yをインクリメント(+1) $C0/47F7 CPX #$0C00 ;Xレジスタと$0C00を減算比較 $C0/47FA BNE $F2 [$47EE] ;ゼロフラグが立っていないとき[$47EE]分岐(ループ頭に戻る) ;ループ終了(最大でX:0B01がX:0C00になるまで全アイテム欄チェック) ; ;持ち物欄に同一アイテムIDがなかった時の処理 $C0/47FC PLY ;Yレジスタにスタックから値をプル $C0/47FD PLX ;Xレジスタにスタックから値をプル ; ;持ち物欄のアイテム個数 ループ処理 X:0B01 Y:0C01から開始 $C0/47FE LDA $0000,y ;Aに[$00:0C??]をロード(?? = $01~$FF) $C0/4801 BEQ $1D [$4820] ;ゼロフラグが立っているとき[$4820]分岐(空欄があった時の分岐) $C0/4803 INX ;Xをインクリメント(+1) $C0/4804 INY ;Yをインクリメント(+1) $C0/4805 CPX #$0C00 ;Xレジスタと$0C00を減算比較 $C0/4808 BNE $F4 [$47FE] ;ゼロフラグが立っていないとき[$47FE]分岐(ループ頭に戻る) ;ループ終了(最大でX:0B01がX:0C00になるまで全アイテム欄チェック) ; ;持ち物欄に同一アイテムIDがない かつ 持ち物欄に空欄があった時の処理 $C0/4820 LDA $013D [$00:013D] ;Aに[$00:013D](アイテムID)をロード $C0/4823 STA $00,x ;Aを[$00,x](アイテム欄の最初の空欄[$00:0B??])に書き込み $C0/4825 LDA #$01 ;Aに$01をロード $C0/4827 STA $0000,y ;Aを[$0000,y](アイテム欄の最初の空欄の個数[$00:0C??])に書き込み $C0/482A PLB ;DBレジスタにスタックから値をプル $C0/482B CLC ;キャリーフラグクリア $C0/482C RTS ;サブルーチン戻り
最初のループ処理では、取得したアイテムと同一のIDのアイテムを既に所持しているか、持ち物欄の枠をすべてチェックしている。
今回は初入手のアイテムなので、このループは全て抜けて次の処理に進む。
次のループ処理は、持ち物欄の最初の空欄のチェックである。つまり所持個数が$00の枠を探している。
見つけた枠に、アイテムIDと、個数の$01を書き込んで処理終了している。
もし、既に所持しているアイテムを更にもうひとつ入手する場合だったら、最初のループで$C0/4811に飛ぶことになるのだが、その時の処理は以下の通りである。
$C0/4811 LDA $0000,y ;Aに[$00:0C??](該当アイテムの所持数)をロード $C0/4814 CMP #$63 ;$63(10進数99)と減算比較 $C0/4816 BCS $F4 [$480C] ;キャリーフラグが立っているとき[$C0/481C]へ分岐 $C0/4818 INC A ;Aをインクリメント(+1) $C0/4819 STA $0000,y ;Aを[$00:0C??]に書き込み $C0/481C PLY ;Yレジスタにスタックから値をプル $C0/481D PLX ;Xレジスタにスタックから値をプル $C0/481E BRA $0A [$482A] ;[$C0/482A]へ
所持しているアイテムの個数と$63(10進数99)を比較している。
つまり、入手したアイテムの個数が既に99かどうかのチェックである。
99未満なら$C0/4818で所持数を+1し、新たな所持数としてセットする。
99個所持している場合、所持数には何の処理もしない。
処理を終えたら$C0/482Aに合流して、サブルーチン終了となる。
$C0/482Bでキャリーフラグをクリアしていたのは、上の部分でキャリーフラグが立つ場合があり得るためだろう。
サモにアイテムを与えた時、つまり所持アイテムの中のアイテム個数を-1するサブルーチンは、$C0/4839~$C0/4853と、$C0/482Cの直後にある。
$C0/7D2B AD CE 0A LDA $0ACE [$00:0ACE] A:0003 X:0000 Y:0000 P:envMxdIzC $C0/7D2E C9 08 CMP #$08 A:0008 X:0000 Y:0000 P:envMxdIzC $C0/7D30 F0 32 BEQ $32 [$7D64] A:0008 X:0000 Y:0000 P:envMxdIZC $C0/7D64 DA PHX A:0008 X:0000 Y:0000 P:envMxdIZC $C0/7D65 A9 00 LDA #$00 A:0008 X:0000 Y:0000 P:envMxdIZC $C0/7D67 EB XBA A:0000 X:0000 Y:0000 P:envMxdIZC $C0/7D68 A5 26 LDA $26 [$00:0026] A:0000 X:0000 Y:0000 P:envMxdIZC $C0/7D6A AA TAX A:0000 X:0000 Y:0000 P:envMxdIZC $C0/7D6B LDA $0A10,x[$00:0A10] ;Aに[$00:0A10](=主人公ID)をロード $C0/7D6E PLX ;Xレジスタにスタックから値をプル $C0/7D6F CMP #$04 ; $C0/7D71 BCC $2B [$7D9E] ;