TOP > プログラミング関係解説&調査 > 副能力変動値 > サブルーチン

副能力変動値(その2・サブルーチン)

当ページの内容は、ある程度のプログラミングの知識を必要とする。
まず、プログラミング関係解説&調査をひととおり読んでいただきたい。

前ページの続き。
このページでは、実際に、ゲーム内の装備画面でどんなサブルーチンが処理されて、「副能力変動」が計算されているのかを紹介する。

メニュー画面から「装備」を選ぶと、各キャラのステータス表示なども含めた装備画面が表示される。
装備箇所でAボタンを押すと、現在所持している装備品の中で、該当の装備位置に装備可能な装備品が右下ウィンドウに並ぶが、装備を変更せずとも、装備品にカーソルが移動した時点で、画面左下には装備による変動値が表示されるようになっている。
本作をプレイしている方ならよくご存知かと思うが、こんな感じである。一番右の列が変動値になる。

攻撃  16 16
防御   0  2
力 21 ±0 +4
速 23 ±0 ±0
体 20 ±0 ±0
知 36 ±0 ±0

つまり、実際に装備を変更するよりも前、この時点で既に、「副能力変動」の値が計算されている。

ここで、メニュー画面関連のメモリアドレスを紹介する。
メニュー→装備画面を開くと、該当キャラについて、$7E:2A00$7E:2A3Fに該当キャラのシナリオ別キャラデータがそのまま入る($23のみ例外、異なる値が入る)。
さらに、装備変更を行おうとしている時、つまり上のように変動値が出ている時、以下のようにメモリアドレスに値が入る。

アドレス数値
$7E:2A46装備変更時 現在装備中のアイテムID
$7E:2A47装備変更時 装備品一覧のカーソルが示すアイテムID
$7E:2A51~52装備変更時 装備変更による力変動値(符号付き16ビット整数)
$7E:2A53~54装備変更時 装備変更による速変動値(符号付き16ビット整数)
$7E:2A55~56装備変更時 装備変更による体変動値(符号付き16ビット整数)
$7E:2A57~58装備変更時 装備変更による知変動値(符号付き16ビット整数)
$7E:2A6A~6B装備変更時 装備変更による攻撃変動値(符号付き16ビット整数)
$7E:2A6C~6D装備変更時 装備変更による防御変動値(符号付き16ビット整数)

$7E:2A51~は装備変更時に、カーソルを移動させただけで実際に装備変更しなくても入る変動値部分である。
装備画面左下の、一番右の列の値である。
マイナスされる場合は「-5」などと表示されるようになっているが、負の値の表現のため、$7E:2A51以降は2バイトの値を使って、符号付き16ビット整数で正負の値を表現する。
2バイトの領域を使うのは、上1バイトで正負を示し、下1バイトで絶対値を示すようにしないと、前ページの計算方法でも触れたように、除算時に「絶対値÷2」の計算にならないからであろう。

では、$7E:2A51$7E:2A58がどう計算されるのか、実際のサブルーチンを必要な箇所だけかいつまんで紹介する。
今回確認したいのは、副能力変動値がどう計算されているのかであるから、例として、「グラブ」(右と左に装備可能)を主装備位置ではない左に装備した場合の処理を記す。
ここでは詳細を省くが、まず、変更しようとしている装備品(ここでは「グラブ」)のアイテムデータ16バイトの値が、$7E:2A7D$7E:2A8Cに丸ごと入る。
カーソルで示している装備品のアイテムデータが入るアドレスということであり、カーソルが移動するたびに毎回上書きされる。

アイテムデータアドレス数値内容
00$7E:2A7D$2B上4ビット:1~7は主装備位置、8以上なら罠アイテム/下4ビット:装備品なら装備種類、装備品以外ならアイテム種別
01$7E:2A7E$60上6ビット:装備部位/下1ビット:材料アイテム
02$7E:2A7F$00無効状態異常(8ビット)
03$7E:2A80$00上4ビット:装備タイプ/下4ビット:フィールド吸収
04$7E:2A81$00回避属性1(8ビット)
05$7E:2A82$00回避属性2(8ビット)
06$7E:2A83$00力 主能力変動値
07$7E:2A84$00速 主能力変動値
08$7E:2A85$00体 主能力変動値
09$7E:2A86$00知 主能力変動値
10$7E:2A87$00使用時に技が発動する場合、味方技IDの値
11$7E:2A88$45副能力変動値計算用の値
12$7E:2A89$00メカがアクセサリ装備で技追加($08$0F
13$7E:2A8A$00上4ビット:戦闘開始時行動異常を起こす敵種族/下4ビット:戦闘開始時行動異常
14$7E:2A8B$04下6ビット:攻撃または防御の値
15$7E:2A8C$00状態異常追加効果(8ビット)

$7E:2A83$7E:2A86に、力・速・体・知の主能力変動値が入っており、以下のように順にループ処理される。
以下は最初の力の処理の場合だが、$7E:2A51$7E:2A58には、ここまでの計算で、既に装備している装備品の変動値が入った状態での計算になる。何も装備していない状態だと00が入っている。
なお、16bitモードでの計算になっている。

$C2/492C LDA $2A83,x[$7E:2A83]   ;Aに[$7E:2A83](アイテムデータ06 力 主能力変動値)をロード
$C2/492F AND #$00FF              ;A([$7E:2A83])と#$00FFで論理積
$C2/4932 JSR $472C  [$C2:472C]   ;[$C2:472C]へジャンプ
;
$C2/472C BIT #$0080              ;A([$7E:2A83])と$0080で論理積、フラグ変更のみ
$C2/472F BEQ $03    [$4734]      ;ゼロフラグが立っているとき(Aが$0080未満 = 正の数だとゼロフラグON)[$4734]分岐
$C2/4731 ORA #$FF00              ;(ゼロフラグなし = [$7E:2A83]が負の数)Aと$FF00で論理和
$C2/4734 RTS                     ;サブルーチン戻り
;
$C2/4935 LSR A                   ;Aを論理右シフト /2
$C2/4936 JSR $472C  [$C2:472C]   ;[$C2:472C]へジャンプ
;
$C2/472C BIT #$0080              ;A([$7E:2A83]/2)と$0080で論理積 フラグ変更のみ
$C2/472F BEQ $03    [$4734]      ;ゼロフラグが立っているとき[$4734]分岐
$C2/4731 ORA #$FF00              ;(ゼロフラグなし = [$7E:2A83]が負の数)Aと$FF00で論理和
$C2/4734 RTS                     ;サブルーチン戻り
;
$C2/4939 CLC                     ;キャリーフラグクリア
$C2/493A ADC $2A51,y[$7E:2A51]   ;A+[$7E:2A52][$7E:2A51](装備変更による力変動値(符号付き16ビット整数))
$C2/493D STA $2A51,y[$7E:2A51]   ;Aを[$7E:2A52][$7E:2A51]に書き込み

[$7E:2A83]のアイテムデータ06(力 主能力変動値)は、$C2/472Cで、$0080と論理積をとっている(フラグ変更のみ)。
$80と論理積をとってゼロフラグが立つのは、[$7E:2A83]$80未満、つまり8ビット表記で一番上の位に1が立っていない = 0の時である。
符号付き8ビット整数において、一番上の位が0だと正の数を示し、1だと負の数を示す。
よって、このゼロフラグ分岐は、主能力変動値が正の数か負の数かの分岐である。
正の数だとゼロフラグが立つため、$C2/4731は飛ばし、$C2/4734に進む。
負の数だとゼロフラグが立たないので、$C2/4731の処理を行う。

$C2/4731では、A$FF00で論理和をとっている。
元の主能力変動値の上に「FF」をつけることと同じである。
前ページでも触れたが、符号付き8ビット整数を÷2する時、絶対値部分(下7ビット)を÷2したいという場合、正の値ならそのまま÷2で問題ないのだが、負の値だと上1ビットに1が立っている都合で、絶対値部分(下7ビット)だけを÷2できない。
そこで強引に、上に「FF」をつけ、符号付き16ビット整数にしてから÷2するという方法を使っている。
その準備のための処理が、$C2/472C$C2/4734ということである。
例えば、主能力変動値が$FA(10進数-6)の場合、$FFFAに値が変更される。

$C2/4935で論理右シフトにより÷2を行っている。
先の処理により、主能力変動値が正の値でも負の値でも、絶対値部分(下2桁)を÷2できたことになる。
$FFFAの場合は、$FFFA/2 = $7FFDで、下2桁部分は$FD(10進数-3)で、数値だけが半分になった。
ただし、負の値だと、上2桁に余計な値「$7F」がついたままである。
この後にもう一度$C2/472C$C2/4734を繰り返しているが、これは実質、主能力変動値が負の時のための処理である。
$FF00ともう一度論理和を取ることで、$7FFD$FFFDとなり、符号付き16ビット整数での10進数-3と同値になった。

$C2/493A$C2/493Dで、この計算値が、[$7E:2A52][$7E:2A51](装備変更時 装備変更による力変動値(符号付き16ビット整数))に加算される。
なぜ加算なのかというと、装備変更による力変動値は、前に入っていた装備品の変動値との差だからである。

上のサブルーチンは「力」の分だが、この後、速・体・知も同じように計算されて、$7E:2A52$7E:2A58に計算値が入る。

以上は、前ページでいうところの、副能力用計算値の算出である。
つまり、以下の計算を行ったことになる。

  • 主能力変動値が正の値($00~$79)の場合は、
    副能力用計算値 = trunc(主能力変動値/2)
  • 主能力変動値が負の値($80~$FF)の場合は、頭に$FFをつけて符号付き16ビット整数値として扱い、
    副能力用計算値 = trunc($FF(主能力変動値)/2) & $00FF
    例:主能力変動値が$F5(10進数-11)なら、trunc($FFF5/2) = $7FFA、下1バイト$FA(10進数-6)が副能力変動値。

アイテムデータ11の値が$00なら、副能力用計算値が副能力変動値として扱われるので処理終了である。
ただし「グラブ」の場合、アイテムデータ11に$45が入っているため、処理が続く。
アイテムデータ11用の処理が以下になる。
引き続き16bitモードで計算していく。

$C2/4949 LDA $2A88  [$7E:2A88]   ;Aに[$7E:2A88](アイテムデータ11)をロード
$C2/494C AND #$00C0              ;Aと$00C0(%1100 0000)で論理積 = $0040(%0000 0000 0100 0000)
$C2/494F ASL A                   ;算術左シフト *2 (%0000 0000 1000 0000 = $0080)
$C2/4950 ASL A                   ;算術左シフト *2 (%0000 0001 0000 0000 = $0100)
$C2/4951 ASL A                   ;算術左シフト *2 (%0000 0010 0000 0000 = $0200)
$C2/4952 XBA                     ;Aレジスタの上位バイトと下位バイトを交換 $0200 → $0002
$C2/4953 TAX                     ;Aレジスタの値をXレジスタに転送
$C2/4954 LDA $2A88  [$7E:2A88]   ;Aに[$7E:2A88](アイテムデータ11)をロード
$C2/4957 AND #$003F              ;Aと$003F(%0011 1111)で論理積 = $05(%0000 0101)
$C2/495A EOR #$FFFF              ;Aと$FFFFで排他的論理和XOR $0005 XOR $FFFF = $FFFA
$C2/495D INC A                   ;A + 1 = $FFFA + 1 = $FFFB
$C2/495E CLC                     ;キャリーフラグクリア
$C2/495F ADC $2A51,x[$7E:2A53]   ;A + [$2A51,x] / X:0002, [$2A51,x] = [$7E:2A53](速変動値)
$C2/4962 STA $2A51,x[$7E:2A53]   ;Aを[$7E:2A53]に書き込み

$C2/494Cで、[$7E:2A88](アイテムデータ11)と$00C0(%1100 0000)で論理積を取っている。つまりアイテムデータ11の上位2ビットを取っている。
16bitモードだから、

%0000 0000 ??00 0000

??部分に、上位2ビットが入る。
この後に、算術左シフトを3回繰り返している。

%0000 0??0 0000 0000

??部分が上のように移動したことになる。
$C2/4952で、Aレジスタの上位バイトと下位バイトを交換しているので、

%0000 0000 0000 0??0

となり、Xレジスタに転送されている。
??に入るのは、00,01,10,11のいずれかなので、

??Xレジスタに入る値
00%0000 0000 0000 0000 = $0000
01%0000 0000 0000 0010 = $0002
10%0000 0000 0000 0100 = $0004
11%0000 0000 0000 0110 = $0006

少し先の処理になるが、このXの値は、$C2/495Fの「ADC $2A51,x」におけるアドレス呼び出しの値になっている。
つまり2ずつずれた値を加算することになる。

??Xレジスタアドレス内容
00$0000[$2A51,x] = [$7E:2A51]装備変更による力変動値(副能力用計算値が入っている)
01$0002[$2A51,x] = [$7E:2A53]装備変更による速変動値(副能力用計算値が入っている)
10$0004[$2A51,x] = [$7E:2A55]装備変更による体変動値(副能力用計算値が入っている)
11$0006[$2A51,x] = [$7E:2A57]装備変更による知変動値(副能力用計算値が入っている)

このようになり、??の値、つまりアイテムデータ11の上位2ビットは、力・速・体・知に対応していたことがわかった。

一方、$C2/4954からは、[$7E:2A88](アイテムデータ11)と$003F(%0011 1111)で論理積を取っているので、アイテムデータ11の下6ビットの呼び出しである。
$C2/495Aでは、下6ビットの値と、$FFFFで排他的論理和を取っている。
$FFFFとの排他的論理和は、実質、2進数で見た時に値を全て逆にするのと同じである。
「グラブ」だと、アイテムデータ11の下6ビットの値が$0005なので、$FFFFと排他的論理和を取ると、

$0005(0000 0000 0000 0101) XOR $FFFF(1111 1111 1111 1111) = $FFFA(1111 1111 1111 1010)

このようになる。
$C2/495Dでインクリメント(+1)しているから、

$FFFA + 1 = $FFFB

であるが、これは10進数で見たら、「アイテムデータ11の下6ビット」にマイナスを付けた値と同値である。
要するに正負を逆にしたのである。
この値が、$C2/495Fで、該当のステータスのアドレスに書き込まれている。
アドレスには既に、副能力用計算値が入っているため、

副能力用計算値 + (アイテムデータ11の下6ビットと$FFの排他的論理和 + 1)

この値が最終的に、副能力変動値となった。
ただし下1バイトのアドレス部分のみが副能力変動値の扱いになる。
よって、

副能力変動値 = (副能力用計算値 + (アイテムデータ11の下6ビットと$FFの排他的論理和 + 1)) & $FF

である。
8ビットで上の計算をすると、計算結果が$100以上、つまり繰り上がりが発生する場合があるため、$FFと論理積を取るなどして下2桁だけを副能力変動値とすれば良い。
実際には16ビットのデータを記録した2バイト分のアドレスから下1バイトのアドレスの値を取ることになる。

以上が副能力変動値計算のサブルーチンである。
ここからはおまけなので、飛ばしても構わない。

最初にあっさりと「アイテムデータ16バイトの値が、$7E:2A7D$7E:2A8Cに丸ごと入る」と説明したが、その処理をざっと紹介しておく。
以下も「グラブ」の処理になる。
アイテムIDは$A8で、元のアイテムデータのアドレス位置は$D5:7C44$D5:7C54である。

$C2/49BB LDA $2A7B  [$7E:2A7B]   ;A:00D5 X:0001 Y:0002 Aに[$7E:2A7B](「グラブ」のアイテムID)をロード
$C2/49BE JSR $6238  [$C2:6238]   ;A:00A8 X:0001 Y:0002 [$C2:6238]へジャンプ
;
$C2/6238 PHP                     ;A:00A8 X:0001 Y:0002 ステータスレジスタをスタックへプッシュ
$C2/6239 PHY                     ;A:00A8 X:0001 Y:0002 Yレジスタをスタックへプッシュ
$C2/623A SEP #$20                ;A:00A8 X:0001 Y:0002 MフラグON Aレジスタは8bit幅
$C2/623C STA $004202[$00:4202]   ;A:00A8 X:0001 Y:0002 符号付8bit x 8bit被乗数( = アイテムID)
$C2/6240 LDY #$0004              ;A:00A8 X:0001 Y:0002 Yに$0004をロード
$C2/6243 LDA [$A3],y[$D5:006C]   ;A:00A8 X:0001 Y:0004 Aに[$D5:006C]をロード
$C2/6245 STA $004203[$00:4203]   ;A:0010 X:0001 Y:0004 符号付8bit x 8bit乗数[$D5:006C] = $10
$C2/6249 NOP                     ;A:0010 X:0001 Y:0004 乗算計算待ち
$C2/624A NOP                     ;A:0010 X:0001 Y:0004 乗算計算待ち
$C2/624B NOP                     ;A:0010 X:0001 Y:0004 乗算計算待ち
$C2/624C REP #$20                ;A:0010 X:0001 Y:0004 Mフラグをクリア Aレジスタは16bit幅
$C2/624E LDA $004216[$00:4216]   ;A:0010 X:0001 Y:0004 Aに乗算結果を16ビットでロード [$00:4217][$00:4216] = $0A80
$C2/6252 CLC                     ;A:0A80 X:0001 Y:0004 キャリーフラグクリア
$C2/6253 ADC [$A3]  [$D5:0068]   ;A:0A80 X:0001 Y:0004 $0A80 + [$D5:0068] = $7C44
$C2/6255 PLY                     ;A:7C44 X:0001 Y:0004 Yレジスタにスタックからプル
$C2/6256 PLP                     ;A:7C44 X:0001 Y:0002 Pレジスタにスタックからプル
$C2/6257 RTS                     ;A:7C44 X:0001 Y:0002 サブルーチン戻り
;
$C2/49C1 TAX                     ;A:7C44 X:0001 Y:0002 Aレジスタの値をXレジスタに転送
$C2/49C2 LDY #$2A7D              ;A:7C44 X:7C44 Y:0002 Yに$2A7Dをロード
$C2/49C5 LDA $D5006C[$D5:006C]   ;A:7C44 X:7C44 Y:2A7D Aに[$D5:006C]をロード
$C2/49C9 DEC A                   ;A:0010 X:7C44 Y:2A7D A-1
$C2/49CA MVN D5 7E               ;A:000F X:7C44 Y:2A7D データ転送 オペランドに転送元と転送先バンクを指定:D5 7E
$C2/49CD PLP                     ;A:FFFF X:7C54 Y:2A8D Pレジスタにスタックからプル
$C2/49CE PLY                     ;A:FFFF X:7C54 Y:2A8D Yレジスタにスタックからプル
$C2/49CF PLX                     ;A:FFFF X:7C54 Y:0002 Xレジスタにスタックからプル
$C2/49D0 RTS                     ;A:FFFF X:0001 Y:0002 サブルーチン戻り

$C2/49BBで、「グラブ」のアイテムIDを読み込んだ後は、$C2/6238$C2/6257で「$7C44」という値を算出するための計算を行っている。
この値は、グラブのアイテムデータの開始位置$D5:7C44のアドレスになる。
その後のアイテムデータの呼び出しは、アドレスを+1しながらのループ処理ではなく、MVNというニーモニックを使っている。
MVNはデータ転送命令で、転送元と転送先のアドレスを比較した場合、後者の方が小さい場合に使う。逆の時はMVPというニーモニックを使う。
転送時には、

  • オペランド(MVNの後の値):転送元・転送先バンクを指定
  • Aレジスタ:転送したいデータサイズ-1
  • Xレジスタ:転送元のデータの開始アドレス
  • Yレジスタ:転送先の開始アドレス

を指定しておく。
すると、実際にAレジスタの値を-1しながら0000まで、XYレジスタは+1しながらAレジスタが0000になるまで、データ転送を行う。
上では、「MVN D5 7E」だから、転送元バンクがD5、転送先バンクが7E
A:000F X:7C44 Y:2A7Dなので、

  • 転送したいデータサイズ:$10
  • 転送元のデータの開始アドレス:7C44
  • 転送先の開始アドレス:2A7D

となる。
まとめると、

  • 転送元:$D5:7C44から$D5:7C54
  • 転送先:$7E:2A7Dから$7E:2A8D

であり、「グラブ」のアイテムデータがまとめて転送されることがわかる。



このページをシェアする

上へ