基礎知識
戦闘関係
メニュー画面→隊形では、隊形でキャラの位置の入れ替えができる。
隊形の仕様を含め、この時のサブルーチンを紹介する。
空欄との交換により、次に加入するキャラ位置が変わることがある理由も、サブルーチンの処理で説明できる。
隊形入れ替えは、アイテム並び替えで紹介した、アイテム入れ替え機能と似た部分があり、使用するメモリアドレスも共通部分がある。
隊形自体が記録されているアドレスは以下の通り。
| アドレス | 内容 |
|---|---|
$00:0AB0 | 主人公の行動順-1 |
$00:0AB1 | 味方キャラ1の行動順-1 |
$00:0AB2 | 味方キャラ2の行動順-1 |
$00:0AB3 | 味方キャラ3の行動順-1 |
つまり$00~$03のいずれかが入る。
戦闘では数値の小さい順に行動していくことになる(上・左・右・下が順に$00~$03に対応)。
味方1~3の順は加入順。
該当の味方キャラがいない場合でも値は入っている。
メニュー画面ではミラーリング先のバンク$7Eから呼び出すこともあり、$7E:0AB0~$7E:0AB3が対応。
例えば、4人パーティ時、ちょっと変則的に、以下のように値が入っていた場合、
| アドレス | 内容 |
|---|---|
$00:0AB0 | $02(右) |
$00:0AB1 | $01(左) |
$00:0AB2 | $03(下) |
$00:0AB3 | $00(上) |
上と左のキャラの位置を入れ替えると、$00(上)と$01(左)の入れ替えになるから、
| アドレス | 内容 |
|---|---|
$00:0AB0 | $02(右) |
$00:0AB1 | $00(上) |
$00:0AB2 | $03(下) |
$00:0AB3 | $01(左) |
というように値が変わる、ということになる。
メニュー画面は左から順に装備、持ち物、技、隊形、音声、セーブとコマンドが並ぶが、各コマンドを選択すると、左から順に01からナンバリングを振った値が、[$00:056A][$00:0569]に入る。
隊形なら$0004が入る。
| コマンド | [$00:056A][$00:0569] |
|---|---|
| 装備 | $0001 |
| 持ち物 | $0002 |
| 技 | $0003 |
| 隊形 | $0004 |
| 音声 | $0005 |
| セーブ | $0006 |
[$00:0544][$00:0545]の2バイトで、現在のカーソル位置の値が入る。
[$00:0544]は隊形に限らず、メニュー画面ならどの階層(ページ)でもカーソルの位置が入る。
隊形の場合は、上・左・右・下の順に、$0000~$0003が入る。
隊形でキャラを入れ替える時は、メニュー画面を開いた時点からメモリアドレスに各種の値が書き込まれるので、順に説明していく。
まず、メニュー画面を開いた時の処理について。
$C2/4B51 PHP ;ステータスレジスタをスタックへプッシュ $C2/4B52 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/4B54 LDY $00 [$00:0500] ;Yに[$00:0500]($0000)をロード($C2/00C6の処理) ;ループ処理 $C2/4B56 LDA $0AB0,y ;Aに[$7E:0AB0+y]をロード $C2/4B59 AND #$0003 ;Aと$0003で論理積 $C2/4B5C TAX ;Aの値をXレジスタに転送 $C2/4B5D TYA ;Yレジスタの値をAレジスタに転送 $C2/4B5E SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4B60 STA $51,x ;Aを[$00:0551+x]に書き込み $C2/4B62 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/4B64 INY ;Yをインクリメント +1 $C2/4B65 CPY #$0004 ;Yと$0004を減算比較(ステータスレジスタ変更のみ) $C2/4B68 BNE $EC [$4B56] ;ゼロフラグが立っていないとき[$4B56]分岐 ;ループ処理ここまで $C2/4B6A PLP ;Pレジスタにスタックからプル $C2/4B6B RTS ;サブルーチン戻り
隊形の値が入っている$7E:0AB0~$7E:0AB3を、[$00:0551]~[$00:0554]にコピーするループ処理が上。
コピー先のメモリアドレスで各種計算処理を行っていく。
続いて、メニュー画面で「隊形」を選択すると、以下の処理が入る。
$C2/09CE LDA #$FFFF ;Aに$FFFFをロード $C2/09D1 STA $65 [$00:0565] ;Aを[$00:0566][$00:0565]に書き込み $C2/09D3 STA $67 [$00:0567] ;Aを[$00:0568][$00:0567]に書き込み $C2/09D5 JSR $4298 [$C2:4298] ;[$C2:4298]へジャンプ
上の処理で、[$00:0565]~[$00:0568]の4バイト分すべて$FFで埋まる。
持ち物欄を開いた時の$C2/0EA5~$C2/0EAFの処理とよく似た処理である。
これが後に、カーソルの状態などの判定に使われることになる。
また、先に紹介したが、[$00:056A][$00:0569]にはメニュー画面で「隊形」を選択した時に$0004が入る。
では、カーソルがあっているキャラ(選択元キャラとする)をAボタンで選択する。
この時、[$00:0545][$00:0544]には、カーソル位置により$0000~$0003が入っている。
$C2/0651 BCS $03 [$0656] ;キャリーフラグが立っているとき[$0656]分岐 $C2/0653 JMP ($0532)[$C2:08A0] ;[$C2:08A0]へジャンプ(レジスタをスタックに積まない) $C2/08A0 LDA $69 [$00:0569] ;Aに[$00:056A][$00:0569](コマンド番号$0004)をロード $C2/08A2 CMP #$0004 ;Aと$0004を減算比較(ステータスレジスタ変更のみ) $C2/08A5 BNE $03 [$08AA] ;ゼロフラグが立っていないとき[$08AA]分岐 ;ゼロフラグON 隊形の処理 $C2/08A7 JMP $091E [$C2:091E] ;[$C2:091E]へジャンプ(レジスタをスタックに積まない) $C2/091E LDA $65 [$00:0565] ;Aに[$00:0566][$00:0565]($FFFF)をロード $C2/0920 BPL $0C [$092E] ;ネガティブフラグが立っていないとき[$092E]分岐 ;ネガティブフラグOFF $C2/0922 LDA $44 [$00:0544] ;Aに[$00:0545][$00:0544](選択元キャラカーソル位置)をロード $C2/0924 STA $65 [$00:0565] ;Aを[$00:0566][$00:0565]に書き込み $C2/0926 LDA #$0404 ;Aに$0404をロード $C2/0929 JSR $4281 [$C2:4281] ;[$C2:4281]へジャンプ
[$00:056A][$00:0569]にはメニュー画面で選んだコマンドの値、隊形だと$0004が入っているから、$C2/08A5でゼロフラグが立って、そのまま$C2/08A7の隊形における処理に進む。
$C2/09CE~$C2/09D5で[$00:0565]~[$00:0568]には$FFが入ったが、Aボタンでキャラ選択すると、$C2/091E~$C2/0924で[$00:0566][$00:0565]に選択元キャラのカーソル位置の値[$00:0545][$00:0544]を書き込む処理が入る。
[$00:0545]は必ず$00になり、[$00:0564]に入る値が、カーソル位置により$00~$03のいずれかになる、ということである。
この状態で、選択先のキャラ位置にカーソルを移動すると、[$00:0544]に移動先カーソルの値$00~$03が入る、つまり値が更新される。
さらにAボタンを押すと、キャラ入れ替え処理に入る。
$C2/0651 BCS $03 [$0656] ;キャリーフラグが立っているとき[$0656]分岐 $C2/0653 JMP ($0532)[$C2:08A0] ;[$C2:08A0]へジャンプ(レジスタをスタックに積まない) $C2/08A0 LDA $69 [$00:0569] ;Aに[$00:056A][$00:0569](コマンド番号$0004)をロード $C2/08A2 CMP #$0004 ;Aと$0004を減算比較(ステータスレジスタ変更のみ) $C2/08A5 BNE $03 [$08AA] ;ゼロフラグが立っていないとき[$08AA]分岐 ;ゼロフラグON 隊形の処理 $C2/08A7 JMP $091E [$C2:091E] ;[$C2:091E]へジャンプ(レジスタをスタックに積まない) $C2/091E LDA $65 [$00:0565] ;Aに[$00:0566][$00:0565]($0000)をロード $C2/0920 BPL $0C [$092E] ;ネガティブフラグが立っていないとき[$092E]分岐 ;ネガティブフラグOFF(キャラ選択中) $C2/092E LDA $44 [$00:0544] ;Aに[$00:0545][$00:0544](選択先キャラカーソル位置)をロード $C2/0930 CMP $65 [$00:0565] ;Aと[$00:0566][$00:0565](選択元キャラカーソル位置)を減算比較(ステータスレジスタ変更のみ) $C2/0932 BEQ $2B [$095F] ;ゼロフラグが立っているとき[$095F]分岐 ;ゼロフラグOFF(同一キャラを選択していない) $C2/0934 STA $67 [$00:0567] ;A(選択先キャラカーソル位置)を[$00:0568][$00:0567]に書き込み $C2/0936 AND #$0003 ;Aと$0003で論理積 $C2/0939 TAX ;Aの値をXレジスタに転送 $C2/093A JSR $0968 [$C2:0968] ;[$C2:0968]へジャンプ
再び$C2/08A7の処理に戻ってきたが、先にAボタンでキャラを選んでいる場合、[$00:0566]は必ず$00が入っているので、ネガティブフラグが立たない。
よって今回はネガティブフラグOFFの$C2/092Eの処理に進む。
$C2/092E~$C2/0932は、現在のカーソル位置(選択先キャラカーソル位置)[$00:0545][$00:0544]と、選択元キャラカーソル位置[$00:0566][$00:0565]の減算比較である。
ここでゼロフラグが立つ場合は、選択先キャラと選択元キャラが同一なので、入れ替え処理をする必要がない。
ゼロフラグが立っていないのなら、$C2/0934以降の入れ替え処理に続く。
選択先のキャラのカーソル位置が[$00:0545][$00:0544]から[$00:0568][$00:0567]にコピーされ、処理が続く。
この時点で、
[$00:0566][$00:0565]:選択元キャラのカーソル位置($0000~$0003)[$00:0568][$00:0567]:選択先キャラのカーソル位置($0000~$0003)が、入ったことになる。
ちなみに、ここまでは、カーソル位置にキャラがいるかどうかは判定をしていない。
上処理の直後に判定が入る。
$C2/0968 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/096A LDA $0A14 [$7E:0A14] ;Aに[$7E:0A14](パーティ内の仲間の人数)をロード $C2/096D CMP $51,x ;Aと[$00:0551+x]を減算比較(ステータスレジスタ変更のみ) $C2/096F REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/0971 RTS ;サブルーチン戻り $C2/093D BPL $0B [$094A] ;A:0003 X:0001 Y:4308 P:envmxdIzC;ネガティブフラグが立っていないとき[$094A]分岐 ;ネガティブフラグON(空欄位置の選択) $C2/093F LDA $65 [$00:0565] ;Aに[$00:0565](選択元キャラのカーソル位置)をロード $C2/0941 AND #$0003 ;Aと$0003で論理積 $C2/0944 TAX ;Aの値をXレジスタに転送 $C2/0945 JSR $0968 [$C2:0968] ;[$C2:0968]へジャンプ ;ネガティブフラグOFF(空欄ではない位置の選択) $C2/094A LDA #$35CE ;Aに$35CEをロード $C2/094D STA $32 [$00:0532] ;Aを[$00:0532]に書き込み $C2/094F LDA $65 [$00:0565] ;Aに[$00:0565](選択元キャラのカーソル位置)をロード $C2/0951 JSR $0882 [$C2:0882] ;[$C2:0882]へジャンプ
上が、カーソル位置にキャラがいるかどうかの判定である。
[$7E:0A14]にはパーティ内の仲間の人数が入っている。つまり$00~$03のいずれかが入っている。
ここから、選択先キャラのカーソル位置の値を引く。
[$7E:0A14](パーティ内の仲間の人数)-(選択先キャラのカーソル位置)
である。
その後の$C2/093Dでは、ネガティブフラグ判定をしている。
もし、パーティメンバーが4人(仲間が3人)だったら、
$03 - ($00~$03)
だから、減算結果が負になることはなく、必ずネガティブフラグはOFFで、$C2/094Aに進む。
$C2/094Aの先は、キャラ交換処理である。
パーティメンバーが4人なら、隊形ですべての位置がキャラで埋まっているから、必ずキャラ交換処理をする、ということである。
一方、仲間の人数が$00~$02だと、減算で負になる可能性がある。
つまり、空欄位置を選んだ場合である。
例えば、パーティメンバーが3人の時([$7E:0A14]が$02)、空欄位置は$03が入っているので、減算が負になりネガティブフラグが立つ。
空欄でなければ$00~$02だから、減算は0以上となりネガティブフラグが立たない。
この場合は$C2/093Fに進んで別処理になる。
$C2/093Fからは、Xに[$00:0565](選択元キャラのカーソル位置)を書き込んでから、$C2/0968にジャンプである。
$C2/0968 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/096A LDA $0A14 [$7E:0A14] ;Aに[$7E:0A14](パーティ内の仲間の人数)をロード $C2/096D CMP $51,x ;Aと[$00:0551+x](選択元キャラのカーソル位置)を減算比較(ステータスレジスタ変更のみ) $C2/096F REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/0971 RTS ;サブルーチン戻り $C2/0948 BMI $15 [$095F] ;ネガティブフラグが立っているとき[$095F]分岐 ;ネガティブフラグOFF(空欄ではない位置の選択) $C2/094A LDA #$35CE ;Aに$35CEをロード $C2/094D STA $32 [$00:0532] ;Aを[$00:0532]に書き込み $C2/094F LDA $65 [$00:0565] ;Aに[$00:0565]をロード $C2/0951 JSR $0882 [$C2:0882] ;[$C2:0882]へジャンプ ;ネガティブフラグON(空欄位置の選択) $C2/095F JSR $09CE [$C2:09CE] ;[$C2:09CE]へジャンプ
$C2/0968では、
[$7E:0A14](パーティ内の仲間の人数)-(選択元キャラのカーソル位置)
を計算し、先程と同じようにネガティブフラグ判定である。
ここでネガティブフラグが立つのなら、選択元キャラのカーソル位置、選択先キャラのカーソル位置、どちらも空欄ということになる。
この場合は$C2/095Fに進み、最終的に交換処理を行わずに抜ける。
一方、ネガティブフラグが立たないなら、空欄とキャラ位置の交換になるため、交換処理を行う[$C2:0882]へ進むことになる。
最後に、実際に交換処理を行う。
$C2/4B6C PHP ;ステータスレジスタをスタックへプッシュ $C2/4B6D REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/4B6F LDA $65 [$00:0565] ;Aに[$00:0566][$00:0565](選択元キャラカーソル位置)をロード $C2/4B71 AND #$0003 ;Aと$0003で論理積 $C2/4B74 TAX ;Aの値をXレジスタに転送 $C2/4B75 LDA $51,x ;Aに[$00:0551+x]をロード $C2/4B77 AND #$0003 ;Aと$0003で論理積 $C2/4B7A TAX ;Aの値をXレジスタに転送 $C2/4B7B LDA $67 [$00:0567] ;Aに[$00:0568][$00:0567](選択先キャラカーソル位置)をロード $C2/4B7D AND #$0003 ;Aと$0003で論理積 $C2/4B80 TAY ;Aの値をYレジスタに転送 $C2/4B81 LDA $0551,y ;Aに[$00:0551+y]をロード $C2/4B84 AND #$0003 ;Aと$0003で論理積 $C2/4B87 TAY ;Aの値をYレジスタに転送 $C2/4B88 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4B8A LDA $0AB0,x ;Aに[$7E:0AB0+x]をロード $C2/4B8D XBA ;Aレジスタの上位バイトと下位バイトを交換 $C2/4B8E LDA $0AB0,y ;Aに[$7E:0AB0+y]をロード $C2/4B91 STA $0AB0,x ;Aを[$7E:0AB0+x]に書き込み $C2/4B94 XBA ;Aレジスタの上位バイトと下位バイトを交換 $C2/4B95 STA $0AB0,y ;Aを[$7E:0AB0+y]に書き込み $C2/4B98 JSR $4B51 [$C2:4B51] ;[$C2:4B51]へジャンプ
[$00:0551]~[$00:0554]には元の$7E:0AB0~$7E:0AB3がコピーされているが、$C2/4B75でAに呼び出す[$00:0551+x]は選択元キャラのアドレス、$C2/4B81でAに呼び出す[$00:0551+y]は選択先キャラのアドレスである。
$C2/4B8Aからは、[$7E:0AB0+x]を[$7E:0AB0+y]へ、[$7E:0AB0+y]を[$7E:0AB0+x]へと書き換える処理を、Aレジスタの上位バイトと下位バイトの交換を利用して行っている。
これで$7E:0AB0~$7E:0AB3の隊列書き換えが終了である。
さて、上の処理から、キャラと空欄の位置の交換が可能であることがわかる。
ただ、空欄との交換を行うとちょっと妙なことが起こる。
例えば、近未来編で、アキラとタロイモのみがいるとする。
近未来編の最初のバトルで無法松が加入するがすぐ抜けるため、タロイモは仲間キャラ1人目として$7E:0AB1に値が入る。
隊形を変更しなければ、上にアキラ、左にタロイモ、とキャラが配置される。
右と下にはキャラがいない。
| アドレス | 値 |
|---|---|
$00:0AB0 | $00(上)アキラ |
$00:0AB1 | $01(左)タロイモ |
$00:0AB2 | $02(右)空欄 |
$00:0AB3 | $03(下)空欄 |
ここで、アキラと下の空欄を入れ替えると、
| アドレス | 値 |
|---|---|
$00:0AB0 | $03(下)アキラ |
$00:0AB1 | $01(左)タロイモ |
$00:0AB2 | $02(右)空欄 |
$00:0AB3 | $00(上)空欄 |
アキラには$03が入る。
戦闘では$00から順に行動するはずだが、$00は空欄なので、飛ばしてタロイモが1番目に行動し、次にアキラが行動、となる。
パーティが2人しかいないからといって値を詰めることはなく、「値が小さい順に行動」ということになることがわかる。
さて、上の状態で無法松が再加入したとするとどうなるか。
仲間2人目の位置、$00:0AB2に無法松の値が入るから、右に無法松が入ることになる。
初期状態の、
| アドレス | 値 |
|---|---|
$00:0AB0 | $00(上)アキラ |
$00:0AB1 | $01(左)タロイモ |
$00:0AB2 | $02(右)空欄 |
$00:0AB3 | $03(下)空欄 |
この時でも$00:0AB2に入っているのは$02だから、無法松は右に入る。
また、初期状態で、アキラと右、アキラ(右)と下、アキラ(下)と上、というように順に入れ替えると、空欄を以下のように入れ替えできる。
| アドレス | 値 |
|---|---|
$00:0AB0 | $00(上)アキラ |
$00:0AB1 | $01(左)タロイモ |
$00:0AB3 | $03(下)空欄 |
$00:0AB2 | $02(右)空欄 |
この場合は、$00:0AB3に$03が入っているので、下に無法松が入る、という仕組みである。
あまりやる人はいないかもしれないが、空欄との入れ替えをしておくと次に加入するキャラ位置が変わることがあり得る、ということである。
先の処理から、空欄同士の入れ替えはできないので、キャラと空欄の入れ替えを繰り返した時だけ、空欄位置入れ替えが可能である。