TOP > プログラミング関係解説&調査 > カラーパレット変更

カラーパレット変更

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

本作ではいくつかの箇所で、段階的に画面全体の色を変化させる処理が使われている。
単純に明るさだけ変化させたり、カラーマスや固定色を使って加算・減算で変化させるケースもあるが、カラーパレットで画面全体の色を徐々に変化させていく処理もある。
幕末編のエンディングで、背景全体が夜明けの色合いになる場面や、最終編ベストエンドで、最終編のマップの色が徐々に中世編の色味へ戻っていく場面などが該当する。

カラーパレットを段階的に変動させる場面の変則的な例として、最終編の異次元空間で、50マス移動後に背景(各シナリオの一部)が現れたり消えたりする場面もあるが、これはまた少し異なる仕組みなので別途紹介する。

ここでは例として、最終編のマップの色が徐々に中世編の色味へ戻っていく処理を紹介する。
ちょうど「Live over Again」が流れ始めるタイミングで、魔王山、ファミリオの村、勇者の山、ルクレチア城と順に色が戻っていく。

理屈としては、背景のタイルデータはそのままでカラーパレットのみ3フレーム毎に変更していく、という処理である。
カラーマスや固定色処理ではない。

実際の処理を見ると、CGRAMアドレスの全メモリアドレス、つまり$200バイト分の領域を3フレーム毎に更新しているが、実際に変更されるのは$0040~$00FF、つまりカラーパレット番号$3~$8にあたる。

色データ(BGR555フォーマット)

スプライトについてにも記したが、色データの表記方法について、ここにもう一度記しておく。
スーパーファミコンにおける色データは、BGR555フォーマットという形式を使用している。
2バイト値で、青(B)・緑(G)・赤(R)にそれぞれ5ビットずつ(%00000~%11111)、各色の色の輝度を割り振る。
%0 00000 00000 00000
と16ビット=2バイトに色データが収まる(一番上のビットは使わず0とする)。
Bit0~4に赤、Bit5~9に緑、Bit10~14に青、と値が入るということである。

%11111 = $1F(31)だから、各色が$00~$1Fの32段階色調で表現できることになる。
3色を混ぜる度合いで、32,768色の表現が可能となっている。
ベストエンド時のように、滑らかに色味を変えていく場合、2バイトの値を単純に最終編→中世編の値へ少しずつ変えていけば良い、ということではない。
元となる赤、緑、青の3色に分けて(5ビットずつの値に分けて)、各色の成分を少しずつ変化させていくことになる。

今回のカラーパレット変化の話とはあまり関係ないが、BGR555の値のまま、現代のコンピュータなどで汎用的に使われている24ビットカラー(各色8ビット)に変換すると、やや暗めの色になる。
各色5ビットで表した「白」は%0 11111 11111 11111 = $7FFFである。
24ビットカラーは各色8ビットなので、3ビット分のデータを追加しなければならない。
その追加分をとりあえず%000とすると、

B: %11111 000 = $F8
G: %11111 000 = $F8
R: %11111 000 = $F8

$F8F8F8(やや灰色)になってしまう。
単純に%000を下にくっつけただけでは5ビットカラーから8ビットカラーにはできない。
そこで補正(正規化)として

R補正 = R + R / 32
G補正 = G + G / 32
B補正 = B + B / 32

としている。

サブルーチン

ここでは例として、ファミリオ村での演出部分を取り上げる。他の場面も基本的には同じ処理である。

最初のカラーパレット処理(最終編の色)

まず、ファミリオ村の場面に切り替わる際の処理。
ここでは通常通りに、最終編の色の設定で、カラーパレットのデータがCGRAMに転送される。
パレット番号の$0~$1は共通部分、$2~$8が背景用、$9~$Fがスプライト用。

パレット番号の$0~$1処理
$C0/9ACA LDA #$00                ;Aに$00をロード
$C0/9ACC STA $4300  [$00:4300]   ;Aを[$00:4300]に書き込み
$C0/9ACF LDA #$22                ;Aに$22をロード
$C0/9AD1 STA $4301  [$00:4301]   ;Aを[$00:4301]に書き込み
$C0/9AD4 LDX #$F920              ;Xに$F920をロード
$C0/9AD7 STX $4302  [$00:4302]   ;Xを[$00:4303][$00:4302]に書き込み
$C0/9ADA LDA #$CF                ;Aに$CFをロード
$C0/9ADC STA $4304  [$00:4304]   ;Aを[$00:4304]に書き込み
$C0/9ADF LDA $70    [$00:0070]   ;Aに[$00:0070]をロード
$C0/9AE1 CMP #$01                ;Aと$01を減算比較(ステータスレジスタ変更のみ)
$C0/9AE3 BEQ $11    [$9AF6]      ;ゼロフラグが立っているとき[$9AF6]分岐
$C0/9AE5 JSR $B925  [$C0:B925]   ;[$C0:B925]へジャンプ
;(中略)
$C0/9AE8 STZ $2121  [$00:2121]   ;[$00:2121]に$00を書き込み
$C0/9AEB LDX #$0040              ;Xに$0040をロード
$C0/9AEE STX $4305  [$00:4305]   ;Xを[$00:4306][$00:4305]に書き込み
$C0/9AF1 LDA #$01                ;Aに$01をロード
$C0/9AF3 STA $420B  [$00:420B]   ;Aを[$00:420B]に書き込み
;
;[$00:2121]	CGRAMのパレットアドレス開始位置 ※word単位	$00
;[$00:4300]	GDMA / HDMA設定レジスタ	$00
;[$00:4301]	転送先指定	$22=CGRAM
;[$00:4303][$00:4302]	データ元アドレス(開始位置)	$F920
;[$00:4304]	データ元バンク	$CF
;[$00:4306][$00:4305]	転送するデータ量	$0040
;$CF:F920 -> CGRAM $00word($0000byte) +$0040byte
;
$C0/9AF6 RTS                     ;サブルーチン戻り

以上の処理は、

元アドレスCGRAMアドレス
$CF:F920~$CF:F95F$0000~$003F
パレット番号の$9~$F処理
$C0/9A09 LDY #$3900              ;Yに$3900をロード
$C0/9A0C LDA $0A00  [$00:0A00]   ;Aに[$00:0A00]をロード
$C0/9A0F CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C0/9A11 BNE $03    [$9A16]      ;ゼロフラグが立っていないとき[$9A16]分岐
$C0/9A13 LDA $0A10  [$00:0A10]   ;Aに[$00:0A10]をロード
$C0/9A16 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/9A18 AND #$00FF              ;Aと$00FFで論理積
$C0/9A1B ASL A                   ;Aを算術左シフト *2
$C0/9A1C ASL A                   ;Aを算術左シフト *2
$C0/9A1D ASL A                   ;Aを算術左シフト *2
$C0/9A1E ASL A                   ;Aを算術左シフト *2
$C0/9A1F ASL A                   ;Aを算術左シフト *2
$C0/9A20 CLC                     ;キャリーフラグクリア
$C0/9A21 ADC #$F960              ;A + $F960
$C0/9A24 TAX                     ;Aの値をXレジスタに転送
$C0/9A25 PHB                     ;DBレジスタをスタックにプッシュ
$C0/9A26 LDA #$001F              ;Aに$001Fをロード
$C0/9A29 MVN CF 7E               ;データ転送
;A:転送データサイズ-1 $001F
;X:転送元アドレス $F9C0
;Y:転送先アドレス $3900
;オペランド:転送元と転送先バンク CF -> 7E
;$CF:F9C0 -> $7E:3900 +$001Fバイト
$C0/9A2C PLB                     ;DBレジスタにスタックからプル
$C0/9A2D SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/9A2F LDA $0A0B  [$00:0A0B]   ;Aに[$00:0A0B]をロード
$C0/9A32 BIT #$01                ;Aと$01で論理積(ステータスフラグ変更のみ)
$C0/9A34 BNE $05    [$9A3B]      ;ゼロフラグが立っていないとき[$9A3B]分岐
$C0/9A36 LDX #$FA60              ;Xに$FA60をロード
$C0/9A39 BRA $03    [$9A3E]      ;フラグにかかわりなく常に分岐[$9A3E]
$C0/9A3E REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/9A40 PHB                     ;DBレジスタをスタックにプッシュ
$C0/9A41 LDA #$007F              ;Aに$007Fをロード
$C0/9A44 MVN CF 7E               ;データ転送
;A:転送データサイズ-1 $007F
;X:転送元アドレス $FA60
;Y:転送先アドレス $3920
;オペランド:転送元と転送先バンク CF -> 7E
;$CF:FA60 -> $7E:3920 +$007Fバイト
$C0/9A47 PLB                     ;DBレジスタにスタックからプル
$C0/9A48 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/9A4A LDA $0A0B  [$00:0A0B]   ;Aに[$00:0A0B]をロード
$C0/9A4D BIT #$02                ;Aと$02で論理積(ステータスフラグ変更のみ)
$C0/9A4F BNE $19    [$9A6A]      ;ゼロフラグが立っていないとき[$9A6A]分岐
$C0/9A51 LDA $0ACF  [$00:0ACF]   ;Aに[$00:0ACF]をロード
$C0/9A54 XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C0/9A55 LDA #$00                ;Aに$00をロード
$C0/9A57 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/9A59 LSR A                   ;Aを論理右シフト (/2)
$C0/9A5A LSR A                   ;Aを論理右シフト (/2)
$C0/9A5B STA $32    [$00:0032]   ;Aを[$00:0032]に書き込み
$C0/9A5D LSR A                   ;Aを論理右シフト (/2)
$C0/9A5E CLC                     ;キャリーフラグクリア
$C0/9A5F ADC $32    [$00:0032]   ;A + [$00:0032]
$C0/9A61 CLC                     ;キャリーフラグクリア
$C0/9A62 ADC #$FAE0              ;A + $FAE0
$C0/9A65 TAX                     ;Aの値をXレジスタに転送
$C0/9A66 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/9A68 BRA $18    [$9A82]      ;フラグにかかわりなく常に分岐[$9A82]
$C0/9A82 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/9A84 PHB                     ;DBレジスタをスタックにプッシュ
$C0/9A85 LDA #$005F              ;Aに$005Fをロード
$C0/9A88 MVN CF 7E               ;データ転送
;A:転送データサイズ-1 $005F
;X:転送元アドレス $FDE0
;Y:転送先アドレス $3920
;オペランド:転送元と転送先バンク CF -> 7E
;$CF:FDE0 -> $7E:39A0 +$005Fバイト
$C0/9A8B PLB                     ;DBレジスタにスタックからプル
$C0/9A8C SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/9A8E LDY #$3920              ;Yに$3920をロード
$C0/9A91 LDA #$07                ;Aに$07をロード
$C0/9A93 JSR $C449  [$C0:C449]   ;[$C0:C449]へジャンプ

$C0/C449 STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
$C0/C44B LDA $10E2  [$00:10E2]   ;Aに[$00:10E2]をロード
$C0/C44E BEQ $4D    [$C49D]      ;ゼロフラグが立っているとき[$C49D]分岐
$C0/C49D RTS                     ;サブルーチン戻り

$C0/9A96 LDA $70    [$00:0070]   ;Aに[$00:0070]をロード
$C0/9A98 CMP #$01                ;Aと$01を減算比較(ステータスレジスタ変更のみ)
$C0/9A9A BEQ $12    [$9AAE]      ;ゼロフラグが立っているとき[$9AAE]分岐
$C0/9A9C REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/9A9E PHB                     ;DBレジスタをスタックにプッシュ
$C0/9A9F LDX #$F920              ;Xに$F920をロード
$C0/9AA2 LDY #$3800              ;Yに$3800をロード
$C0/9AA5 LDA #$003F              ;Aに$003Fをロード
$C0/9AA8 MVN CF 7E               ;データ転送
;A:転送データサイズ-1 $003F
;X:転送元アドレス $F920
;Y:転送先アドレス $3800
;オペランド:転送元と転送先バンク CF -> 7E
;$CF:F920 -> $7E:3800 +$003Fバイト
$C0/9AAB PLB                     ;DBレジスタにスタックからプル
$C0/9AAC SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/9AAE LDA #$80                ;Aに$80をロード
$C0/9AB0 STA $2121  [$00:2121]   ;Aを[$00:2121]に書き込み
$C0/9AB3 LDX #$3900              ;Xに$3900をロード
$C0/9AB6 STX $4302  [$00:4302]   ;Xを[$00:4303][$00:4302]に書き込み
$C0/9AB9 LDA #$7E                ;Aに$7Eをロード
$C0/9ABB STA $4304  [$00:4304]   ;Aを[$00:4304]に書き込み
$C0/9ABE LDX #$0100              ;Xに$0100をロード
$C0/9AC1 STX $4305  [$00:4305]   ;Xを[$00:4306][$00:4305]に書き込み
$C0/9AC4 LDA #$01                ;Aに$01をロード
$C0/9AC6 STA $420B  [$00:420B]   ;Aを[$00:420B]に書き込み
;
;[$00:2121]	CGRAMのパレットアドレス開始位置 ※word単位	$80
;[$00:4300]	GDMA / HDMA設定レジスタ	$00
;[$00:4301]	転送先指定	$22=CGRAM
;[$00:4303][$00:4302]	データ元アドレス(開始位置)	$3900
;[$00:4304]	データ元バンク	$7E
;[$00:4306][$00:4305]	転送するデータ量	$0100
;$7E:3900 -> CGRAM $80word($0100byte) +$0100byte
;
$C0/9AC9 RTS                     ;サブルーチン戻り

以上の処理は、

元アドレスCGRAMアドレス
$7E:3900~$7E:39FF$0100~$01FF

$7E:3900~$7E:39FFの元のデータは、

元アドレスバンク$7Eコピー
$CF:F9C0~$CF:F9DF$7E:3900~$7E:391F
$CF:FA60~$CF:FADF$7E:3920~$7E:399F
$CF:FDE0~$CF:FE3F$7E:39A0~$7E:39FF
$CF:F920~$CF:F95F$7E:3800~$7E:383F
パレット番号の$2~$8処理
$C0/4D17 LDA $0A00  [$00:0A00]   ;Aに[$00:0A00]をロード
$C0/4D1A CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C0/4D1C BEQ $05    [$4D23]      ;ゼロフラグが立っているとき[$4D23]分岐
$C0/4D23 LDA $0123  [$00:0123]   ;Aに[$00:0123]をロード
$C0/4D26 INC A                   ;Aをインクリメント +1
$C0/4D27 INC A                   ;Aをインクリメント +1
$C0/4D28 CLC                     ;キャリーフラグクリア
$C0/4D29 ADC $0A0A  [$00:0A0A]   ;A + [$00:0A0A]
$C0/4D2C REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/4D2E AND #$00FF              ;Aと$00FFで論理積
$C0/4D31 ASL A                   ;Aを算術左シフト *2
$C0/4D32 ASL A                   ;Aを算術左シフト *2
$C0/4D33 STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
$C0/4D35 ASL A                   ;Aを算術左シフト *2
$C0/4D36 ASL A                   ;Aを算術左シフト *2
$C0/4D37 STA $32    [$00:0032]   ;Aを[$00:0032]に書き込み
$C0/4D39 ASL A                   ;Aを算術左シフト *2
$C0/4D3A STA $34    [$00:0034]   ;Aを[$00:0034]に書き込み
$C0/4D3C ASL A                   ;Aを算術左シフト *2
$C0/4D3D ASL A                   ;Aを算術左シフト *2
$C0/4D3E CLC                     ;キャリーフラグクリア
$C0/4D3F ADC $30    [$00:0030]   ;A + [$00:0030]
$C0/4D41 CLC                     ;キャリーフラグクリア
$C0/4D42 ADC $32    [$00:0032]   ;A + [$00:0032]
$C0/4D44 CLC                     ;キャリーフラグクリア
$C0/4D45 ADC $34    [$00:0034]   ;A + [$00:0034]
$C0/4D47 CLC                     ;キャリーフラグクリア
$C0/4D48 ADC #$E000              ;A + $E000
$C0/4D4B TAX                     ;Aの値をXレジスタに転送
$C0/4D4C SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/4D4E PHB                     ;DBレジスタをスタックにプッシュ
$C0/4D4F LDA #$7E                ;Aに$7Eをロード
$C0/4D51 PHA                     ;Aレジスタをスタックにプッシュ
$C0/4D52 PLB                     ;DBレジスタにスタックからプル
$C0/4D53 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/4D55 LDY #$3840              ;Yに$3840をロード
$C0/4D58 LDA #$0006              ;Aに$0006をロード
$C0/4D5B STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
;ループ処理
$C0/4D5D LDA #$0000              ;Aに$0000をロード
$C0/4D60 STA $0000,y[$7E:3840]   ;Aを[$7E:3841+20n][$7E:3840+20n]に書き込み
$C0/4D63 INY                     ;Yをインクリメント +1
$C0/4D64 INY                     ;Yをインクリメント +1
$C0/4D65 LDA #$001D              ;Aに$001Dをロード
$C0/4D68 MVN DE 7E               ;データ転送
;A:転送データサイズ-1 $001D
;X:転送元アドレス $E9D8, E9F6, EA14, EA32, EA50, EA6E
;Y:転送先アドレス $3842, 3862, 3882, 38A2, 38C2, 38E2
;オペランド:転送元と転送先バンク DE -> 7E
;$DE:E9D8 -> $7E:3842 +$001Dバイト
$C0/4D6B DEC $30    [$00:0030]   ;[$00:0030]をデクリメント -1
$C0/4D6D BNE $EE    [$4D5D]      ;ゼロフラグが立っていないとき[$4D5D]分岐
;ループ処理ここまで
$C0/4D6F SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/4D71 PLB                     ;DBレジスタにスタックからプル
$C0/4D72 LDA $0240  [$00:0240]   ;Aに[$00:0240]をロード
$C0/4D75 BEQ $20    [$4D97]      ;ゼロフラグが立っているとき[$4D97]分岐
$C0/4D97 LDY #$3840              ;Yに$3840をロード
$C0/4D9A LDA #$06                ;Aに$06をロード
$C0/4D9C JSR $C449  [$C0:C449]   ;[$C0:C449]へジャンプ

$C0/C449 STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
$C0/C44B LDA $10E2  [$00:10E2]   ;Aに[$00:10E2]をロード
$C0/C44E BEQ $4D    [$C49D]      ;ゼロフラグが立っているとき[$C49D]分岐
$C0/C49D RTS                     ;サブルーチン戻り

$C0/4D9F LDA #$00                ;Aに$00をロード
$C0/4DA1 STA $4300  [$00:4300]   ;Aを[$00:4300]に書き込み
$C0/4DA4 LDA #$22                ;Aに$22をロード
$C0/4DA6 STA $4301  [$00:4301]   ;Aを[$00:4301]に書き込み
$C0/4DA9 LDX #$3840              ;Xに$3840をロード
$C0/4DAC STX $4302  [$00:4302]   ;Xを[$00:4303][$00:4302]に書き込み
$C0/4DAF LDA #$7E                ;Aに$7Eをロード
$C0/4DB1 STA $4304  [$00:4304]   ;Aを[$00:4304]に書き込み
$C0/4DB4 LDX #$00C0              ;Xに$00C0をロード
$C0/4DB7 STX $4305  [$00:4305]   ;Xを[$00:4306][$00:4305]に書き込み
$C0/4DBA LDA #$20                ;Aに$20をロード
$C0/4DBC STA $2121  [$00:2121]   ;Aを[$00:2121]に書き込み
$C0/4DBF LDA #$01                ;Aに$01をロード
$C0/4DC1 STA $420B  [$00:420B]   ;Aを[$00:420B]に書き込み
;
;[$00:2121]	CGRAMのパレットアドレス開始位置 ※word単位	$20
;[$00:4300]	GDMA / HDMA設定レジスタ	$00
;[$00:4301]	転送先指定	$22=CGRAM
;[$00:4303][$00:4302]	データ元アドレス(開始位置)	$3840
;[$00:4304]	データ元バンク	$7E
;[$00:4306][$00:4305]	転送するデータ量	$00C0
;$7E:3840 -> CGRAM $20word($0040byte) +$00C0byte
;
$C0/4DC4 LDA #$01                ;Aに$01をロード
$C0/4DC6 STA $4300  [$00:4300]   ;Aを[$00:4300]に書き込み
$C0/4DC9 LDA #$18                ;Aに$18をロード
$C0/4DCB STA $4301  [$00:4301]   ;Aを[$00:4301]に書き込み
$C0/4DCE LDX #$0000              ;Xに$0000をロード
$C0/4DD1 STX $2116  [$00:2116]   ;Xを[$00:2116]に書き込み
$C0/4DD4 LDX #$2000              ;Xに$2000をロード
$C0/4DD7 STX $26    [$00:0026]   ;Xを[$00:0026]に書き込み
$C0/4DD9 LDA $0120  [$00:0120]   ;Aに[$00:0120]をロード
$C0/4DDC STA $20    [$00:0020]   ;Aを[$00:0020]に書き込み
$C0/4DDE STZ $22    [$00:0022]   ;[$00:0022]に$00を書き込み
$C0/4DE0 JSR $4E8A  [$C0:4E8A]   ;[$C0:4E8A]へジャンプ

以上の処理は、

元アドレスCGRAMアドレス
$7E:3840~$7E:38FF$0040~$00FF

$7E:3840~$7E:38FFの元のデータは、

元アドレスバンク$7Eコピー
$DE:E9D8~$DE:E9F5$7E:3842~$7E:385F
$DE:FA60~$DE:FA7D$7E:3862~$7E:387F
$DE:FDE0~$DE:FDFD$7E:3882~$7E:389F
$DE:F920~$DE:F93D$7E:38A2~$7E:38BF
$DE:EA50~$DE:EA6D$7E:38C2~$7E:38DF
$DE:EA6E~$DE:EA8B$7E:38E2~$7E:38FF

以上、CGRAMについてまとめると、以下のようになる。

パレット
番号
元アドレスWRAMCGRAMアドレス
$0,$1$CF:F920~$CF:F95F$7E:3800~$7E:383F$0000~$003F
※書き込み時WRAM経由なし
$2$DE:E9D8~$DE:E9F5$7E:3842~$7E:385F$0040~$00FF
※$7E:3840~$7E:3841など
頭2バイトには$0000が入っている
$3$DE:FA60~$DE:FA7D$7E:3862~$7E:387F
$4$DE:FDE0~$DE:FDFD$7E:3882~$7E:389F
$5$DE:F920~$DE:F93D$7E:38A2~$7E:38BF
$6$DE:EA50~$DE:EA6D$7E:38C2~$7E:38DF
$7$DE:EA6E~$DE:EA8B$7E:38E2~$7E:38FF
$8$CF:F9C0~$CF:F9DF$7E:3900~$7E:391F$0100~$01FF
$9~$C$CF:FA60~$CF:FADF$7E:3920~$7E:399F
$D~$F$CF:FDE0~$CF:FE3F$7E:39A0~$7E:39FF

最終的には$7E:3800~$7E:39FFの$200バイトが、CGRAMアドレス$0000~$01FFにそっくりコピーされたことになる。

CGRAMアドレス$0000~$00FFに入るのが背景のカラーパレット、$0100~$01FFに入るのがスプライト(OBJ)のカラーパレットになるが、エンディングでのファミリオ村のシーンにはスプライトは表示されない。BG1とBG2ですべてのグラフィックを表示している。
よって、今回については、背景のカラーパレットを注目してみよう。
なお、CGRAM$0000~$003Fに入るパレット番号$0, $1は共通部分(セリフウィンドウなど)のカラーパレットで、BG用ではない(はず)。
ということで、ファミリオ村のマップで使われているカラーパレット番号は実質$2~$7である。
カラーパレット番号$2~$7の値を更新することで、色味を最終編から中世編へと変化させていくのだろう、ということがここでだいたいわかる。

なお、幕末編エンディングの場合は、おぼろ丸ととらわれの男のスプライトが表示されているので、スプライトのカラーパレットも変動させている。

カラーパレット変更処理

では、どのようにしてカラーパレットの値を計算し更新しているのか。
サブルーチンを見ると、最終編の色→中世編の色への変更でカラーパレットを32回更新している。
(3フレームに1回更新だから、トータルで96フレーム、1.5秒くらいかけて変化している)
だが、カラーパレットが32個分用意してあるのではなく、最終編用カラーパレットと中世編用カラーパレットの値から変化中の色を計算している。

また、GDMA転送処理を見ると、$7E:3C00~$7E:3DFFの$200バイトが、CGRAMアドレス$0000~$01FFに転送されている。
ファミリオ村表示の時は$7E:3800~$7E:39FFを転送していたから、WRAM内で何らかの計算をして$7E:3C00~$7E:3DFFに入った値を転送している、ということが予想できる。

$C0/C14F PHB                     ;DBレジスタをスタックにプッシュ
$C0/C150 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C152 LDX #$3800              ;Xに$3800をロード
$C0/C155 LDY #$3A00              ;Yに$3A00をロード
$C0/C158 LDA #$01FF              ;Aに$01FFをロード
$C0/C15B MVN 7E 7E               ;データ転送
;A:転送データサイズ-1 $01FF
;X:転送元アドレス $3800
;Y:転送先アドレス $3A00
;オペランド:転送元と転送先バンク 7E -> 7E
;$7E:3800 -> $7E:3A00 +$01FFバイト
$C0/C15E LDA $20    [$00:0020]   ;Aに[$00:0020]をロード
$C0/C160 AND #$00FF              ;Aと$00FFで論理積
$C0/C163 ASL A                   ;Aを算術左シフト *2
$C0/C164 ASL A                   ;Aを算術左シフト *2
$C0/C165 STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
$C0/C167 ASL A                   ;Aを算術左シフト *2
$C0/C168 ASL A                   ;Aを算術左シフト *2
$C0/C169 STA $32    [$00:0032]   ;Aを[$00:0032]に書き込み
$C0/C16B ASL A                   ;Aを算術左シフト *2
$C0/C16C STA $34    [$00:0034]   ;Aを[$00:0034]に書き込み
$C0/C16E ASL A                   ;Aを算術左シフト *2
$C0/C16F ASL A                   ;Aを算術左シフト *2
$C0/C170 CLC                     ;キャリーフラグクリア
$C0/C171 ADC $30    [$00:0030]   ;A + [$00:0030]
$C0/C173 CLC                     ;キャリーフラグクリア
$C0/C174 ADC $32    [$00:0032]   ;A + [$00:0032]
$C0/C176 CLC                     ;キャリーフラグクリア
$C0/C177 ADC $34    [$00:0034]   ;A + [$00:0034]
$C0/C179 CLC                     ;キャリーフラグクリア
$C0/C17A ADC #$E000              ;A + $E000
$C0/C17D TAX                     ;Aの値をXレジスタに転送
$C0/C17E LDY #$3840              ;Yに$3840をロード
$C0/C181 LDA #$0006              ;Aに$0006をロード
$C0/C184 STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
;ループ処理開始
$C0/C186 LDA #$0000              ;Aに$0000をロード
$C0/C189 STA $0000,y[$7E:3840]   ;Aを[$7E:3840]に書き込み
$C0/C18C INY                     ;Yをインクリメント +1
$C0/C18D INY                     ;Yをインクリメント +1
$C0/C18E LDA #$001D              ;Aに$001Dをロード
$C0/C191 MVN DE 7E               ;データ転送
;A:転送データサイズ-1 $001D
;X:転送元アドレス $E870, $E88E, $E8AC, $E8CA, $E8E8, $E906
;Y:転送先アドレス $3842, $3862, $3882, $38A2, $38C2, $38E2
;オペランド:転送元と転送先バンク DE -> 7E
;$DE:E870 -> $7E:3842 +$001Dバイト
$C0/C194 DEC $30    [$00:0030]   ;[$00:0030]をデクリメント -1
$C0/C196 BNE $EE    [$C186]      ;ゼロフラグが立っていないとき[$C186]分岐
;ループ処理ここまで
$C0/C198 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C19A LDA $21    [$00:0021]   ;Aに[$00:0021]をロード
$C0/C19C AND #$1F                ;Aと$1Fで論理積
$C0/C19E BEQ $72    [$C212]      ;ゼロフラグが立っているとき[$C212]分岐
$C0/C212 PLB                     ;DBレジスタにスタックからプル
$C0/C213 LDA #$20                ;Aに$20をロード
$C0/C215 STA $0109  [$00:0109]   ;A($20)を[$00:0109]に書き込み
$C0/C218 JSR $C2F5  [$C0:C2F5]   ;[$C0:C2F5]へジャンプ

まず最初の処理が以下になる。

転送元転送先
$7E:3800~$7E:39FF$7E:3A00~$7E:3BFF
$DE:E870~$DE:E88D$7E:3842~$7E:385F
$DE:E88E~$DE:E8AB$7E:3862~$7E:387F
$DE:E8AC~$DE:E8C9$7E:3882~$7E:389F
$DE:E8CA~$DE:E8E7$7E:38A2~$7E:38BF
$DE:E8E8~$DE:E905$7E:38C2~$7E:38DF
$DE:E906~$DE:E923$7E:38E2~$7E:38FF

最終編用にCGRAMに転送した$7E:3800~$7E:39FFを、$7E:3A00~$7E:3BFFへ丸ごとコピーする。
そして、$7E:3800~$7E:39FFのカラーパレット番号$2~$7にあたるアドレスだけ($7E:3840~$7E:38FF)、上のように値を上書きする。
新たに$7E:3840~$7E:38FFに入ったのが中世編における背景用カラーパレットの値である。
$7E:3800~$7E:383Fは変化なしだが、この領域はカラーパレット番号$0~$1で、システム関係領域にあたり、背景とは関係ないし、中世編でも最終編でも同一の値である。

ということで、

アドレス内容
$7E:3A00~$7E:3BFF最終編用カラーパレット(CGRAM$200バイト分のデータ)
$7E:3800~$7E:38FF中世編用カラーパレット(CGRAM$200バイト分のデータ)

というように値が更新された、と見なして良い。
(ただし$7E:3800~$7E:383Fと$7E:3A00~$7E:3B3Fは同一)

また、$C0/C213~$C0/C215の処理で、[$00:0109]に$20が書き込まれている。
これが実質、カラーパレットの色の変化回数であり、[$00:0109]の値をデクリメント(-1)しながら処理が進む。
[$00:0109]をデクリメントしてゼロフラグが立たない場合の処理の飛び先(つまりループ処理開始地点)も以下の$C0/C2F5である。

$C0/C2F5 LDA #$10                ;Aに$10をロード
$C0/C2F7 LDY #$3800              ;Yに$3800をロード
$C0/C2FA STA $2A    [$00:002A]   ;A($10)を[$00:002A]に書き込み
$C0/C2FC LDA $0109  [$00:0109]   ;Aに[$00:0109]($20)をロード
$C0/C2FF STA $20    [$00:0020]   ;A($20)を[$00:0020]に書き込み
$C0/C301 PHB                     ;DBレジスタをスタックにプッシュ
$C0/C302 LDA #$7E                ;Aに$7Eをロード
$C0/C304 PHA                     ;Aレジスタをスタックにプッシュ
$C0/C305 PLB                     ;DBレジスタにスタックからプル
$C0/C306 LDX #$4200              ;Xに$4200をロード
$C0/C309 LDA #$10                ;Aに$10をロード
$C0/C30B STA $2B    [$00:002B]   ;Aを[$00:002B]に書き込み
$C0/C30D JSR $C36A  [$C0:C36A]   ;[$C0:C36A]へジャンプ

ここで先に述べた[$00:0109](初期値$20)が、[$00:0020]にコピーされる。
[$00:002A]に$10が書き込まれるが、これはCGRAMのカラーパレットの個数。
また、[$00:002B]に$10が書き込まれるが、これは次の処理のループ回数にあたる。

;ループ処理開始([$00:002B] = $10回分)
$C0/C36A REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C36C LDA $0000,y[$7E:3800]   ;Aに[$7E:3801+2n][$7E:3800+2n]をロード
$C0/C36F CMP $0200,y[$7E:3A00]   ;Aと[$7E:3A01+2n][$7E:3A00+2n]を減算比較(ステータスレジスタ変更のみ)
$C0/C372 BEQ $51    [$C3C5]      ;ゼロフラグが立っているとき[$C3C5]分岐
;$C0/C372ゼロフラグON
$C0/C3C5 STA $0400,y[$7E:3C00]   ;Aを[$7E:3C01+2n][$7E:3C00+2n]に書き込み
$C0/C3C8 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C3CA INY                     ;Yをインクリメント +1
$C0/C3CB INY                     ;Yをインクリメント +1
$C0/C3CC RTS                     ;サブルーチン戻り

$C0/C310 DEC $2B    [$00:002B]   ;[$00:002B]をデクリメント -1
$C0/C312 BNE $F9    [$C30D]      ;ゼロフラグが立っていないとき[$C30D]分岐
$C0/C30D JSR $C36A  [$C0:C36A]   ;[$C0:C36A]へジャンプ
;ループ処理ここまで

ここでループ処理に入る。
ループ中で$C0/C372のゼロフラグ判定があり、ゼロフラグOFF時はループを抜けて別処理になる。
では、ループ内では何を判定しているのか。
見てみると、最初のループでは、

[$7E:3801][$7E:3800]と[$7E:3A01][$7E:3A00]を減算比較して、ゼロフラグが立つなら[$7E:3801][$7E:3800]を[$7E:3C01][$7E:3C00]に書き込む

と、なっている。
先ほどの$C0/C14F~$C0/C218の処理から、

4バイト分(1色分)の「中世編用カラーパレットの値」と「最終編用カラーパレットの値」を比較して、同一なら「中世編用カラーパレットの値」を[$7E:3C01][$7E:3C00]に書き込む

と、いうことになる。
後でわかるが、$7E:3C00~$7E:3DFFが「変化中のカラーパレットの値を書き込む領域」なのである。
上ループ処理は、中世編用カラーパレットと最終編用カラーパレットの同一部分を判別するためだったことがわかる。
同一箇所があるなら、カラーパレットの変更処理はしなくて良いので、中世編用カラーパレットの値をそのまま$7E:3C00~に書き込めば良い。

$7E:3800~$7E:383Fと$7E:3A00~$7E:3B3Fは同一なので、上処理でループを抜けることはない。
また、背景用のカラーパレットの1色目のデータは共通して$0000なので、これもループを抜けることはない。
最初にループを抜けるのは、[$7E:3843][$7E:3842]以降のはずだ。
実際そのとおりに[$7E:3843][$7E:3842]では$C0/C372でゼロフラグがOFFになり、$C0/C374に処理が進む。
その処理が以下である。

;$C0/C372ゼロフラグOFF
$C0/C374 STA $30    [$00:0030]   ;Aを[$00:0031][$00:0030]に書き込み([$7E:38nn+1][$7E:38nn]、中世編用カラーパレット)
$C0/C376 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C378 LDA #$20                ;Aに$20をロード
$C0/C37A SEC                     ;キャリーフラグON
$C0/C37B SBC $20    [$00:0020]   ;A($20) - [$00:0020]($20)
$C0/C37D JSR $C3CD  [$C0:C3CD]   ;[$C0:C3CD]へジャンプ

まず、相違が生じた中世編用カラーパレットの中の2バイト値を[$00:0031][$00:0030]に書き込む。
$C0/C37Bで $20 - [$00:0020] を計算し、Aレジスタに入った状態で、$C0/C3CDへ進む。
$20 - [$00:0020]だが、[$00:0020]は先の通り、[$00:0109](初期値$20)をコピーしたアドレスで、この時点では$20だから、Aレジスタに$00が入ることになる。

$C0/C3CD STA $02,x  [$00:4202]   ;A(00)を[$00:4202]に書き込み
$C0/C3CF LDA $30    [$00:0030]   ;Aに[$00:0030]([$7E:38nn]、中世編用カラーパレット下1バイト)をロード
$C0/C3D1 AND #$1F                ;Aと$1Fで論理積 ★赤(R)5ビット値
$C0/C3D3 STA $03,x  [$00:4203]   ;Aを[$00:4203]に書き込み
;符号付8bit x 8bit
;($20-(カラーパレット変化回数)) * [$00:0030](中世編用カラーパレット下1桁) & $1F(★赤(R)5ビット値)
$C0/C3D5 NOP                     ;計算待ち
$C0/C3D6 NOP                     ;計算待ち
$C0/C3D7 NOP                     ;計算待ち
$C0/C3D8 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C3DA LDA $16,x  [$00:4216]   ;Aに[$00:4217][$00:4216]をロード 乗算結果
$C0/C3DC STA $32    [$00:0032]   ;Aを[$00:0033][$00:0032]に書き込み A * [$00:0030] & $1F
$C0/C3DE LDA $30    [$00:0030]   ;Aに[$00:0031][$00:0030](中世編用カラーパレット2バイト値)をロード
$C0/C3E0 ASL A                   ;Aを算術左シフト *2
$C0/C3E1 ASL A                   ;Aを算術左シフト *2
$C0/C3E2 ASL A                   ;Aを算術左シフト *2
$C0/C3E3 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C3E5 XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C0/C3E6 AND #$1F                ;Aと$1Fで論理積 [$00:0031][$00:0030]*8/100 &$1F ★緑(G)5ビット値
$C0/C3E8 STA $03,x  [$00:4203]   ;Aを[$00:4203]に書き込み
;($20-(カラーパレット変化回数)) * ([$00:0031][$00:0030]*8/100 & $1F)(★緑(G)5ビット値)
$C0/C3EA NOP                     ;計算待ち
$C0/C3EB NOP                     ;計算待ち
$C0/C3EC NOP                     ;計算待ち
$C0/C3ED REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C3EF LDA $16,x  [$00:4216]   ;Aに[$00:4217][$00:4216]をロード
$C0/C3F1 STA $34    [$00:0034]   ;Aを[$00:0035][$00:0034]に書き込み
$C0/C3F3 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C3F5 LDA $31    [$00:0031]   ;Aに[$00:0031](中世編用カラーパレット上1バイト)をロード
$C0/C3F7 LSR A                   ;Aを論理右シフト (/2)
$C0/C3F8 LSR A                   ;Aを論理右シフト (/2)
$C0/C3F9 AND #$1F                ;Aと$1Fで論理積 ★青(B)5ビット値
$C0/C3FB STA $03,x  [$00:4203]   ;Aを[$00:4203]に書き込み
;($20-(カラーパレット変化回数)) * ([$00:0031]/4 & $1F)(★青(B)5ビット値)
$C0/C3FD NOP                     ;計算待ち
$C0/C3FE NOP                     ;計算待ち
$C0/C3FF NOP                     ;計算待ち
$C0/C400 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C402 LDA $16,x  [$00:4216]   ;Aに[$00:4217][$00:4216]をロード
$C0/C404 STA $36    [$00:0036]   ;Aを[$00:0037][$00:0036]に書き込み
$C0/C406 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C408 RTS                     ;サブルーチン戻り

上処理を見ると、符号付8bit x 8bitの乗算を3回行っている。
[$00:4202](被乗数)×[$00:4203](乗数)の結果2バイトが[$00:4217][$00:4216]に入るようになっている。

順に見ていくと、乗算3回とも、[$00:4202]の値は先に計算しAレジスタに入った「$20 - [$00:0020]」で固定である。
ということは、上の乗算の結果は3回とも0を掛け算しているから、答えも0なのだが、それはさておき[$00:4203]に入る値は何になるのか。

まず、$C0/C3D3の時点だが、[$00:4203]に入るのは、

[$00:0030](中世編用カラーパレット下1バイト) & $1F(%0001 1111)

つまり下5ビットを取り出しているから、BGR555フォーマットの一番下、赤(R)の輝度の5ビット値である。
2回目の乗算、$C0/C3E8の時点だと、

[$00:0031][$00:0030](中世編用カラーパレット2バイト)*8/$100 & $1F

ちょっとややこしいが、「*8/$100」は2バイト値を左に3ビット分ずらして上1バイトの値だけ取るのと同じことだから、
%0000 0011 1110 0000
の
%0001 1111
を取り出したのと同じことである。
これに先と同じく、下5ビットを取り出すため$1Fと論理積を取るから、結局はBGR555フォーマットの中央、緑(G)の輝度の5ビット値である。

最後の3回目の乗算、$C0/C3FBの時点では、

[$00:0031](中世編用カラーパレット上1バイト)/4 & $1F

上1バイトを右に2ビット分ずらして、$1Fと論理積を取るから、BGR555フォーマットの上の値、青(B)の輝度の5ビット値である。

以上をまとめると、

  • ($20 - [$00:0020]) * 中世編カラーパレットの赤(R)の輝度 → [$00:0033][$00:0032]
  • ($20 - [$00:0020]) * 中世編カラーパレットの緑(G)の輝度 → [$00:0035][$00:0034]
  • ($20 - [$00:0020]) * 中世編カラーパレットの青(B)の輝度 → [$00:0037][$00:0036]

という計算をして、[$00:0032]~[$00:0037]に書き込んでいることがわかる。

この続き、$C0/C380~$C0/C397の処理は以下。

$C0/C380 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C382 LDA $32    [$00:0032]   ;Aに[$00:0033][$00:0032]をロード
$C0/C384 STA $22    [$00:0022]   ;Aを[$00:0023][$00:0022]に書き込み
$C0/C386 LDA $34    [$00:0034]   ;Aに[$00:0035][$00:0034]をロード
$C0/C388 STA $24    [$00:0024]   ;Aを[$00:0025][$00:0024]に書き込み
$C0/C38A LDA $36    [$00:0036]   ;Aに[$00:0037][$00:0036]をロード
$C0/C38C STA $26    [$00:0026]   ;Aを[$00:0027][$00:0026]に書き込み
$C0/C38E LDA $0200,y[$7E:3A42]   ;Aに[$7E:3A01+2n][$7E:3A00+2n](最終編カラーパレット)をロード
$C0/C391 STA $30    [$00:0030]   ;Aを[$00:0031][$00:0030]に書き込み
$C0/C393 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C395 LDA $20    [$00:0020]   ;Aに[$00:0020]をロード
$C0/C397 JSR $C3CD  [$C0:C3CD]   ;[$C0:C3CD]へジャンプ

上での処理は、

  • [$00:0033][$00:0032] → [$00:0023][$00:0022]
  • [$00:0035][$00:0034] → [$00:0025][$00:0024]
  • [$00:0037][$00:0036] → [$00:0027][$00:0026]
  • [$7E:3A01+2n][$7E:3A00+2n](最終編カラーパレット) → [$00:0031][$00:0030]

という値のコピーである。

この後、Aに[$00:0020]をロードして、再び$C0/C3CDにジャンプする。
つまり先の各輝度の乗算だが、今回は、

  • [$00:0031][$00:0030]に[$7E:3801+2n][$7E:3800+2n](最終編カラーパレット)
  • Aに[$00:0020]($20)

が入る。

$C0/C3CD STA $02,x  [$00:4202]   ;Aを[$00:4202]に書き込み
$C0/C3CF LDA $30    [$00:0030]   ;Aに[$00:0030]をロード
$C0/C3D1 AND #$1F                ;Aと$1Fで論理積
$C0/C3D3 STA $03,x  [$00:4203]   ;Aを[$00:4203]に書き込み
$C0/C3D5 NOP                     ;計算待ち
$C0/C3D6 NOP                     ;計算待ち
$C0/C3D7 NOP                     ;計算待ち
$C0/C3D8 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C3DA LDA $16,x  [$00:4216]   ;Aに[$00:4216]をロード
$C0/C3DC STA $32    [$00:0032]   ;Aを[$00:0032]に書き込み
$C0/C3DE LDA $30    [$00:0030]   ;Aに[$00:0030]をロード
$C0/C3E0 ASL A                   ;Aを算術左シフト *2
$C0/C3E1 ASL A                   ;Aを算術左シフト *2
$C0/C3E2 ASL A                   ;Aを算術左シフト *2
$C0/C3E3 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C3E5 XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C0/C3E6 AND #$1F                ;Aと$1Fで論理積
$C0/C3E8 STA $03,x  [$00:4203]   ;Aを[$00:4203]に書き込み
$C0/C3EA NOP                     ;計算待ち
$C0/C3EB NOP                     ;計算待ち
$C0/C3EC NOP                     ;計算待ち
$C0/C3ED REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C3EF LDA $16,x  [$00:4216]   ;Aに[$00:4216]をロード
$C0/C3F1 STA $34    [$00:0034]   ;Aを[$00:0034]に書き込み
$C0/C3F3 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C3F5 LDA $31    [$00:0031]   ;Aに[$00:0031]をロード
$C0/C3F7 LSR A                   ;Aを論理右シフト (/2)
$C0/C3F8 LSR A                   ;Aを論理右シフト (/2)
$C0/C3F9 AND #$1F                ;Aと$1Fで論理積
$C0/C3FB STA $03,x  [$00:4203]   ;Aを[$00:4203]に書き込み
$C0/C3FD NOP                     ;計算待ち
$C0/C3FE NOP                     ;計算待ち
$C0/C3FF NOP                     ;計算待ち
$C0/C400 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C402 LDA $16,x  [$00:4216]   ;Aに[$00:4216]をロード
$C0/C404 STA $36    [$00:0036]   ;Aを[$00:0036]に書き込み
$C0/C406 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C408 RTS                     ;サブルーチン戻り

今回は、

  • [$00:0020] * 最終編カラーパレットの赤(R)の輝度 → [$00:0033][$00:0032]
  • [$00:0020] * 最終編カラーパレットの緑(G)の輝度 → [$00:0035][$00:0034]
  • [$00:0020] * 最終編カラーパレットの青(B)の輝度 → [$00:0037][$00:0036]

と、乗算を行うことになる。

ここまでをまとめると以下のようになる。

アドレス値
[$00:0023][$00:0022]($20 - [$00:0020]) * 中世編カラーパレットの赤(R)の輝度
[$00:0025][$00:0024]($20 - [$00:0020]) * 中世編カラーパレットの緑(G)の輝度
[$00:0027][$00:0026]($20 - [$00:0020]) * 中世編カラーパレットの青(B)の輝度
[$00:0033][$00:0032][$00:0020] * 最終編カラーパレットの赤(R)の輝度
[$00:0035][$00:0034][$00:0020] * 最終編カラーパレットの緑(G)の輝度
[$00:0037][$00:0036][$00:0020] * 最終編カラーパレットの青(B)の輝度

この表記だと長いので、以下は下のように少し省略表記する。

アドレス値
[$00:0023][$00:0022]($20 - [$00:0020]) * 中世R
[$00:0025][$00:0024]($20 - [$00:0020]) * 中世G
[$00:0027][$00:0026]($20 - [$00:0020]) * 中世B
[$00:0033][$00:0032][$00:0020] * 最終R
[$00:0035][$00:0034][$00:0020] * 最終G
[$00:0037][$00:0036][$00:0020] * 最終B
$C0/C39A REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/C39C LDA $22    [$00:0022]   ;Aに[$00:0023][$00:0022]をロード
$C0/C39E CLC                     ;キャリーフラグクリア
$C0/C39F ADC $32    [$00:0032]   ;A + [$00:0033][$00:0032]
$C0/C3A1 LSR A                   ;Aを論理右シフト (/2)
$C0/C3A2 LSR A                   ;Aを論理右シフト (/2)
$C0/C3A3 LSR A                   ;Aを論理右シフト (/2)
$C0/C3A4 LSR A                   ;Aを論理右シフト (/2)
$C0/C3A5 LSR A                   ;Aを論理右シフト (/2)
$C0/C3A6 AND #$001F              ;Aと$001F(%0000 0000 0001 1111)で論理積
$C0/C3A9 STA $22    [$00:0022]   ;Aを[$00:0023][$00:0022]に書き込み
$C0/C3AB LDA $24    [$00:0024]   ;Aに[$00:0025][$00:0024]をロード
$C0/C3AD CLC                     ;キャリーフラグクリア
$C0/C3AE ADC $34    [$00:0034]   ;A + [$00:0035][$00:0034]
$C0/C3B0 AND #$03E0              ;Aと$03E0(%0000 0011 1110 0000)で論理積
$C0/C3B3 ORA $22    [$00:0022]   ;Aと[$00:0023][$00:0022]で論理和
$C0/C3B5 STA $22    [$00:0022]   ;Aを[$00:0023][$00:0022]に書き込み
$C0/C3B7 LDA $26    [$00:0026]   ;Aに[$00:0027][$00:0026]をロード
$C0/C3B9 CLC                     ;キャリーフラグクリア
$C0/C3BA ADC $36    [$00:0036]   ;A + [$00:0037][$00:0036]
$C0/C3BC LSR A                   ;Aを論理右シフト (/2)
$C0/C3BD LSR A                   ;Aを論理右シフト (/2)
$C0/C3BE LSR A                   ;Aを論理右シフト (/2)
$C0/C3BF XBA                     ;Aレジスタの上位バイトと下位バイトを交換
$C0/C3C0 AND #$7C00              ;Aと$7C00(%0111 1100 0000 0000)で論理積
$C0/C3C3 ORA $22    [$00:0022]   ;Aと[$00:0023][$00:0022]で論理和
;処理合流
$C0/C3C5 STA $0400,y[$7E:3C42]   ;Aを[$7E:3C01+2n][$7E:3C00+2n]に書き込み
$C0/C3C8 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/C3CA INY                     ;Yをインクリメント +1
$C0/C3CB INY                     ;Yをインクリメント +1
$C0/C3CC RTS                     ;サブルーチン戻り

$C0/C3C5の処理合流まで、順に以下の計算をしている。

  1. ([$00:0023][$00:0022] + [$00:0033][$00:0032])/$20 & $001F → [$00:0023][$00:0022]
  2. (([$00:0025][$00:0024] + [$00:0035][$00:0034]) & $03E0) | [$00:0023][$00:0022] → [$00:0023][$00:0022]
  3. (([$00:0027][$00:0026] + [$00:0037][$00:0036])/$8*$100 & $7C00) | [$00:0023][$00:0022] → [$00:0023][$00:0022]

最終的に、処理合流後に[$00:0023][$00:0022]が[$7E:3C01+2n][$7E:3C00+2n]に入る。
つまり、[$00:0023][$00:0022]が、「変化中のカラーパレットの値」になった。

では上の計算は何をしているのか?
最初の、

([$00:0023][$00:0022] + [$00:0033][$00:0032])/$20 & $001F

カッコ内の加算部分が、

$20 - [$00:0020]) * 中世R + [$00:0020] * 最終R

であるから、

$20 - [$00:0020]) * 中世R + [$00:0020] * 最終R)/$20 & $001F
= ((1-[$00:0020]/$20) * 中世R + [$00:0020]/$20 * 最終R) & $001F

[$00:0020]の初期値は$20で、後で紹介する通りカラーパレットの値が変更される度に-1されていく。
[$00:0020] = $20なら、

((1-$20/$20) * 中世R + $20/$20 * 最終R) & $001F
= (0 * 中世R + 1 * 最終R) & $001F

最後に$001Fとの論理積を取るので、5ビット値となるが、これだけなら最終編の赤のカラーパレット値と変わらない。
[$00:0020] = $1Fと値を-1すると、

((1-$1F/$20) * 中世R + $1F/$20 * 最終R) & $001F
= (1/32 * 中世R + 31/32 * 最終R) & $001F

と、割合にして1/32は中世編の赤のカラーパレット値、31/32は最終編の赤のカラーパレット値となる。
このようにして、中世編の赤のカラーパレット値と、最終編の赤のカラーパレット値の割合を1/32ずつ変化させることで「変化中のカラーパレットの値」とし、32段階でカラーパレットを少しずつ変化させ最終編の色から中世編の色へと変更しているのである。

以上から、赤は、

([$00:0023][$00:0022] + [$00:0033][$00:0032])/$20 & $001F → [$00:0023][$00:0022]

で「変化中のカラーパレットの値」となり、[$00:0023][$00:0022]に値が入った。つまりBit0~4に新たな赤の値が入った。
後は、緑、青の順に処理を続ける。

緑は、

(([$00:0025][$00:0024] + [$00:0035][$00:0034]) & $03E0) | [$00:0023][$00:0022]
= ((中世G + 最終G) & $03E0) | [$00:0023][$00:0022](変化中のカラーパレットの値)

$03E0 = %0000 0011 1110 0000だから、ちょうどBGR555の中央の緑の5ビット分(Bit5~9)である。
赤の手順では、

([$00:0023][$00:0022] + [$00:0033][$00:0032])/$20 & $001F

と、加算して/$20していたが、今回はその後に*$20してBGR555の中央の緑の5ビット分の位置(Bit5~9)に合わせる必要があるから、/$20*$20で結局計算の必要がない。
よって、

(([$00:0025][$00:0024] + [$00:0035][$00:0034]) & $03E0)

これで、中世編の緑のカラーパレット値と、最終編の緑のカラーパレット値を割合で混ぜたことになる。
そして、既に赤のカラーパレットが入った、[$00:0023][$00:0022]との論理和を取ることで、Bit0~4に赤、Bit5~9に緑の値が入ったことになる。

青は、

(([$00:0027][$00:0026] + [$00:0037][$00:0036])/$8*$100 & $7C00) | [$00:0023][$00:0022]
= ((中世B + 最終B)/$8*$100 & $7C00) | [$00:0023][$00:0022](変化中のカラーパレットの値)

今回は加算後、/$20して、Bit10~14位置に合わせるために*$400する必要がある。つまり*$20である。
/$8*$100でちょうど*$20である。
$7C00 = %0111 1100 0000 0000なので、BGR555の青の5ビット分(Bit10~14)と論理積を取る。
最後に、[$00:0023][$00:0022]との論理和を取ることで、Bit0~4に赤、Bit5~9に緑、Bit10~14に青の値が入った、「変化中のカラーパレットの値」が完成した。

こうして計算した、新たなカラーパレット1色分の値[$00:0023][$00:0022]は、[$7E:3C01+2n][$7E:3C00+2n]に書き込まれる。

$C0/C310 DEC $2B    [$00:002B]   ;[$00:002B]をデクリメント -1
$C0/C312 BNE $F9    [$C30D]      ;ゼロフラグが立っていないとき[$C30D]分岐
;ゼロフラグOFF
$C0/C30D JSR $C36A  [$C0:C36A]   ;[$C0:C36A]へジャンプ
;ゼロフラグON
$C0/C314 DEC $2A    [$00:002A]   ;[$00:002A](カラーパレットの個数)をデクリメント -1
$C0/C316 BEQ $03    [$C31B]      ;ゼロフラグが立っているとき[$C31B]分岐
;ゼロフラグOFF
$C0/C318 BRL $FFEE  [$C309]      ;フラグにかかわりなく常に分岐[$C309](次のカラーパレット処理へ)
;ゼロフラグON
$C0/C31B PLB                     ;DBレジスタにスタックからプル
$C0/C31C RTS                     ;サブルーチン戻り

ここで、[$00:002B](初期値$10)が-1される。
1色の処理で-1されたから、$10色、つまりカラーパレット1個分の16色を処理し終えるまで上ループが続く。

$C0/C312でゼロフラグONなら、カラーパレット1個分の処理が終了したということだから、[$00:002A](カラーパレットの個数、初期値$10)が-1される。
カラーパレット$10個の処理が終わるまで、上ループが続き、最終的に[$00:002A]が$00になったらすべてのカラーパレットの処理終了である。
$C0/C31Cへ抜けて、「変化中のカラーパレット16色分」が$7E:3C00~$7E:3DFFに書き込まれた。

後は、このカラーパレットの値をCGRAMへ転送すれば良い。

$C0/C425 LDA #$00                ;Aに$00をロード
$C0/C427 STA $4300  [$00:4300]   ;Aを[$00:4300]に書き込み
$C0/C42A LDA #$22                ;Aに$22をロード
$C0/C42C STA $4301  [$00:4301]   ;Aを[$00:4301]に書き込み
$C0/C42F LDX #$3C00              ;Xに$3C00をロード
$C0/C432 STX $4302  [$00:4302]   ;Xを[$00:4303][$00:4302]に書き込み
$C0/C435 LDA #$7E                ;Aに$7Eをロード
$C0/C437 STA $4304  [$00:4304]   ;Aを[$00:4304]に書き込み
$C0/C43A STZ $2121  [$00:2121]   ;[$00:2121]に$00を書き込み
$C0/C43D LDX #$0200              ;Xに$0200をロード
$C0/C440 STX $4305  [$00:4305]   ;Xを[$00:4305]に書き込み
$C0/C443 LDA #$01                ;Aに$01をロード
$C0/C445 STA $420B  [$00:420B]   ;Aを[$00:420B]に書き込み
;
;[$00:2121]	CGRAMのパレットアドレス開始位置 ※word単位	$00
;[$00:4300]	GDMA / HDMA設定レジスタ	$00
;[$00:4301]	転送先指定	$22=CGRAM
;[$00:4303][$00:4302]	データ元アドレス(開始位置)	$3C00
;[$00:4304]	データ元バンク	$7E
;[$00:4306][$00:4305]	転送するデータ量	$0200
;$7E:3C00 -> CGRAM $00word($0000byte) +$0200byte
;
$C0/C448 RTS                     ;サブルーチン戻り

$7E:3C00~$7E:3DFFがCGRAMアドレス$0000~$0200に転送されている。つまりCGRAMを丸ごと上書きである。

少し処理を飛ばして、転送後は、以下処理で[$00:0109]の値が-1される。

$C0/C266 LDA $0109  [$00:0109]   ;Aに[$00:0109]をロード
$C0/C269 BEQ $34    [$C29F]      ;ゼロフラグが立っているとき[$C29F]分岐
;ゼロフラグOFF
$C0/C26B DEC A                   ;Aをデクリメント -1
$C0/C26C STA $0109  [$00:0109]   ;Aを[$00:0109]に書き込み
$C0/C26F JSR $C2F5  [$C0:C2F5]   ;[$C0:C2F5]へジャンプ
;ゼロフラグON
$C0/C29F RTS                     ;サブルーチン戻り

[$00:0109]の初期値は$20で、カラーパレット処理が終わる度に-1される(3フレームに1回の処理)。
[$00:0109]の値が$00でなければ再びカラーパレット処理開始で、$00ならカラーパレット処理は終わり。
この時にカラーパレットは中世編の色に戻っているはずである。
[$00:0109]の値は[$00:0020]にコピーしてからカラーパレット処理を行うが、

[$00:0020] = $20なら、

((1-$20/$20) * 中世R + $20/$20 * 最終R) & $001F
= (0 * 中世R + 1 * 最終R) & $001F

[$00:0020] = $00なら、

((1-$00/$20) * 中世R + $00/$20 * 最終R) & $001F
= (1 * 中世R + 0 * 最終R) & $001F

と、計算できるからだ。

以上の処理により、ベストエンド時、最終編のカラーパレット→中世編のカラーパレットと、滑らかに色味が変化していくように見えている。



このページをシェアする

上へ