TOP > プログラミング関係解説&調査 > SF編・主人公データ切り替え

SF編・主人公データ切り替え

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

功夫編終盤解説で、功夫編主人公のデータが老師から弟子に切り替わる処理について紹介したが、似たような処理がSF編でも起こるので紹介する。
SF編は最初から最後までキューブが主人公だが、途中のミニゲーム「キャプテンスクウェア」でキャプテンスクウェアを操作する都合で、SF編を開始したタイミングでは、シナリオ別キャラデータのSF編主人公のデータ($00:0D80$00:0DBF)は、「ステータスはキャプテンスクウェアで、名前はキューブ」というキャラが登録されている。
このため、キャプテンスクウェアをプレイしている最中、戦闘画面の上部に表示される操作キャラの名前が「キューブ」となっている。
(ゲーム的には、キューブがキャプテンスクウェアを操作しているというメタ的な意味になるのかもしれない)
SF編において、メニュー画面で装備や技などを見ることができないよう処理されているのも、キャプテンスクウェアのデータが登録されている状態だからである。

さて、データの切り替えタイミングだが、ラストバトル開始前のイベント中、キャプテンスクウェアが起動しようとしたところで中断された直後、「KILL YOU‥‥」が表示される寸前である。
これ以降は、SF編主人公のデータにはキューブのデータが入ったままになる。

処理は以下のようになっている。
功夫編の場合は、既に存在している弟子のデータをそのまま功夫編主人公へとまとめて移動する、つまり仲間キャラのデータを主人公キャラのデータへコピーする処理だったが、SF編の場合はキャプテンスクウェアのデータしか存在しないため、専用処理及びキューブの元々のキャラデータからシナリオ別キャラデータへコピーしている。
実質、各シナリオ開始時のシナリオ別キャラデータ生成と同じ処理である。

$C1/0BF7 LDA $0A00  [$00:0A00]   ;Aに[$00:0A00](シナリオID/SF編$02)をロード
$C1/0BFA CMP #$02                ;Aと$02を減算比較(ステータスレジスタ変更のみ)
$C1/0BFC BNE $07    [$0C05]      ;ゼロフラグが立っていないとき[$0C05]分岐
$C1/0BFE LDA $0040  [$00:0040]   ;Aに[$00:0040]をロード
$C1/0C01 CMP #$F3                ;Aと$F3を減算比較(ステータスレジスタ変更のみ)
$C1/0C03 BEQ $01    [$0C06]      ;ゼロフラグが立っているとき[$0C06]分岐
;ゼロフラグOFF
$C1/0C05 RTS                     ;サブルーチン戻り
;ゼロフラグON
$C1/0C06 LDA #$14                ;Aに$14(キューブのキャラID)をロード
$C1/0C08 STA $1B    [$00:031B]   ;A($14)を[$00:031B]に書き込み
$C1/0C0A XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C1/0C0B LDA #$02                ;Aに$02をロード
$C1/0C0D JSR $EC4B  [$C1:EC4B]   ;[$C1:EC4B]へジャンプ

上の処理が、キューブに主人公データを切り替えるための専用分岐である。
[$00:0A00](シナリオID)が$02はSF編であり、[$00:0040](シナリオ進行関係のフラグ)が$F3だと最終編ラストバトル確定。
キャプテンスクウェアプレイ開始時にも上処理は行われるが、[$00:0040]$D3なので$C1/0C03でゼロフラグが立たず、$C1/0C05でサブルーチンから抜ける。
ゼロフラグが立った場合は$C1/0C06以降の処理が続くが、$C1/0C06~で[$00:031B]に書き込んだ$14は、後の処理でわかるが、キューブのキャラIDとして計算に使われることになる。
$C1/0C0Bで読み込まれた$02はジャンプ先でアドレス計算に使われる。

$C1/EC4B XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C1/EC4C LDA #$00                ;Aに$00をロード(A:0200)
$C1/EC4E REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/EC50 LSR A                   ;Aを論理右シフト (/2)
$C1/EC51 LSR A                   ;Aを論理右シフト (/2)
$C1/EC52 ADC #$0D00              ;A(0080) + $0D00
$C1/EC55 TAY                     ;A(0D80)の値をYレジスタに転送(Y:0D80)
$C1/EC56 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/EC58 RTS                     ;サブルーチン戻り
;
$C1/0C10 JSR $F040  [$C1:F040]   ;[$C1:F040]へジャンプ
;
$C1/F040 CMP #$FF                ;Aと$FFを減算比較(ステータスレジスタ変更のみ)
$C1/F042 BNE $01    [$F045]      ;ゼロフラグが立っていないとき[$F045]分岐
$C1/F045 LDA $1B    [$00:031B]   ;Aに[$00:031B]($14)をロード
$C1/F047 STA $0000,y[$00:0D80]   ;A($14)を[$00:0D80]に書き込み
$C1/F04A LDA #$00                ;Aに$00をロード
$C1/F04C STA $0001,y[$00:0D81]   ;A($00)を[$00:0D81]に書き込み
$C1/F04F STA $000C,y[$00:0D8C]   ;A($00)を[$00:0D8C]に書き込み
$C1/F052 STA $000F,y[$00:0D8F]   ;A($00)を[$00:0D8F]に書き込み
$C1/F055 STA $001E,y[$00:0D9E]   ;A($00)を[$00:0D9E]に書き込み
$C1/F058 STA $0007,y[$00:0D87]   ;A($00)を[$00:0D87]に書き込み
$C1/F05B TYX                     ;Yレジスタの値をXレジスタに転送
$C1/F05C JSR $EC25  [$C1:EC25]   ;[$C1:EC25]へジャンプ

$C1/EC4B$C1/EC58はアドレス計算で、$C1/EC55Yレジスタに転送される$0D80は、シナリオ別キャラデータのSF編主人公のアドレス開始位置かつキャラIDが書き込まれる$00:0D80を呼び出す値になる。

$C1/F040~以降なのだが、味方との戦闘 > 最終編オルステッド主人公時でも紹介した、対戦相手が味方キャラかつ、主人公以外の仲間キャラの場合(原始編のゴリやべる・ざき、西部編のマッド、幕末編のとらわれの男)に、キャラの初期ステータスをセットする処理と同一である。
$C1/F045~で、[$00:031B]に書き込んだ$14が、[$00:0D80]に書き込まれる。
[$00:0D80]には直前まで、キャプテンスクウェアのキャラID$15が入っており、$14に上書きしたことでキャラIDがキューブに変更された。
ここからはアドレス開始位置[$00:0D80]を基準として、シナリオ別キャラデータにキューブのステータスなどを書き込んでいく。
$C1/F04C$C1/F058は指定アドレスに$00を書き込んでいる。書き込み先の内容は以下の通り。

アドレス内容
[$00:0D81]
[$00:0D8C]ひとつ前に出した技
[$00:0D8F]
[$00:0D9E]経験値
[$00:0D87]

?ばかりだがおそらく戦闘中の計算値・アドレス指定先などが入ると思われる。
ひとつ前に出した技の技IDや経験値は初期値をセットしている。

この先の処理も最終編オルステッド主人公時と同一なのだが、先のリンク先では詳細な処理は省略した箇所もあるので、ここで紹介しておく。

$C1/EC25 LDA $0000,x[$00:0D80]   ;Aに[$00:0D80]($14)をロード
$C1/EC28 CMP #$20                ;Aと$20を減算比較(ステータスレジスタ変更のみ)
$C1/EC2A BCS $1E    [$EC4A]      ;キャリーフラグが立っているとき[$EC4A]分岐
;キャリーフラグOFF
$C1/EC2C STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の下位バイト
$C1/EC2F LDA #$00                ;Aに$00をロード
$C1/EC31 STA $211B  [$00:211B]   ;符号付16bit x 8bit 被乗数の上位バイト
$C1/EC34 LDA #$2D                ;Aに$2Dをロード
$C1/EC36 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/EC39 STA $211C  [$00:211C]   ;符号付16bit x 8bit 乗数
$C1/EC3C REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/EC3E LDA $2134  [$00:2134]   ;Aに[$00:2135][$00:2134](乗算結果2バイト)をロード
$C1/EC41 ADC $D50008[$D5:0008]   ;A + [$D5:0009][$D5:0008]
$C1/EC45 STA $0004,x[$00:0D84]   ;A(0444)を[$00:0D85][$00:0D84]に書き込み
$C1/EC48 SEP #$20                ;MフラグON Aレジスタは8bit幅
;処理合流
$C1/EC4A RTS                     ;サブルーチン戻り

上はキューブのキャラID$14から、$0014*$2D + [$D5:0009][$D5:0008]を計算して[$00:0D85][$00:0D84]に書き込んでいる。
シナリオ別キャラデータの[$00:0D84][$00:0D85]に入る値+$D50000が、味方キャラデータの開始アドレスに相当する。
上が[$00:0D84][$00:0D85]の算出方法ということになる。
つまりキューブのキャラデータ開始アドレスは$0444 + $D50000で、$D5:0444からとなる(最初の値はHP計算値)。

なお、最初の$C1/EC25$C1/EC2Aでは、キャラID - $20の結果でキャリーフラグが立つか判定しているが、シナリオ別キャラデータ$00に入る味方キャラIDは$00$16の範囲だから、通常、キャリーフラグは立たない(減算結果が必ず負)。
例外はブリキ大王で、ブリキ大王は味方キャラID内に登録されておらず、シナリオ別キャラデータ$00$40が入る。このためブリキ大王だけ$C1/EC2C$C1/EC48の処理が飛ばされることになる。

以降はキューブの味方キャラデータから、ステータスなどをシナリオ別キャラデータにコピーする処理になる。

$C1/F05F LDX $0004,y[$00:0D84]   ;Xに[$00:0D84]をロード
$C1/F062 LDA $D50000,x[$D5:0444] ;Aに[$D5:0444]をロード(キャラデータ開始アドレス)
$C1/F066 BPL $0B    [$F073]      ;ネガティブフラグが立っていないとき[$F073]分岐
$C1/F068 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C1/F06A AND #$007F              ;Aと$007Fで論理積
$C1/F06D ASL A                   ;Aを算術左シフト *2
$C1/F06E ASL A                   ;Aを算術左シフト *2
$C1/F06F ASL A                   ;Aを算術左シフト *2
$C1/F070 ASL A                   ;Aを算術左シフト *2
$C1/F071 BRA $05    [$F078]      ;フラグにかかわりなく常に分岐[$F078]
$C1/F078 STA $000A,y[$00:0D8A]   ;A(00F0)を[$00:0D8B][$00:0D8A]に書き込み
$C1/F07B STA $0008,y[$00:0D88]   ;A(00F0)を[$00:0D89][$00:0D88]に書き込み

$C1/F062で呼び出したのがキューブの味方キャラデータ開始アドレス[$D5:0444]である。
味方キャラデータの詳細は各種データアドレス > 味方キャラデータの一覧表も参照のこと。
味方キャラデータの最初の値、味方キャラデータ00はキャラのHP計算値である。
$C1/F066$C1/F07BでHPを計算している。
HP計算値が$80以上(ネガティブフラグが立つ値の場合)だと、計算方法は、上の処理から、

実際のHP = (味方キャラデータ00 & $7F) * $10

であり、キューブなら

実際のHP = ($8F & $7F) * $10 = $F0 = 240

となる。
この値が、シナリオ別キャラデータの[$00:0D8B][$00:0D8A](最大HP・2バイト)と、[$00:0D89][$00:0D88](現在HP・2バイト)に入る。

$C1/F07E LDA $D50019,x[$D5:045D] ;Aに[$D5:045E][$D5:045D](レベル 初期値)をロード
$C1/F082 AND #$001F              ;Aと$001Fで論理積
$C1/F085 INC A                   ;Aをインクリメント +1
$C1/F086 STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
$C1/F088 LDA #$0000              ;Aに$0000をロード
;習得技確認ループ処理
$C1/F08B SEC                     ;キャリーフラグON
$C1/F08C ROL A                   ;Aをキャリーフラグを含めた17bitで左ローテート
$C1/F08D DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/F08F BNE $FA    [$F08B]      ;ゼロフラグが立っていないとき[$F08B]分岐
;習得技確認ループ処理ここまで
$C1/F091 STA $000D,y[$00:0D8D]   ;Aを[$00:0D8E][$00:0D8D](習得技)に書き込み

上は、キャラの初期レベル+1(=[$00:0308])の値を-1しつつ、$0000をキャリーフラグを含めた17bitで左ローテートするループ処理である。
どういうことかというと、$0000 = %0000 0000 0000 0000だから、ループ頭でキャリーフラグをONにした時、
%1 0000 0000 0000 0000
という17ビットの2進数になり、これを左に1桁ずつずらして左ローテートすると、
%0 0000 0000 0000 0001
一番下の桁に1が入る。
これをレベル+1回分、キューブの場合なら8回分ループで繰り返すと([$00:0308]のデクリメント回数に相当)、一番下の桁に1を入れながら左に数値がずれていくのだから、
%0 0000 0000 1111 1111
ループの回数だけ下の位から順に1で埋める作業になる。
各桁を技LVと見れば、これがそのまま、キャラの初期状態での技習得状況になる。
初期状態での習得技はレベルの値に従って、下位レベルから順に埋まっていく方式だから、以上で問題は起こらない。
ループ終了時のAの値が、シナリオ別キャラデータの[$00:0D8E][$00:0D8D](習得技)に書き込まれる。

$C1/F094 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/F096 LDA $D50001,x[$D5:0445] ;Aに[$D5:0445](力 初期値)をロード
$C1/F09A STA $0011,y[$00:0D91]   ;Aを[$00:0D91]に書き込み
$C1/F09D STA $0010,y[$00:0D90]   ;Aを[$00:0D90]に書き込み
$C1/F0A0 LDA $D50002,x[$D5:0446] ;Aに[$D5:0446](速 初期値)をロード
$C1/F0A4 STA $0014,y[$00:0D94]   ;Aを[$00:0D94]に書き込み
$C1/F0A7 STA $0013,y[$00:0D93]   ;Aを[$00:0D93]に書き込み
$C1/F0AA LDA $D50003,x[$D5:0447] ;Aに[$D5:0447](体 初期値)をロード
$C1/F0AE STA $0017,y[$00:0D97]   ;Aを[$00:0D97]に書き込み
$C1/F0B1 STA $0016,y[$00:0D96]   ;Aを[$00:0D96]に書き込み
$C1/F0B4 LDA $D50004,x[$D5:0448] ;Aに[$D5:0448](知 初期値)をロード
$C1/F0B8 STA $001A,y[$00:0D9A]   ;Aを[$00:0D9A]に書き込み
$C1/F0BB STA $0019,y[$00:0D99]   ;Aを[$00:0D99]に書き込み

上の処理で、味方キャラデータの力・速・体・知を、シナリオ別キャラデータの各ステータス及び、各ステータス+装備上昇分のアドレスに書き込んでいる。
力であれば、[$00:0D91](力)と、[$00:0D90](力+装備上昇分)の両方に同じ値を書き込む。
つまり、装備補正なしの値が書き込まれたという扱いである。

$C1/F0BE LDA $D50019,x[$D5:045D] ;Aに[$D5:045D](レベル 初期値)をロード
$C1/F0C2 STA $0006,y[$00:0D86]   ;Aを[$00:0D86]に書き込み
$C1/F0C5 LDA $D5001C,x[$D5:0460] ;Aに[$D5:0460](無効状態異常(8ビット))をロード
$C1/F0C9 STA $0020,y[$00:0DA0]   ;Aを[$00:0DA0]に書き込み
$C1/F0CC LDA $D5001D,x[$D5:0461] ;Aに[$D5:0461](上4ビット:背後補正/下4ビット:側面補正)をロード
$C1/F0D0 AND #$0F                ;Aと$0Fで論理積
$C1/F0D2 STA $001C,y[$00:0D9C]   ;Aを[$00:0D9C]に書き込み
$C1/F0D5 LDA $D5001D,x[$D5:0461] ;Aに[$D5:0461](上4ビット:背後補正/下4ビット:側面補正)をロード
$C1/F0D9 AND #$F0                ;Aと$F0で論理積
$C1/F0DB LSR A                   ;Aを論理右シフト (/2)
$C1/F0DC LSR A                   ;Aを論理右シフト (/2)
$C1/F0DD LSR A                   ;Aを論理右シフト (/2)
$C1/F0DE LSR A                   ;Aを論理右シフト (/2)
$C1/F0DF STA $001D,y[$00:0D9D]   ;Aを[$00:0D9D]に書き込み
$C1/F0E2 LDA $D5001E,x[$D5:0462] ;Aに[$D5:0462](回避属性1(8ビット))をロード
$C1/F0E6 STA $0021,y[$00:0DA1]   ;Aを[$00:0DA1]に書き込み
$C1/F0E9 LDA $D5001F,x[$D5:0463] ;Aに[$D5:0463](回避属性2(8ビット))をロード
$C1/F0ED STA $0022,y[$00:0DA2]   ;Aを[$00:0DA2]に書き込み

更に、レベル、無効状態異常、背後補正、側面補正、回避属性が味方キャラデータからシナリオ別キャラデータの該当アドレスにコピーされる。
背後補正・側面補正は味方キャラデータ29に上4ビット:背後補正/下4ビット:側面補正の形で記録されているため、$C1/F0CC$C1/F0DFでは、論理積と右シフト4回を利用して各桁を取り出し、[$00:0D9C](側面補正)と[$00:0D9D](背後補正)に分けて書き込んでいる。

$C1/F0F0 LDA #$0C                ;Aに$0Cをロード
$C1/F0F2 STA $08    [$00:0308]   ;A($0C)を[$00:0308]に書き込み
$C1/F0F4 PHY                     ;Yをスタックへプッシュ
;装備品などのコピーループ処理
$C1/F0F5 LDA $D50020,x           ;Aに[$D50020,x]をロード
$C1/F0F9 INX                     ;Xをインクリメント +1
$C1/F0FA STA $0024,y             ;Aを[$0024,y]に書き込み
$C1/F0FD INY                     ;Yをインクリメント +1
$C1/F0FE DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/F100 BNE $F3    [$F0F5]      ;ゼロフラグが立っていないとき[$F0F5]分岐
;装備品などのコピーループ処理ここまで

上は、味方キャラデータ32~43(12バイト)を、シナリオ別キャラデータ$24$2Fにそのままコピーするループ処理である。
これで、以下の値が丸ごとコピーされる仕組みである。

味方キャラ
データ
内容
32頭 初期装備(アイテムID)
33右 初期装備(アイテムID)
34左 初期装備(アイテムID)
35体 初期装備(アイテムID)
36足 初期装備(アイテムID)
37アクセサリ1 初期装備(アイテムID)
38アクセサリ2 初期装備(アイテムID)
39アクセサリ3 初期装備(アイテムID)
40アクセサリ4 初期装備(アイテムID)
41アクセサリ5 初期装備(アイテムID)
42(全キャラ$00
43防御合計値(初期値は味方キャラデータ43, 全キャラ$00)

つまり装備アイテムのアイテムIDをコピーする処理と見て良い。
最後の2バイトだが、味方キャラデータ43は装備品の防御の値の合計値が入るシナリオ別キャラデータ$2Fへコピーされる。
だが、味方キャラデータ43自体は全キャラ共通で$00であり、コピー後に防御の値が改めて計算される仕組み。
なお、アクセサリも初期装備で装備しているキャラがいないため、味方キャラデータ37~43は全キャラ$00である。
キューブの場合、装備品自体がないので味方キャラデータ32~43($D5:0464$D5:046F)は$00で埋まっているため、上ループ処理でコピー先($00:0DA4$00:0DAF)にコピーするのはすべて$00である。

$C1/F107 STY $10    [$00:0310]   ;Yを[$00:0310]に書き込み
$C1/F109 LDX #$0000              ;Xに$0000をロード
$C1/F10C STX $20    [$00:0320]   ;Xを[$00:0320]に書き込み
$C1/F10E STX $22    [$00:0322]   ;Xを[$00:0322]に書き込み
$C1/F110 STX $24    [$00:0324]   ;Xを[$00:0324]に書き込み
$C1/F112 STX $26    [$00:0326]   ;Xを[$00:0326]に書き込み
$C1/F114 LDA #$00                ;Aに$00をロード
$C1/F116 STA $0030,y[$00:0DB0]   ;Aを[$00:0DB0]に書き込み
$C1/F119 STA $0031,y[$00:0DB1]   ;Aを[$00:0DB1]に書き込み
$C1/F11C STA $0032,y[$00:0DB2]   ;Aを[$00:0DB2]に書き込み
$C1/F11F STA $0033,y[$00:0DB3]   ;Aを[$00:0DB3]に書き込み
$C1/F122 STY $12    [$00:0312]   ;Yを[$00:0312]に書き込み
$C1/F124 STZ $17    [$00:0317]   ;[$00:0317]に$00を書き込み
$C1/F126 STZ $14    [$00:0314]   ;[$00:0314]に$00を書き込み
$C1/F128 LDA #$0C                ;Aに$0Cをロード
$C1/F12A STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
$C1/F12C LDA $14    [$00:0314]   ;Aに[$00:0314]をロード
$C1/F12E INC A                   ;Aをインクリメント +1
$C1/F12F CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/F131 BEQ $02    [$F135]      ;ゼロフラグが立っているとき[$F135]分岐
$C1/F133 STA $14    [$00:0314]   ;Aを[$00:0314]に書き込み
$C1/F135 LDY $12    [$00:0312]   ;Yに[$00:0312]をロード
$C1/F137 JSR $F194  [$C1:F194]   ;[$C1:F194]へジャンプ

$00:0DB0$00:0DB3、つまりシナリオ別キャラデータの$30$33$00を書き込んでいる。
シナリオ別キャラデータ$31は装備によるフィールド吸収の値になるが、この時点では$00を書きこむのみである。

この後に装備関係の処理が入るが、キューブは初期装備が何もないので処理については省略。
また、キャラ名は元々「キューブ」のままなので、名前のコピーも行われない。
以上が、味方キャラデータからシナリオ別キャラデータへのキューブのデータのコピー処理である。
更にこの後は戦闘開始時のデータ処理が行われることになる。



このページをシェアする

上へ