基礎知識
戦闘関係
先に背景グラフィックと効果に目を通していただきたい。
戦闘画面の背景(BG1部分)は、シナリオ及びエリアによって変わることは、プレイすればわかる通り。
戦闘画面の背景サイズは32×32タイルなので、8×8ピクセルのタイルで1マスとして戦闘画面を大雑把に区分けすると以下のようになる。
戦闘フィールドの7×7マス部分は、横4×縦3タイル(横32×縦24ピクセル)が49個分で構成されている。
戦闘における味方キャラのスプライトは32×32ピクセルサイズだから、一番上のマスにいると上8ピクセル分がフィールド上にはみ出すのだが、そのはみ出し分を考慮して、上部ウィンドウ位置と7×7マス部分の間に8ピクセル分の隙間がある(と思われる。それ以外にこの隙間を設ける必要があるかどうかは筆者にはわからない)
敵タイルは横4×縦3タイル内、つまりマス目の中に収まるようになっているが、代わりに複数マスにまたがることが大半。
また、実際のゲーム画面は横32×縦28タイルだから、下部の高さ4タイル分は画面に表示されない未使用部分ということになる。
戦闘がBGMode1の時について大まかにまとめると、だいたい以下のようになる。
| 背景 | 背景サイズ (横×縦、タイル数) | VRAMアドレス(単位byte) | |
|---|---|---|---|
| タイルデータ | BGマップデータ | ||
| BG1 | 32×32 | $A000~$BFFF | $C000~$C7FF |
| BG2 | 32×32 | $2000~$3FFF | $C800~$CFFF |
| BG3 | 32×32 | $0000~$1FFF | $D000~$D7FF |
このページで紹介するのは戦闘開始時の主な背景の処理である。
実際には戦闘中など様々な別処理が入るので、以下はあくまでも一部の処理であることをご了承いただきたい。
また、Window効果(戦闘突入時の三角形が回転する処理など)といった、特殊な処理もここでは省いている。
戦闘は通常、フィールドマップ上でウィンドウ効果による三角形の黒い画面回転→ブラックアウト→主人公中心にアイリスイン(円形に画面が広がる効果)という順で画面が変化する。
ブラックアウト直前あたりまではバンク$C0のサブルーチンで、その後から戦闘用のバンク$C1のサブルーチンに移行する。
戦闘用サブルーチンは$C1/0000から始まるが、その直後にグラフィックのI/Oレジスタ関係初期設定が行われる。
$C1/0000 PHB ;DBレジスタをスタックにプッシュ ;(中略) $C1/006F LDA #$8F ;Aに$8Fをロード $C1/0071 STA $2100 [$00:2100] ;Aを[$00:2100]に書き込み スクリーン初期化 $C1/0074 LDA #$01 ;Aに$01をロード $C1/0076 STA $4200 [$00:4200] ;Aを[$00:4200]に書き込み 割り込み有効フラグ $C1/0079 JSR $F4A9 [$C1:F4A9] ;[$C1:F4A9]へジャンプ $C1/F4A9 LDA #$5C ;Aに$5Cをロード $C1/F4AB STA $00F8 [$00:00F8] ;Aを[$00:00F8]に書き込み $C1/F4AE STA $00FC [$00:00FC] ;Aを[$00:00FC]に書き込み $C1/F4B1 LDX #$856C ;Xに$856Cをロード $C1/F4B4 STX $00F9 [$00:00F9] ;Xを[$00:00F9]に書き込み $C1/F4B7 LDX #$85EB ;Xに$85EBをロード $C1/F4BA STX $00FD [$00:00FD] ;Xを[$00:00FD]に書き込み $C1/F4BD LDA #$C1 ;Aに$C1をロード $C1/F4BF STA $00FB [$00:00FB] ;Aを[$00:00FB]に書き込み $C1/F4C2 STA $00FF [$00:00FF] ;Aを[$00:00FF]に書き込み $C1/F4C5 RTS ;サブルーチン戻り $C1/007C JSR $F338 [$C1:F338] ;[$C1:F338]へジャンプ $C1/F338 LDA #$21 ;Aに$21をロード $C1/F33A STA $2101 [$00:2101] ;Aを[$00:2101]に書き込み スプライトサイズ・データ領域選択 $C1/F33D STA $2103 [$00:2103] ;Aを[$00:2103]に書き込み OAM上位アドレスとスプライト優先順位 $C1/F340 LDA #$09 ;Aに$09をロード $C1/F342 STA $2105 [$00:2105] ;Aを[$00:2105]に書き込み BGモード・キャラクタサイズ設定 $C1/F345 LDA #$00 ;Aに$00をロード $C1/F347 STA $2106 [$00:2106] ;Aを[$00:2106]に書き込み モザイク表示用サイズ・スクリーン設定 $C1/F34A LDA #$60 ;Aに$60をロード $C1/F34C STA $2107 [$00:2107] ;Aを[$00:2107]に書き込み BG1タイルマップアドレスとサイズ $C1/F34F LDA #$64 ;Aに$64をロード $C1/F351 STA $2108 [$00:2108] ;Aを[$00:2108]に書き込み BG2タイルマップアドレスとサイズ $C1/F354 LDA #$68 ;Aに$68をロード $C1/F356 STA $2109 [$00:2109] ;Aを[$00:2109]に書き込み BG3タイルマップアドレスとサイズ $C1/F359 LDA #$15 ;Aに$15をロード $C1/F35B STA $210B [$00:210B] ;Aを[$00:210B]に書き込み BG1,BG2キャラクタデータ領域設定 $C1/F35E LDA #$00 ;Aに$00をロード $C1/F360 STA $210C [$00:210C] ;Aを[$00:210C]に書き込み BG3,BG4キャラクタデータ領域設定 $C1/F363 STA $210D [$00:210D] ;Aを[$00:210D]に書き込み BG1水平スクロール量 $C1/F366 STA $210D [$00:210D] ;Aを[$00:210D]に書き込み $C1/F369 STA $210E [$00:210E] ;Aを[$00:210E]に書き込み BG1垂直スクロール量 $C1/F36C STA $210E [$00:210E] ;Aを[$00:210E]に書き込み $C1/F36F STA $210F [$00:210F] ;Aを[$00:210F]に書き込み BG2水平スクロール量 $C1/F372 STA $210F [$00:210F] ;Aを[$00:210F]に書き込み $C1/F375 STA $2110 [$00:2110] ;Aを[$00:2110]に書き込み BG2垂直スクロール量 $C1/F378 STA $2110 [$00:2110] ;Aを[$00:2110]に書き込み $C1/F37B STA $2111 [$00:2111] ;Aを[$00:2111]に書き込み BG3水平スクロール量 $C1/F37E STA $2111 [$00:2111] ;Aを[$00:2111]に書き込み $C1/F381 STA $2112 [$00:2112] ;Aを[$00:2112]に書き込み BG3垂直スクロール量 $C1/F384 STA $2112 [$00:2112] ;Aを[$00:2112]に書き込み $C1/F387 LDA #$80 ;Aに$80をロード $C1/F389 STA $2115 [$00:2115] ;Aを[$00:2115]に書き込み ビデオポート調整 $C1/F38C LDA #$17 ;Aに$17をロード $C1/F38E STA $212C [$00:212C] ;Aを[$00:212C]に書き込み メインスクリーン指定 $C1/F391 STA $212D [$00:212D] ;Aを[$00:212D]に書き込み サブスクリーン指定 $C1/F394 LDA #$00 ;Aに$00をロード $C1/F396 STA $212E [$00:212E] ;Aを[$00:212E]に書き込み メインスクリーンウインドウマスク指定 $C1/F399 STA $212F [$00:212F] ;Aを[$00:212F]に書き込み サブスクリーンウインドウマスク指定 $C1/F39C STA $2130 [$00:2130] ;Aを[$00:2130]に書き込み 色追加選択 $C1/F39F STA $2131 [$00:2131] ;Aを[$00:2131]に書き込み 色計算指定 $C1/F3A2 STA $2133 [$00:2133] ;Aを[$00:2133]に書き込み スクリーンモード/ビデオ設定 $C1/F3A5 LDA #$E0 ;Aに$E0をロード $C1/F3A7 STA $2132 [$00:2132] ;Aを[$00:2132]に書き込み 固定色のデータ $C1/F3AA RTS ;サブルーチン戻り
一覧にすると以下の通り。
| アドレス | 名称 | 内容 | 値 |
|---|---|---|---|
[$00:2100] | INIDISP | スクリーン初期化 | $8F |
[$00:4200] | NMITIMEN | 割り込み有効フラグ | $01 |
[$00:2101] | OBJSEL | スプライトサイズ・データ領域選択 | $21 |
[$00:2103] | OAMADDH | OAM上位アドレスとスプライト優先順位 | $21 |
[$00:2105] | BGMODE | BG制御レジスタ(BGMode設定) | $09 |
[$00:2106] | MOSAIC | モザイク表示用サイズ・スクリーン設定 | $00 |
[$00:2107] | BG1SC | BG1のBGマップ設定 | $60 |
[$00:2108] | BG2SC | BG2のBGマップ設定 | $64 |
[$00:2109] | BG3SC | BG3のBGマップ設定 | $68 |
[$00:210B] | BG12NBA | BG1,BG2キャラクタデータ領域設定 | $15 |
[$00:210C] | BG34NBA | BG3,BG4キャラクタデータ領域設定 | $00 |
[$00:210D] | BG1HOFS | BG1水平スクロール量 | $00 |
[$00:210E] | BG1VOFS | BG1垂直スクロール量 | $00 |
[$00:210F] | BG2HOFS | BG2水平スクロール量 | $00 |
[$00:2110] | BG2VOFS | BG2垂直スクロール量 | $00 |
[$00:2111] | BG3HOFS | BG3水平スクロール量 | $00 |
[$00:2112] | BG3VOFS | BG3垂直スクロール量 | $00 |
[$00:2115] | VMAINC | ビデオポート調整 | $80 |
[$00:212C] | TM | メインスクリーン指定 | $17 |
[$00:212D] | TS | サブスクリーン指定 | $17 |
[$00:212E] | TMW | メインスクリーンウインドウマスク指定 | $00 |
[$00:212F] | TSW | サブスクリーンウインドウマスク指定 | $00 |
[$00:2130] | CGSWSEL | カラーマス制御レジスタA 色追加選択 | $00 |
[$00:2131] | CGADSUB | カラーマス制御レジスタB 色計算指定 | $00 |
[$00:2133] | SETINI | スクリーンモード/ビデオ設定 | $00 |
[$00:2132] | COLDATA | 固定色のデータ | $E0 |
[$00:2105]のBG制御レジスタ(BGMode設定)はフィールドと同じく$09が入るので、BGモードはMode1である。
その他、背景関係で重要な設定をまとめてみると以下のようになる。
| アドレス | 背景 | 背景サイズ(タイル) | VRAMでのデータ開始アドレス |
|---|---|---|---|
[$00:2107] | BG1 | 32×32 | %011000($18) * $0800 = $C000 |
[$00:2108] | BG2 | 32×32 | %011001($19) * $0800 = $C800 |
[$00:2109] | BG3 | 32×32 | %011010($1A) * $0800 = $D000 |
| アドレス | 値 |
|---|---|
[$00:210B] | $15 = %0001 0101 |
[$00:210C] | $00 = %0000 0000 |
| BG | VRAM開始アドレス |
|---|---|
| BG1 | %0101 * $2000 = $A000(byte) |
| BG2 | %0001 * $2000 = $2000(byte) |
| BG3 | %0000 * $2000 = $0000(byte) |
| アドレス | 値 |
|---|---|
[$00:212C] | $17 = %0001 0111 |
[$00:212D] | $17 = %0001 0111 |
| Bit | 内容 |
|---|---|
| Bit0 | BG1は1: 有効 |
| Bit1 | BG2は1: 有効 |
| Bit2 | BG3は1: 有効 |
| Bit3 | BG4は0: 無効 |
| Bit4 | OBJは1: 有効 |
フィールド初期設定と大きく異なるのがメインスクリーンとサブスクリーン設定で、戦闘開始の時点でBG1~BG3とOBJ(スプライト)を全てメインスクリーンとサブスクリーンで有効とする、つまり表示するという設定になっている。
戦闘開始時に画面上部ウィンドウに敵の名前などが一通り表示された後コマンド表示もあるため、最初からBG3が有効になっている。
その他、上の設定から、VRAMのアドレス割り振りが以下のようになっていることがわかる。
さらに、スプライトのサブルーチンで紹介したが、戦闘開始時に$C1/F2D1~$C1/F319の処理で、GDMA転送の8チャネルに以下のような初期設定が行われる。
| アドレス | n | 0 | 1 | 2 | 3 | 4 | 5 | 7 |
|---|---|---|---|---|---|---|---|---|
| GDMA / HDMA 設定レジスタ | $01 | $00 | - | $00 | $41 | $01 | $09 |
| 値 | $18 | $22 | $80 | $04 | $26 | $26 | $18 |
| 転送先 | VRAM | CGRAM | WRAM | OAM | Window 座標1L | Window 座標1L | VRAM | |
| データ元アドレス | - | - | - | - | $A700 | $E9FD | - |
| データ元バンク | - | - | - | - | $7E | $C1 | - |
| 間接HDMAアドレス | - | - | - | - | $7E | $C1 | - |
以上を元にサブルーチンを見ていく。
まず、VRAMアドレス$0000~$4000(BG3とBG2のタイルデータ領域)初期化が行われる。
$C1/0088 JSR $EB18 [$C1:EB18] ;[$C1:EB18]へジャンプ $C1/EB18 LDX #$029A ;Xに$029Aをロード $C1/EB1B STX $4372 [$00:4372] ;Xを[$00:4373][$00:4372]に書き込み $C1/EB1E LDA #$C1 ;Aに$C1をロード $C1/EB20 STA $4374 [$00:4374] ;Aを[$00:4374]に書き込み $C1/EB23 LDX #$4000 ;Xに$4000をロード $C1/EB26 STX $4375 [$00:4375] ;Xを[$00:4376][$00:4375]に書き込み $C1/EB29 LDX #$0000 ;Xに$0000をロード $C1/EB2C STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/EB2F LDA #$80 ;Aに$80をロード $C1/EB31 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル7 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $0000 ;[$00:4370] GDMA / HDMA設定レジスタ $09 fixed ;[$00:4371] 転送先指定 $18=VRAM ;[$00:4373][$00:4372] データ元アドレス(開始位置) $029A ;[$00:4374] データ元バンク $C1 ;[$00:4376][$00:4375] 転送するデータ量 $4000 ;$C1:029A -> VRAM $0000word($0000byte) +$4000byte ; $C1/EB34 RTS ;サブルーチン戻り $C1/008B JSR $8A67 [$C1:8A67] ;[$C1:8A67]へジャンプ
上処理を見ると、GDMAチャネル7で、$C1:029Aの値をVRAMアドレス$0000~$3FFFに転送している。
[$00:4370]に入る値が$09なので、送り元のアドレスは増えずに$C1:029Aで固定である。
なお、$C1:029Aの値は$00なので、$00で値が埋まる、ということである。
$C1/00B0 JSR $641B [$C1:641B] ;[$C1:641B]へジャンプ $C1/641B LDA #$C1 ;Aに$C1をロード $C1/641D STA $4374 [$00:4374] ;Aを[$00:4374]に書き込み $C1/6420 LDX #$4000 ;Xに$4000をロード $C1/6423 STX $4375 [$00:4375] ;Xを[$00:4376][$00:4375]に書き込み $C1/6426 LDX #$3000 ;Xに$3000をロード $C1/6429 STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/642C LDA #$80 ;Aに$80をロード $C1/642E STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル7 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $3000 ;[$00:4370] GDMA / HDMA設定レジスタ $09 fixed ;[$00:4371] 転送先指定 $18=VRAM ;[$00:4373][$00:4372] データ元アドレス(開始位置) $029A ;[$00:4374] データ元バンク $C1 ;[$00:4376][$00:4375] 転送するデータ量 $4000 ;$C1:029A -> VRAM $3000word($6000byte) +$4000byte ; $C1/6431 LDA $54 [$00:0354] ;Aに[$00:0354]をロード $C1/6433 STA $54 [$00:0354] ;Aを[$00:0354]に書き込み $C1/6435 JSR $6931 [$C1:6931] ;[$C1:6931]へジャンプ
上の処理は実質、VRAMアドレスが変更になっただけで、GDMAチャネル7で、$C1:029Aの値をVRAMアドレス$6000~$9FFFに転送している。
ここは実際にはスプライトのタイルの領域で、敵のタイルデータが入るのだが(詳細は後述)、ここを初期化したということになる。
次の処理は戦闘によって変わるため、ひとまず近未来編ワールドマップでの処理を掲載する。
VRAMへの転送は以下の通り。
$C1/6948 LDA $60 [$00:0360] ;Aに[$00:0360]($21)をロード $C1/694A STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/694C BNE $01 [$694F] ;ゼロフラグが立っていないとき[$694F]分岐 $C1/694F LDX #$5000 ;Xに$5000をロード $C1/6952 STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/6955 LDA #$7E ;Aに$7Eをロード $C1/6957 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/695A LDX #$A800 ;Xに$A800をロード ;ループ処理 $C1/695D REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/695F LDA $7E0000,x[$7E:A800] ;Aに[$7E:A801+2n][$7E:A800+2n]をロード $C1/6963 INX ;Xをインクリメント +1 $C1/6964 INX ;Xをインクリメント +1 $C1/6965 ASL A ;Aを算術左シフト *2 $C1/6966 ASL A ;Aを算術左シフト *2 $C1/6967 ASL A ;Aを算術左シフト *2 $C1/6968 ASL A ;Aを算術左シフト *2 $C1/6969 ASL A ;Aを算術左シフト *2 $C1/696A ADC #$2000 ;A + $2000 $C1/696D STA $4302 [$00:4302] ;Aを[$00:4303][$00:4302]に書き込み $C1/6970 LDA #$0020 ;Aに$0020をロード $C1/6973 STA $4305 [$00:4305] ;Aを[$00:4306][$00:4305]に書き込み $C1/6976 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/6978 LDA #$01 ;Aに$01をロード $C1/697A STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $5000 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) A + $2000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0020 ;$7E:???? -> VRAM $5000word($A000byte) +$0020byte $C1/697D DEC $08 [$00:0308] ;[$00:0308]をデクリメント -1 $C1/697F BNE $DC [$695D] ;ゼロフラグが立っていないとき[$695D]分岐 ;ループ処理ここまで $C1/6981 LDX #$5800 ;Xに$5800をロード $C1/6984 STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/6987 LDX #$0000 ;Xに$0000をロード $C1/698A STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C1/698D LDA #$DF ;Aに$DFをロード $C1/698F STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/6992 LDX #$0600 ;Xに$0600をロード $C1/6995 STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/6998 LDA #$01 ;Aに$01をロード $C1/699A STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $5800 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $0000 ;[$00:4304] データ元バンク $DF ;[$00:4306][$00:4305] 転送するデータ量 $0600 ;$DF:0000 -> VRAM $5800word($B000byte) +$0600byte $C1/699D RTS ;サブルーチン戻り $C1/6438 LDA $54 [$00:0354] ;Aに[$00:0354]をロード $C1/643A JSR $6759 [$C1:6759] ;[$C1:6759]へジャンプ $C1/6759 JSR $67D9 [$C1:67D9] ;[$C1:67D9]へジャンプ
上はループ処理でのVRAMへの転送だが、ループ回数はサブルーチン頭の[$00:0360]に入っている値なので可変である。
近未来編ワールドマップだと$21が入っているため、33回ループする。
ループ中に転送処理が入るが、転送先VRAMアドレスが$A000から開始で$20バイトずつ転送することと、転送元バンクが$7Eであることは固定。
問題は転送元アドレスで、例えば初回のループなら、
[$7E:A801][$7E:A800] * $20 + $2000
が転送元アドレスだから、これより前の処理で[$7E:A800]~に何が入っていたかで決まるということになる。
よって、戦闘によりここで転送される値やアドレスはまちまちということになる。
ただ、最後のループを抜けた後の$C1/6981~$C1/699DのGDMA転送だけは固定値の転送である。
また、どの転送も転送先はVRAMのBG1タイルデータの領域である。
まとめると、
$7E:????~をVRAMアドレス$A000~に転送(ループ内処理)$DF:0000~$DF:05FFをVRAMアドレス$B000~$B5FFに転送($C1/6981~$C1/699D処理)ということになるのだが、結論を言うとループ内処理での転送は、戦闘におけるBG1、背景用タイルのデータである。
近未来編ワールドマップだと、ループで以下のように値が入る。
| 転送元 | 転送先VRAM |
|---|---|
$7E:2000~$7E:23FF | $A000~$A3FF |
$7E:25E0~$7E:25FF | $A400~$A41F |
$A000~$A41Fのタイルが、背景のビルや道路用の画像になっている。
背景のデータ量によって転送元と転送量が変わってくる。
例えば最終編ラストバトル(オディオ戦)だと、$7E:3200~$7E:3FFFをVRAMの$A000~$AD80に転送しており、近未来編ワールドマップよりデータ量が多いことがわかる。
固定の送信である、$DF:0000~$DF:05FFをVRAMアドレス$B000~$B5FFに転送する処理だが、これはダメージフィールドのマス目のタイルである。
ダメージフィールドのマス1つ分のデータが$180バイト(タイル12個)×ダメージフィールドの種類4種ということである。
8×8ピクセルのタイルを横に4、縦に3並べて、1マス分(横32ピクセル×縦24ピクセル)のダメージフィールドになる、ということである。
以上から、$C1/6948~$C1/699Dは、該当の戦闘のBG1背景用タイル及び、ダメージフィールド4種用タイルのVRAM転送処理だということがわかった。
では、これより前のバンク$7Eへの転送はどうなっているのか。
遡ってみると、$C1/6B70~$C1/6CABでタイルデータ(バンク$DAあたりの値)を読み出して$7E:2000~$7E:37FFにへ書き込む処理を行っていることがわかる。詳細は省略する。
次の転送はVRAMアドレス$C000~$C7FF、つまりBG1のBGマップデータまるごとである。
まず、$C1/67D9~$C1/6919の処理で、バンク$D6などあたりから、$7E:8000~$7E:8FFFに値が入る。処理は省略。
その後、$C1/675C~$C1/67D8のサブルーチンで転送を行うのだが、その前に$7E:8000~$7E:8FFFの値の前処理をする。
$C1/675C PHB ;DBレジスタをスタックにプッシュ $C1/675D LDA #$7E ;Aに$7Eをロード $C1/675F PHA ;Aレジスタをスタックにプッシュ $C1/6760 PLB ;DBレジスタにスタックからプル $C1/6761 LDA $54 [$00:0354] ;Aに[$00:0354]をロード $C1/6763 CMP #$22 ;Aと$22を減算比較(ステータスレジスタ変更のみ) $C1/6765 BNE $05 [$676C] ;ゼロフラグが立っていないとき[$676C]分岐 $C1/676C LDX #$8100 ;Xに$8100をロード ;ループ処理開始 $C1/676F INX ;Xをインクリメント +1 $C1/6770 LDA $0000,x[$7E:8101] ;Aに[$7E:8101~$7E:86FF]をロード $C1/6773 EOR #$0C ;Aと$0Cで排他的論理和XOR $C1/6775 STA $0000,x[$7E:8101] ;Aを[$7E:8101~$7E:86FF]に書き込み $C1/6778 INX ;Xをインクリメント +1 $C1/6779 CPX #$8700 ;Xと$8700を減算比較(ステータスレジスタ変更のみ) $C1/677C BNE $F1 [$676F] ;ゼロフラグが立っていないとき[$676F]分岐 ;ループ処理ここまで $C1/677E LDX #$86C0 ;Xに$86C0をロード ;ループ処理開始 $C1/6781 LDA $0000,x[$7E:87BE] ;Aに[$7E:86C0~$7E:87BE]をロード $C1/6784 STA $0040,x[$7E:87FE] ;Aを[$7E:8700~$7E:87FE]に書き込み $C1/6787 INX ;Xをインクリメント +1 $C1/6788 LDA $0000,x[$7E:87BF] ;Aに[$7E:86C1~$7E:87BF]をロード $C1/678B STA $0040,x[$7E:87FF] ;Aを[$7E:8701~$7E:87FF]に書き込み $C1/678E INX ;Xをインクリメント +1 $C1/678F CPX #$87C0 ;Xと$87C0を減算比較(ステータスレジスタ変更のみ) $C1/6792 BNE $ED [$6781] ;ゼロフラグが立っていないとき[$6781]分岐 ;ループ処理ここまで $C1/6794 LDA $54 [$00:0354] ;Aに[$00:0354]をロード $C1/6796 CMP #$23 ;Aと$23を減算比較(ステータスレジスタ変更のみ) $C1/6798 BNE $12 [$67AC] ;ゼロフラグが立っていないとき[$67AC]分岐 $C1/67AC LDX #$8000 ;Xに$8000をロード ;ループ処理開始 $C1/67AF LDA $0000,x[$7E:8000] ;Aに[$7E:8000~$7E:87FF]をロード $C1/67B2 STA $0800,x[$7E:8800] ;Aを[$7E:8800~$7E:8FFF]に書き込み $C1/67B5 INX ;Xをインクリメント +1 $C1/67B6 CPX #$8800 ;Xと$8800を減算比較(ステータスレジスタ変更のみ) $C1/67B9 BNE $F4 [$67AF] ;ゼロフラグが立っていないとき[$67AF]分岐 ;ループ処理ここまで $C1/67BB PLB ;DBレジスタにスタックからプル $C1/67BC LDX #$8000 ;Xに$8000をロード $C1/67BF STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C1/67C2 LDA #$7E ;Aに$7Eをロード $C1/67C4 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/67C7 LDX #$0800 ;Xに$0800をロード $C1/67CA STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/67CD LDX #$6000 ;Xに$6000をロード $C1/67D0 STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/67D3 LDA #$01 ;Aに$01をロード $C1/67D5 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $6000 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $8000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0800 ;$7E:8000 -> VRAM $6000word($C000byte) +$0800byte $C1/67D8 RTS ;サブルーチン戻り
処理を見ると、
$7E:8100~$7E:86FFは、2バイトのデータ中、上1バイトだけ、元の値と$0Cで排他的論理和XORを取って書き戻している$7E:8000~$7E:87FFは、$7E:8800~$7E:8FFFにコピーされているこの後、そのままVRAMに送られ、BG1のBGマップデータ$800バイト分のデータ(VRAMアドレス$C000~$C7FF)になっている。
まず、$7E:8000~$7E:86FF→VRAM$C000~$C6FFだが、この範囲が実際にゲーム内で表示される1画面分のデータである。
図の、Y軸(縦方向)で1~28の範囲にあたる。
その中で、$7E:8100~$7E:86FF→$C100~$C6FFは、上部ウィンドウ部分だけ除いた範囲である。
$7E:8100~$7E:86FFのタイル1個分の2バイトのデータの、上1バイトだけ、元の値と$0C(%0000 1100)で排他的論理和XORを取って書き戻すという作業は何をしているのか。
BGマップデータの値は以下のように入る。
| 位(Bit) | 内容 |
|---|---|
| Bit0~9 | タイル番号 ($000~$3FF) ※アドレスではなく、タイルのナンバリング |
| Bit10~12 | パレット番号 (0~7) |
| Bit13 | BG優先度 (0:低, 1:高) |
| Bit14 | X反転 (0:反転しない、 1:水平方向に反転) |
| Bit15 | Y反転 (0:反転しない、 1:垂直方向に反転) |
2バイトの値の中で$0C00 = %0000 1100 0000 0000の部分はBit10~11、パレット番号3ビットの中で下2ビット部分である。
どうやら元々のカラーパレットのNo.を変更する処理のようである。
なお、$7E:8000~$7E:87FF→$7E:8800~$7E:8FFFの処理だが、VRAMへ送るのは$7E:8000~$7E:87FFの範囲だから、単純にコピーを行ったというだけで転送自体には何ら影響はない。
何はともあれ、これでBG1のBGマップデータをVRAMに送信できた。
続いて、敵のタイルマップを転送する。
先の設定からして、敵が表示されるBG2は、VRAMアドレス$2000からタイルデータ、$C800からBGマップデータが入ることになる。
なのだが、実際には、VRAMアドレス$2000には透過部分しか入らず、$6000以降に敵のタイルマップが入るようになっている。
また、$7E:2000~に敵のタイルデータが書き込んでからVRAMアドレス$6000~にGDMA転送を行う。
敵種類nのタイルデータを$7E:2000~に書き込み→VRAMアドレスにGDMA転送→敵種類nのカラーパレットをCGRAMへ転送、を敵種類1、2、3、4と順に行う。
先に結論だけ書いてしまうと、VRAMアドレス$6000~$7FFFには敵の右下向きのグラフィック、$8000~$9FFFには敵の右上向きのグラフィックが入る。
戦闘における敵種類番号順にグラフィックが入っていくのだが、敵の場合、戦闘フィールドの1マス分は横4×縦3タイル(横32×縦24ピクセル)なので、1マスにつきタイルデータが12個必要である。複数マスにまたがる敵はマスの数だけ掛け算することになる。
例えば近未来編ワールドマップで、「ラジコン2号×14、クルセイダーRS×1」という敵パーティ編成だったら、敵種類1はラジコン2号(縦1×横1マスサイズ)、敵種類2はクルセイダーRS(縦2×横1マスサイズ)である。
この時、VRAMアドレス$6000~には敵種類2種類分のタイルデータ(グラフィック)が入ることになる。
図にすると下のような感じになる。マス1個が8×8タイルで、横に16個($200バイト分)を並べると、ちょうど32の倍数で区切られてわかりやすくなる。
背景黄色部分がそのままラジコン2号のグラフィックになる。
ただし、一番下の縦1×横4タイル、$6600~$607Fはマスの外であるから必要ない(実際に、データの書き込みも行われない)。
つまり、敵グラフィックは横4タイルを3回分縦に並べることで1マス分のデータという扱いである。
縦方向に並べる数が3回と奇数なので、敵種類数によっては配置とアドレスがややこしくなっていくのだが、容量節約のためアドレスを詰めて配置されるようである。
クルセイダーRSは青色背景部分を縦に3つ繋げて、縦2×横1マスサイズ(縦6タイル×横4タイル)のグラフィックが完成する、ということである。
この、縦2×横4タイルを並べる順序は、敵種類がどれだけ増えても変わらない。
また、敵グラフィックは1回の戦闘で$6000~$7FFFに収まる範囲でなければならない、ということもわかる。
VRAMアドレスで見るとなかなか複雑で、ラジコン2号の右下向きのタイルだったら、
$6000~$607Fと$6200~$627F$6400~$647F(※$6600~$667Fにはデータは入らない)クルセイダーRSだったら、
$6080~$60EFと$6280~$62EF$6480~$64EFと$6680~$66EF$6100~$617Fと$6300~$637Fというようにタイルデータが入る仕組みである。
以上を踏まえた上でサブルーチンを見てみる。
例として、上と同じように「ラジコン2号×14、クルセイダーRS×1」での転送処理としよう。
1回のVRAM転送で、$80バイト、つまり縦1×横4タイル分を転送する処理をする。
ラジコン2号は$80バイトを3回転送、クルセイダーRSは$80バイトを6回転送で、右下向きのタイルデータの転送が完了。
さらに、右上向きのデータをVRAMアドレス$8000から同じようにラジコン2号は$80バイトを3回転送、クルセイダーRSは$80バイトを6回転送。
合計で9回のGDMA転送を行うことになる。
敵のサイズによってVRAMのどこからどこまでデータを書き込むかが変わってくることから、まず敵に関する諸々の数値の読み出し部分から説明をする。
敵種類1に関するデータ読み出し部分とする。
ここでは敵ID$97のラジコン2号の例とする。
$C1/63C1 STZ $9E [$00:039E] ;[$00:039E]に$00を書き込み $C1/63C3 JSR $FD76 [$C1:FD76] ;[$C1:FD76]へジャンプ $C1/FD76 LDA $0A00 [$00:0A00] ;Aに[$00:0A00](シナリオID)をロード $C1/FD79 CMP #$08 ;Aと$08を減算比較(ステータスレジスタ変更のみ) $C1/FD7B BNE $03 [$FD80] ;ゼロフラグが立っていないとき[$FD80]分岐 ;ゼロフラグON $C1/FD7D LDA $0A10 [$00:0A10] ;Aに[$00:0A10](最終編主人公)をロード ;ゼロフラグOFF $C1/FD80 RTS ;サブルーチン戻り $C1/63C6 BNE $18 [$63E0] ;ゼロフラグが立っていないとき[$63E0]分岐 ;ゼロフラグOFF 最終編オルステッド主人公時以外 $C1/63E0 LDX #$1A00 ;Xに$1A00をロード $C1/63E3 LDA $0001,x[$00:1A01] ;Aに[$00:1A01](敵種類1状態)をロード $C1/63E6 BIT #$60 ;Aと$60で論理積(ステータスフラグ変更のみ) $C1/63E8 BEQ $08 [$63F2] ;ゼロフラグが立っているとき[$63F2]分岐 ;ゼロフラグOFF(該当の敵がいる) $C1/63EA JSR $63F6 [$C1:63F6] ;[$C1:63F6]へジャンプ $C1/63F6 PHX ;Xレジスタをスタックにプッシュ $C1/63F7 JSR $65E9 [$C1:65E9] ;[$C1:65E9]へジャンプ $C1/65E9 STX $9A [$00:039A] ;X(1A00)を[$00:039B][$00:039A]に書き込み $C1/65EB LDA $0000,x[$00:1A00] ;Aに[$00:1A00](敵種類1敵ID)をロード $C1/65EE XBA ;Aレジスタの上位バイトと下位バイトを交換 $C1/65EF LDA #$00 ;Aに$00をロード $C1/65F1 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/65F3 LSR A ;Aを論理右シフト (/2) $C1/65F4 LSR A ;Aを論理右シフト (/2) $C1/65F5 LSR A ;Aを論理右シフト (/2) $C1/65F6 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み [$00:1A00]*$20 $C1/65F8 LDA $D50010[$D5:0010] ;Aに[$D5:0010]($04CB)をロード $C1/65FC ADC $10 [$00:0310] ;A([$D5:0010]($04CB)) + [$00:0310](敵ID*$20) $C1/65FE TAX ;Aの値をXレジスタに転送 $C1/65FF SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/6601 LDA $D50003,x[$D5:17AE] ;Aに[$D5:17AE](敵キャラデータ03 A9)をロード $C1/6605 PHA ;Aレジスタをスタックにプッシュ $C1/6606 LDA $D50002,x[$D5:17AD] ;Aに[$D5:17AD](敵キャラデータ02 A8)をロード $C1/660A JSR $6706 [$C1:6706] ;[$C1:6706]へジャンプ $C1/6706 JSR $6612 [$C1:6612] ;[$C1:6612]へジャンプ $C1/6612 STZ $A5 [$00:03A5] ;[$00:03A5]に$00を書き込み $C1/6614 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/6616 AND #$00FF ;Aと$00FFで論理積 $C1/6619 STA $10 [$00:0310] ;A(00A8)を[$00:0311][$00:0310]に書き込み $C1/661B ASL A ;Aを算術左シフト *2 $C1/661C ADC $10 [$00:0310] ;A([$00:0311][$00:0310]*2) + [$00:0311][$00:0310] $C1/661E ADC $D50050[$D5:0050] ;A([$00:0311][$00:0310]*3) + [$D5:0051][$D5:0050](F979) $C1/6622 TAX ;Aの値をXレジスタに転送 $C1/6623 LDA $D50000,x[$D5:FB71] ;Aに[$D5:FB72][$D5:FB71](88DC)をロード $C1/6627 STA $A2 [$00:03A2] ;Aを[$00:03A3][$00:03A2]に書き込み $C1/6629 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/662B LDA $D50002,x[$D5:FB73] ;Aに[$D5:FB73](89)をロード $C1/662F STA $9C [$00:039C] ;Aを[$00:039C]に書き込み $C1/6631 AND #$C0 ;Aと$C0(1100 0000)で論理積 $C1/6633 LSR A ;Aを論理右シフト (/2) $C1/6634 LSR A ;Aを論理右シフト (/2) $C1/6635 LSR A ;Aを論理右シフト (/2) $C1/6636 LSR A ;Aを論理右シフト (/2) $C1/6637 LSR A ;Aを論理右シフト (/2) $C1/6638 LSR A ;Aを論理右シフト (/2) $C1/6639 ADC #$DB ;A([$D5:FB73]の上2ビット) + $DB $C1/663B STA $A0 [$00:03A0] ;Aを[$00:03A0]に書き込み $C1/663D JSR $6B43 [$C1:6B43] ;[$C1:6B43]へジャンプ
$C1/63E0以降は最終編オルステッド主人公以外の処理になる。
かつ、敵種類1が戦闘にいる場合、$C1/65EBで敵IDを読み込んで、各種の値を計算している。
$C1/6601は敵キャラデータ03、$C1/6606は敵キャラデータ02の呼び出しになる。
バンク$D5、アドレスは「(敵キャラデータ02) *3 + $F979」が、$C1/6623で呼び出される値になっていて、[$00:03A3][$00:03A2]に書き込まれる。
かつ、そのアドレスに+2したアドレスの値が$C1/662Bでロードされて、[$00:039C]に書き込まれる。
この[$00:039C]に書き込まれる値が後々でサイズ計算などに必要になってくる。
簡単に言えば[$00:039C]のBit3~5が敵の横幅(マス数)、Bit0~2が敵の縦幅(マス数)で、横幅*4が横タイル数、縦幅*3が縦タイル数である。
1×1マスサイズが、横タイル4枚(8ピクセル×4)、縦タイル3枚(8ピクセル×3)で構成されているのでこのような計算になる。
つまり、バンク$D5、アドレス「(敵キャラデータ02) *3 + $F97B」の値に、敵のサイズが格納されていることになる。
続いて、敵のタイルデータを$7E:2000~に書き込む。
下の処理を見るとわかるが、さきほど[$00:03A3][$00:03A2]に書き込まれた値をアドレス、「[$00:039C]の上2ビット + $DB」をバンクとしたアドレスが、該当の敵のタイルデータの開始位置である。
ラジコン2号だと[$DD:88DD]が開始位置。
適当に中略する。
$C1/6B43 PHB ;DBレジスタをスタックにプッシュ $C1/6B44 LDA $A0 [$00:03A0] ;Aに[$00:03A0]をロード $C1/6B46 PHA ;Aレジスタをスタックにプッシュ $C1/6B47 PLB ;DBレジスタにスタックからプル $C1/6B48 LDY $A2 [$00:03A2] ;Yに[$00:03A3][$00:03A2]をロード $C1/6B4A LDA #$00 ;Aに$00をロード $C1/6B4C STA $A4 [$00:03A4] ;Aを[$00:03A4]に書き込み $C1/6B4E LDX $A4 [$00:03A4] ;Xに[$00:03A4]をロード $C1/6B50 JSR $6B70 [$C1:6B70] ;[$C1:6B70]へジャンプ $C1/6B70 LDA #$00 ;Aに$00をロード $C1/6B72 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/6B74 STA $09 [$00:0309] ;Aを[$00:0309]に書き込み $C1/6B76 LDA $0000,y[$DD:88DC] ;Aに[$DD:88DC]をロード $C1/6B79 INY ;Yをインクリメント +1 $C1/6B7A LSR A ;Aを論理右シフト (/2) $C1/6B7B BCC $03 [$6B80] ;キャリーフラグが立っていないとき[$6B80]分岐 $C1/6B80 LSR A ;Aを論理右シフト (/2) $C1/6B81 BCS $67 [$6BEA] ;キャリーフラグが立っているとき[$6BEA]分岐 $C1/6B83 LSR A ;Aを論理右シフト (/2) $C1/6B84 BCS $2B [$6BB1] ;キャリーフラグが立っているとき[$6BB1]分岐 $C1/6B86 LSR A ;Aを論理右シフト (/2) $C1/6B87 ORA $08 [$00:0308] ;Aと[$00:0308]で論理和 $C1/6B89 INC A ;Aをインクリメント +1 $C1/6B8A STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/6B8C INC $09 [$00:0309] ;A[$00:0309]をインクリメント +1 $C1/6B8E LDA $0000,y[$DD:88DD] ;Aに[$DD:88DD]をロード $C1/6B91 INY ;Yをインクリメント +1 $C1/6B92 STA $7E2000,x[$7E:2000] ;Aを[$7E:2000]に書き込み ;(中略) $C1/6B76 LDA $0000,y[$DD:8A8C] ;Aに[$DD:8A8C]をロード $C1/6B79 INY ;Yをインクリメント +1 $C1/6B7A LSR A ;Aを論理右シフト (/2) $C1/6B7B BCC $03 [$6B80] ;キャリーフラグが立っていないとき[$6B80]分岐 $C1/6B7D BRL $00B1 [$6C31] ;フラグにかかわりなく常に分岐[$6C31] $C1/6C31 LSR A ;Aを論理右シフト (/2) $C1/6C32 BCS $3F [$6C73] ;キャリーフラグが立っているとき[$6C73]分岐 $C1/6C73 LSR A ;Aを論理右シフト (/2) $C1/6C74 BCS $0F [$6C85] ;キャリーフラグが立っているとき[$6C85]分岐 $C1/6C85 AND #$1E ;Aと$1Eで論理積 $C1/6C87 BNE $0A [$6C93] ;ゼロフラグが立っていないとき[$6C93]分岐 $C1/6C89 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/6C8B ORA $08 [$00:0308] ;Aと[$00:0308]で論理和 $C1/6C8D ORA $09 [$00:0309] ;Aと[$00:0309]で論理和 $C1/6C8F BEQ $1A [$6CAB] ;ゼロフラグが立っているとき[$6CAB]分岐 $C1/6CAB RTS ;サブルーチン戻り $C1/6B5C LDA #$10 ;Aに$10をロード $C1/6B5E STA $A4 [$00:03A4] ;Aを[$00:03A4]に書き込み $C1/6B60 LDX $A4 [$00:03A4] ;Xに[$00:03A4]をロード $C1/6B62 JSR $6B70 [$C1:6B70] ;[$C1:6B70]へジャンプ $C1/6B70 LDA #$00 ;Aに$00をロード ;(中略) $C1/6CAB RTS ;サブルーチン戻り $C1/6B65 LDA #$11 ;Aに$11をロード $C1/6B67 STA $A4 [$00:03A4] ;Aを[$00:03A4]に書き込み $C1/6B69 LDX $A4 [$00:03A4] ;Xに[$00:03A4]をロード $C1/6B6B JSR $6B70 [$C1:6B70] ;[$C1:6B70]へジャンプ $C1/6B70 LDA #$00 ;Aに$00をロード ;(中略) $C1/6CAB RTS ;サブルーチン戻り $C1/6B6E PLB ;DBレジスタにスタックからプル $C1/6B6F RTS ;サブルーチン戻り $C1/6640 RTS ;サブルーチン戻り $C1/672C JSR $6641 [$C1:6641] ;[$C1:6641]へジャンプ
結果、$7E:2000~$7E:217F($180バイト)にタイルデータがコピーされる。
ラジコン2号は縦3タイル×横4タイル、つまり12タイル分のデータで構成されており、タイル1個のデータは$20バイト×12 = $180バイトだから、これですべてコピーされたことになる。
この後にVRAMに転送することになるが、その前に敵の縦と横サイズ関係の処理を行う。
$C1/6641 LDA $9C [$00:039C] ;Aに[$00:039C]をロード $C1/6643 AND #$38 ;Aと$38(0011 1000)で論理積 $C1/6645 LSR A ;Aを論理右シフト (/2) $C1/6646 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/6648 LSR A ;Aを論理右シフト (/2) $C1/6649 LSR A ;Aを論理右シフト (/2) $C1/664A STA $20 [$00:0320] ;Aを[$00:0320]に書き込み([$00:039C]のBit3-5) $C1/664C LDA $9C [$00:039C] ;Aに[$00:039C]をロード $C1/664E AND #$07 ;Aと$07(0000 0111)で論理積 $C1/6650 CMP #$07 ;Aと$07を減算比較(ステータスレジスタ変更のみ) $C1/6652 BEQ $08 [$665C] ;ゼロフラグが立っているとき[$665C]分岐 $C1/6654 STA $21 [$00:0321] ;Aを[$00:0321]に書き込み([$00:039C]のBit0-2) $C1/6656 ASL A ;Aを算術左シフト *2 $C1/6657 ADC $21 [$00:0321] ;A + [$00:0321] $C1/6659 STA $11 [$00:0311] ;Aを[$00:0311]に書き込み $C1/665B RTS ;サブルーチン戻り $C1/670C LDX $9A [$00:039A] ;Xに[$00:039B][$00:039A]をロード $C1/670E LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/6710 STA $0002,x[$00:1A02] ;A(04)を[$00:1A02]に書き込み 横タイル数 $C1/6713 LDA $11 [$00:0311] ;Aに[$00:0311]をロード $C1/6715 STA $0003,x[$00:1A03] ;A(03)を[$00:1A03]に書き込み 縦タイル数 $C1/6718 LDA $20 [$00:0320] ;Aに[$00:0320]をロード $C1/671A STA $0004,x[$00:1A04] ;A(01)を[$00:1A04]に書き込み 横マス数 $C1/671D LDA $21 [$00:0321] ;Aに[$00:0321]をロード $C1/671F STA $0005,x[$00:1A05] ;A(01)を[$00:1A05]に書き込み 縦マス数 $C1/6722 LDY #$3000 ;Yに$3000をロード $C1/6725 JSR $6665 [$C1:6665] ;[$C1:6665]へジャンプ
上処理で、[$00:039C]の値から、敵の縦と横サイズをそれぞれマス数・タイル数で計算し、[$00:1A02]~[$00:1A05]に書き込んでいる。
さらに、Yに$3000をロードして処理が続くが、この$3000は後でGDMA転送時にVRAMの開始アドレスに設定される。
今回の処理は初回のタイル書き込みなのでVRAMの開始アドレスは$3000だが、この先は$80バイト(タイル4個分)データ転送する毎に値が加算されていく。
$C1/6665 STY $10 [$00:0310] ;Y($3000)を[$00:0311][$00:0310]に書き込み $C1/6667 LDY #$2000 ;Yに$2000をロード $C1/666A STY $12 [$00:0312] ;Y($2000)を[$00:0313][$00:0312]に書き込み $C1/666C LDA $9E [$00:039E] ;Aに[$00:039E]をロード $C1/666E STA $0006,x[$00:1A06] ;Aを[$00:1A06]に書き込み 00 $C1/6671 STA $0007,x[$00:1A07] ;Aを[$00:1A07]に書き込み 00 $C1/6674 LDA $0003,x[$00:1A03] ;Aに[$00:1A03]をロード(縦タイル数) $C1/6677 STA $09 [$00:0309] ;Aを[$00:0309]に書き込み $C1/6679 LDA #$04 ;Aに$04をロード $C1/667B STA $0A [$00:030A] ;A($04)を[$00:030A]に書き込み $C1/667D LDA $0002,x[$00:1A02] ;Aに[$00:1A02]をロード(横タイル数) $C1/6680 LSR A ;Aを論理右シフト (/2) $C1/6681 LSR A ;Aを論理右シフト (/2) $C1/6682 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/6684 LDA $0006,x[$00:1A06] ;Aに[$00:1A06](00)をロード $C1/6687 JSR $66D3 [$C1:66D3] ;[$C1:66D3]へジャンプ
ここで[$00:1A06]と[$00:1A07]には[$00:039E]が書き込まれている。
前に$C1/63C1で[$00:039E]に00がセットされたから、この時点では必ず$00が[$00:1A06]に入る。[$00:1A07]も同様。
つまり敵種類1の[$00:1A06]は$00で固定である。
ただ、[$00:1A07]については、この後GDMA転送がおこなわれるたびにインクリメント(+1)されていく。
つまり、タイル横1列につき+1されていくのだが、最終的に[$00:1A07]にはその敵の縦タイル数が入るのではない。
その敵が占める縦のタイル数が入る。
図だと以下のようになる。
敵の縦幅によって[$00:1A03]と[$00:1A07]が同じだったり1だけ異なったりする、ということになる。
なお、[$00:1An6]は、該当の敵のVRAMグラフィック読み込み開始位置になる。
下のようにVRAMアドレス$6000~$7FFFをを4タイル×4タイルで区切って、どの位置からどの敵のタイルが開始になるかという値が[$00:1An6]に$00~$0Fで入る。
「ラジコン2号×14、クルセイダーRS×1」の敵パーティなら、ラジコン2号の[$00:1A06]は$00だし、クルセイダーRSの[$00:1A46]は$01となる。
$C1/66D3 STA $0007,x[$00:1A07] ;A(00)を[$00:1A07]に書き込み $C1/66D6 STA $1F [$00:031F] ;Aを[$00:031F]に書き込み $C1/66D8 AND #$03 ;Aと$03で論理積 $C1/66DA CLC ;キャリーフラグクリア $C1/66DB ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/66DC ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/66DD ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/66DE STA $1E [$00:031E] ;Aを[$00:031E]に書き込み $C1/66E0 LDA $1F [$00:031F] ;Aに[$00:031F]をロード $C1/66E2 AND #$0C ;Aと$0Cで論理積 $C1/66E4 STA $1F [$00:031F] ;A(00)を[$00:031F]に書き込み $C1/66E6 LDA #$04 ;Aに$04をロード $C1/66E8 SEC ;キャリーフラグON $C1/66E9 SBC $0A [$00:030A] ;A($04) - [$00:030A]($04) $C1/66EB CLC ;キャリーフラグクリア $C1/66EC ADC $1F [$00:031F] ;A + [$00:031F](00) $C1/66EE ADC $11 [$00:0311] ;A + [$00:0311]($30) $C1/66F0 STA $1F [$00:031F] ;Aを[$00:031F]に書き込み $C1/66F2 RTS ;サブルーチン戻り $C1/668A LDY $1E [$00:031E] ;Yに[$00:031F][$00:031E](3000)をロード $C1/668C STY $2116 [$00:2116] ;Yを[$00:2117][$00:2116]に書き込み $C1/668F LDY $12 [$00:0312] ;Yに[$00:0313][$00:0312]($2000)をロード $C1/6691 STY $4302 [$00:4302] ;Yを[$00:4303][$00:4302]に書き込み $C1/6694 LDA $12 [$00:0312] ;Aに[$00:0312]をロード $C1/6696 CLC ;キャリーフラグクリア $C1/6697 ADC #$80 ;A + $80 $C1/6699 STA $12 [$00:0312] ;Aを[$00:0312]に書き込み $C1/669B BCC $02 [$669F] ;キャリーフラグが立っていないとき[$669F]分岐 ;キャリーフラグON([$00:0312]に繰り上がりがあったら、[$00:0313]も繰り上げる) $C1/669D INC $13 [$00:0313] ;[$00:0313]をインクリメント +1 ;キャリーフラグOFF([$00:0312]に繰り上がりなし) $C1/669F LDA $0000,x[$00:1A00] ;Aに[$00:1A00](敵ID)をロード $C1/66A2 CMP #$D1 ;Aと$D1(ピュアオディオ)を減算比較(ステータスレジスタ変更のみ) $C1/66A4 BNE $03 [$66A9] ;ゼロフラグが立っていないとき[$66A9]分岐 ;ゼロフラグON(敵種類がピュアオディオ) $C1/66A6 JSR $6145 [$C1:6145] ;[$C1:6145]にジャンプ ;ゼロフラグOFF(敵種類がピュアオディオではない) $C1/66A9 LDA #$7E ;Aに$7Eをロード $C1/66AB STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/66AE LDY #$0080 ;Yに$0080をロード $C1/66B1 STY $4305 [$00:4305] ;Yを[$00:4306][$00:4305]に書き込み $C1/66B4 LDA #$01 ;Aに$01をロード $C1/66B6 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $3000 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $2000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0080 ;$7E:2000 -> VRAM $3000word($6000byte) +$0080byte ; $C1/66B9 JSR $66F3 [$C1:66F3] A:0101 X:1A00 Y:0080 P:envMxdIzc;[$C1:66F3]へジャンプ
話がだいぶ寄り道したが、ここでようやく最初のGDMA転送が行われる。
VRAM開始アドレスは[$00:031F][$00:031E]、データ元開始アドレスは[$00:0313][$00:0312]が指定されている。
[$00:0313][$00:0312]は[$00:4303][$00:4302]にコピーされた直後に、+$80しているから、次のデータ元開始アドレスが既にここで指定されているということになる。
また、$C1/669F~$C1/66A4で敵IDが$D1ではないか、つまり、敵ID$D1のピュアオディオかどうかで分岐がある。
オディオ戦の開始時、ピュアオディオのデータ(グラフィック含め)は呼び出されず、ピュアオディオが出現したタイミングで各種データが読み込まれるという仕様があるため、ここで分岐を設けている。
ピュアオディオではない、通常戦闘だと$C1/66A9以降の処理が続いて、GDMA転送を行う。
転送量は$7E:2000~$7E:207Fの$80バイト分、つまり縦1×横4タイル分である。
後はアドレスを変えながら転送を続けていくことになる。
$C1/66F3 INC $0007,x[$00:1A07] ;[$00:1A07]をインクリメント +1 $C1/66F6 LDA $1E [$00:031E] ;Aに[$00:031E]をロード $C1/66F8 CLC ;キャリーフラグクリア $C1/66F9 ADC #$40 ;A + $40 $C1/66FB STA $1E [$00:031E] ;Aを[$00:031E]に書き込み $C1/66FD BCC $06 [$6705] ;キャリーフラグが立っていないとき[$6705]分岐 ;キャリーフラグON(VRAM開始アドレス下2桁処理,繰り上がり時) $C1/66FF LDA $1F [$00:031F] ;Aに[$00:031F]をロード $C1/6701 ADC #$03 ;A + $03 $C1/6703 STA $1F [$00:031F] ;Aを[$00:031F]に書き込み ;キャリーフラグOFF $C1/6705 RTS ;サブルーチン戻り $C1/66BC DEC $08 [$00:0308] ;[$00:0308]をデクリメント -1 $C1/66BE BNE $CA [$668A] ;ゼロフラグが立っていないとき[$668A]分岐 $C1/66C0 DEC $0A [$00:030A] ;[$00:030A]をデクリメント -1 ($03) $C1/66C2 BNE $0A [$66CE] ;ゼロフラグが立っていないとき[$66CE]分岐 $C1/66CE DEC $09 [$00:0309] ;[$00:0309]をデクリメント -1 $C1/66D0 BNE $AB [$667D] ;ゼロフラグが立っていないとき[$667D]分岐 $C1/667D LDA $0002,x[$00:1A02] ;Aに[$00:1A02]をロード $C1/6680 LSR A ;Aを論理右シフト (/2) $C1/6681 LSR A ;Aを論理右シフト (/2) $C1/6682 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/6684 LDA $0006,x[$00:1A06] ;Aに[$00:1A06]をロード $C1/6687 JSR $66D3 [$C1:66D3] ;[$C1:66D3]へジャンプ
$C1/667D以降が初回転送の途中からの合流地点、つまりループ部分になる。
[$00:0308]は敵の横タイル数で、横回数分転送したかのチェック。
[$00:030A]はVRAMの$200バイト区切り部分のチェックである。
[$00:0309]は敵の縦タイル数で、こちらも縦に何回分転送したかのチェック。
ここで[$00:0308]、[$00:030A]、[$00:0309]がデクリメント-1されることで、次の処理でのアドレス位置が変わる。
それより前の$C1/66F3~$C1/6705は、[$00:1A07]をインクリメントしているので、縦幅1列書き終えた後の処理。
[$00:031E]に$40を加算するのは、VRAM開始アドレス[$00:031F][$00:031E]を$40ずつ増やすため(VRAMアドレスの単位はワードなので、実際は+$100バイト)
$C1/66FDで分岐があるのは、[$00:031F][$00:031E]の下2桁である[$00:031E]に繰り上がりが生じたら、[$00:031F]を+3して次の書き込み位置を変えるためである。
$C1/66D3 STA $0007,x[$00:1A07] ;Aを[$00:1A07]に書き込み $C1/66D6 STA $1F [$00:031F] ;Aを[$00:031F]に書き込み $C1/66D8 AND #$03 ;Aと$03で論理積 $C1/66DA CLC ;キャリーフラグクリア $C1/66DB ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/66DC ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/66DD ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/66DE STA $1E [$00:031E] ;Aを[$00:031E]に書き込み $C1/66E0 LDA $1F [$00:031F] ;Aに[$00:031F]をロード $C1/66E2 AND #$0C ;Aと$0Cで論理積 $C1/66E4 STA $1F [$00:031F] ;Aを[$00:031F]に書き込み $C1/66E6 LDA #$04 ;Aに$04をロード $C1/66E8 SEC ;キャリーフラグON $C1/66E9 SBC $0A [$00:030A] ;A - [$00:030A]($03) $C1/66EB CLC ;キャリーフラグクリア $C1/66EC ADC $1F [$00:031F] ;A + [$00:031F] $C1/66EE ADC $11 [$00:0311] ;A + [$00:0311] $C1/66F0 STA $1F [$00:031F] ;Aを[$00:031F]に書き込み $C1/66F2 RTS ;サブルーチン戻り
[$00:030A]がデクリメントされたことで$C1/66F0で[$00:031F]に書き込まれる値が初回より+1される。
また、先のGDMA転送前に既に、[$00:0313][$00:0312]は+$80されている。
よって、この後のGDMA転送のVRAM開始位置アドレスと転送元開始アドレスが初回から変動する。
$C1/668A LDY $1E [$00:031E] ;Yに[$00:031F][$00:031E](3100)をロード $C1/668C STY $2116 [$00:2116] ;Yを[$00:2117][$00:2116]に書き込み $C1/668F LDY $12 [$00:0312] ;Yに[$00:0313][$00:0312](2080)をロード $C1/6691 STY $4302 [$00:4302] ;Yを[$00:4303][$00:4302]に書き込み $C1/6694 LDA $12 [$00:0312] ;Aに[$00:0312]をロード $C1/6696 CLC ;キャリーフラグクリア $C1/6697 ADC #$80 ;A + $80 $C1/6699 STA $12 [$00:0312] ;Aを[$00:0312]に書き込み $C1/669B BCC $02 [$669F] ;キャリーフラグが立っていないとき[$669F]分岐 $C1/669D INC $13 [$00:0313] ;A[$00:0313]をインクリメント +1 $C1/669F LDA $0000,x[$00:1A00] ;Aに[$00:1A00]をロード $C1/66A2 CMP #$D1 ;Aと$D1を減算比較(ステータスレジスタ変更のみ) $C1/66A4 BNE $03 [$66A9] ;ゼロフラグが立っていないとき[$66A9]分岐 $C1/66A9 LDA #$7E ;Aに$7Eをロード $C1/66AB STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/66AE LDY #$0080 ;Yに$0080をロード $C1/66B1 STY $4305 [$00:4305] ;Yを[$00:4306][$00:4305]に書き込み $C1/66B4 LDA #$01 ;Aに$01をロード $C1/66B6 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $3100 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $2080 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0080 ;$7E:2080 -> VRAM $3100word($6200byte) +$0080byte ; $C1/66B9 JSR $66F3 [$C1:66F3] A:0101 X:1A00 Y:0080 P:enVMxdIzc;[$C1:66F3]へジャンプ
このように、初回転送は、
$7E:2000 -> VRAM $3000word($6000byte) +$0080byte
だったのが、2回目(つまり2列目)は、
$7E:2080 -> VRAM $3100word($6200byte) +$0080byte
と変わったことがわかる。
以上の処理にて、VRAMへ敵のタイルデータがすべて転送されていく。
この後は敵種類1のカラーパレットCGRAM転送である。
$C1/62EF LDX $9A [$00:039A] ;Xに[$00:039A]をロード $C1/62F1 LDA $0009,x[$00:1A09] ;Aに[$00:1A09]をロード $C1/62F4 STA $2121 [$00:2121] ;Aを[$00:2121]に書き込み $C1/62F7 LDA $0000,x[$00:1A00] ;Aに[$00:1A00](敵ID)をロード $C1/62FA XBA ;Aレジスタの上位バイトと下位バイトを交換 $C1/62FB LDA #$00 ;Aに$00をロード $C1/62FD REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/62FF LSR A ;Aを論理右シフト (/2) $C1/6300 LSR A ;Aを論理右シフト (/2) $C1/6301 LSR A ;Aを論理右シフト (/2) $C1/6302 ADC $D60010[$D6:0010] ;A + [$D6:0010] $C1/6306 STA $4312 [$00:4312] ;Aを[$00:4313][$00:4312]に書き込み $C1/6309 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/630B LDA $0000,x[$00:1A00] ;Aに[$00:1A00]をロード $C1/630E CMP #$D1 ;Aと$D1を減算比較(ステータスレジスタ変更のみ) $C1/6310 BNE $03 [$6315] ;ゼロフラグが立っていないとき[$6315]分岐 ;ゼロフラグOFF(ピュアオディオ以外) $C1/6315 LDA #$D6 ;Aに$D6をロード $C1/6317 STA $4314 [$00:4314] ;Aを[$00:4314]に書き込み $C1/631A LDX #$0020 ;Xに$0020をロード $C1/631D STX $4315 [$00:4315] ;Xを[$00:4315]に書き込み $C1/6320 LDA #$02 ;Aに$02をロード $C1/6322 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル1 ;[$00:2121] CGRAMアドレス ※word単位 [$00:1A09]と同値、$40~$70 ;[$00:4310] GDMA / HDMA設定レジスタ $00 ;[$00:4311] 転送先指定 $22=CGRAM ;[$00:4313][$00:4312] データ元アドレス(開始位置) 敵ID*$20 + $80 ;[$00:4314] データ元バンク $D6 ;[$00:4316][$00:4315] 転送するデータ量 $0080 ;$D6:(敵ID*$20 + $80) -> CGRAM $40~$70word +$0080byte ; $C1/6325 RTS ;サブルーチン戻り
上処理で敵のカラーパレットのデータがCGRAMにGDMA転送される。
転送先アドレスはCGRAM$40~$70word、敵種類1~4の順に入る。
ここでも敵ID$D1、つまりピュアオディオについては別処理になる分岐がある。
上から、敵のカラーパレットのデータはバンク$D6、アドレス敵ID*$20+$80にある、ということがわかる。
つまり敵ID$00から$FFまで、$D6:0080~$D6:207Fに格納されているということである。
味方キャラのカラーパレットが$D6:2080から開始になるのでつながっていることがわかる。
といっても、敵ID$EB以降には味方キャラのデータが入っている。カラーパレットだと$D6:1DE0~にあたるが、$D6:2080~のカラーパレットとは一致しない。ダミーデータ的なものなのか何かしらの意味があるのかは未確認。
ここまでで敵のタイルデータをVRAMに送信した。
この後にBG2のBGマップデータの送信が行われるのだが、その前に敵の配置を決めなければマップデータが作れない。
$7E:F000~$7E:F7FEにBGマップデータを置いて、VRAM転送、という方法になるが、まず$7E:F000~$7E:F7FEを$C1/1528~$C1/156Cのサブルーチンで初期化する($00で埋める)。
この後、戦闘に出てくる敵を1体ずつ、座標からBGマップデータに必要なデータを取り出していく。
(この時点で敵パーティデータから各敵座標は計算済みである)
詳細は省略するが、
$C1/E80C~$C1/E832:敵番号nの状態を確認してから座標・敵キャラタイル位置処理へジャンプ~処理終了$C1/E7BD~$C1/E808:敵番号nのX・Y座標(マス目)と向きから敵キャラタイルの向き判定$C1/6326~$C1/6337:敵番号nのX・Y座標(マス目)からロードする敵キャラタイルアドレス計算$C1/6338~$C1/63A7:敵番号nの縦・横タイル幅からVRAMデータマップ書き込み位置計算$C1/64A6~$C1/64FB:敵番号nのBG2のマップデータを$7E:F000~$7E:F7FE内に書き込み大まかにはこんな感じで、これをすべての敵番号に繰り返して処理する。
$7E:F000~$7E:F7FEのBGマップデータが完成したら、
$C1/E80B RTS ;サブルーチン戻り $C1/EA79 PLX ;Xレジスタにスタックからプル $C1/EA7A BRA $F2 [$EA6E] ;フラグにかかわりなく常に分岐[$EA6E] $C1/EA6E JSR $E1CB [$C1:E1CB] ;[$C1:E1CB]へジャンプ $C1/E1CB REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/E1CD TXA ;Xレジスタの値をAレジスタに転送 $C1/E1CE ADC #$0010 ;A + $0010 $C1/E1D1 TAX ;Aの値をXレジスタに転送 $C1/E1D2 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/E1D4 CPX #$1C00 ;Xと$1C00を減算比較(ステータスレジスタ変更のみ) $C1/E1D7 BEQ $11 [$E1EA] ;ゼロフラグが立っているとき[$E1EA]分岐 $C1/E1D9 LDA $0001,x[$00:1BF1] ;Aに[$00:1BF1]をロード $C1/E1DC BIT #$20 ;Aと$20で論理積(ステータスフラグ変更のみ) $C1/E1DE BNE $07 [$E1E7] ;ゼロフラグが立っていないとき[$E1E7]分岐 $C1/E1E0 CMP #$80 ;Aと$80を減算比較(ステータスレジスタ変更のみ) $C1/E1E2 BEQ $06 [$E1EA] ;ゼロフラグが立っているとき[$E1EA]分岐 $C1/E1EA LDA #$80 ;Aに$80をロード $C1/E1EC RTS ;サブルーチン戻り $C1/EA71 BMI $09 [$EA7C] ;ネガティブフラグが立っているとき[$EA7C]分岐 $C1/EA7C JSR $8B6B [$C1:8B6B] ;[$C1:8B6B]へジャンプ $C1/8B6B LDX #$6400 ;Xに$6400をロード $C1/8B6E STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/8B71 LDX #$F000 ;Xに$F000をロード $C1/8B74 STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C1/8B77 LDA #$7E ;Aに$7Eをロード $C1/8B79 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/8B7C LDX #$0740 ;Xに$0740をロード $C1/8B7F STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/8B82 LDA #$01 ;Aに$01をロード $C1/8B84 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $6400 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $F000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0740 ;$7E:F000 -> VRAM $6400word($C800byte) +$0740byte $C1/8B87 RTS ;サブルーチン戻り $C1/00BC JSR $8B88 [$C1:8B88] ;[$C1:8B88]へジャンプ $C1/8B88 LDX #$00C0 ;Xに$00C0をロード $C1/8B8B BRA $F2 [$8B7F] ;フラグにかかわりなく常に分岐[$8B7F] $C1/8B7F STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/8B82 LDA #$01 ;Aに$01をロード $C1/8B84 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $67A0 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $F740 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $00C0 ;$7E:F740 -> VRAM $67A0word($CF40byte) +$00C0byte $C1/8B87 RTS ;サブルーチン戻り
$C1/EA7Cの後の$C1/8B84で$7E:F000~$7E:F73FがVRAMの$C800~$CF3Fに転送される。
しかしBGマップデータは、背景1面につき$800バイト分であるから、$C0バイト足りない。
といっても、先に述べた通り、実際にゲーム画面に表示されるのは$700バイトまでで事足りるので、必要な部分は送信できている。
この時点で、BG2には敵の配置は転送されている。
$C1/00BCの後でもう一度転送処理があるが、ここが最後の$C0バイト分の転送である。
上処理の後、BG3のBGマップデータ転送処理が行われる。
$C1/1528 PHB ;DBレジスタをスタックにプッシュ $C1/1529 LDA #$7E ;Aに$7Eをロード $C1/152B PHA ;Aレジスタをスタックにプッシュ $C1/152C PLB ;DBレジスタにスタックからプル $C1/152D REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/152F LDA $A0 [$00:03A0] ;Aに[$00:03A0]をロード ;ループ処理ここから $C1/1531 STA $0000,x ;Aを[$7E:C000~$7E:C720]に書き込み $C1/1534 STA $0002,x ;Aを[$7E:C002~$7E:C722]に書き込み $C1/1537 STA $0004,x ;Aを[$7E:C004~$7E:C724]に書き込み $C1/153A STA $0006,x ;Aを[$7E:C006~$7E:C726]に書き込み $C1/153D STA $0008,x ;Aを[$7E:C008~$7E:C728]に書き込み $C1/1540 STA $000A,x ;Aを[$7E:C00A~$7E:C72A]に書き込み $C1/1543 STA $000C,x ;Aを[$7E:C00C~$7E:C72C]に書き込み $C1/1546 STA $000E,x ;Aを[$7E:C00E~$7E:C72E]に書き込み $C1/1549 STA $0010,x ;Aを[$7E:C010~$7E:C730]に書き込み $C1/154C STA $0012,x ;Aを[$7E:C012~$7E:C732]に書き込み $C1/154F STA $0014,x ;Aを[$7E:C014~$7E:C734]に書き込み $C1/1552 STA $0016,x ;Aを[$7E:C016~$7E:C736]に書き込み $C1/1555 STA $0018,x ;Aを[$7E:C018~$7E:C738]に書き込み $C1/1558 STA $001A,x ;Aを[$7E:C01A~$7E:C73A]に書き込み $C1/155B STA $001C,x ;Aを[$7E:C01C~$7E:C73C]に書き込み $C1/155E STA $001E,x ;Aを[$7E:C01E~$7E:C73E]に書き込み $C1/1561 TXA ;Xレジスタの値をAレジスタに転送 $C1/1562 ADC #$0020 ;A + $0020 $C1/1565 TAX ;Aの値をXレジスタに転送 $C1/1566 DEY ;Yをデクリメント -1 $C1/1567 BNE $C6 [$152F] ;ゼロフラグが立っていないとき[$152F]分岐 ;ループここまで $C1/1569 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/156B PLB ;DBレジスタにスタックからプル $C1/156C RTS ;サブルーチン戻り $C1/5156 PLY ;Yレジスタにスタックからプル $C1/5157 RTS ;サブルーチン戻り $C1/5143 JSR $8D42 [$C1:8D42] ;[$C1:8D42]へジャンプ $C1/8D42 STZ $FC [$00:03FC] ;[$00:03FC]に$00を書き込み $C1/8D44 LDX #$6800 ;Xに$6800をロード $C1/8D47 STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/8D4A LDX #$C000 ;Xに$C000をロード $C1/8D4D STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C1/8D50 LDA #$7E ;Aに$7Eをロード $C1/8D52 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/8D55 LDX #$0740 ;Xに$0740をロード $C1/8D58 STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/8D5B LDA #$01 ;Aに$01をロード $C1/8D5D STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $6800 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $C000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0740 ;$7E:C000 -> VRAM $6800word($D000byte) +$0740byte ; $C1/8D60 RTS ;サブルーチン戻り $C1/5146 RTS ;サブルーチン戻り $C1/00D1 JSR $3516 [$C1:3516] ;[$C1:3516]へジャンプ
以上は、BG3のBGマップデータ領域であるVRAMアドレス$D000~の初期化である。
その前の処理を見ればわかるが、固定値の[$00:03A0]を$7E:C000~$7E:C73Fにコピーした後、$7E:C000~$7E:C73FをVRAMアドレス$D000~$D73Fに転送していることになる。何らかの文字データなどを送信しているのではない。
戦闘開始の時点では戦闘画面に文字列は表示されず、少し間を置いて上部ウィンドウに敵の名前が表示されることになる。
最後に、戦闘画面表示直後に送られるBG3のBGマップデータ(ウィンドウと文字列)の処理を紹介しておく。
まず、文字部分を$7E:B000~にコピーする。
サブルーチンは、$C1/5095~$C1/50EA、文字数と位置判定は$C1/3ED1~$C1/3F7D。
さらに、BG3のBGマップデータを$7E:B000~にコピーする。
サブルーチンは、$C1/E4CD~$C1:E552, $C1:E55C~$C1/E5A3。
VRAMへの転送は、途中にウィンドウ用のカラーパレット転送を挟んで、以下のようになる。
;ウィンドウ文字転送 $C1/8D0F LDA $FB [$00:03FB] ;Aに[$00:03FB]をロード $C1/8D11 BNE $01 [$8D14] ;ゼロフラグが立っていないとき[$8D14]分岐 $C1/8D14 STZ $FB [$00:03FB] ;[$00:03FB]に$00を書き込み $C1/8D16 LDX #$B000 ;Xに$B000をロード $C1/8D19 STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C1/8D1C LDA #$7E ;Aに$7Eをロード $C1/8D1E STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/8D21 LDX $DA [$00:03DA] ;Xに[$00:03DB][$00:03DA]をロード $C1/8D23 BEQ $0D [$8D32] ;ゼロフラグが立っているとき[$8D32]分岐 $C1/8D25 STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/8D28 LDX $D8 [$00:03D8] ;Xに[$00:03D9][$00:03D8]をロード $C1/8D2A STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/8D2D LDA #$01 ;Aに$01をロード $C1/8D2F STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 [$00:03D9][$00:03D8]($1E00) ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $B000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 [$00:03DB][$00:03DA](画面上部ウィンドウなら$0300) ;$7E:B000 -> VRAM $1E00word($3C00byte) +$0300byte ; $C1/8D32 RTS ;サブルーチン戻り ; ;(中略) ; $C1/85B8 JSR $8913 [$C1:8913] ;[$C1:8913]へジャンプ ; ;ウィンドウカラーパレット転送 $C1/8913 LDA $F4 [$00:03F4] ;Aに[$00:03F4]をロード $C1/8915 BNE $01 [$8918] ;ゼロフラグが立っていないとき[$8918]分岐 $C1/8918 STZ $F4 [$00:03F4] ;[$00:03F4]に$00を書き込み $C1/891A CMP #$01 ;Aと$01を減算比較(ステータスレジスタ変更のみ) $C1/891C BEQ $15 [$8933] ;ゼロフラグが立っているとき[$8933]分岐 $C1/891E CMP #$02 ;Aと$02を減算比較(ステータスレジスタ変更のみ) $C1/8920 BEQ $2D [$894F] ;ゼロフラグが立っているとき[$894F]分岐 $C1/8922 CMP #$03 ;Aと$03を減算比較(ステータスレジスタ変更のみ) $C1/8924 BEQ $30 [$8956] ;ゼロフラグが立っているとき[$8956]分岐 $C1/8926 CMP #$04 ;Aと$04を減算比較(ステータスレジスタ変更のみ) $C1/8928 BEQ $33 [$895D] ;ゼロフラグが立っているとき[$895D]分岐 $C1/895D LDX #$02B2 ;Xに$02B2をロード $C1/8960 LDA #$C1 ;Aに$C1をロード $C1/8962 BRA $D4 [$8938] ;フラグにかかわりなく常に分岐[$8938] $C1/8938 STX $4312 [$00:4312] ;Xを[$00:4313][$00:4312]に書き込み $C1/893B STA $4314 [$00:4314] ;Aを[$00:4314]に書き込み $C1/893E LDX #$0008 ;Xに$0008をロード $C1/8941 STX $4315 [$00:4315] ;Xを[$00:4316][$00:4315]に書き込み $C1/8944 LDA #$00 ;Aに$00をロード $C1/8946 STA $2121 [$00:2121] ;Aを[$00:2121]に書き込み $C1/8949 LDA #$02 ;Aに$02をロード $C1/894B STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル1 ;[$00:2121] CGRAMアドレス ※word単位 $00 ;[$00:4310] GDMA / HDMA設定レジスタ $00 ;[$00:4311] 転送先指定 $22=CGRAM ;[$00:4313][$00:4312] データ元アドレス(開始位置) $02B2 ;[$00:4314] データ元バンク $C1 ;[$00:4316][$00:4315] 転送するデータ量 $0008 ;$C1:02B2 -> CGRAM $00word +$0008byte ; $C1/894E RTS ;サブルーチン戻り $C1/85BB JSR $85EC [$C1:85EC] ;[$C1:85EC]へジャンプ $C1/85EC LDA $77 [$00:0377] ;Aに[$00:0377]をロード $C1/85EE AND #$0F ;Aと$0Fで論理積 $C1/85F0 CMP #$06 ;Aと$06を減算比較(ステータスレジスタ変更のみ) $C1/85F2 BEQ $01 [$85F5] ;ゼロフラグが立っているとき[$85F5]分岐 $C1/85F4 RTS ;サブルーチン戻り $C1/85BE JSR $8667 [$C1:8667] ;[$C1:8667]へジャンプ $C1/8667 LDA $0070 [$00:0070] ;Aに[$00:0070]をロード $C1/866A CMP #$01 ;Aと$01を減算比較(ステータスレジスタ変更のみ) $C1/866C BNE $17 [$8685] ;ゼロフラグが立っていないとき[$8685]分岐 $C1/8685 RTS ;サブルーチン戻り ; $C1/85C1 JSR $8D33 [$C1:8D33] ;[$C1:8D33]へジャンプ ; ;BG3のBGマップデータ転送 $C1/8D33 LDA $FC [$00:03FC] ;Aに[$00:03FC]をロード $C1/8D35 BEQ $0A [$8D41] ;ゼロフラグが立っているとき[$8D41]分岐 $C1/8D37 CMP #$FF ;Aと$FFを減算比較(ステータスレジスタ変更のみ) $C1/8D39 BEQ $07 [$8D42] ;ゼロフラグが立っているとき[$8D42]分岐 $C1/8D42 STZ $FC [$00:03FC] ;[$00:03FC]に$00を書き込み $C1/8D44 LDX #$6800 ;Xに$6800をロード $C1/8D47 STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/8D4A LDX #$C000 ;Xに$C000をロード $C1/8D4D STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C1/8D50 LDA #$7E ;Aに$7Eをロード $C1/8D52 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/8D55 LDX #$0740 ;Xに$0740をロード $C1/8D58 STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/8D5B LDA #$01 ;Aに$01をロード $C1/8D5D STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ; ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $6800 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $C000 ;[$00:4304] データ元バンク $7E ;[$00:4306][$00:4305] 転送するデータ量 $0740 ;$7E:C000 -> VRAM $6800word($D000byte) +$0740byte ; $C1/8D60 RTS ;サブルーチン戻り
まず、ウィンドウ文字転送だが、[$00:4306][$00:4305]に書き込む転送量がウィンドウにより変わる。
戦闘開始時なら画面上部の敵の名前表示だが、この画面上部位置は$0300バイトである。
コマンドウィンドウやアイテム一覧などは表示文字数が変わるため変動する。
VRAM転送先は[$00:03D9][$00:03D8]に入っているが、おそらくだいたいの場合VRAMアドレス$1E00バイトである。
あとは$7E:B000~以降をVRAMへGDMA転送する。
カラーパレットの転送だが、カラーパレットNo.00に8バイトだけ転送を行っている。
カラーパレット1つ分のデータは32バイト、2バイトで1色のデータだから、4色分のデータを転送している。
BG3は4色が使えるため、BG3用の4色を転送している。
戦闘開始時の画面上部ウィンドウは、敵名表示の時、赤系の色になるため、この時用のデータを転送しているのである。
つまり、この時の赤系の色のカラーパレットデータは、$C1:02B2~$C1:02B9に入っていた、ということがわかる。
最後にBG3のBGマップデータ転送で、$7E:C000~$7E:C73FをVRAM$6800~$6F3Fに転送し、ウィンドウと文字が表示されることになる。
背景ではなくスプライト扱いではあるが、戦闘で汎用の記号や数字、攻撃をミスした時の「ミス!」といった画像をVRAMに送る処理も戦闘開始時に行われるので、紹介しておく。
VRAMのアドレス$5000~$569Fにタイルデータを入れる処理になる。
$C1/4D8A LDX #$4E16 ;Xに$4E16をロード ;ループ処理 $C1/4D8D LDA $C10000,x[$C1:4E16] ;Aに[$C1:4E16+7n]をロード $C1/4D91 STA $4302 [$00:4302] ;Aを[$00:4302]に書き込み $C1/4D94 LDA $C10001,x[$C1:4E17] ;Aに[$C1:4E17+7n]をロード $C1/4D98 STA $4303 [$00:4303] ;Aを[$00:4303]に書き込み $C1/4D9B LDA $C10002,x[$C1:4E18] ;Aに[$C1:4E18+7n]をロード $C1/4D9F STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/4DA2 LDA $C10003,x[$C1:4E19] ;Aに[$C1:4E19+7n]をロード $C1/4DA6 STA $4305 [$00:4305] ;Aを[$00:4305]に書き込み $C1/4DA9 LDA $C10004,x[$C1:4E1A] ;Aに[$C1:4E1A+7n]をロード $C1/4DAD STA $4306 [$00:4306] ;Aを[$00:4306]に書き込み $C1/4DB0 LDA $C10005,x[$C1:4E1B] ;Aに[$C1:4E1B+7n]をロード $C1/4DB4 STA $2116 [$00:2116] ;Aを[$00:2116]に書き込み $C1/4DB7 LDA $C10006,x[$C1:4E1C] ;Aに[$C1:4E1C+7n]をロード $C1/4DBB STA $2117 [$00:2117] ;Aを[$00:2117]に書き込み $C1/4DBE LDA #$01 ;Aに$01をロード $C1/4DC0 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み $C1/4DC3 REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/4DC5 TXA ;Xレジスタの値をAレジスタに転送 $C1/4DC6 ADC #$0007 ;A + $0007 $C1/4DC9 TAX ;Aの値をXレジスタに転送 $C1/4DCA SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/4DCC CPX #$4EB0 ;Xと$4EB0を減算比較(ステータスレジスタ変更のみ) $C1/4DCF BNE $BC [$4D8D] ;ゼロフラグが立っていないとき[$4D8D]分岐 ;ループ処理ここまで $C1/4DD1 JSR $4EB0 [$C1:4EB0] ;[$C1:4EB0]へジャンプ ;符号付16bit x 8bit $0080 * シナ $C1/4EB0 LDA #$80 ;Aに$80をロード $C1/4EB2 STA $211B [$00:211B] ;Aを[$00:211B]に書き込み $C1/4EB5 LDA #$00 ;Aに$00をロード $C1/4EB7 STA $211B [$00:211B] ;Aを[$00:211B]に書き込み $C1/4EBA JSR $4D7E [$C1:4D7E] ;[$C1:4D7E]へジャンプ $C1/4D7E LDA $0A00 [$00:0A00] ;Aに[$00:0A00](シナリオID)をロード $C1/4D81 CMP #$08 ;Aと$08を減算比較(ステータスレジスタ変更のみ) $C1/4D83 BCS $01 [$4D86] ;キャリーフラグが立っているとき[$4D86]分岐 ;キャリーフラグOFF(最終編以外) $C1/4D85 RTS ;サブルーチン戻り ;キャリーフラグON(最終編) $C1/4D86 LDA $0A10 [$00:0A10] ;Aに[$00:0A10](最終編主人公ID)をロード $C1/4D89 RTS ;サブルーチン戻り $C1/4EBD STA $211C [$00:211C] ;Aを[$00:211C]に書き込み $C1/4EC0 STA $211C [$00:211C] ;Aを[$00:211C]に書き込み $C1/4EC3 REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/4EC5 LDA $2134 [$00:2134] ;Aに[$00:2135][$00:2134]($80 * シナリオID)をロード $C1/4EC8 ADC #$F520 ;A + $F520 $C1/4ECB STA $4302 [$00:4302] ;Aを[$00:4303][$00:4302]に書き込み $C1/4ECE SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/4ED0 LDA #$CF ;Aに$CFをロード $C1/4ED2 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/4ED5 LDX #$0040 ;Xに$0040をロード $C1/4ED8 STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/4EDB LDX #$2820 ;Xに$2820をロード $C1/4EDE STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/4EE1 LDA #$01 ;Aに$01をロード $C1/4EE3 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $2820 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $80 * シナリオID + $F520 ;[$00:4304] データ元バンク $CF ;[$00:4306][$00:4305] 転送するデータ量 $0040 ;$CF:($80 * シナリオID + $F520) -> VRAM $2820word($5040byte) +$0040byte $C1/4EE6 LDX #$0040 ;Xに$0040をロード $C1/4EE9 STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C1/4EEC LDX #$2920 ;Xに$2920をロード $C1/4EEF STX $2116 [$00:2116] ;Xを[$00:2117][$00:2116]に書き込み $C1/4EF2 LDA #$01 ;Aに$01をロード $C1/4EF4 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA チャネル0 ;[$00:2117][$00:2116] VRAMアドレス ※word単位 $2920 ;[$00:4300] GDMA / HDMA設定レジスタ $01 ;[$00:4301] 転送先指定 $18=VRAM ;[$00:4303][$00:4302] データ元アドレス(開始位置) $80 * シナリオID + $F520 ;[$00:4304] データ元バンク $CF ;[$00:4306][$00:4305] 転送するデータ量 $0040 ;$CF:($80 * シナリオID + $F520)+$40 -> VRAM $2920word($5240byte) +$0040byte $C1/4EF7 LDA #$20 ;Aに$20をロード $C1/4EF9 STA $211B [$00:211B] ;Aを[$00:211B]に書き込み $C1/4EFC LDA #$00 ;Aに$00をロード $C1/4EFE STA $211B [$00:211B] ;Aを[$00:211B]に書き込み $C1/4F01 JSR $4D7E [$C1:4D7E] ;[$C1:4D7E]へジャンプ
$C1/4D8A~$C1/4DD1のループ処理は最初にXが$4E16で固定だから、固定値の転送である。
大まかに分けると以下のとおり。
$CF:F4A0~$CF:F51F:塗りつぶし用の四角形$D6:F900~$D6:FAFF:HPやダメージ量の数字と、「ミス!」の文字$D6:FC00~$D6:FFFF:技範囲内の枠や、キャラ範囲枠、✕マークなどさらに、$C1/4EBD~$C1/4EF4は最初にシナリオID(最終編は最終編主人公ID)で分岐するが、これはカーソル形状の画像のロードのため、シナリオまたは最終編なら最終編主人公で変わることになる。
カーソル画像のバンクは$CF、開始アドレスは、
$80 * シナリオID + $F520
で計算されるので、$CF:F520から、中世、原始、SF、功夫、西部、現代、近未来、幕末の順にカーソル画像が並んでいる(各$80バイト)、ということになる。
つまりカーソル画像は$CF:F520~$CF:F91Fに格納されている。
次ページでは、メニュー画面でのサブルーチンを紹介する。