基礎知識
戦闘関係
※調査中の部分も含むが、大まかなまとめということで。
また、グラフィック処理の話を含むため、グラフィックについて以降の解説もお読みいただきたい。
技データ00~05の合計6バイトは、技のモーション・エフェクト(効果音含む)のデータである。
何の効果音を鳴らすかは、技データ20及び技データ24で決まる。
ただ、この6バイトから、実際のモーションやエフェクトを処理するサブルーチンまで辿り着くのがとてもややこしい。
まず、技データ00~05の内容だが、以下のようになっている、と思われる。
2バイト区切りなので、実際にデータを読む時には、上位バイト・下位バイトの順で読む。
実際の数値は、世界の合言葉は森部様などを参照のこと。
アドレスは、味方技データ$D5:24CB~$D5:3DB1, 敵技データ$D5:3DB2~$D5:56CA。
| No. | 内容 |
|---|---|
| 00 | 技モーション・技を使用するキャラのエフェクト・モーション(2バイト) |
| 01 | |
| 02 | 技モーション・技のエフェクト(2バイト) |
| 03 | |
| 04 | 技モーション・技をくらうキャラのエフェクト・モーション(2バイト) |
| 05 |
例えば「ローキック」なら$D5:2DF9~$D5:2DFEで、値は順にF8 9D 90 ED 3A F6なので、2バイト区切りだと$9DF8, $ED90, $F63Aである。
「セントアリシア」なら$D5:56B2~$D5:56B7で、値は順に35 B6 A1 CE 1F F9なので、2バイト区切りだと$B635, $CEA1, $F91Fである。
というように値を見てみると、技により共通する値があることがわかる。
例えばキューブの技「マインドハック」「アンチフィールド」「ノイズストリーム」「インフォリサーチ」は、技データ00~01がすべて$AFC7である。
アイテム技は、技データ00~01がだいたい$B0ACなので(例外あり)、使用者が誰であっても共通の何かしらのモーションを使う指示だろうと推測できる。
「シュラのイン」「体力ドレイン」「魔力吸引」など吸収系の技の技データ02~03は共通で$D4FAなので、吸収系の技のエフェクトは同じサブルーチンで処理していることがわかる。
技のモーション関係の計算値は、技を出している間にWRAMの$7E:A000~$7E:A47F($480バイトの領域)に入るようである。
実際のサブルーチンをわかる範囲で見てみる。
今回は味方が技を使用した時の処理のみ紹介する。敵側はまた別のサブルーチンのようである。
技を使用した時に技名が画面上ウィンドウに表示されるが、その直後に$7E:A000~$7E:A47Fが初期化される。
技データ00~05を獲得して処理していくのは$C1/329F以降。
ここでは例としてレベル6のアキラが「ローキック」を敵に命中させた時の処理を見ていく。
なお、レベル6だと「ローキック」命中時、確定で1ヒットである。
$C1/A43B JSR $329F [$C1:329F] ;[$C1:329F]へジャンプ $C1/329F REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/32A1 LDX $68 [$00:0368] ;Xに[$00:0369][$00:0368]をロード $C1/32A3 LDA $D50000,x[$D5:2DF9] ;Aに[$D5:2DFA][$D5:2DF9]をロード 技データ00,01:9DF8 $C1/32A7 STA $10 [$00:0310] ;Aを[$00:0311][$00:0310]に書き込み $C1/32A9 LDA $D50002,x[$D5:2DFB] ;Aに[$D5:2DFC][$D5:2DFB]をロード 技データ02,03:ED90 $C1/32AD STA $12 [$00:0312] ;Aを[$00:0313][$00:0312]に書き込み $C1/32AF LDA $D50004,x[$D5:2DFD] ;Aに[$D5:2DFE][$D5:2DFD]をロード 技データ04,05:F63A $C1/32B3 STA $14 [$00:0314] ;Aを[$00:0315][$00:0314]に書き込み $C1/32B5 LDX #$A000 ;Xに$A000をロード $C1/32B8 LDA $10 [$00:0310] ;Aに[$00:0311][$00:0310]をロード 技データ00,01 $C1/32BA STA $7E0028,x[$7E:A028] ;Aを[$7E:A029][$7E:A028]に書き込み $C1/32BE LDX #$A040 ;Xに$A040をロード $C1/32C1 LDA $14 [$00:0314] ;Aに[$00:0315][$00:0314]をロード 技データ04,05 $C1/32C3 STA $7E0028,x[$7E:A068] ;Aを[$7E:A069][$7E:A068]に書き込み $C1/32C7 LDX #$A080 ;Xに$A080をロード $C1/32CA LDA $12 [$00:0312] ;Aに[$00:0313][$00:0312]をロード 技データ02,03 $C1/32CC STA $7E0028,x[$7E:A0A8] ;Aを[$7E:A0A9][$7E:A0A8]に書き込み $C1/32D0 TXA ;Xレジスタの値をAレジスタに転送 $C1/32D1 CLC ;キャリーフラグクリア $C1/32D2 ADC #$0040 ;A + $0040 $C1/32D5 TAX ;Aの値をXレジスタに転送 $C1/32D6 CPX #$A480 ;Xと$A480を減算比較(ステータスレジスタ変更のみ) $C1/32D9 BNE $EF [$32CA] ;ゼロフラグが立っていないとき[$32CA]分岐 ;ループ処理 $C1/32CA LDA $12 [$00:0312] ;Aに[$00:0313][$00:0312]をロード 技データ02,03 $C1/32CC STA $7E0028,x[$7E:A0E8] ;Aを[$7E:A0E9][$7E:A0E8]に書き込み 以降+$40n, [$7E:A469][$7E:A468]まで $C1/32D0 TXA ;Xレジスタの値をAレジスタに転送 $C1/32D1 CLC ;キャリーフラグクリア $C1/32D2 ADC #$0040 ;A + $0040 $C1/32D5 TAX ;Aの値をXレジスタに転送 $C1/32D6 CPX #$A480 ;Xと$A480を減算比較(ステータスレジスタ変更のみ) $C1/32D9 BNE $EF [$32CA] ;ゼロフラグが立っていないとき[$32CA]分岐 ;ループ処理ここまで $C1/32DB SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/32DD RTS ;サブルーチン戻り
$C1/32A3~$C1/32B3が、「ローキック」の技データ00~05($D5:2DF9~$D5:2DFE)の呼び出しである。
$C1/32A1でXに読み込まれる[$00:0369][$00:0368]はアキラの技データ呼び出し用の値で、これより前の$C1/3D7A~$C1/3DAFで計算され、$C1/7852で[$00:0369][$00:0368]に書き込まれている。
$C1/7852~$C1/786Bは前にも紹介したが、アキラの技データを丸ごと[$7E:9010]~[$7E:9028]にコピーする処理。
$C1/32A3~$C1/32B3では、その後の処理も含めて、以下のように値が書き込まれている。
| 技データの値 | コピー先 |
|---|---|
| 技データ00,01 ($9DF8) | [$00:0311][$00:0310][$7E:A029][$7E:A028] |
| 技データ02,03 ($ED90) | [$00:0313][$00:0312][$7E:A0A9][$7E:A0A8][$7E:A0E9][$7E:A0E8][$7E:A129][$7E:A128][$7E:A169][$7E:A168][$7E:A1A9][$7E:A1A8][$7E:A1E9][$7E:A1E8][$7E:A229][$7E:A228][$7E:A269][$7E:A268][$7E:A2A9][$7E:A2A8][$7E:A2E9][$7E:A2E8][$7E:A329][$7E:A328][$7E:A369][$7E:A368][$7E:A3A9][$7E:A3A8][$7E:A3E9][$7E:A3E8][$7E:A429][$7E:A428][$7E:A469][$7E:A468] |
| 技データ04,05 ($F63A) | [$00:0315][$00:0314][$7E:A069][$7E:A068] |
;最大連発数・ヒット数パターン判定 $C1/A43E LDA $7E9023[$7E:9023] ;Aに[$7E:9023]をロード 技データ19 $C1/A442 BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/A444 BNE $05 [$A44B] ;ゼロフラグが立っていないとき[$A44B]分岐 ;ゼロフラグON 範囲内に1ヒットではない $C1/A446 JSR $3037 [$C1:3037] ;[$C1:3037]へジャンプ ;ゼロフラグOFF 範囲内に1ヒット $C1/A44B JSR $A56D [$C1:A56D] ;[$C1:A56D]へジャンプ
次は使用技が「範囲内に1ヒット」かどうかの判定である。
[$7E:9023]、つまり使用技の技データ19と$80で論理積を取ってゼロフラグ判定だから、技データ19の一番上のビットが1かどうかの判定である。1なら「範囲内に1ヒット」である。
よって、「範囲内に1ヒット」の場合はゼロフラグが立たず、「範囲内に1ヒット」ではないのならゼロフラグが立つ。
「範囲内に1ヒット」かどうかでモーションの処理が分岐することになる。
ちなみに上処理前(モーション処理前)にヒット数計算自体は終了している。
攻撃技のヒット数を参照していただきたい。
(「ほいこーろー」はモーション中にSTEPS数を乱数で決めていく方式のため例外)
「ローキック」は「範囲内に1ヒットではない」ので、処理が$C1/3037に続く。
この後も分岐や[$7E:A000]~の更新が続くが、ひとまず横におく。
次に技データ00~01が呼び出されるのは以下になる。
;X:A000から開始 $C1/A666 LDA #$00 ;Aに$00をロード $C1/A668 STA $7D [$00:037D] ;Aを[$00:037D]に書き込み $C1/A66A BRL $000E [$A67B] ;フラグにかかわりなく常に分岐[$A67B] $C1/A67B LDA $7E003F,x[$7E:A03F] ;Aに[$7E:A03F]をロード $C1/A67F BEQ $02 [$A683] ;ゼロフラグが立っているとき[$A683]分岐 $C1/A681 BPL $01 [$A684] ;ネガティブフラグが立っていないとき[$A684]分岐 $C1/A684 STX $1E [$00:031E] ;Xを[$00:031F][$00:031E]に書き込み X:A000 $C1/A686 LDA $7E0028,x[$7E:A028] ;Aに[$7E:A028]をロード 技データ01 $C1/A68A STA $1C [$00:031C] ;Aを[$00:031C]に書き込み $C1/A68C LDA $7E0029,x[$7E:A029] ;Aに[$7E:A029]をロード 技データ00 $C1/A690 STA $1D [$00:031D] ;Aを[$00:031D]に書き込み $C1/A692 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード 技データ00,01 X:9DF8 $C1/A694 LDX $1E [$00:031E] ;Xに[$00:031F][$00:031E]をロード X:A000 $C1/A696 LDA $7E002E,x[$7E:A02E] ;Aに[$7E:A02E]をロード $C1/A69A ORA $7E002F,x[$7E:A02F] ;Aと[$7E:A02F]で論理和 $C1/A69E BEQ $03 [$A6A3] ;ゼロフラグが立っているとき[$A6A3]分岐 $C1/A6A3 CPX #$A080 ;Xと$A080を減算比較(ステータスレジスタ変更のみ) $C1/A6A6 BCC $11 [$A6B9] ;キャリーフラグが立っていないとき[$A6B9]分岐 $C1/A6B9 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード 技データ00,01 $C1/A6BB LDA $D50000,x[$D5:9DF8] ;Aに[$D5:9DF8]をロード $D5:技データ00,01 X:9DF8 $C1/A6BF STA $10 [$00:0310] ;A($FD)を[$00:0310]に書き込み $C1/A6C1 INX ;Xをインクリメント +1 $C1/A6C2 STX $1C [$00:031C] ;Xを[$00:031D][$00:031C]に書き込み X:9DF8+1 $C1/A6C4 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/A6C6 AND #$00F0 ;Aと$00F0で論理積 FD & F0 $C1/A6C9 LSR A ;Aを論理右シフト (/2) $C1/A6CA LSR A ;Aを論理右シフト (/2) $C1/A6CB LSR A ;Aを論理右シフト (/2) $C1/A6CC ADC #$A5E6 ;A(001E) + $A5E6 $C1/A6CF TAX ;Aの値をXレジスタに転送 $C1/A6D0 LDA $C10000,x[$C1:A604] ;Aに[$C1:A605][$C1:A604]をロード A:B000 $C1/A6D4 STA $1A [$00:031A] ;Aを[$00:031B][$00:031A]に書き込み $C1/A6D6 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード X:9DF9 $C1/A6D8 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/A6DA JMP ($031A)[$C1:B000] ;[$C1:B000]へジャンプ(レジスタをスタックに積まない) A:B000がアドレス $C1/B000 LDA $10 [$00:0310] ;Aに[$00:0311][$00:0310]($FD)をロード $C1/B002 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/B004 AND #$000F ;Aと$000Fで論理積 $C1/B007 ASL A ;Aを算術左シフト *2 $C1/B008 ADC #$A646 ;A + $A646 $C1/B00B TAX ;Aの値をXレジスタに転送 $C1/B00C LDA $C10000,x[$C1:A660] ;Aに[$C1:A661][$C1:A660]をロード A:B413 $C1/B010 STA $1A [$00:031A] ;Aを[$00:031B][$00:031A]に書き込み $C1/B012 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/B014 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/B016 JMP ($031A)[$C1:B413] ;[$C1:B413]へジャンプ(レジスタをスタックに積まない) $C1/B413 JSR $A6E7 [$C1:A6E7] ;[$C1:A6E7]へジャンプ
[$7E:A029][$7E:A028]に技データ00~01が書き込まれているが、$C1/A686~$C1/A690で[$00:031D][$00:031C]にコピーされる。
「ローキック」なら$9DF8である。
$C1/A6B9で、Xに[$00:031D][$00:031C]がロードされ、$C1/A6BBでAに$D50000,xがロードされる。
つまり、$D50000 + 技データ00~01($9DF8)で、[$D5:9DF8](値は$FD)がロードされる。
ここから、技データ00~01はバンク$D5のアドレスを呼び出すための値だったことがわかる。
[$D5:9DF8]の値は[$00:0310]に書き込まれる。
$C1/A6C1~で、Xに入っている「$D50000 + 技データ00~01」を+1し、$9DF8+1でX:9DF9となる。これが新たな[$00:031D][$00:031C]として書き込まれる。
Aに書き込まれている[$D5:9DF8]の値は、
([$D5:9DF8] & $00F0)/8 + $A5E6
を計算して、Xレジスタに転送され、今度は$C1/A6D0で[$C10000,x+1][$C10000,x]を呼び出している。
[$D5:9DF8]は$FDなので、
($00FD & $00F0)/8 + $A5E6 = $A604
つまり、$C1/A6D0で[$C1:A605][$C1:A604]をAにロードする(値は$B000)。
この値は[$00:031B][$00:031A]に書き込まれる。
この後に、
$C1/A6DA JMP ($031A)
という命令がある。
これは、バンクは現在の$C1、アドレスはAレジスタに入っている$B000、つまり$C1/B000にジャンプしろという意味になる。
$C1/B000からは、[$D5:9DF8] = [$00:0310]($FD)をふたたび使って計算をする。
([$D5:9DF8] & $000F)*2 + $A646
= ($00FD & $000F)*2 + $A646
= $A660
がXレジスタに入り、$C1/B00Cで[$C10000,x+1][$C10000,x]を呼び出す。
今回は[$C1:A661][$C1:A660](値は$B413)がロードされ、[$00:031B][$00:031A]に書き込まれる。
Xに[$00:031D][$00:031C]($9DF8+1でX:9DF9)をロードしてから、$C1/B016で、JMP ($031A)だから、$C1/B413にジャンプ。
$C1/B413では、$C1:A6E7にジャンプの指示がある。
$C1/A6E7 LDA $D50000,x[$D5:9DF9] ;Aに[$D5:9DF9]をロード AB $C1/A6EB STA $11 [$00:0311] ;Aを[$00:0311]に書き込み $C1/A6ED INX ;Xをインクリメント +1 $C1/A6EE LDA $D50000,x[$D5:9DFA] ;Aに[$D5:9DFA]をロード FE $C1/A6F2 STA $12 [$00:0312] ;Aを[$00:0312]に書き込み $C1/A6F4 INX ;Xをインクリメント +1 $C1/A6F5 STX $1C [$00:031C] ;Xを[$00:031D][$00:031C]に書き込み $C1/A6F7 RTS ;サブルーチン戻り
$C1/A6E7~で、[$D5:9DF9](値は$AB)を[$00:0311]に、[$D5:9DFA](値は$FE)を[$00:0312]にロードする。
つまり、「$D50000 + 技データ00~01」を+1ずつした値が[$00:0311][$00:0312]の2バイトに入ったことになる。
$C1/B416 BRL $0307 [$B720] ;フラグにかかわりなく常に分岐[$B720] $C1/B720 LDX $1E [$00:031E] ;Xに[$00:031F][$00:031E]をロード X:A000 $C1/B722 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/B724 LDA $1C [$00:031C] ;Aに[$00:031D][$00:031C]をロード 9DFB $C1/B726 STA $7E002A,x[$7E:A02A] ;Aを[$7E:A02B][$7E:A02A]に書き込み $C1/B72A LDA $11 [$00:0311] ;Aに[$00:0312][$00:0311]をロード FEAB $C1/B72C STA $1C [$00:031C] ;Aを[$00:031D][$00:031C]に書き込み $C1/B72E SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/B730 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/B732 BRL $EF86 [$A6BB] ;フラグにかかわりなく常に分岐[$A6BB] $C1/A6BB LDA $D50000,x[$D5:FEAB] ;Aに[$D5:FEAB]をロード $C1/A6BF STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/A6C1 INX ;Xをインクリメント +1 $C1/A6C2 STX $1C [$00:031C] ;Xを[$00:031D][$00:031C]に書き込み $C1/A6C4 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/A6C6 AND #$00F0 ;Aと$00F0で論理積 $C1/A6C9 LSR A ;Aを論理右シフト (/2) $C1/A6CA LSR A ;Aを論理右シフト (/2) $C1/A6CB LSR A ;Aを論理右シフト (/2) $C1/A6CC ADC #$A5E6 ;A + $A5E6 $C1/A6CF TAX ;Aの値をXレジスタに転送 $C1/A6D0 LDA $C10000,x[$C1:A5F2] ;Aに[$C1:A5F3][$C1:A5F2]をロード $C1/A6D4 STA $1A [$00:031A] ;Aを[$00:031B][$00:031A]に書き込み $C1/A6D6 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/A6D8 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/A6DA JMP ($031A)[$C1:A810] ;[$C1:A810]へジャンプ(レジスタをスタックに積まない)
$C1/B416, $C1/B720~$C1/B732で、
[$7E:A02B][$7E:A02A]に書き込み[$D5:9DFA][$D5:9DF9]($FEAB)を[$00:031D][$00:031C]に書き込みしてから、X:FEABの状態で再び$C1/A6BB~$C1/A6DAの処理が入る。
すると今回は、$C1/A6BBで[$D5:FEAB]($60)がロードされ、
([$D5:FEAB]($60) & $00F0)/8 + $A5E6 = $A5F2
[$C1:A5F3][$C1:A5F2] = $A810
$C1/A6DAのJMP ($031A)は、$C1:A810へのジャンプとなる。
……というように、アドレスがアドレスを呼び出して、[$7E:A000]~の更新を延々と続けるため、何が何やら非常にわかりにくい。
最終的に$7E:A000~$7E:A47F($480バイトの領域)の中の一部が、キャラの座標計算などに使われたりする。
また、モーションはキャラの画像(スプライト)の変更でもあるので、計算値からスプライトのデータのGDMA転送(OAMへオブジェクト属性、VRAMにタイルデータ)も行うが、そのあたりの数値も入るようである。
$C1/2E9C LDX #$A000 ;Xに$A000をロード $C1/2E9F LDY $78 [$00:0378] ;Yに[$00:0378]をロード $C1/2EA1 LDA $79 [$00:0379] ;Aに[$00:0379]をロード $C1/2EA3 CMP #$1C ;Aと$1Cを減算比較(ステータスレジスタ変更のみ) $C1/2EA5 BEQ $1A [$2EC1] ;ゼロフラグが立っているとき[$2EC1]分岐 $C1/2EC1 LDA $0001,y[$00:1C01] ;Aに[$00:1C01]をロード 味方キャラ1戦闘状態 $C1/2EC4 CMP #$40 ;Aと$40を減算比較(ステータスレジスタ変更のみ) $C1/2EC6 BNE $06 [$2ECE] ;ゼロフラグが立っていないとき[$2ECE]分岐 $C1/2ECE LDA $7E001D,x[$7E:A01D] ;Aに[$7E:A01D]をロード $C1/2ED2 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/2ED4 ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/2ED5 ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/2ED6 LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/2ED8 ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/2ED9 AND #$03 ;Aと$03で論理積 $C1/2EDB STA $11 [$00:0311] ;Aを[$00:0311]に書き込み ([$7E:A01D]<<1) & $03 $C1/2EDD LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/2EDF AND #$80 ;Aと$80で論理積 [$7E:A01D] & $80 $C1/2EE1 ORA $11 [$00:0311] ;Aと[$00:0311]で論理和 ([$7E:A01D] & $80) | (([$7E:A01D]<<1) & $03) $C1/2EE3 STA $001D,y[$00:1C1D] ;Aを[$00:1C1D]に書き込み 向き(右下00/左下01/右上02/左上03) $C1/2EE6 LDA $0039,y[$00:1C39] ;Aに[$00:1C39]をロード 状態異常 $C1/2EE9 BPL $07 [$2EF2] ;ネガティブフラグが立っていないとき[$2EF2]分岐 ;ネガティブフラグON キャラが石化または戦闘不能 $C1/2EEB LDA $001E,y[$00:1C1E] ;Aに[$00:1C1E]をロード $C1/2EEE AND #$1F ;Aと$1Fで論理積 $C1/2EF0 BRA $04 [$2EF6] ;フラグにかかわりなく常に分岐[$2EF6] ;ネガティブフラグOFF キャラが石化または戦闘不能ではない $C1/2EF2 LDA $7E001C,x[$7E:A01C] ;Aに[$7E:A01C]をロード $C1/2EF6 STA $001E,y[$00:1C1E] ;Aを[$00:1C1E]に書き込み $C1/2EF9 JSR $2F1C [$C1:2F1C] ;[$C1:2F1C]へジャンプ
上処理は、技を出したキャラの向き(1人目なら[$00:1C1D])に、
([$7E:A01D] & $80) | (([$7E:A01D]<<1) & $03)
を新たに書き込む処理である。
つまり、技使用中のキャラの向きは[$7E:A01D]で処理されていることがわかる。
$C1/2F1C LDA #$FF ;Aに$FFをロード $C1/2F1E STA $13 [$00:0313] ;Aを[$00:0313]に書き込み $C1/2F20 LDA $7E0008,x[$7E:A008] ;Aに[$7E:A008]をロード $C1/2F24 ASL A ;Aを算術左シフト *2 $C1/2F25 LDA $7E0009,x[$7E:A009] ;Aに[$7E:A009]をロード $C1/2F29 ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/2F2A BCS $02 [$2F2E] ;キャリーフラグが立っているとき[$2F2E]分岐 ;キャリーフラグOFF $C1/2F2C STZ $13 [$00:0313] ;[$00:0313]に$00を書き込み ;キャリーフラグON $C1/2F2E STA $12 [$00:0312] ;Aを[$00:0312]に書き込み $C1/2F30 LDA #$FF ;Aに$FFをロード $C1/2F32 STA $15 [$00:0315] ;Aを[$00:0315]に書き込み $C1/2F34 LDA $7E0031,x[$7E:A031] ;Aに[$7E:A031]をロード $C1/2F38 BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/2F3A BNE $02 [$2F3E] ;ゼロフラグが立っていないとき[$2F3E]分岐 ;ゼロフラグON $C1/2F3C STZ $15 [$00:0315] ;[$00:0315]に$00を書き込み ;ゼロフラグOFF $C1/2F3E STA $14 [$00:0314] ;Aを[$00:0314]に書き込み $C1/2F40 STZ $17 [$00:0317] ;[$00:0317]に$00を書き込み $C1/2F42 STZ $16 [$00:0316] ;[$00:0316]に$00を書き込み $C1/2F44 LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/2F46 BIT #$04 ;Aと$04で論理積(ステータスフラグ変更のみ) $C1/2F48 BNE $06 [$2F50] ;ゼロフラグが立っていないとき[$2F50]分岐 ;ゼロフラグON $C1/2F4A LDA $7E0020,x[$7E:A020] ;Aに[$7E:A020]をロード $C1/2F4E STA $16 [$00:0316] ;Aを[$00:0316]に書き込み ;ゼロフラグOFF $C1/2F50 RTS ;サブルーチン戻り $C1/2EFC REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/2EFE LDA $12 [$00:0312] ;Aに[$00:0313][$00:0312]をロード $C1/2F00 ADC $14 [$00:0314] ;A + [$00:0315][$00:0314] $C1/2F02 CLC ;キャリーフラグクリア $C1/2F03 ADC $16 [$00:0316] ;A + [$00:0317][$00:0316] $C1/2F05 STA $0010,y[$00:1C10] ;Aを[$00:1C11][$00:1C10]に書き込み $C1/2F08 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/2F0A JSR $2F51 [$C1:2F51] ;[$C1:2F51]へジャンプ
以上から、
[$00:0313][$00:0312] = [$7E:A008][$7E:A009]<<1
[$00:0315][$00:0314] = ([$7E:A031]の値次第で)$FF[$7E:A031] または $00[$7E:A031]
[$00:0317][$00:0316] = ([$00:0310]の値次第で)$0000 または $00[$7E:A020]
[$00:1C11][$00:1C10](キャラの位置のX軸(ピクセル単位)) = [$00:0313][$00:0312] + [$00:0315][$00:0314] + [$00:0317][$00:0316]
モーション中にキャラが横方向に移動した場合、キャラの位置は上のように計算されることになる。
$C1/2F51 LDA #$FF ;Aに$FFをロード $C1/2F53 STA $13 [$00:0313] ;Aを[$00:0313]に書き込み $C1/2F55 LDA $7E000A,x[$7E:A00A] ;Aに[$7E:A00A]をロード $C1/2F59 ASL A ;Aを算術左シフト *2 $C1/2F5A LDA $7E000B,x[$7E:A00B] ;Aに[$7E:A00B]をロード $C1/2F5E ROL A ;Aをキャリーフラグを含めた9bitで左ローテート $C1/2F5F BCS $02 [$2F63] ;キャリーフラグが立っているとき[$2F63]分岐 ;キャリーフラグOFF $C1/2F61 STZ $13 [$00:0313] ;[$00:0313]に$00を書き込み ;キャリーフラグON $C1/2F63 STA $12 [$00:0312] ;Aを[$00:0312]に書き込み $C1/2F65 LDA #$FF ;Aに$FFをロード $C1/2F67 STA $15 [$00:0315] ;Aを[$00:0315]に書き込み $C1/2F69 LDA $7E0033,x[$7E:A033] ;Aに[$7E:A033]をロード $C1/2F6D BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/2F6F BNE $02 [$2F73] ;ゼロフラグが立っていないとき[$2F73]分岐 $C1/2F71 STZ $15 [$00:0315] ;[$00:0313]に$00を書き込み $C1/2F73 STA $14 [$00:0314] ;Aを[$00:0314]に書き込み $C1/2F75 STZ $17 [$00:0317] ;[$00:0317]に$00を書き込み $C1/2F77 STZ $16 [$00:0316] ;[$00:0316]に$00を書き込み $C1/2F79 LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/2F7B BIT #$02 ;Aと$02で論理積(ステータスフラグ変更のみ) $C1/2F7D BNE $06 [$2F85] ;ゼロフラグが立っていないとき[$2F85]分岐 $C1/2F7F LDA $7E0022,x[$7E:A022] ;Aに[$7E:A022]をロード $C1/2F83 STA $16 [$00:0316] ;Aを[$00:0316]に書き込み $C1/2F85 RTS ;サブルーチン戻り $C1/2F0D REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/2F0F LDA $12 [$00:0312] ;Aに[$00:0313][$00:0312]をロード $C1/2F11 ADC $14 [$00:0314] ;A + [$00:0315][$00:0314] $C1/2F13 CLC ;キャリーフラグクリア $C1/2F14 ADC $16 [$00:0316] ;A + [$00:0317][$00:0316] $C1/2F16 STA $0012,y[$00:1C12] ;Aを[$00:1C13][$00:1C12]に書き込み キャラの位置のY軸(ピクセル単位) $C1/2F19 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/2F1B RTS ;サブルーチン戻り $C1/A4BF JSR $6111 [$C1:6111] ;[$C1:6111]へジャンプ
こちらはY座標、つまり縦方向に移動した場合の計算。
[$00:0313][$00:0312] = [$7E:A00B][$7E:A00A]<<1
[$00:0315][$00:0314] = ([$7E:A033]の値次第で)$FF[$7E:A033] または $00[$7E:A033]
[$00:0317][$00:0316] = ([$00:0310]の値次第で)$0000 または $00[$7E:A022]
[$00:1C13][$00:1C12](キャラの位置のY軸(ピクセル単位)) = [$00:0313][$00:0312] + [$00:0315][$00:0314] + [$00:0317][$00:0316]
となる。
[$7E:A008][$7E:A009], [$7E:A00B][$7E:A00A]
[$7E:A032][$7E:A031], [$7E:A034][$7E:A033]
[$7E:A020][$7E:A01F], [$7E:A022][$7E:A021]
あたりが、キャラのモーション中のピクセル単位の座標計算に関わっていることがわかる。
ここから少し処理を飛ばす。
$C1/5F89 STZ $FE [$00:03FE] ;[$00:03FE]に$00を書き込み $C1/5F8B LDY #$D000 ;Yに$D000をロード $C1/5F8E STY $1C [$00:031C] ;Yを[$00:031D][$00:031C]に書き込み $C1/5F90 LDY #$0000 ;Yに$0000をロード $C1/5F93 STY $16 [$00:0316] ;Yを[$00:0317][$00:0316]に書き込み $C1/5F95 LDX #$1C00 ;Xに$1C00をロード $C1/5F98 STX $1E [$00:031E] ;Xを[$00:031F][$00:031E]に書き込み $C1/5F9A LDA $0000,x[$00:1C00] ;Aに[$00:1C00](キャラID)をロード $C1/5F9D BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/5F9F BNE $2D [$5FCE] ;ゼロフラグが立っていないとき[$5FCE]分岐 $C1/5FA1 LDA $0001,x[$00:1C01] ;Aに[$00:1C01]をロード(戦闘状態) $C1/5FA4 BIT #$20 ;Aと$20で論理積(ステータスフラグ変更のみ) $C1/5FA6 BEQ $26 [$5FCE] ;ゼロフラグが立っているとき[$5FCE]分岐 $C1/5FA8 LDA $001E,x[$00:1C1E] ;Aに[$00:1C1E]をロード $C1/5FAB BIT #$80 ;Aと$80で論理積(ステータスフラグ変更のみ) $C1/5FAD BNE $0D [$5FBC] ;ゼロフラグが立っていないとき[$5FBC]分岐 $C1/5FAF ORA #$80 ;Aと$80で論理和 $C1/5FB1 STA $001E,x[$00:1C1E] ;Aを[$00:1C1E]に書き込み $C1/5FB4 LDY $000C,x[$00:1C0C] ;Yに[$00:1C0C]をロード $C1/5FB7 JSR $5FE0 [$C1:5FE0] ;[$C1:5FE0]へジャンプ $C1/5FE0 STA $18 [$00:0318] ;Aを[$00:0318]に書き込み $C1/5FE2 INC $FE [$00:03FE] ;A[$00:03FE]をインクリメント +1 $C1/5FE4 PHX ;Xレジスタをスタックにプッシュ $C1/5FE5 LDX $16 [$00:0316] ;Xに[$00:0316]をロード $C1/5FE7 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/5FE9 TYA ;Yレジスタの値をAレジスタに転送 $C1/5FEA STA $7EE000,x[$7E:E000] ;Aを[$7E:E000]に書き込み $C1/5FEE INX ;Xをインクリメント +1 $C1/5FEF INX ;Xをインクリメント +1 $C1/5FF0 STX $16 [$00:0316] ;Xを[$00:0316]に書き込み $C1/5FF2 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/5FF4 PLX ;Xレジスタにスタックからプル $C1/5FF5 LDA $18 [$00:0318] ;Aに[$00:0318]をロード $C1/5FF7 BIT #$10 ;Aと$10で論理積(ステータスフラグ変更のみ) $C1/5FF9 BEQ $29 [$6024] ;ゼロフラグが立っているとき[$6024]分岐 $C1/6024 LDA $0000,x[$00:1C00] ;Aに[$00:1C00]をロード キャラID $C1/6027 XBA ;Aレジスタの上位バイトと下位バイトを交換 $C1/6028 LDA #$00 ;Aに$00をロード $C1/602A REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/602C LSR A ;Aを論理右シフト (/2) $C1/602D STA $10 [$00:0310] ;Aを[$00:0311][$00:0310]に書き込み = $(キャラID)00 /2 $C1/602F SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/6031 LDA $001D,x[$00:1C1D] ;Aに[$00:1C1D]をロード キャラの向き $C1/6034 AND #$02 ;Aと$02で論理積 $C1/6036 BEQ $07 [$603F] ;ゼロフラグが立っているとき[$603F]分岐 $C1/6038 LDA #$40 ;Aに$40をロード $C1/603A CLC ;キャリーフラグクリア $C1/603B ADC $10 [$00:0310] ;A + [$00:0310] $C1/603D STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/603F LDA $18 [$00:0318] ;Aに[$00:0318]をロード $C1/6041 ASL A ;Aを算術左シフト *2 $C1/6042 ASL A ;Aを算術左シフト *2 $C1/6043 CLC ;キャリーフラグクリア $C1/6044 ADC $10 [$00:0310] ;A + [$00:0310] $C1/6046 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/6048 REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/604A LDA $10 [$00:0310] ;Aに[$00:0311][$00:0310]をロード $(キャラID)00 /2 + $40 $C1/604C ADC $D50020[$D5:0020] ;A + [$D5:0021][$D5:0020](56CB) $C1/6050 TAX ;Aの値をXレジスタに転送 $C1/6051 LDA $D50000,x[$D5:5F8B] ;Aに[$D5:5F8C][$D5:5F8B]をロード $(キャラID)00 /2 + $40 + [$D5:0021][$D5:0020](56CB) $C1/6055 STA $10 [$00:0310] ;Aを[$00:0311][$00:0310]に書き込み $C1/6057 AND #$07FF ;Aと$07FFで論理積 $C1/605A ASL A ;Aを算術左シフト *2 $C1/605B ASL A ;Aを算術左シフト *2 $C1/605C ASL A ;Aを算術左シフト *2 $C1/605D ASL A ;Aを算術左シフト *2 $C1/605E ASL A ;Aを算術左シフト *2 $C1/605F STA $12 [$00:0312] ;Aを[$00:0313][$00:0312]に書き込み $C1/6061 LDA $D50002,x[$D5:5F8D] ;Aに[$D5:5F8E][$D5:5F8D]をロード $C1/6065 STA $14 [$00:0314] ;Aを[$00:0315][$00:0314]に書き込み $C1/6067 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/6069 LDA $11 [$00:0311] ;Aに[$00:0311]をロード $C1/606B AND #$F8 ;Aと$F8で論理積 $C1/606D LSR A ;Aを論理右シフト (/2) $C1/606E LSR A ;Aを論理右シフト (/2) $C1/606F LSR A ;Aを論理右シフト (/2) $C1/6070 ADC #$D7 ;A + $D7 $C1/6072 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み [$00:0310] = ([$00:0311] & $F8)/8 + $D7 $C1/6074 LDA #$10 ;Aに$10をロード $C1/6076 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/6078 ASL $15 [$00:0315] ;[$00:0315]([$D5:5F8E])を算術左シフト *2 $C1/607A ROL $14 [$00:0314] ;[$00:0314]([$D5:5F8D])をキャリーフラグを含めた9bitで左ローテート $C1/607C BCS $2C [$60AA] ;キャリーフラグが立っているとき[$60AA]分岐 ;キャリーフラグOFF $C1/607E LDA #$08 ;Aに$08をロード $C1/6080 STA $4320 [$00:4320] ;Aを[$00:4320]に書き込み $C1/6083 LDX #$029A ;Xに$029Aをロード $C1/6086 STX $4322 [$00:4322] ;Xを[$00:4323][$00:4322]に書き込み $C1/6089 LDA #$C1 ;Aに$C1をロード $C1/608B STA $4324 [$00:4324] ;Aを[$00:4324]に書き込み $C1/608E LDX #$0020 ;Xに$0020をロード $C1/6091 STX $4325 [$00:4325] ;Xを[$00:4326][$00:4325]に書き込み $C1/6094 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/6096 STX $2181 [$00:2181] ;Xを[$00:2182][$00:2181]に書き込み $C1/6099 LDA #$00 ;Aに$00をロード $C1/609B STA $2183 [$00:2183] ;Aを[$00:2183]に書き込み $C1/609E JSR $34EA [$C1:34EA] ;[$C1:34EA]へジャンプ $C1/60A1 LDA #$04 ;Aに$04をロード $C1/60A3 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み $C1/60A6 REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/60A8 BRA $30 [$60DA] ;フラグにかかわりなく常に分岐[$60DA] ;キャリーフラグON $C1/60AA LDA #$00 ;Aに$00をロード $C1/60AC STA $4320 [$00:4320] ;Aを[$00:4320]に書き込み $C1/60AF LDX $12 [$00:0312] ;Xに[$00:0313][$00:0312]をロード $C1/60B1 STX $4322 [$00:4322] ;Xを[$00:4323][$00:4322]に書き込み $C1/60B4 LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/60B6 STA $4324 [$00:4324] ;Aを[$00:4324]に書き込み $C1/60B9 LDX #$0020 ;Xに$0020をロード $C1/60BC STX $4325 [$00:4325] ;Xを[$00:4326][$00:4325]に書き込み $C1/60BF LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/60C1 STX $2181 [$00:2181] ;Xを[$00:2182][$00:2181]に書き込み $C1/60C4 LDA #$00 ;Aに$00をロード $C1/60C6 STA $2183 [$00:2183] ;Aを[$00:2183]に書き込み $C1/60C9 JSR $34EA [$C1:34EA] ;[$C1:34EA]へジャンプ ; ;(中略) ; $C1/60CC LDA #$04 ;Aに$04をロード $C1/60CE STA $420B [$00:420B] ;Aを[$00:420B]に書き込み GDMA転送(チャネル2)転送 ;(後略)
$C1/607C以降はGDMA転送(チャネル2)の処理。
$C1/607Cでキャリーフラグによる分岐があるが、これは[$D5:5F8E]*2の後(キャリーフラグが変動する)、[$D5:5F8D]<<2(キャリーフラグを含めた9bitで左ローテート)の結果、キャリーフラグが立つかどうかで決まっている。
アキラの場合、[$D5:5F8E][$D5:5F8D] = $EEEF(%1110 1110 1110 1111)である。
%1110 1110*2はキャリーオーバー(一番左の桁繰り上がり)だからキャリーフラグが立ち、%1 1110 1111を左ローテートしてもキャリーフラグは立ったまま。
ということだからアキラの場合は$C1/60AAにジャンプする。
[$D5:5F8E][$D5:5F8D]がどういう計算で出た値かは後述。
キャリーフラグOFFの$C1/607E~$C1/60A8についても後述。
$C1/60AA~$C1/60C9は、GDMA転送(チャネル2)の準備である。
スプライトについて > サブルーチンで紹介したが、戦闘では開始時にGDMA転送8チャネルの中で7チャネル分には初期設定がなされる。
チャネル2は[$00:4321]に$80(WRAM)が入っているので、WRAMへの転送ということになる。
| GDMA チャネル2 アドレス | 内容 | 値 |
|---|---|---|
[$00:4320] | GDMA / HDMA設定レジスタ | $00 1レジスタ1書き込み |
[$00:4321] | 転送先指定 | $80(WRAM) |
[$00:4323][$00:4322] | データ元アドレス(開始位置) | [$00:0313][$00:0312]($1600) |
[$00:4324] | データ元バンク | [$00:0310]($DA) |
[$00:4326][$00:4325] | 転送するデータ量 | $0020 |
[$00:2183][$00:2182][$00:2181] | WRAMアドレス | $00:[$00:031D][$00:031C]($D000) |
WRAMアドレス$00:[$00:031D][$00:031C]にバンク[$00:0310]:アドレス[$00:0313][$00:0312]から$20バイト分の値を転送しろということになる。
実際の転送は$C1/60CEで行われている。
ちなみに上の処理だが、$DA:1600~$DA:161FをWRAM$00:D000~$00:D01F(バンク$00は、バンク$7Eのミラーリング)に転送している。
最終的に、$7E:D000~$7E:D380は、VRAMアドレス$4000~$4680に配置されるのだが、これはキャラの(ここではアキラの)タイルデータ1人分、つまり8×8マスサイズのタイル16個で構成された32×32ピクセルサイズのスプライトである。
つまり、$DA:1600~のあたりにアキラの戦闘用スプライトのタイルデータが格納されていて、$DA:1600というアドレスを計算するため$C1/5FE0~$C1/607Cで処理をしていたということだ。
$DA:1600~$DA:161Fはタイル1個分(8×8マスサイズ)に相当する。
ということで、[$00:0310],[$00:0313][$00:0312], [$00:031D][$00:031C]は何なのか、その前の処理を見てみる。
[$00:0310]((キャラID,アキラは$11)*$100) /2 + $40 + [$D5:0021][$D5:0020](56CB) = $5F8B
を計算し、計算値をアドレスにして、バンク$D5の値を2バイトで呼び出す。
[$D5:5F8C][$D5:5F8B] = $18B0(アキラの場合)
[$00:0310] = ([$D5:5F8C] & $F8)/8 + $D7 = ($18 & $F8)/8 + $D7 = $DA
[$00:0313][$00:0312][$00:0313][$00:0312] = ([$D5:5F8C][$D5:5F8B] & $07FF) * $20
= ($18B0 & $07FF) * $20 = $1600
[$00:031D][$00:031C]$C1/5F8Eで設定した$D000
以上から、キャラIDで計算される[$D5:5F8C][$D5:5F8B](2バイト)に格納された数値は、該当キャラの戦闘用スプライトのタイルデータが格納されたアドレスを計算するための数値だった、ということがわかる。
また、$C1/607Cのキャリーフラグによる分岐で処理が変わる時、[$D5:5F8E][$D5:5F8D]の値の計算値で分岐したから、[$D5:5F8B]+2バイトのアドレスもセットで、4バイトの値が諸々の計算に必要であることもわかる。
((キャラID)*$100) /2 + $40 + $56CBを味方キャラ全員計算して、バンク$D5をつけてみると以下のようになる。
実際の値も併記した。2バイトの値なので、アドレスは+1した値も含む。
| ID | キャラクター名 | アドレス | 値(2バイト) |
|---|---|---|---|
| $00 | オルステッド | $D5:570B | $00B1 |
| $01 | ストレイボウ | $D5:578B | $0213 |
| $02 | ウラヌス | $D5:580B | $0376 |
| $03 | ハッシュ | $D5:588B | $04E8 |
| $04 | ポゴ | $D5:590B | $0658 |
| $05 | ゴリ | $D5:598B | $07D4 |
| $06 | べる | $D5:5A0B | $0931 |
| $07 | ざき | $D5:5A8B | $0A8B |
| $08 | 心山拳老師 | $D5:5B0B | $0BE2 |
| $09 | ユン・ジョウ | $D5:5B8B | $0D1D |
| $0A | レイ・クウゴ | $D5:5C0B | $0E6C |
| $0B | サモ・ハッカ | $D5:5C8B | $0FDE |
| $0C | サンダウン | $D5:5D0B | $1174 |
| $0D | マッドドッグ | $D5:5D8B | $12EB |
| $0E | おぼろ丸 | $D5:5E0B | $145B |
| $0F | とらわれの男 | $D5:5E8B | $15BC |
| $10 | 高原 日勝 | $D5:5F0B | $1724 |
| $11 | アキラ | $D5:5F8B | $18B0 |
| $12 | タロイモ | $D5:600B | $1A2F |
| $13 | 無法松 | $D5:608B | $1B96 |
| $14 | キューブ | $D5:610B | $1CE0 |
| $15 | キャプテンS | $D5:618B | $1E29 |
| $16 | カラクリ丸 | $D5:620B | $145B |
おぼろ丸とカラクリ丸のスプライトは共通なので(色データのみ異なる)、同じ値が入っている。
ここからさらに、スプライトのタイルデータが入ったアドレスを計算してみる。
バンクは、(上1バイト & $F8)/8 + $D7
アドレスは、(2バイト値 & $07FF) * $20
| ID | キャラクター名 | 値(2バイト) | バンク | アドレス |
|---|---|---|---|---|
| $00 | オルステッド | $00B1 | $D7 | $1620 |
| $01 | ストレイボウ | $0213 | $D7 | $4260 |
| $02 | ウラヌス | $0376 | $D7 | $6EC0 |
| $03 | ハッシュ | $04E8 | $D7 | $9D00 |
| $04 | ポゴ | $0658 | $D7 | $CB00 |
| $05 | ゴリ | $07D4 | $D7 | $FA80 |
| $06 | べる | $0931 | $D8 | $2620 |
| $07 | ざき | $0A8B | $D8 | $5160 |
| $08 | 心山拳老師 | $0BE2 | $D8 | $7C40 |
| $09 | ユン・ジョウ | $0D1D | $D8 | $A3A0 |
| $0A | レイ・クウゴ | $0E6C | $D8 | $CD80 |
| $0B | サモ・ハッカ | $0FDE | $D8 | $FBC0 |
| $0C | サンダウン | $1174 | $D9 | $2E80 |
| $0D | マッドドッグ | $12EB | $D9 | $5D60 |
| $0E | おぼろ丸 | $145B | $D9 | $8B60 |
| $0F | とらわれの男 | $15BC | $D9 | $B780 |
| $10 | 高原 日勝 | $1724 | $D9 | $E480 |
| $11 | アキラ | $18B0 | $DA | $1600 |
| $12 | タロイモ | $1A2F | $DA | $45E0 |
| $13 | 無法松 | $1B96 | $DA | $72C0 |
| $14 | キューブ | $1CE0 | $DA | $9C00 |
| $15 | キャプテンS | $1E29 | $DA | $C520 |
| $16 | カラクリ丸 | $145B | $D9 | $8B60 |
$D7:1620~$DA:DA2F(あたり)が戦闘中の味方キャラのスプライトのタイルデータと思われる。
ちなみに敵のタイルデータは$DB:F979から。
おおまかに、$D6:F900~$DF:FFFFは戦闘関係のタイルデータの格納領域と思われる。
さて、$C1/607Cでキャリーフラグが立たない時の処理$C1/607E~$C1/60A8も見ておこう。
こちらでもGDMA チャネル2の設定をしているが、$C1/60A3の時点で[$00:420B]に$04を書き込んでいるのでGDMA転送を行っている。
| GDMA チャネル2 アドレス | 内容 | 値 |
|---|---|---|
[$00:4320] | GDMA / HDMA設定レジスタ | $08 1レジスタ1書き込み アドレスfixed(固定) |
[$00:4321] | 転送先指定 | $80(WRAM) |
[$00:4323][$00:4322] | データ元アドレス(開始位置) | $029A |
[$00:4324] | データ元バンク | $C1 |
[$00:4326][$00:4325] | 転送するデータ量 | $0020 |
[$00:2183][$00:2182][$00:2181] | WRAMアドレス | $00:[$00:031D][$00:031C] |
転送先自体はWRAMアドレスで、値が[$00:031D][$00:031C]と変わらない。
ただ、転送するデータは$C1:029Aの固定値である。値は$00。
何らかの理由でタイルデータを$00で埋めるための分岐である。
さて、話がいろいろと飛んだ気もするが、結局、技データ00~05から何がどう読み取れたのか。
アキラの「ローキック」の場合、技データ00~01の値が「$9DF8」で、$D5:9DF8からジャンプ先を計算して……と値を読んではアドレスを計算していくことになるが、読み込まれるのは$D5:9E26まで($D5:9E27から「ヘビーブロウ」のデータ)。
「ローキック」のアキラ自身のモーションは、初期構え→直立→歩き→1ピクセル下がる→蹴り上げ1→蹴り上げ2→直立というような感じになるが、この動きとタイミング、サウンドエフェクトが$D5:9DF8~$D5:9E26で読み込まれている。
[$D5:9DF9][$D5:9DFA]のように2バイトで次のアドレスを読むこともあったりするのでややこしい。
$C1/A6D0 LDA $C10000,x[$C1:A604] ;Aに[$C1:A605][$C1:A604]をロード A:B000 $C1/A6D4 STA $1A [$00:031A] ;Aを[$00:031B][$00:031A]に書き込み $C1/A6D6 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード X:9DF9 $C1/A6D8 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/A6DA JMP ($031A)[$C1:B000] ;[$C1:B000]へジャンプ(レジスタをスタックに積まない) A:B000がアドレス
の処理が肝というか問題で、$C10000,xで呼び出す値がそのままJMP ($031A)の飛び先アドレスになる。
X = (($D50000 + 技データ00~01のアドレスの値) & $00F0)/8 + $A5E6
だから、$C10000,xは最小値が$A5E6で、最大値が&F0/8 + $A5E6 = $A604であろう。
ということは2バイトずつ値を取っていくから、$C1/A6D0でロードされるアドレスは、$C1:A5E6~$C1:A605(2バイトずつ)のはずだ。
$C1:A5E6~$C1:A605の実際の値は以下のようになっている(16バイト区切り)。
2バイト区切りで並び替えて、バンク$C1をつけると、
$C1:A72F
$C1:A751
$C1:A76E
$C1:A79E
$C1:A7CE
$C1:A7EF
$C1:A810
$C1:A82D
$C1:A839
$C1:A8C9
$C1:ACA0
$C1:AF58
$C1:AF84
$C1:AFA8
$C1:AFD6
$C1:B000
上の値から始まるサブルーチンにジャンプすることになる。
この時点で、モーションのサブルーチン開始位置が16あるらしいことがわかる。
なお、ほとんどの技で、「$D50000 + 技データ00~01のアドレスの値」(アキラなら$D5:9DF8 = $FD)は$FDなので、だいたいの場合、$C1/A6DAで最初に呼び出すジャンプ先は$C1/B000である。
$C1/B000~でも似たような処理でジャンプ先を計算する。
$C1/B00C LDA $C10000,x[$C1:A660] ;Aに[$C1:A661][$C1:A660]をロード A:B413 $C1/B010 STA $1A [$00:031A] ;Aを[$00:031B][$00:031A]に書き込み $C1/B012 LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/B014 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/B016 JMP ($031A)[$C1:B413] ;[$C1:B413]へジャンプ(レジスタをスタックに積まない)
X = (($D50000 + 技データ00~01のアドレスの値) & $000F)*2 + $A646
だから、$C10000,xは最小値が$A646で、最大値が&0F*2 + $A646 = $A664であろう。
$C1:A646~$C1:A664の実際の値は、
2バイト区切りで並び替えて、バンク$C1をつけると、
$C1:B019
$C1:B023
$C1:B02D
$C1:B200
$C1:B254
$C1:B26B
$C1:B273
$C1:B30F
$C1:B315
$C1:B39E
$C1:B3D3
$C1:B408
$C1:B410
$C1:B413
$C1:B419
$C1:B41F
これらもモーション関係のサブルーチンの開始位置ということになる。
$C1/A72F~$C1/B421あたりにモーション関係のサブルーチンがひととおり設置されているということのようだが、とりあえずこれ以上の紹介は省く(筆者もよくわからないので)。
なお、実際の処理を見ると、$C1/B79CのRTS処理までがモーション関係のサブルーチンのようである。
技モーションの指定の中には効果音も含まれる。
処理を見ていくと、技データ20と技データ24が効果音を設定していることがわかる。
効果音を鳴らす処理は$C1/11D1~$C1/11E3で行われており、間の$C3/0004~$C3/017Fの処理で、サウンド用CPUのAPUにI/Oポートアドレス$00:2140~$00:2143を通してデータを書き込んでいる。
$C1/11D1 LDA $0070 [$00:0070] ;Aに[$00:0070]をロード デモ画面だと$01、それ以外$00 $C1/11D4 CMP #$01 ;Aと$01を減算比較(ステータスレジスタ変更のみ) $C1/11D6 BEQ $0B [$11E3] ;ゼロフラグが立っているとき[$11E3]分岐 $C1/11D8 LDA $0040 [$00:0040] ;Aに[$00:0040]をロード $C1/11DB CMP #$04 ;Aと$04を減算比較(ステータスレジスタ変更のみ) 最終編ベストエンド $C1/11DD BEQ $04 [$11E3] ;ゼロフラグが立っているとき[$11E3]分岐 $C1/11DF JSL $C30004[$C3:0004] ;[$C3:0004]へジャンプ $C3/0004 JMP $0131 [$C3:0131] ;[$C3:0131]へジャンプ(レジスタをスタックに積まない) $C3/0131 PHB ;DBレジスタをスタックにプッシュ $C3/0132 PHD ;Dレジスタをスタックへプッシュ $C3/0133 PHP ;ステータスレジスタをスタックへプッシュ $C3/0134 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C3/0136 REP #$10 ;X/Yレジスタを16bit幅に変更、Xフラグをクリア $C3/0138 PHA ;Aレジスタをスタックにプッシュ $C3/0139 PHX ;Xレジスタをスタックにプッシュ $C3/013A PHY ;Yレジスタをスタックへプッシュ $C3/013B SEP #$20 ;MフラグON Aレジスタは8bit幅 $C3/013D LDA #$00 ;Aに$00をロード $C3/013F PHA ;Aレジスタをスタックにプッシュ $C3/0140 PLB ;DBレジスタにスタックからプル $C3/0141 LDX #$1D00 ;Xに$1D00をロード $C3/0144 PHX ;Xレジスタをスタックにプッシュ $C3/0145 PLD ;Dレジスタにスタックからプル $C3/0146 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C3/0148 LDA $00 [$00:1D00] ;Aに[$00:1D00]をロード $C3/014A BEQ $25 [$0171] ;ゼロフラグが立っているとき[$0171]分岐 $C3/014C BMI $19 [$0167] ;ネガティブフラグが立っているとき[$0167]分岐 $C3/014E CMP #$10 ;Aと$10を減算比較(ステータスレジスタ変更のみ) $C3/0150 BCC $1F [$0171] ;キャリーフラグが立っていないとき[$0171]分岐 $C3/0152 CMP #$18 ;Aと$18を減算比較(ステータスレジスタ変更のみ) $C3/0154 BCS $0D [$0163] ;キャリーフラグが立っているとき[$0163]分岐 $C3/0163 CMP #$30 ;Aと$30を減算比較(ステータスレジスタ変更のみ) $C3/0165 BCS $03 [$016A] ;キャリーフラグが立っているとき[$016A]分岐 $C3/0167 JMP $05AC [$C3:05AC] ;[$C3:05AC]へジャンプ(レジスタをスタックに積まない) $C3/05AC SEP #$20 ;MフラグON Aレジスタは8bit幅 $C3/05AE LDA $03 [$00:1D03] ;Aに[$00:1D03]をロード $C3/05B0 STA $2143 [$00:2143] ;Aを[$00:2143]に書き込み $C3/05B3 LDA $02 [$00:1D02] ;Aに[$00:1D02]をロード $C3/05B5 STA $2142 [$00:2142] ;Aを[$00:2142]に書き込み$0302 $C3/05B8 LDA $01 [$00:1D01] ;Aに[$00:1D01]をロード $C3/05BA STA $2141 [$00:2141] ;Aを[$00:2141]に書き込み $C3/05BD LDA $00 [$00:1D00] ;Aに[$00:1D00]をロード $C3/05BF LDX #$0800 ;Xに$0800をロード $C3/05C2 STA $2140 [$00:2140] ;Aを[$00:2140]に書き込み ;ループ処理 $C3/05C5 CMP $2140 [$00:2140] ;Aと[$00:2140]を減算比較(ステータスレジスタ変更のみ) $C3/05C8 BEQ $05 [$05CF] ;ゼロフラグが立っているとき[$05CF]分岐 $C3/05CA DEX ;Xをデクリメント -1 $C3/05CB BNE $F8 [$05C5] ;ゼロフラグが立っていないとき[$05C5]分岐 ;ループ処理ここまで $C3/05C5 CMP $2140 [$00:2140] ;Aと[$00:2140]を減算比較(ステータスレジスタ変更のみ) $C3/05C8 BEQ $05 [$05CF] ;ゼロフラグが立っているとき[$05CF]分岐 $C3/05CF CMP #$F0 ;Aと$F0を減算比較(ステータスレジスタ変更のみ) $C3/05D1 BCC $21 [$05F4] ;キャリーフラグが立っていないとき[$05F4]分岐 $C3/05F4 JMP $0171 [$C3:0171] ;[$C3:0171]へジャンプ(レジスタをスタックに積まない) $C3/0171 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C3/0173 STZ $00 [$00:1D00] ;[$00:1D00]に$00を書き込み $C3/0175 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C3/0177 REP #$10 ;X/Yレジスタを16bit幅に変更、Xフラグをクリア $C3/0179 PLY ;Yレジスタにスタックからプル $C3/017A PLX ;Xレジスタにスタックからプル $C3/017B PLA ;Aレジスタにスタックからプル $C3/017C PLP ;Pレジスタにスタックからプル $C3/017D PLD ;Dレジスタにスタックからプル $C3/017E PLB ;DBレジスタにスタックからプル $C3/017F RTL ;サブルーチン戻り $C1/11E3 RTS ;サブルーチン戻り
$C1/11D1~$C1/11DDは、[$00:0070]が$01ではない、かつ、[$00:0040]が$04ではない場合は効果音を鳴らす処理に進む、という意味になる。
[$00:0070]が$01はタイトルデモでの戦闘のこと。
[$00:0040]が$04になるのは最終編ベストエンドで主人公たちが最強技を見せる場面。
タイトルデモ中やエンディングにて、技を出す場面で効果音が鳴らないのはここの処理のため。
以上から、
$00:1D00→$00:2140
$00:1D01→$00:2141
$00:1D02→$00:2142
$00:1D03→$00:2143
と書き込むことになるが、細かいことは横に置いておくとして、技データ20と技データ24は[$00:1D01]に書き込まれて効果音として流れる。
$C1/11D1より前の処理を見ると、ローキックの場合、以下のようになる。
ローキックの技データ20は$14、技データ24は$98。
;技データ20書き込み $C1/1255 LDA #$00 ;Aに$00をロード $C1/1257 STA $7EFEFC[$7E:FEFC] ;Aを[$7E:FEFC]に書き込み $C1/125B PHX ;Xレジスタをスタックにプッシュ $C1/125C LDX $68 [$00:0368] ;Xに[$00:0369][$00:0368]をロード 技データ呼び出し用の値 $C1/125E LDA $D50014,x[$D5:2E0D] ;Aに[$D5:2E0D]をロード 技データ20 $C1/1262 PLX ;Xレジスタにスタックからプル $C1/1263 BRA $45 [$12AA] ;フラグにかかわりなく常に分岐[$12AA] $C1/12AA CMP #$00 ;Aと$00を減算比較(ステータスレジスタ変更のみ) $C1/12AC BEQ $10 [$12BE] ;ゼロフラグが立っているとき[$12BE]分岐 ;ゼロフラグOFF(技データ20が00ではない) $C1/12AE STA $1D01 [$00:1D01] ;Aを[$00:1D01]に書き込み $C1/12B1 LDA #$18 ;Aに$18をロード $C1/12B3 STA $1D00 [$00:1D00] ;Aを[$00:1D00]に書き込み $C1/12B6 LDA #$80 ;Aに$80をロード $C1/12B8 STA $1D02 [$00:1D02] ;Aを[$00:1D02]に書き込み $C1/12BB JSR $11D1 [$C1:11D1] ;[$C1:11D1]へジャンプ ;$C1/12ACゼロフラグON $C1/12BE RTS ;サブルーチン戻り
技データ20の場合は、技データ20が$00ではない場合は[$00:1D01]に書き込み、[$00:1D00]に$18、[$00:1D02]に$80を書き込んで[$C1:11D1]にジャンプ、というシンプルな処理である。
技データ20が$00だと$C1/12BEにジャンプするのみで効果音の書き込みは行わない。
技データ20が$00の時は、効果音なしということになる。
[$00:1D01]に書き込む値により、$C3/017Fのサウンド処理が終了した時点で鳴る効果音が変わるため、技データ20に入る値は技の効果音だということがわかる。
ちなみに技データ20の$14は「しゅっしゅっ」という感じの音。
また、$7E:FEFCに$00を書き込んでいるが、$7E:FEFCは後の処理で技データ24の効果音が鳴る回数カウンターにあたる。
技データ20は効果音が鳴っても鳴らなくても、この後の技データ24処理用に、ここで$7E:FEFCをリセットしているということになる。
逆に言えば、技データ20の効果音は繰り返し再生できず、多段ヒットする技などで繰り返し同じ音を鳴らしたい時は技データ24に値を書き込んでおく必要がある。
;技データ24書き込み $C1/1265 PHX ;Xレジスタをスタックにプッシュ $C1/1266 LDA $77 [$00:0377] ;Aに[$00:0377]をロード $C1/1268 AND #$0F ;Aと$0Fで論理積 $C1/126A CMP #$06 ;Aと$06を減算比較(ステータスレジスタ変更のみ) $C1/126C BNE $35 [$12A3] ;ゼロフラグが立っていないとき[$12A3]分岐 $C1/126E LDX $68 [$00:0368] ;Xに[$00:0369][$00:0368]をロード 技データ呼び出し用の値 $C1/1270 LDA $D50013,x[$D5:2E0C] ;Aに[$D5:2E0C]をロード 技データ19 $C1/1274 BIT #$40 ;Aと$40で論理積(ステータスフラグ変更のみ) $C1/1276 BEQ $1E [$1296] ;ゼロフラグが立っているとき[$1296]分岐 ;ゼロフラグOFF 技データ19の上2ビット(Bit6)に1が立っている $C1/1278 LDA $7EFEFC[$7E:FEFC] ;Aに[$7E:FEFC]をロード $C1/127C INC A ;Aをインクリメント +1 $C1/127D STA $7EFEFC[$7E:FEFC] ;Aを[$7E:FEFC]に書き込み $C1/1281 LDA $D50015,x[$D5:2E0E] ;Aに[$D5:2E0E]をロード 技データ21 $C1/1285 BPL $1E [$12A5] ;ネガティブフラグが立っていないとき[$12A5]分岐 ;ネガティブフラグON 技データ21の上1ビット(Bit7)に1が立っている(自爆999ダメージ/自滅) $C1/1287 LSR A ;Aを論理右シフト (/2) $C1/1288 LSR A ;Aを論理右シフト (/2) $C1/1289 LSR A ;Aを論理右シフト (/2) $C1/128A LSR A ;Aを論理右シフト (/2) $C1/128B SEC ;キャリーフラグON $C1/128C SBC #$08 ;A(技データ21下1桁) - $08 $C1/128E CMP $7EFEFC[$7E:FEFC] ;Aと[$7E:FEFC]を減算比較(ステータスレジスタ変更のみ) $C1/1292 BMI $11 [$12A5] ;ネガティブフラグが立っているとき[$12A5]分岐 $C1/1294 BRA $0D [$12A3] ;フラグにかかわりなく常に分岐[$12A3] ;$C1/1276でゼロフラグON 技データ19の上2ビット(Bit6)に0が立っている $C1/1296 LDA $7EFEFC[$7E:FEFC] ;Aに[$7E:FEFC]をロード $C1/129A BNE $07 [$12A3] ;ゼロフラグが立っていないとき[$12A3]分岐 ;ゼロフラグON $C1/129C INC A ;Aをインクリメント +1 $C1/129D STA $7EFEFC[$7E:FEFC] ;Aを[$7E:FEFC]に書き込み $C1/12A1 BRA $02 [$12A5] ;フラグにかかわりなく常に分岐[$12A5] ;$C1/126CゼロフラグOFF, $C1/1294ジャンプ先 $C1/12A3 PLX ;Xレジスタにスタックからプル $C1/12A4 RTS ;サブルーチン戻り ;処理合流 $C1/12A5 LDA $D50018,x[$D5:2E11] ;Aに[$D5:2E11]をロード 技データ24 $C1/12A9 PLX ;Xレジスタにスタックからプル $C1/12AA CMP #$00 ;Aと$00を減算比較(ステータスレジスタ変更のみ) $C1/12AC BEQ $10 [$12BE] ;ゼロフラグが立っているとき[$12BE]分岐 $C1/12AE STA $1D01 [$00:1D01] ;Aを[$00:1D01]に書き込み $C1/12B1 LDA #$18 ;Aに$18をロード $C1/12B3 STA $1D00 [$00:1D00] ;Aを[$00:1D00]に書き込み $C1/12B6 LDA #$80 ;Aに$80をロード $C1/12B8 STA $1D02 [$00:1D02] ;Aを[$00:1D02]に書き込み $C1/12BB JSR $11D1 [$C1:11D1] ;[$C1:11D1]へジャンプ ;$C1/12ACゼロフラグON $C1/12BE RTS ;サブルーチン戻り
技データ24の方は、技データ20で説明した通り、繰り返し鳴らすことが可能な効果音を登録できるため、処理が分岐する。
最初の判定が$C1/1270の技データ19の上2ビット(Bit6)の値である。
ここが%0の場合は$C1/1296にジャンプして、$7E:FEFCの値を判定する。
$7E:FEFCが$00でない場合、$7E:FEFCを+1してから、技データ24が$00でなければ技データ24を[$00:1D01]に書き込み、[$00:1D00]に$18、[$00:1D02]に$80を書き込んで[$C1:11D1]にジャンプ、と技データ20とほぼ同じ処理である。
ローキック1ヒットの場合は技データ24の効果音を1回鳴らして、効果音処理は終了である。
$7E:FEFCに$00以外の値が入っている場合は$C1/12A3→$C1/12A4でサブルーチンを抜けるため、効果音が鳴らないことになる。
これは「多段ヒットだがヒット数によって音が変わらない」という場合が該当する。
$C1/1265~の処理は多段ヒットの場合1ヒット毎に通るのだが、1回効果音を鳴らしている場合は$7E:FEFCに01が入った状態なので、効果音を鳴らさない処理へ飛ぶ、ということのようである。
技データ19の上2ビット(Bit6)の値は、多段ヒットの場合に技データ24の効果音をヒット数の分だけ鳴らすかどうか、という判定と思われる。
%0が入っていたら、ヒット数に関わらず1回のみ鳴らす、ということと見てよさそうだ。
では技データ19の上2ビット(Bit6)が%1だとどうなるかというと、まず$7E:FEFCを+1する。
$C1/1285の技データ21のネガティブフラグ判定は、上1ビット(Bit7)に1が立っているかどうかで、1が立っていたら自爆999ダメージまたは自滅技である。
自爆999ダメージまたは自滅技の場合は$C1/1287~$C1/1294の処理が続くが割愛。
自爆999ダメージまたは自滅技でなければ$C1/12A5に進むため、効果音が鳴る。こちらが多段ヒットかつ1ヒット毎に効果音が鳴る技になる(「手裏剣乱糸」など)。
以上が技モーション中の効果音の仕組みである。