基礎知識
戦闘関係
前ページでスプライトについてひととおり説明したので、このページではサブルーチンを紹介する。
ここからはフィールド、戦闘、メニュー画面に分けてサブルーチンを紹介していく。
スプライトのデータ呼び出しのサブルーチンは$CE/D47E~$CE/D89D。
大まかに見ると、スプライトのタイル1つ分、つまり8×8ピクセルサイズの$20バイト分のデータをループ処理でコピー、を繰り返しているのだが、前ページにて述べた通りに左右対称にドットを配置する場合や、後にスプライトを配置する時の都合で途中分岐が多くなっている。
下はサンダウンの例。
$CE/D47E PHB ;DBレジスタをスタックにプッシュ $CE/D47F LDA $65 [$00:0065] ;Aに[$00:0065]をロード $CE/D481 PHA ;Aレジスタをスタックにプッシュ $CE/D482 PLB ;DBレジスタにスタックからプル $CE/D483 LDY #$0001 ;Yに$0001をロード $CE/D486 LDX $63 [$00:0063] ;Xに[$00:0063]をロード $CE/D488 LDA [$60],y[$CF:074A] ;Aに[$CF:074A]をロード $CE/D48A INY ;Yをインクリメント +1 $CE/D48B CMP #$F0 ;Aと$F0を減算比較(ステータスレジスタ変更のみ) $CE/D48D BCC $0E [$D49D] ;キャリーフラグが立っていないとき[$D49D]分岐 $CE/D49D INC A ;Aをインクリメント +1 $CE/D49E LSR A ;Aを論理右シフト (/2) $CE/D49F STA $66 [$00:0066] ;Aを[$00:0066]に書き込み $CE/D4A1 BCC $0B [$D4AE] ;キャリーフラグが立っていないとき[$D4AE]分岐 $CE/D4AE STZ $67 [$00:0067] ;[$00:0067]に$00を書き込み $CE/D4B0 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア ;ループ開始 $CE/D4B2 LDA [$60],y[$CF:074B] ;Aに[$CF:074B]をロード $CE/D4B4 STA $0000,x[$7E:8000] ;Aを[$7E:8000]に書き込み $CE/D4B7 INX ;Xをインクリメント +1 $CE/D4B8 INX ;Xをインクリメント +1 $CE/D4B9 INY ;Yをインクリメント +1 $CE/D4BA INY ;Yをインクリメント +1 $CE/D4BB DEC $66 [$00:0066] ;[$00:0066]をデクリメント -1 $CE/D4BD BNE $F3 [$D4B2] ;ゼロフラグが立っていないとき[$D4B2]分岐 ;ループここまで $CE/D48F STX $63 [$00:0063] ;Xを[$00:0063]に書き込み $CE/D491 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $CE/D493 AND #$000F ;Aと$000Fで論理積 $CE/D496 ASL A ;Aを算術左シフト *2 $CE/D497 TAX ;Aの値をXレジスタに転送 $CE/D498 SEP #$20 ;MフラグON Aレジスタは8bit幅 $CE/D49A JMP ($D89E,x)[$CE:D4C3] ;[$CE:D4C3]へジャンプ(レジスタをスタックに積まない) ;(中略) $CE/D4BD BNE $F3 [$D4B2] ;ゼロフラグが立っていないとき[$D4B2]分岐 $CE/D4B2 LDA [$60],y[$CF:0905] ;Aに[$CF:0905]をロード $CE/D4B4 STA $0000,x[$7E:81FE] ;Aを[$7E:81FE]に書き込み $CE/D4B7 INX ;Xをインクリメント +1 $CE/D4B8 INX ;Xをインクリメント +1 $CE/D4B9 INY ;Yをインクリメント +1 $CE/D4BA INY ;Yをインクリメント +1 $CE/D4BB DEC $66 [$00:0066] ;[$00:0066]をデクリメント -1 $CE/D4BD BNE $F3 [$D4B2] ;ゼロフラグが立っていないとき[$D4B2]分岐 $CE/D4BF SEP #$20 ;MフラグON Aレジスタは8bit幅 $CE/D4C1 BRA $C5 [$D488] ;フラグにかかわりなく常に分岐[$D488] $CE/D488 LDA [$60],y[$CF:0907] ;Aに[$CF:0907]をロード $CE/D48A INY ;Yをインクリメント +1 $CE/D48B CMP #$F0 ;Aと$F0を減算比較(ステータスレジスタ変更のみ) $CE/D48D BCC $0E [$D49D] ;キャリーフラグが立っていないとき[$D49D]分岐 $CE/D48F STX $63 [$00:0063] ;Xを[$00:0063]に書き込み $CE/D491 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $CE/D493 AND #$000F ;Aと$000Fで論理積 $CE/D496 ASL A ;Aを算術左シフト *2 $CE/D497 TAX ;Aの値をXレジスタに転送 $CE/D498 SEP #$20 ;MフラグON Aレジスタは8bit幅 $CE/D49A JMP ($D89E,x)[$CE:D89C] ;[$CE:D89C]へジャンプ(レジスタをスタックに積まない) $CE/D89C PLB ;DBレジスタにスタックからプル $CE/D89D RTL ;サブルーチン戻り
途中をかなり省略したが、上がサンダウンの$200バイト分のデータ$CF:074B~$CF:0905を、$7E:8000~$7E:81FEにコピーする処理である(実際には途中で計算処理して値が変わっている場合もある。そのデータがそのままVRAMに送られる)。
基本は$CE/D4B2~$CE/D4BDのループ部分のコピー処理で、$20バイト毎に分岐処理をしているが、細かい説明は端折る。
この後、$7E:8200~以降にも他データがコピーされ、VRAMへデータが送られる。
$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:2117][$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]へジャンプ ;(中略) $C0/4ECE LDX #$8000 ;Xに$8000をロード $C0/4ED1 STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C0/4ED4 LDA #$7E ;Aに$7Eをロード $C0/4ED6 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C0/4ED9 LDY #$2000 ;Yに$2000をロード $C0/4EDC STY $4305 [$00:4305] ;Yを[$00:4306][$00:4305]に書き込み $C0/4EDF LDA #$01 ;Aに$01をロード $C0/4EE1 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み
VRAMへのデータ転送は上で、書き込み先のアドレスはI/Oレジスタ、つまりVRAMとやりとりをするためのメモリアドレスになる。
詳しくは
snes-docs-ja/memory/dma
snes-docs-ja/memory/dma/ioreg.md
を参照していただきたいが、GDMA(General Direct Memory Access)転送という方法で、スーパーファミコンのメモリからVRAMへデータを送る。
HDMA(HBlank DMA)という、HBlank(テレビ画面に横1列分のドット絵を描写してから、次の行への描写へ移る僅かな時間)を利用した転送もあるが、ここで使っているのはそれ以外でも使えるGDMA。
ちなみにGDMAで転送中は、スーファミ自体のCPUは一時停止する(計算を一時停止する)。
| アドレス(I/Oレジスタ) | 内容 |
|---|---|
[$00:4300] | GDMA/HDMA設定レジスタ |
[$00:4301] | 転送先指定 $04=OAM, $18=VRAM, $22=CGRAM, $80=WRAM |
[$00:4303][$00:4302] | データ元アドレス(開始位置) |
[$00:4304] | データ元バンク |
[$00:4306][$00:4305] | 転送するデータ量 |
[$00:420B] | GDMA(General Direct Memory Access)チャネルレジスタ |
[$00:2117][$00:2116] | 転送先 VRAMアドレス |
$00:43n?のnは0~7の値をとり、それぞれでGDMA(HDMA含む)転送を設定できる。
上だと、n=0である。
つまりGDMA転送は最大8つ設定できる(8チャネル)。
上は、$7E:8000~$7E:9FFFのデータをVRAMに転送する処理になる。
準備として、転送先に$18(VRAM)、データ元バンクに$7E、データ元アドレス2バイトに$8000、転送するデータ量に$2000を指定しておく。
つまり、GDMA転送のチャネル0を使用する。
[$00:420B]は、GDMAチャネルレジスタで、8ビットの値が下から順にチャネル0~チャネル7に対応している。
該当チャネルのビットに1を書き込むと、そのチャネルのGDMA転送を行う、という仕組みである。
チャネル0の転送を行うのなら、%0000 0001 = $01を[$00:420B]に書き込むと転送開始になる。
$C0/4EDF~$C0/4EE1が、該当部分にあたる。
GDMA転送は高速なので、画像関係の大量のデータ転送は、基本的にGDMA転送を行う。
この先のサブルーチンでも頻出となる。
また、複数のGDMAチャネルの設定をしてから、[$00:420B]に複数のチャネルの対応ビットに1を書き込むと、HDMA転送が最優先、後はチャネル番号が小さいほど優先で転送を行う仕組みになっている。
$00:4300のGDMA/HDMA設定レジスタの説明は長くなるので、下ウェブサイトなどを参照していただきたい。
[$00:2117][$00:2116]には、転送先であるVRAMの開始アドレスを指定する。
今回は$C0/4DCE~の指定から、VRAMの開始アドレスは$0000である。
VRAMのアドレス指定の時、アドレスをbyteではなくwordで指定するため注意が必要。2byte(バイト)で1word(ワード)になる。
今回は$0000なので、そのままで構わないが、もしバイトに直すのならワードを2倍する必要がある、ということである。
フィールドでは、マップ切り替え時にカラーパレットが呼び出される。
$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](転送先指定 $22=CGRAM)に書き込み ;(中略) $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]分岐 ;ゼロフラグON(最終編) $C0/9A13 LDA $0A10 [$00:0A10] ;Aに[$00:0A10](主人公ID)をロード ;ゼロフラグOFF(最終編以外) $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: 1F (転送データサイズ-1) ;X: 主人公ID*$20 + $F960 (転送元アドレス) ;Y: 3900 (転送先アドレス) ;オペランド: 転送元バンクCF 転送先バンク7E ;$CF:(主人公ID*$20 + $F960) -> $7E:3900 +$20 $C0/9A29 MVN CF 7E ;データ転送 $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]分岐 ;ゼロフラグON $C0/9A36 LDX #$FA60 ;Xに$FA60をロード $C0/9A39 BRA $03 [$9A3E] ;フラグにかかわりなく常に分岐[$9A3E] ;ゼロフラグOFF $C0/9A3B LDX #$FE20 ;Xに$FE20をロード $C0/9A3E REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C0/9A40 PHB ;DBレジスタをスタックにプッシュ $C0/9A41 LDA #$007F ;Aに$007Fをロード $C0/9A44 MVN CF 7E ;データ転送 ;A: 7F (転送データサイズ-1) ;X: FE20またはFA60 (転送元アドレス) ;Y: 3920 (転送先アドレス) ;オペランド: 転送元バンクCF 転送先バンク7E ;$CF:FE20/FA60 -> $7E:3920 +$7F $C0/9A44 MVN CF 7E ;データ転送 $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]分岐 ;ゼロフラグON $C0/9A51 LDA $0ACF [$00:0ACF] ;Aに[$00:0ACF](操作中のキャラID)をロード $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] ;ゼロフラグOFF $C0/9A6A LDA $0ACF [$00:0ACF] ;Aに[$00:0ACF](操作中のキャラID)をロード $C0/9A6D CMP #$04 ;Aと$04を減算比較(ステータスレジスタ変更のみ) $C0/9A6F BEQ $0E [$9A7F] ;ゼロフラグが立っているとき[$9A7F]分岐(西部) $C0/9A71 CMP #$07 ;Aと$07を減算比較(ステータスレジスタ変更のみ) $C0/9A73 BEQ $05 [$9A7A] ;ゼロフラグが立っているとき[$9A7A]分岐(幕末) $C0/9A75 LDX #$FEA0 ;Xに$FEA0をロード $C0/9A78 BRA $08 [$9A82] ;フラグにかかわりなく常に分岐[$9A82] $C0/9A7A LDX #$FF60 ;Xに$FF60をロード $C0/9A7D BRA $03 [$9A82] ;フラグにかかわりなく常に分岐[$9A82] $C0/9A7F LDX #$FF00 ;Xに$FF00をロード ;処理合流 $C0/9A82 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C0/9A84 PHB ;DBレジスタをスタックにプッシュ $C0/9A85 LDA #$005F ;Aに$005Fをロード $C0/9A88 MVN CF 7E ;データ転送 ;A: 5F (転送データサイズ-1) ;X: ???? (転送元アドレス) ;Y: 39A0 (転送先アドレス) ;オペランド: 転送元バンクCF 転送先バンク7E ;$CF:???? -> $7E:39A0 +$5F $C0/9A88 MVN CF 7E ;データ転送 $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: 3F (転送データサイズ-1) ;X: F920 (転送元アドレス) ;Y: 3800 (転送先アドレス) ;オペランド: 転送元バンクCF 転送先バンク7E ;$CF:F920 -> $7E:3800 +$3F $C0/9AA8 MVN CF 7E ;データ転送 $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]に書き込み $C0/9AC9 RTS ;サブルーチン戻り
[$00:4301]に指定する転送先が$22=CGRAMとなっているので、カラーパレットの設定を行うサブルーチンである、ということがわかる。
さらに、$C0/9A0CでシナリオIDでの分岐を行っている。
最終編の場合は最終編主人公のIDをロードしている。
これらIDから、カラーパレットの最初のアドレスを計算している。
つまり、カラーパレットの最初のアドレスは、
主人公ID*$20 + $F960
で計算され、バンクは$CFで指定されているから、
$CF:(主人公ID*$20 + $F960)
から$20バイト分に、対応シナリオの何らかのカラーパレットがあるようである。
ちなみにフィールドのキャラのパレットは他キャラと兼ねていることが多く、カラーパレット開始位置は以下のようになる。
キャラは代表的なもののみ。
| カラーパレット 開始アドレス | 主なキャラクター |
|---|---|
$CF:FA60 | オルステッド、ストレイボウ、ウラヌス、ハッシュ、レイ、サモ |
$CF:FA80 | ポゴ、サンダウン、心山拳老師、カラクリ丸、とらわれの男、クルセイダーズ |
$CF:FAA0 | ウェイン、保安官 |
$CF:FAC0 | アキラ、おぼろ丸、ユン、マッドドッグ、お館さま、ポゴ集落の人 |
$CF:FAE0 | ルクレチア国王、大臣 |
$CF:FB00 | アリシア |
$CF:FB40 | ゴリ、ざき、ポゴ集落族長、クー族 |
$CF:FB60 | べる |
$CF:FBA0 | カトゥー、ヒューイ、カーク、レイチェル、伍長 |
$CF:FBC0 | キューブ |
$CF:FC00 | 孫子王手下 |
$CF:FC20 | 孫子王 |
$CF:FC60 | サンチョ・デロス・パンチョ、アニー、セザール、ビリ― |
$CF:FC80 | マスター、ジェンマ |
$CF:FCA0 | クリント |
$CF:FCC0 | 高原日勝 |
$CF:FD20 | タロイモ、妙子、藤兵衛 |
$CF:FD40 | 無法松 |
$CF:FDA0 | ハヤテ |
$CF:FE40 | カラクリ丸(暗)、とらわれの男(暗) |
$CF:FE80 | おぼろ丸(暗) |
先に記した通りに、CGRAMのアドレスの指定方法も、VRAMと同じく「Word(ワード)」単位であることに注意。
$C0/9AB0で[$00:2121](パレットアドレス)に$80を書き込んでいるが、この$80はワード単位でのアドレスなので、バイト単位にすると$0100である。
ということで、カラーパレットのデータ$7E:3900~は最終的に、$C0/9AB0~で、CGRAMのアドレス$0100~$01FFに送られている。
マップ背景用のカラーパレット(CGRAMのアドレス$0000~$00FFに入る)は別途処理をしているがここでは省く。
[$00:43n1]に$22を書き込んでいる、つまり下のような、
LDA #$22 STA $4301 [$00:4301]
こういう処理は、転送先がCGRAM、カラーパレットで確定のため、この後からカラーパレットの処理をしているということがサブルーチンでもわかりやすいということである。
各スプライトの配置や、どのカラーパレットを使うかといったデータはOAM(Object Attribute Memory)に転送される。
まず、OAMへデータを転送するサブルーチンを見てみる。転送はGDMAを使っている。
$C0/BC6D STZ $2102 [$00:2102] ;[$00:2102]に$00を書き込み $C0/BC70 STZ $2103 [$00:2103] ;[$00:2103]に$00を書き込み $C0/BC73 LDA #$00 ;Aに$00をロード $C0/BC75 STA $4300 [$00:4300] ;Aを[$00:4300]に書き込み $C0/BC78 LDA #$04 ;Aに$04をロード $C0/BC7A STA $4301 [$00:4301] ;Aを[$00:4301]に書き込み $C0/BC7D LDX #$0700 ;Xに$0700をロード $C0/BC80 STX $4302 [$00:4302] ;Xを[$00:4303][$00:4302]に書き込み $C0/BC83 LDA #$00 ;Aに$00をロード $C0/BC85 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C0/BC88 LDX #$0220 ;Xに$0220をロード $C0/BC8B STX $4305 [$00:4305] ;Xを[$00:4306][$00:4305]に書き込み $C0/BC8E LDA #$01 ;Aに$01をロード $C0/BC90 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み $C0/BC93 RTS ;サブルーチン戻り
| アドレス | 内容 | 値 |
|---|---|---|
[$00:2102] | OAMアドレス(下位8bit) | $00 |
[$00:2103] | OAMアドレス(上位1bit) | $00 |
[$00:4300] | GDMA/HDMA設定レジスタ | $00 |
[$00:4301] | 転送先指定 | $04(OAM) |
[$00:4303][$00:4302] | データ元アドレス | $0700 |
[$00:4304] | データ元バンク | $00 |
[$00:4306][$00:4305] | 転送量 | $0220 |
以上から、OAMへの転送は、$00:0700から$0220バイトのデータをOAMアドレス$0000を開始位置として行う、ということがわかる。
OAMのサイズは$220バイトだから、1画面分のスプライトのデータを一気に転送していることがわかる。
ゲーム画面のスプライトは1フレーム毎に移動したり向き変えしたりしているのだから、当然といえば当然のことである。
[$00:420B]に$01が書き込まれた時点で転送を開始する。
つまり、転送するデータは、$00:0700~$00:0920に入っているということであり、しかも毎フレーム更新され続けているということである。
$00:0700~$00:0920の値の計算をしているのが以下サブルーチンである。
$C0/BB15 STZ $0103 [$00:0103] ;[$00:0103]に$00を書き込み $C0/BB18 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C0/BB1A LDX #$0700 ;Xに$0700をロード $C0/BB1D LDY #$0010 ;Yに$0010をロード ;ループ開始 $C0/BB20 LDA #$E000 ;Aに$E000をロード $C0/BB23 STA $00,x [$00:0700] ;Aを[$00:0700+20x]に書き込み $C0/BB25 STZ $02,x [$00:0702] ;[$00:0702+20x]に$00を書き込み $C0/BB27 STA $04,x [$00:0704] ;Aを[$00:0704+20x]に書き込み $C0/BB29 STZ $06,x [$00:0706] ;[$00:0706+20x]に$00を書き込み $C0/BB2B STA $08,x [$00:0708] ;Aを[$00:0708+20x]に書き込み $C0/BB2D STZ $0A,x [$00:070A] ;[$00:070A+20x]に$00を書き込み $C0/BB2F STA $0C,x [$00:070C] ;Aを[$00:070C+20x]に書き込み $C0/BB31 STZ $0E,x [$00:070E] ;[$00:070E+20x]に$00を書き込み $C0/BB33 STA $10,x [$00:0710] ;Aを[$00:0710+20x]に書き込み $C0/BB35 STZ $12,x [$00:0712] ;[$00:0712+20x]に$00を書き込み $C0/BB37 STA $14,x [$00:0714] ;Aを[$00:0714+20x]に書き込み $C0/BB39 STZ $16,x [$00:0716] ;[$00:0716+20x]に$00を書き込み $C0/BB3B STA $18,x [$00:0718] ;Aを[$00:0718+20x]に書き込み $C0/BB3D STZ $1A,x [$00:071A] ;[$00:071A+20x]に$00を書き込み $C0/BB3F STA $1C,x [$00:071C] ;Aを[$00:071C+20x]に書き込み $C0/BB41 STZ $1E,x [$00:071E] ;[$00:071E+20x]に$00を書き込み $C0/BB43 TXA ;Xレジスタの値をAレジスタに転送 $C0/BB44 CLC ;キャリーフラグクリア $C0/BB45 ADC #$0020 ;A + $0020 $C0/BB48 TAX ;Aの値をXレジスタに転送 $C0/BB49 DEY ;Yをデクリメント -1 $C0/BB4A BNE $D4 [$BB20] ;ゼロフラグが立っていないとき[$BB20]分岐 ;ループここまで $C0/BB4C STZ $00,x [$00:0900] ;[$00:0900]に$00を書き込み $C0/BB4E STZ $02,x [$00:0902] ;[$00:0902]に$00を書き込み $C0/BB50 STZ $04,x [$00:0904] ;[$00:0904]に$00を書き込み $C0/BB52 STZ $06,x [$00:0906] ;[$00:0906]に$00を書き込み $C0/BB54 STZ $08,x [$00:0908] ;[$00:0908]に$00を書き込み $C0/BB56 STZ $0A,x [$00:090A] ;[$00:090A]に$00を書き込み $C0/BB58 STZ $0C,x [$00:090C] ;[$00:090C]に$00を書き込み $C0/BB5A STZ $0E,x [$00:090E] ;[$00:090E]に$00を書き込み $C0/BB5C STZ $10,x [$00:0910] ;[$00:0910]に$00を書き込み $C0/BB5E STZ $12,x [$00:0912] ;[$00:0912]に$00を書き込み $C0/BB60 STZ $14,x [$00:0914] ;[$00:0914]に$00を書き込み $C0/BB62 STZ $16,x [$00:0916] ;[$00:0916]に$00を書き込み $C0/BB64 STZ $18,x [$00:0918] ;[$00:0918]に$00を書き込み $C0/BB66 STZ $1A,x [$00:091A] ;[$00:091A]に$00を書き込み $C0/BB68 STZ $1C,x [$00:091C] ;[$00:091C]に$00を書き込み $C0/BB6A STZ $1E,x [$00:091E] ;[$00:091E]に$00を書き込み $C0/BB6C LDA $0100 [$00:0100] ;Aに[$00:0100]をロード $C0/BB6F BNE $03 [$BB74] ;ゼロフラグが立っていないとき[$BB74]分岐 $C0/BB74 LDY #$0700 ;Yに$0700をロード $C0/BB77 LDA #$0900 ;Aに$0900をロード $C0/BB7A STA $32 [$00:0032] ;Aを[$00:0032]に書き込み $C0/BB7C STZ $34 [$00:0034] ;[$00:0034]に$00を書き込み $C0/BB7E LDA $0102 [$00:0102] ;Aに[$00:0102]をロード $C0/BB81 BEQ $74 [$BBF7] ;ゼロフラグが立っているとき[$BBF7]分岐 ;ゼロフラグOFF $C0/BB83 STA $30 [$00:0030] ;Aを[$00:0030]に書き込み ;(中略) $C0/BBF5 BNE $91 [$BB88] ;ゼロフラグが立っていないとき[$BB88]分岐 ;ゼロフラグON $C0/BBF7 LDA $0100 [$00:0100] ;Aに[$00:0100]をロード $C0/BBFA STA $30 [$00:0030] ;Aを[$00:0030]に書き込み $C0/BBFC LDX #$1A00 ;Xに$1A00をロード ;ループ開始 $C0/BBFF LDA $00,x [$00:1A00] ;Aに[$00:1A00]をロード $C0/BC01 BIT #$0080 ;Aと$0080で論理積(ステータスフラグ変更のみ) $C0/BC04 BNE $56 [$BC5C] ;ゼロフラグが立っていないとき[$BC5C]分岐 $C0/BC06 LDA $01,x [$00:1A01] ;Aに[$00:1A01]をロード $C0/BC08 STA $0000,y[$00:0700] ;Aを[$00:0700]に書き込み $C0/BC0B LDA $03,x [$00:1A03] ;Aに[$00:1A03]をロード $C0/BC0D STA $0002,y[$00:0702] ;Aを[$00:0702]に書き込み $C0/BC10 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C0/BC12 PHY ;Yレジスタをスタックへプッシュ $C0/BC13 LDY $32 [$00:0032] ;Yに[$00:0032]をロード $C0/BC15 LDA $34 [$00:0034] ;Aに[$00:0034]をロード $C0/BC17 BEQ $32 [$BC4B] ;ゼロフラグが立っているとき[$BC4B]分岐 $C0/BC4B LDA $00,x [$00:1A00] ;Aに[$00:1A00]をロード $C0/BC4D AND #$03 ;Aと$03で論理積 $C0/BC4F STA $0000,y[$00:0900] ;Aを[$00:0900]に書き込み $C0/BC52 INC $34 [$00:0034] ;A[$00:0034]をインクリメント +1 $C0/BC54 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C0/BC56 PLA ;Aレジスタにスタックからプル $C0/BC57 CLC ;キャリーフラグクリア $C0/BC58 ADC #$0004 ;A + $0004 $C0/BC5B TAY ;Aの値をYレジスタに転送 $C0/BC5C TXA ;Xレジスタの値をAレジスタに転送 $C0/BC5D CLC ;キャリーフラグクリア $C0/BC5E ADC #$0005 ;A + $0005 $C0/BC61 TAX ;Aの値をXレジスタに転送 $C0/BC62 DEC $30 [$00:0030] ;[$00:0030]をデクリメント -1 $C0/BC64 BNE $99 [$BBFF] ;ゼロフラグが立っていないとき[$BBFF]分岐 ;ループここまで $C0/BC66 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C0/BC68 RTS ;サブルーチン戻り
細かい説明は省くが、$C0/BB20~$C0/BB6Aで初期値リセット、その後はシナリオ進行などに従って値を書き込んでいるようである。
まず、戦闘開始時に、GDMA関係のアドレスに値を入れる処理がある。
これはどの戦闘でも共通の処理(のはず)。
$C1/F2D1 LDA #$01 ;Aに$01をロード $C1/F2D3 STA $4300 [$00:4300] ;Aを[$00:4300]に書き込み $C1/F2D6 LDA #$18 ;Aに$18をロード $C1/F2D8 STA $4301 [$00:4301] ;Aを[$00:4301]に書き込み $C1/F2DB LDA #$00 ;Aに$00をロード $C1/F2DD STA $4310 [$00:4310] ;Aを[$00:4310]に書き込み $C1/F2E0 LDA #$22 ;Aに$22をロード $C1/F2E2 STA $4311 [$00:4311] ;Aを[$00:4311]に書き込み $C1/F2E5 LDA #$80 ;Aに$80をロード $C1/F2E7 STA $4321 [$00:4321] ;Aを[$00:4321]に書き込み $C1/F2EA LDA #$00 ;Aに$00をロード $C1/F2EC STA $4330 [$00:4330] ;Aを[$00:4330]に書き込み $C1/F2EF LDA #$04 ;Aに$04をロード $C1/F2F1 STA $4331 [$00:4331] ;Aを[$00:4331]に書き込み $C1/F2F4 JSR $F31F [$C1:F31F] ;[$C1:F31F]へジャンプ $C1/F31F LDX #$A700 ;Xに$A700をロード $C1/F322 STX $4342 [$00:4342] ;Xを[$00:4342]に書き込み $C1/F325 LDA #$41 ;Aに$41をロード $C1/F327 STA $4340 [$00:4340] ;Aを[$00:4340]に書き込み $C1/F32A LDA #$26 ;Aに$26をロード $C1/F32C STA $4341 [$00:4341] ;Aを[$00:4341]に書き込み $C1/F32F LDA #$7E ;Aに$7Eをロード $C1/F331 STA $4344 [$00:4344] ;Aを[$00:4344]に書き込み $C1/F334 STA $4347 [$00:4347] ;Aを[$00:4347]に書き込み $C1/F337 RTS ;サブルーチン戻り $C1/F2F7 LDA #$01 ;Aに$01をロード $C1/F2F9 STA $4350 [$00:4350] ;Aを[$00:4350]に書き込み $C1/F2FC LDA #$26 ;Aに$26をロード $C1/F2FE STA $4351 [$00:4351] ;Aを[$00:4351]に書き込み $C1/F301 LDX #$E9FD ;Xに$E9FDをロード $C1/F304 STX $4352 [$00:4352] ;Xを[$00:4352]に書き込み $C1/F307 LDA #$C1 ;Aに$C1をロード $C1/F309 STA $4354 [$00:4354] ;Aを[$00:4354]に書き込み $C1/F30C STA $4357 [$00:4357] ;Aを[$00:4357]に書き込み $C1/F30F LDA #$09 ;Aに$09をロード $C1/F311 STA $4370 [$00:4370] ;Aを[$00:4370]に書き込み $C1/F314 LDA #$18 ;Aに$18をロード $C1/F316 STA $4371 [$00:4371] ;Aを[$00:4371]に書き込み $C1/F319 RTS ;サブルーチン戻り
GDMA転送は8チャネル分設定できる(アドレスの下2桁部分のn=0~7)、と先に説明したが、その8チャネルの中でn=6以外は戦闘開始時にある程度値が設定されていることがわかる。
| アドレス | n | 0 | 1 | 2 | 3 | 4 | 5 | 7 |
|---|---|---|---|---|---|---|---|---|
$00:43n0 | GDMA/HDMA 設定レジスタ | $01 | $00 | - | $00 | $41 | $01 | $09 |
$00:43n1 | 値 | $18 | $22 | $80 | $04 | $26 | $26 | $18 |
| 転送先 | VRAM | CGRAM | WRAM | OAM | Window 座標1L | Window 座標1L | VRAM | |
$00:43n2~3 | データ元アドレス | - | - | - | - | $A700 | $E9FD | - |
$00:43n4 | データ元バンク | - | - | - | - | $7E | $C1 | - |
$00:43n7 | 間接HDMAアドレス | - | - | - | - | $7E | $C1 | - |
※n=4, 5のWindow座標関係はsnes-docs-ja/video/window.mdを参照されたし。スプライト関係ではないので、ここでは詳細を省く。
[$00:43n1]に入れる値で転送先を指定するから、戦闘中は、n=0はVRAM、n=1はCGRAM(カラーパレット)、n=2はWRAM、n=3はOAM(スプライト配置)、とチャネルを予め分けておいていることがわかる。
また、nが小さいほど転送の優先順位が高い。
スプライト関係のデータ転送はn=0~3を使うことになる。
戦闘中のキャラのスプライト(32×32ピクセル)データだが、まずWRAMにキャラのデータをコピーし、そこからVRAMに必要な時にGDMA転送、という方法を取っている。
フィールドでのスプライトは、サイズが小さいため($80バイト)、キャラの横向きや上向きといった、多くのパターンをまとめてVRAMへ送っておけるが、戦闘中のキャラのスプライトは大きいため($200バイト)、1キャラにつきひとつの向きのスプライト(×アニメーション2パターン分)しかVRAMに置く余裕がない。この時点で32×32ピクセルサイズ2体分の容量になる。
戦闘には最大で味方キャラ4人が参加するので、VRAMには32×32ピクセルサイズ8体分を置くことになる。これで$1000バイト分を占めることになる。
別のモーション(倒れた時や攻撃モーションなど)は、その都度WRAMに置いておいたデータをVRAMに転送して処理することになる。
WRAMは$7E:0000~$7F:FFFFにミラーリングされているので、ミラーリングからもアクセスできる。
$??:0000~$??:1FFFの範囲なら、バンク$00~$3F, $80~$BFからもアクセスできる。
まず、WRAMへのコピー処理を見てみる。
WRAMに転送を行う時は[$00:2181]~[$00:2183]の3バイトに、アクセスするアドレスを設定する。
WRAMは128KBの容量があるので、アドレスが$000000~$01FFFFと3バイト(24ビット)に渡るためである。
GDMA転送はn=2を使っている。
以下は例としておぼろ丸正面向きのデータ転送とする。
$C1/5FE0 STA $18 [$00:0318] ;Aを[$00:0318]に書き込み ;(中略、アドレスなど計算) $C1/6072 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/6074 LDA #$10 ;Aに$10をロード $C1/6076 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/6078 ASL $15 [$00:0315] ;A[$00:0315]を算術左シフト *2 $C1/607A ROL $14 [$00:0314] ;A[$00:0314]をキャリーフラグを含めた9bitで左ローテート $C1/607C BCS $2C [$60AA] ;キャリーフラグが立っているとき[$60AA]分岐 ;キャリーフラグON $C1/60AA LDA #$00 ;Aに$00をロード $C1/60AC STA $4320 [$00:4320] ;Aを[$00:4320]に書き込み $C1/60AF LDX $12 [$00:0312] ;Xに[$00:0313][$00:0312]をロード $C1/60B1 STX $4322 [$00:4322] ;Xを[$00:4323][$00:4322]に書き込み $C1/60B4 LDA $10 [$00:0310] ;Aに[$00:0310]をロード $C1/60B6 STA $4324 [$00:4324] ;Aを[$00:4324]に書き込み $C1/60B9 LDX #$0020 ;Xに$0020をロード $C1/60BC STX $4325 [$00:4325] ;Xを[$00:4326][$00:4325]に書き込み $C1/60BF LDX $1C [$00:031C] ;Xに[$00:031D][$00:031C]をロード $C1/60C1 STX $2181 [$00:2181] ;Xを[$00:2182][$00:2181]に書き込み $C1/60C4 LDA #$00 ;Aに$00をロード $C1/60C6 STA $2183 [$00:2183] ;Aを[$00:2183]に書き込み $C1/60C9 JSR $34EA [$C1:34EA] ;[$C1:34EA]へジャンプ ; ;おぼろ丸の時 ;[$00:4320] GDMA/HDMA設定レジスタ $00 ;[$00:4323][$00:4322] データ元アドレス(開始位置) $EE00 ;[$00:4324] データ元バンク $D9 ;[$00:4326][$00:4325] 転送するデータ量 $0020 ;[$00:2183]~[$00:2181] WRAM転送先 $00D000 ; $C1/34EA JSR $34F6 [$C1:34F6] ;[$C1:34F6]へジャンプ ; $C1/34F6 LDA $2137 [$00:2137] ;Aに[$00:2137]をロード H/Vカウンタラッチ $C1/34F9 LDA $213F [$00:213F] ;Aに[$00:213F]をロード PPU2ステータス $C1/34FC LDA $213C [$00:213C] ;Aに[$00:213C]をロード Hカウンタ $C1/34FF XBA ;Aレジスタの上位バイトと下位バイトを交換 $C1/3500 LDA $213C [$00:213C] ;Aに[$00:213C]をロード Hカウンタ $C1/3503 XBA ;Aレジスタの上位バイトと下位バイトを交換 $C1/3504 RTS ;サブルーチン戻り ; $C1/34ED CMP #$E0 ;Aと$E0(224)を減算比較(ステータスレジスタ変更のみ) $C1/34EF BCS $04 [$34F5] ;キャリーフラグが立っているとき[$34F5]分岐 ;キャリーフラグOFF $C1/34F1 CMP #$50 ;Aと$50(80)を減算比較(ステータスレジスタ変更のみ) $C1/34F3 BCS $F5 [$34EA] ;キャリーフラグが立っているとき[$34EA]分岐 ;キャリーフラグON $C1/34F5 RTS ;サブルーチン戻り ; $C1/60CC LDA #$04 ;Aに$04をロード $C1/60CE STA $420B [$00:420B] ;Aを[$00:420B]に書き込み ;GDMA n=2チャネルの転送を開始 ;(中略) $C1/60C9 JSR $34EA [$C1:34EA] ;[$C1:34EA]へジャンプ
$C1/5FE0~$C1/6072はGDMA転送用のアドレス開始位置や転送量の計算になるが、ここでは詳細を省く。
転送するデータ量は$20バイトで固定であり、WRAM転送先アドレスは今回は$00D000になっているが、$20バイトの転送ということは8×8ピクセル、タイル1個分の転送であるから、$00D000以降に次のタイルが次々と転送され、16個分=$200バイト=512バイト転送されると32×32ピクセルのキャラのスプライトになる。
この後の処理を見てみると、$20バイトずつ、$00D000~$00D400(合計$400バイト)に転送されていることから、2体分転送されていることがわかる。
これが、アニメーション2パターン分にあたる。
$C1/34EA, $C1/34F6~$C1/3504及び$C1/34ED~$C1/34F5はおそらくHBlankの区間かどうかの判定である。
$C1/34EA~$C1/34F3がループ処理になっていて、[$00:213C]は画面描画において現在水平位置のどこを描画しているかの値が入る。
$C1/34ED~$C1/34F5の分岐処理を見ると、[$00:213C]が$E0以上である、または$50未満であればサブルーチンを抜けて、$C1/60CCに進み、GDMA転送をしていることがわかる。
ということで、スプライト2体分のデータがWRAMの$00D000~$00D400(ミラーリング$7E:D000~$7E:D400)にコピーされたので、今度はこれをVRAMに送る。
$C1/8DC0 LDA $FE [$00:03FE] ;Aに[$00:03FE]をロード $C1/8DC2 BNE $01 [$8DC5] ;ゼロフラグが立っていないとき[$8DC5]分岐 $C1/8DC5 LDA #$7E ;Aに$7Eをロード $C1/8DC7 STA $4304 [$00:4304] ;Aを[$00:4304]に書き込み $C1/8DCA LDY #$D000 ;Yに$D000をロード $C1/8DCD STY $4302 [$00:4302] ;Yを[$00:4303][$00:4302]に書き込み $C1/8DD0 LDX #$0000 ;Xに$0000をロード ; ;[$00:4300] GDMA/HDMA設定レジスタ $01 ;[$00:4303][$00:4302] データ元アドレス(開始位置) $D000 ;[$00:4304] データ元バンク $7E ; ;ループ処理 $C1/8DD3 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/8DD5 LDA $7EE000,x ;Aに[$7E:E001+x][$7E:E000+x]をロード $C1/8DD9 STA $28 [$00:0328] ;A(初期値2000)を[$00:0329][$00:0328]に書き込み $C1/8DDB INX ;Xをインクリメント +1 $C1/8DDC INX ;Xをインクリメント +1 $C1/8DDD SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/8DDF LDA #$04 ;Aに$04をロード $C1/8DE1 STA $2C [$00:032C] ;Aを[$00:032C]に書き込み ; ;ループ処理 $C1/8DE3 LDY #$0080 ;Yに$0080をロード $C1/8DE6 STY $4305 [$00:4305] ;Yを[$00:4306][$00:4305](転送するデータ量)に書き込み $C1/8DE9 LDY $28 [$00:0328] ;Yに[$00:0329][$00:0328]をロード $C1/8DEB STY $2116 [$00:2116] ;Yを[$00:2117][$00:2116](VRAMアドレス2バイト)に書き込み $C1/8DEE LDA #$01 ;Aに$01をロード $C1/8DF0 STA $420B [$00:420B] ;Aを[$00:420B]に書き込み $C1/8DF3 INC $29 [$00:0329] ;[$00:0329]をインクリメント +1 $C1/8DF5 DEC $2C [$00:032C] ;[$00:032C](初期値$04)をデクリメント -1 $C1/8DF7 BNE $EA [$8DE3] ;ゼロフラグが立っていないとき[$8DE3]分岐 ;ゼロフラグOFFなら$C1/8DE3ループ頭へ ;ゼロフラグON $C1/8DF9 DEC $FE [$00:03FE] ;[$00:03FE]をデクリメント -1 $C1/8DFB BNE $D6 [$8DD3] ;ゼロフラグが立っていないとき[$8DD3]分岐 ;ループ処理ここまで ; $C1/8DFD RTS ;サブルーチン戻り
ループ処理が入れ子になっているが、結局は、$7E:D000~$7E:D400を$80バイト区切りでVRAMの指定アドレスに送る処理である。
送り先であるVRAMのアドレスは最初に$2000が指定される。
ただし、VRAMのアドレスは先に記した通り、word単位である。つまり$2000word = $4000byteである。
wordとbyteが混ざるとややこしいので、以下、アドレスの値はバイトで統一する。
と、合計$400バイトが送られる。
VRAMアドレスは横1行で$200バイト分だから、1行ずつ$80バイト並べていくことになる。
1.~4.で32×32サイズのキャラを1キャラ分、5.~8.でその右横に32×32サイズのキャラを1キャラ分並べたデータがVRAMの中に完成する、ということである。
これがキャラの戦闘中アニメーションになる。
戦闘中のカラーパレットはまず、戦闘開始時にロードされる。
味方キャラは以下サブルーチンで呼び出してCGRAM(カラーパレット用メモリ)に送る。
$C1/62BF TXA ;Xレジスタの値をAレジスタに転送 $C1/62C0 LSR A ;Aを論理右シフト (/2) $C1/62C1 LSR A ;Aを論理右シフト (/2) $C1/62C2 ADC #$80 ;A + $80 $C1/62C4 STA $2121 [$00:2121] ;Aを[$00:2121]に書き込み $C1/62C7 LDA $0000,x ;Aに[$00:1C00+40n](キャラID)をロード $C1/62CA XBA ;Aレジスタの上位バイトと下位バイトを交換 $C1/62CB LDA #$00 ;Aに$00をロード $C1/62CD REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/62CF LSR A ;Aを論理右シフト (/2) $C1/62D0 LSR A ;Aを論理右シフト (/2) $C1/62D1 LSR A ;Aを論理右シフト (/2) $C1/62D2 ADC $D60010[$D6:0010] ;A + [$D6:0011][$D6:0010]($0080) $C1/62D6 ADC #$2000 ;A + $2000 $C1/62D9 STA $4312 [$00:4312] ;Aを[$00:4313][$00:4312]に書き込み $C1/62DC SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/62DE LDA #$D6 ;Aに$D6をロード $C1/62E0 STA $4314 [$00:4314] ;Aを[$00:4314]に書き込み $C1/62E3 LDX #$0020 ;Xに$0020をロード $C1/62E6 STX $4315 [$00:4315] ;Xを[$00:4316][$00:4315]に書き込み $C1/62E9 LDA #$02 ;Aに$02をロード $C1/62EB STA $420B [$00:420B] ;Aを[$00:420B]に書き込み $C1/62EE RTS ;サブルーチン戻り
先に述べた通り、戦闘中においてGDMA転送のチャネル1は、CGRAMへの転送用に[$00:4311]に$22をセット済みである。
また、$C1/62C4で[$00:2121]に書き込むのはCGRAMのパレットアドレス開始位置である。
ここまでの計算は端折ったが、主人公の場合ここでAに$00が入っているので、CGRAMの書き込み開始位置は$0080である。
パレットアドレスの数値はword単位であることに注意。
byteで考えるとパレットアドレス開始位置は$0100である。
単位が混ざるとややこしいので、この先はアドレスはバイト単位で統一する。
処理を見ていくと、[$00:4313][$00:4312](データ元アドレス)は、
キャラID*$100/$8 + [$D6:0011][$D6:0010]($0080) + $2000
で計算されている。
さらに、[$00:4314]に$D6が書き込まれるので、味方キャラのカラーパレットの開始アドレスは、
$D6:(キャラID*$20 + $2080)
で計算されることがわかる。
キャラID$00のオルステッドのアドレス開始位置が$D6:2080で、以降+$20されていく、ということである。
| キャラ | 開始アドレス |
|---|---|
| オルステッド | $D6:2080 |
| ストレイボウ | $D6:20A0 |
| ウラヌス | $D6:20C0 |
| ハッシュ | $D6:20E0 |
| ポゴ | $D6:2100 |
| ゴリ | $D6:2120 |
| べる | $D6:2140 |
| ざき | $D6:2160 |
| 心山拳老師 | $D6:2180 |
| ユン | $D6:21A0 |
| レイ | $D6:21C0 |
| サモ | $D6:21E0 |
| サンダウン | $D6:2200 |
| マッドドッグ | $D6:2220 |
| おぼろ丸 | $D6:2240 |
| とらわれの男 | $D6:2260 |
| 高原日勝 | $D6:2280 |
| アキラ | $D6:22A0 |
| タロイモ | $D6:22C0 |
| 無法松 | $D6:22E0 |
| キューブ | $D6:2300 |
| キャプテンスクウェア | $D6:2320 |
| カラクリ丸 | $D6:2340 |
あとは[$00:420B]に$02が書き込まれたタイミングでCGRAMへの転送開始となる。
上処理をパーティメンバー分行うが、$C1/62C4の[$00:2121]への値が+$10word(+$20バイト)されていくので、CGRAMに書き込まれる位置が+$20バイトされていく。
つまり、戦闘では、$0100~に主人公、$0120~に仲間1、$0140~に仲間2、$0160~に仲間3のカラーパレットが入ることになる。
以上が戦闘開始時における味方キャラのカラーパレット転送処理である。
だが、戦闘中は技のエフェクトで一時的に味方キャラのカラーパレットが変動することがある。
回復時に体が光ったり、アキラの「マザーイメージ」のように自身の残像のようなものを飛ばしたりする時など、読み込むカラーパレットが変わる。
そういった時は技使用時、CGRAMアドレス$0180~以降に技用のカラーパレットがロードされて使用している。
技用のカラーパレットを$7E:F980~$7E:F99Eに呼び出すのが$C1/B0AF~$C1/B18Cのサブルーチンで、$7E:F980~$7E:F99EをCGRAMに転送するのが$C1/8C19~$C1/8C9Eにあたるが、ここでは省略。
フィールドにおけるOAM転送とやり方は似ている。
戦闘でのGDMA戦闘は先に紹介した通り、チャネル3にOAMへの転送が割り振られている。
$C1/8D9C LDA $FD [$00:03FD] ;Aに[$00:03FD]をロード $C1/8D9E BNE $01 [$8DA1] ;ゼロフラグが立っていないとき[$8DA1]分岐 $C1/8DA1 STZ $FD [$00:03FD] ;[$00:03FD]に$00を書き込み $C1/8DA3 LDX #$0000 ;Xに$0000をロード $C1/8DA6 STX $2102 [$00:2102] ;Xを[$00:2103][$00:2102]に書き込み $C1/8DA9 LDX #$0700 ;Xに$0700をロード $C1/8DAC STX $4332 [$00:4332] ;Xを[$00:4333][$00:4332]に書き込み $C1/8DAF LDA #$7E ;Aに$7Eをロード $C1/8DB1 STA $4334 [$00:4334] ;Aを[$00:4334]に書き込み $C1/8DB4 LDX #$0220 ;Xに$0220をロード $C1/8DB7 STX $4335 [$00:4335] ;Xを[$00:4336][$00:4335]に書き込み $C1/8DBA LDA #$08 ;Aに$08をロード $C1/8DBC STA $420B [$00:420B] ;Aを[$00:420B]に書き込み $C1/8DBF RTS ;サブルーチン戻り
| アドレス | 内容 | 値 |
|---|---|---|
[$00:2103][$00:2102] | OAMアドレス(上位1bit)(下位8bit) | $0000 |
[$00:4333][$00:4332] | データ元アドレス | $0700 |
[$00:4334] | データ元バンク | $7E |
[$00:4336][$00:4335] | 転送量 | $0220 |
OAMへの転送は、$7E:0700から$0220バイトのデータをOAMアドレス$0000を開始位置として行う。
戦闘においても1画面分のスプライトのデータ$220バイトをまとめて転送していることがわかる。
フィールドだとバンク$00だったが、戦闘では$7Eに変更されているのが唯一の違いだが、ミラーリングのアドレスを使っているかどうかの違いだけだから、実質、やっていることはフィールドと変わらない。
つまり転送しているデータは、$7E:0700~$7E:0920である。
$7E:0700~$7E:0920の値の計算をしているのが以下サブルーチンである。
大まかに、味方4キャラ分のスプライトのデータと、それ以外のデータ取得とに分かれる。
;味方キャラの下位テーブル用データ獲得 $C1/5AC2 LDA $7EA4A7[$7E:A4A7] ;Aに[$7E:A4A7]をロード $C1/5AC6 BEQ $21 [$5AE9] ;ゼロフラグが立っているとき[$5AE9]分岐 $C1/5AE9 LDY $D0 [$00:03D0] ;Yに[$00:03D0]をロード $C1/5AEB REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/5AED LDA $0010,x[$7E:1C50] ;Aに[$7E:1C50]をロード $C1/5AF0 ADC #$001F ;A + $001F $C1/5AF3 CMP #$011F ;Aと$011Fを減算比較(ステータスレジスタ変更のみ) $C1/5AF6 BCS $0D [$5B05] ;キャリーフラグが立っているとき[$5B05]分岐 $C1/5AF8 LDA $0012,x[$7E:1C52] ;Aに[$7E:1C52]をロード $C1/5AFB ADC #$001F ;A + $001F $C1/5AFE CMP #$00FF ;Aと$00FFを減算比較(ステータスレジスタ変更のみ) $C1/5B01 BCS $02 [$5B05] ;キャリーフラグが立っているとき[$5B05]分岐 $C1/5B03 BRA $03 [$5B08] ;フラグにかかわりなく常に分岐[$5B08] $C1/5B08 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/5B0A LDA $0010,x[$7E:1C50] ;Aに[$7E:1C50]をロード $C1/5B0D STA $0700,y[$7E:0700] ;Aを[$7E:0700](OAM X座標)に書き込み $C1/5B10 INY ;Yをインクリメント +1 $C1/5B11 LDA $0012,x[$7E:1C52] ;Aに[$7E:1C52]をロード $C1/5B14 STA $0700,y[$7E:0701] ;Aを[$7E:0701](OAM Y座標)に書き込み $C1/5B17 INY ;Yをインクリメント +1 $C1/5B18 LDA $0005,x[$7E:1C45] ;Aに[$7E:1C45]をロード $C1/5B1B CMP #$20 ;Aと$20を減算比較(ステータスレジスタ変更のみ) $C1/5B1D BNE $07 [$5B26] ;ゼロフラグが立っていないとき[$5B26]分岐 $C1/5B26 LDA $8E [$00:038E] ;Aに[$00:038E]をロード $C1/5B28 AND #$20 ;Aと$20で論理積 $C1/5B2A LSR A ;Aを論理右シフト (/2) $C1/5B2B LSR A ;Aを論理右シフト (/2) $C1/5B2C LSR A ;Aを論理右シフト (/2) $C1/5B2D ADC $000B,x[$7E:1C4B] ;A + [$7E:1C4B] $C1/5B30 STA $0700,y[$7E:0702] ;Aを[$7E:0702](OAM VRAMアドレス)に書き込み $C1/5B33 INY ;Yをインクリメント +1 $C1/5B34 LDA $001D,x[$7E:1C5D] ;Aに[$7E:1C5D]をロード $C1/5B37 AND #$81 ;Aと$81で論理積 $C1/5B39 BEQ $0C [$5B47] ;ゼロフラグが立っているとき[$5B47]分岐 $C1/5B3B CMP #$01 ;Aと$01を減算比較(ステータスレジスタ変更のみ) $C1/5B3D BEQ $0C [$5B4B] ;ゼロフラグが立っているとき[$5B4B]分岐 $C1/5B4B LDA #$40 ;Aに$40をロード $C1/5B4D BRA $02 [$5B51] ;フラグにかかわりなく常に分岐[$5B51] $C1/5B51 CLC ;キャリーフラグクリア $C1/5B52 ADC $0009,x[$7E:1C49] ;A + [$7E:1C49] $C1/5B55 ADC #$30 ;A + $30 $C1/5B57 STA $0700,y[$7E:0703] ;Aを[$7E:0703](OAM カラーパレットの値など)に書き込み $C1/5B5A LDA $0011,x[$7E:1C51] ;Aに[$7E:1C51]をロード $C1/5B5D ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/5B5E ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/5B5F ROR A ;Aをキャリーフラグを含めた9bitで右ローテート $C1/5B60 AND #$40 ;Aと$40で論理積 $C1/5B62 ORA #$80 ;Aと$80で論理和 $C1/5B64 JSR $5EF5 [$C1:5EF5] ;[$C1:5EF5]へジャンプ $C1/5EF5 CPY #$01FC ;Yと$01FCを減算比較(ステータスレジスタ変更のみ) ;(中略 味方キャラの上位テーブル用データ獲得) $C1/5F1C RTS ;サブルーチン戻り ;(中略) $C1/0273 JSR $5F1D [$C1:5F1D] ;[$C1:5F1D]へジャンプ $C1/5F1D LDA #$FF ;Aに$FFをロード $C1/5F1F STA $FD [$00:03FD] ;Aを[$00:03FD]に書き込み $C1/5F21 LDY $D0 [$00:03D0] ;Yに[$00:03D0]をロード $C1/5F23 CPY #$0200 ;Yと$0200を減算比較(ステータスレジスタ変更のみ) $C1/5F26 BNE $01 [$5F29] ;ゼロフラグが立っていないとき[$5F29]分岐 ;ループ開始 $C1/5F29 LDA #$10 ;Aに$10をロード $C1/5F2B STA $0700,y[$00:0708] ;Aを[$00:0708+y]に書き込み $C1/5F2E INY ;Yをインクリメント +1 $C1/5F2F INY ;Yをインクリメント +1 $C1/5F30 INY ;Yをインクリメント +1 $C1/5F31 INY ;Yをインクリメント +1 $C1/5F32 CPY #$0200 ;Yと$0200を減算比較(ステータスレジスタ変更のみ) $C1/5F35 BNE $F2 [$5F29] ;ゼロフラグが立っていないとき[$5F29]分岐 ;ループここまで $C1/5F37 LDY $D0 [$00:03D0] ;Yに[$00:03D0]をロード $C1/5F39 TYA ;Yレジスタの値をAレジスタに転送 $C1/5F3A AND #$0C ;Aと$0Cで論理積 $C1/5F3C BEQ $26 [$5F64] ;ゼロフラグが立っているとき[$5F64]分岐 $C1/5F3E LSR A ;Aを論理右シフト (/2) $C1/5F3F LSR A ;Aを論理右シフト (/2) $C1/5F40 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/5F42 LDA #$04 ;Aに$04をロード $C1/5F44 SEC ;キャリーフラグON $C1/5F45 SBC $08 [$00:0308] ;A - [$00:0308] $C1/5F47 STA $08 [$00:0308] ;Aを[$00:0308]に書き込み $C1/5F49 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C1/5F4B TYA ;Yレジスタの値をAレジスタに転送 $C1/5F4C LSR A ;Aを論理右シフト (/2) $C1/5F4D LSR A ;Aを論理右シフト (/2) $C1/5F4E LSR A ;Aを論理右シフト (/2) $C1/5F4F LSR A ;Aを論理右シフト (/2) $C1/5F50 AND #$001F ;Aと$001Fで論理積 $C1/5F53 TAY ;Aの値をYレジスタに転送 $C1/5F54 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C1/5F56 LDA $0900,y[$00:0900] ;Aに[$00:0900]をロード $C1/5F59 LSR A ;Aを論理右シフト (/2) $C1/5F5A LSR A ;Aを論理右シフト (/2) $C1/5F5B ORA #$C0 ;Aと$C0で論理和 $C1/5F5D DEC $08 [$00:0308] ;[$00:0308]をデクリメント -1 $C1/5F5F BNE $F8 [$5F59] ;ゼロフラグが立っていないとき[$5F59]分岐 $C1/5F59 LSR A ;Aを論理右シフト (/2) $C1/5F5A LSR A ;Aを論理右シフト (/2) $C1/5F5B ORA #$C0 ;Aと$C0で論理和 $C1/5F5D DEC $08 [$00:0308] ;[$00:0308]をデクリメント -1 $C1/5F5F BNE $F8 [$5F59] ;ゼロフラグが立っていないとき[$5F59]分岐 $C1/5F61 STA $0900,y[$00:0900] ;Aを[$00:0900]に書き込み $C1/5F64 REP #$21 ;Aを16bit幅に変更、キャリーフラグクリア $C1/5F66 LDA $D0 [$00:03D0] ;Aに[$00:03D0]をロード $C1/5F68 ADC #$000C ;A + $000C $C1/5F6B LSR A ;Aを論理右シフト (/2) $C1/5F6C LSR A ;Aを論理右シフト (/2) $C1/5F6D LSR A ;Aを論理右シフト (/2) $C1/5F6E LSR A ;Aを論理右シフト (/2) $C1/5F6F CMP #$0020 ;Aと$0020を減算比較(ステータスレジスタ変更のみ) $C1/5F72 BEQ $12 [$5F86] ;ゼロフラグが立っているとき[$5F86]分岐 $C1/5F74 AND #$001F ;Aと$001Fで論理積 $C1/5F77 TAY ;Aの値をYレジスタに転送 $C1/5F78 SEP #$20 ;MフラグON Aレジスタは8bit幅 ;ループ開始 $C1/5F7A LDA #$FF ;Aに$FFをロード $C1/5F7C STA $0900,y ;Aを[$00:0901+y]に書き込み $C1/5F7F INY ;Yをインクリメント +1 $C1/5F80 CPY #$0020 ;Yと$0020を減算比較(ステータスレジスタ変更のみ) $C1/5F83 BNE $F5 [$5F7A] ;ゼロフラグが立っていないとき[$5F7A]分岐 ;ループここまで $C1/5F85 RTS ;サブルーチン戻り
大雑把に見ると、$C1/5AC2~$C1/5B64は味方キャラのOAM下位テーブル用データ獲得、$C1/5EF5~$C1/5F1Cは味方キャラのOAM上位テーブル用データ獲得、$C1/5F1D~$C1/5F85はその他のOAMデータ獲得、という流れのようである。
[$00:0700]~[$00:0707]は2バイトずつ、味方キャラ4キャラ分のデータで固定ということになるようだ(戦闘に該当キャラがいるかどうかは関係ない)。
また、敵キャラはスプライトではなく背景扱いなので、敵の処理は行わない。
メニュー画面におけるスプライトは、味方キャラ(戦闘用の32×32サイズ)と、各シナリオに対応したカーソル、装備画面のキャラアイコン、持ち物一覧の下矢印カーソルなど、他の場合に比べると数は少ない。
メニュー画面を開いた時、表示するスプライトは、カーソルと味方キャラ(最大4キャラ)だけで、最大で5個、最小で2個ということになる。
以下、近未来編での例とする。
$C2/3534 LDA $0675 [$7E:0675] ;Aに[$7E:0675]をロード
$C2/3537 AND #$001F ;Aと$001Fで論理積
$C2/353A XBA ;Aレジスタの上位バイトと下位バイトを交換
$C2/353B LSR A ;Aを論理右シフト (/2)
$C2/353C PHA ;Aレジスタをスタックにプッシュ
$C2/353D LDA $0677 [$7E:0677] ;Aに[$7E:0677]をロード
$C2/3540 AND #$001F ;Aと$001Fで論理積
$C2/3543 ASL A ;Aを算術左シフト *2
$C2/3544 ASL A ;Aを算術左シフト *2
$C2/3545 CLC ;キャリーフラグクリア
$C2/3546 ADC $01,s [$00:1FED] ;A + [$00:1FED]
$C2/3548 CLC ;キャリーフラグクリア
$C2/3549 ADC $D50020[$D5:0020] ;A + [$D5:0020]
$C2/354D TAX ;Aの値をXレジスタに転送
$C2/354E PLA ;Aレジスタにスタックからプル
$C2/354F LDA $D50002,x[$D5:5F4D] ;Aに[$D5:5F4D]をロード
$C2/3553 XBA ;Aレジスタの上位バイトと下位バイトを交換
$C2/3554 STA $067D [$7E:067D] ;Aを[$7E:067D]に書き込み
$C2/3557 LDA $D50000,x[$D5:5F4B] ;Aに[$D5:5F4B]をロード
$C2/355B AND #$07FF ;Aと$07FFで論理積
$C2/355E ASL A ;Aを算術左シフト *2
$C2/355F ASL A ;Aを算術左シフト *2
$C2/3560 ASL A ;Aを算術左シフト *2
$C2/3561 ASL A ;Aを算術左シフト *2
$C2/3562 ASL A ;Aを算術左シフト *2
$C2/3563 TAY ;Aの値をYレジスタに転送
$C2/3564 LDA $D50000,x[$D5:5F4B] ;Aに[$D5:5F4B]をロード
$C2/3568 AND #$1800 ;Aと$1800で論理積
$C2/356B XBA ;Aレジスタの上位バイトと下位バイトを交換
$C2/356C LSR A ;Aを論理右シフト (/2)
$C2/356D LSR A ;Aを論理右シフト (/2)
$C2/356E TAX ;Aの値をXレジスタに転送
$C2/356F LDA $C274F8,x[$C2:74FC] ;Aに[$C2:74FC]をロード
$C2/3573 STA $0679 [$7E:0679] ;Aを[$7E:0679]に書き込み
$C2/3576 TYX ;Yレジスタの値をXレジスタに転送
$C2/3577 LDA #$0010 ;Aに$0010をロード
$C2/357A STA $067B [$7E:067B] ;Aを[$7E:067B]に書き込み
$C2/357D LDY #$C000 ;Yに$C000をロード
$C2/3580 ASL $067D [$7E:067D] ;A[$7E:067D]を算術左シフト *2
$C2/3583 BCS $08 [$358D] ;キャリーフラグが立っているとき[$358D]分岐
$C2/358D LDA #$001F ;Aに$001Fをロード
$C2/3590 PER $0002 [$3595] ;PCの値にオペランドとして指定した
;16bit値を足した値をスタックに積み
$C2/3593 JMP ($0679)[$C2:35A5] ;[$C2:35A5]へジャンプ(レジスタをスタックに積まない)
$C2/35A5 MVN D9 7E ;データ転送
;
;A:転送データサイズ-1 $001F
;X:転送元アドレス $FC20
;Y:転送先アドレス $C000
;オペランド:転送元と転送先バンク D9 -> 7E
;$D9:FC20 -> $7E:C000 +1Fバイト
;
$C2/35A8 RTS ;サブルーチン戻り
;
;(中略)
;
$C2/3596 DEC $067B [$7E:067B] ;[$7E:067B]をデクリメント -1
$C2/3599 BNE $E5 [$3580] ;ゼロフラグが立っていないとき[$3580]分岐
$C2/3580 ASL $067D [$7E:067D] ;A[$7E:067D]を算術左シフト *2
$C2/3583 BCS $08 [$358D] ;キャリーフラグが立っているとき[$358D]分岐
$C2/358D LDA #$001F ;Aに$001Fをロード
$C2/3590 PER $0002 [$3595] ;PCの値にオペランドとして指定した
;16bit値を足した値をスタックに積み[$3595]
$C2/3593 JMP ($0679)[$C2:35A5] ;[$C2:35A5]へジャンプ(レジスタをスタックに積まない)
$C2/35A5 MVN D9 7E ;データ転送
;
;A:転送データサイズ-1 $001F
;X:転送元アドレス $FD80
;Y:転送先アドレス $C1C0
;オペランド:転送元と転送先バンク D9 -> 7E
;$D9:FD80 -> $7E:C1C0 +1Fバイト
;
$C2/35A8 RTS ;サブルーチン戻り
$C2/3534~$C2/3593はアドレス計算なので詳細は省略するが、$C2/35A5のMVNは、$D9:FC20~$D9:FC3Fを$7E:C000~$7E:C01Fにコピーする処理である。
この$D9:FC20~$D9:FC3Fは、アキラの戦闘用32×32サイズスプライトの、一番最初の8×8サイズタイルの$20バイト分のアドレスにあたる。
この後、残りのスプライトのデータもすべてコピーするので、$7E:C000~$7E:C1EFまで、$200バイト分、アキラのスプライトのデータで埋まる。
$C2/34AC ASL A ;Aを算術左シフト *2 $C2/34AD ASL A ;Aを算術左シフト *2 $C2/34AE ASL A ;Aを算術左シフト *2 $C2/34AF CLC ;キャリーフラグクリア $C2/34B0 ADC #$7528 ;A + $7528 $C2/34B3 TAX ;Aの値をXレジスタに転送 $C2/34B4 LDY #$0689 ;Yに$0689をロード $C2/34B7 LDA #$0007 ;Aに$0007をロード $C2/34BA MVN C2 00 ;データ転送 ; ;A:転送データサイズ-1 $07 ;X:転送元アドレス $7528 ;Y:転送先アドレス $0689 ;オペランド:転送元と転送先バンク C2 -> 00 ;$C2:7528 -> $00:0689 +07バイト ; $C2/34BD LDX #$C000 ;Xに$C000をロード $C2/34C0 LDY $0689 [$00:0689] ;Yに[$00:0689]をロード $C2/34C3 LDA #$007F ;Aに$007Fをロード $C2/34C6 MVN 7E 7E ;データ転送 ; ;A:転送データサイズ-1 $7F ;X:転送元アドレス $C000 ;Y:転送先アドレス $C800 ;オペランド:転送元と転送先バンク 7E -> 7E ;$7E:C000 -> $7E:C800 +7Fバイト ; $C2/34C9 LDY $068B [$7E:068B] ;Yに[$7E:068B]をロード $C2/34CC LDA #$007F ;Aに$007Fをロード $C2/34CF MVN 7E 7E ;データ転送 ; ;A:転送データサイズ-1 $7F ;X:転送元アドレス $C080 ;Y:転送先アドレス $CA00 ;オペランド:転送元と転送先バンク 7E -> 7E ;$7E:C080 -> $7E:CA00 +7Fバイト ; $C2/34D2 LDY $068D [$7E:068D] ;Yに[$7E:068D]をロード $C2/34D5 LDA #$007F ;Aに$007Fをロード $C2/34D8 MVN 7E 7E ;データ転送 ; ;A:転送データサイズ-1 $7F ;X:転送元アドレス $C100 ;Y:転送先アドレス $CC00 ;オペランド:転送元と転送先バンク 7E -> 7E ;$7E:C100 -> $7E:CC00 +7Fバイト ; $C2/34DB LDY $068F [$7E:068F] ;Yに[$7E:068F]をロード $C2/34DE LDA #$007F ;Aに$007Fをロード $C2/34E1 MVN 7E 7E ;データ転送 ; ;A:転送データサイズ-1 $7F ;X:転送元アドレス $C180 ;Y:転送先アドレス $CE00 ;オペランド:転送元と転送先バンク 7E -> 7E ;$7E:C180 -> $7E:CE00 +7Fバイト ; $C2/34E4 RTS ;サブルーチン戻り
上の処理で、
$7E:C000~$7E:C07F → $7E:C800~$7E:C87F
$7E:C080~$7E:C0FF → $7E:CA00~$7E:CA7F
$7E:C100~$7E:C17F → $7E:CC00~$7E:CC7F
$7E:C180~$7E:C1FF → $7E:CE00~$7E:CE7F
と、アキラ1体分のスプライトデータが$7E:C800以降に飛び飛びにコピーされる。
これは後々のVRAMでの配置のためである。
飛び飛びのように見えるが、間に他の仲間キャラのデータも入る。
$C2/3481 PLY ;Yレジスタにスタックからプル $C2/3482 INY ;Yをインクリメント +1 $C2/3483 DEC $067F [$7E:067F] ;[$7E:067F]をデクリメント -1 $C2/3486 BPL $BE [$3446] ;ネガティブフラグが立っていないとき[$3446]分岐 $C2/3488 LDX #$7500 ;Xに$7500をロード $C2/348B LDY #$4340 ;Yに$4340をロード $C2/348E LDA #$0007 ;Aに$0007をロード $C2/3491 PHB ;DBレジスタをスタックにプッシュ $C2/3492 MVN C2 00 ;データ転送 ; ;A:転送データサイズ-1 $0007 ;X:転送元アドレス $7500 ;Y:転送先アドレス $4340 ;オペランド:転送元と転送先バンク C2 -> 00 ;$C2:7500 -> $00:4340 +8バイト ;GDMA転送チャネル4の設定になる ; $C2/3492 MVN C2 00 ;データ転送 $C2/3495 PLB ;DBレジスタにスタックからプル $C2/3496 LDA #$4400 ;Aに$4400をロード $C2/3499 STA $002116[$00:2116] ;Aを[$00:2117][$00:2116]に書き込み $C2/349D SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/349F LDA #$80 ;Aに$80をロード $C2/34A1 STA $002115[$00:2115] ;Aを[$00:2115]に書き込み $C2/34A5 LDA #$10 ;Aに$10をロード $C2/34A7 JSR $4C88 [$C2:4C88] ;[$C2:4C88]へジャンプ $C2/4C88 PHP ;ステータスレジスタをスタックへプッシュ $C2/4C89 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4C8B XBA ;Aレジスタの上位バイトと下位バイトを交換 $C2/4C8C LDA $0600 [$7E:0600] ;Aに[$7E:0600]をロード $C2/4C8F BPL $07 [$4C98] ;ネガティブフラグが立っていないとき[$4C98]分岐 $C2/4C91 XBA ;Aレジスタの上位バイトと下位バイトを交換 $C2/4C92 STA $00420B[$00:420B] ;A($10=%0001 0000)を[$00:420B]に書き込み $C2/4C96 BRA $07 [$4C9F] ;フラグにかかわりなく常に分岐[$4C9F] $C2/4C9F PLP ;Pレジスタにスタックからプル $C2/4CA0 RTS ;サブルーチン戻り
アキラのスプライトのデータをVRAMにGDMA転送するのが上のサブルーチンになる。
$C2/3481~$C2/34A7で、転送前にI/Oポートアドレスの設定を行っている。
$C2/3492のMVNは、$C2:7500~$C2:7507を$00:4340~$00:4347にコピーする処理だが、$00:4340~$00:4347はGDMA転送用チャネル4用のアドレスである。
つまり、$C2:7500~$C2:7507には、メニュー画面におけるGDMA転送用チャネル4用の設定が入っていたのである。
それらの値から、以下のように転送用の値が設定されたことがわかる。
| アドレス | 内容 | 値 |
|---|---|---|
[$00:2115] | VRAMアドレス増加レジスタ | $80 |
[$00:2117][$00:2116] | VRAMアドレス ※word単位 | $4400 |
[$00:4340] | GDMA/HDMA設定レジスタ | $01 |
[$00:4341] | 転送先指定 | $18=VRAM |
[$00:4343][$00:4342] | データ元アドレス(開始位置) | $C800 |
[$00:4344] | データ元バンク | $7E |
[$00:4346][$00:4345] | 転送するデータ量 | $0800 |
最後に$C2/4C92で[$00:420B]に$10 = %0001 0000が書き込まれ、チャネル4のGDMA転送を行う。
つまり、$7E:C800から$800バイト分を、VRAMアドレス$8800(単位はバイトに直している)以降へコピーする、という処理になる。
アキラのスプライトデータは$7E:C800~$7E:CE7Fに入ったから(説明は端折ったが、他の仲間のデータも入っている)、ちょうどアキラ(+仲間キャラ)のスプライトデータの範囲をまるごとGDMA転送でVRAMに送る処理だということがわかる。
メニュー画面の処理では、上のように、MVNでGDMA転送チャネルn($00:43n0~$00:43n6)の設定を行うことが多いようだ。
メニュー画面のカラーパレット転送は、まずカラーパレットの値を$7E:8C00~8E00にコピー、$200バイト分CGRAMへ転送、という方法で行う。
$C2/0115 LDX #$72A8 ;Xに$72A8をロード $C2/0118 LDY #$8C00 ;Yに$8C00をロード $C2/011B LDA #$001F ;Aに$001Fをロード $C2/011E MVN C2 7E ;データ転送 ; ;A:転送データサイズ-1 $1F ;X:転送元アドレス $72A8 ;Y:転送先アドレス $8C00 ;オペランド:転送元と転送先バンク C2 -> 7E ;$C2:72A8 -> $7E:8C00 +$1Fバイト ; $C2/0121 JSR $2DBC [$C2:2DBC] [$C2:2DBC]にジャンプ
メニュー画面のカラーパレットの一番上(CGRAMアドレス$0000~$001F)は上処理で$7E:8C00~に入ることから、どの場合でも固定のようである。
つまり、$C2:72A8~$C2:72C7はメニュー画面のカラーパレットNo.$00で固定である。
CGRAMのアドレス$0100から(カラーパレットNo.08以降)がキャラのスプライトのカラーパレットであり、最初に主人公のアドレスが入る。
つまり、主人公キャラのカラーパレットが$7E:8D00~にコピーされるのだが、その処理が以下である。
$C2/34FC PHP ;ステータスレジスタをスタックへプッシュ $C2/34FD REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/34FF ASL A ;Aを算術左シフト *2 $C2/3500 ASL A ;Aを算術左シフト *2 $C2/3501 ASL A ;Aを算術左シフト *2 $C2/3502 ASL A ;Aを算術左シフト *2 $C2/3503 ASL A ;Aを算術左シフト *2 $C2/3504 CLC ;キャリーフラグクリア $C2/3505 ADC #$8D00 ;A + $8D00 $C2/3508 TAY ;Aの値をYレジスタに転送 $C2/3509 LDA $0675 [$7E:0675] ;Aに[$7E:0675]をロード $C2/350C AND #$001F ;Aと$001Fで論理積 $C2/350F ORA #$0100 ;Aと$0100で論理和 $C2/3512 ASL A ;Aを算術左シフト *2 $C2/3513 ASL A ;Aを算術左シフト *2 $C2/3514 ASL A ;Aを算術左シフト *2 $C2/3515 ASL A ;Aを算術左シフト *2 $C2/3516 ASL A ;Aを算術左シフト *2 $C2/3517 CLC ;キャリーフラグクリア $C2/3518 ADC $D60010[$D6:0010] ;A + [$D6:0010] $C2/351C TAX ;Aの値をXレジスタに転送 $C2/351D LDA #$001F ;Aに$001Fをロード $C2/3520 PHB ;DBレジスタをスタックにプッシュ $C2/3521 MVN D6 7E ;データ転送 ; ;A:転送データサイズ-1 $1F ;X:転送元アドレス $22A0 ;Y:転送先アドレス $8D00 ;オペランド:転送元と転送先バンク D6 -> 7E ;$D6:22A0 -> $7E:8D00 +$1Fバイト ; $C2/3524 PLB ;DBレジスタにスタックからプル $C2/3525 PLP ;Pレジスタにスタックからプル $C2/3526 RTS ;サブルーチン戻り
以上はアキラの例。
アキラの戦闘用スプライトのカラーパレットは$D6:22A0~$D6:22BFに入っており、$C2/3521のMVN転送で$7E:8D00~$7E:8D1Fへコピーされる。
他の仲間キャラも同じサブルーチンで処理が入る。
$C2/4D55 PHB ;DBレジスタをスタックにプッシュ $C2/4D56 PHP ;ステータスレジスタをスタックへプッシュ $C2/4D57 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4D59 LDA #$02 ;Aに$02をロード $C2/4D5B AND $7A [$00:057A] ;Aと[$00:057A]で論理積 $C2/4D5D BNE $17 [$4D76] ;ゼロフラグが立っていないとき[$4D76]分岐 $C2/4D5F REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/4D61 LDX #$7912 ;Xに$7912をロード $C2/4D64 LDY #$4310 ;Yに$4310をロード $C2/4D67 LDA #$0007 ;Aに$0007をロード $C2/4D6A MVN C2 00 ;データ転送 ; ;A:転送データサイズ-1 $0007 ;X:転送元アドレス $7912 ;Y:転送先アドレス $4310 ;オペランド:転送元と転送先バンク C2 -> 00 ;$C2:7912 -> $00:4310 +8バイト ;GDMA転送チャネル1の設定になる $C2/4D6D SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4D6F STZ $2121 [$00:2121] ;[$00:2121]に$00を書き込み $C2/4D72 LDA #$02 ;Aに$02をロード $C2/4D74 TSB $7A [$00:057A] ;Aと[$00:057A]で論理和を取り、メモリに書き戻し $C2/4D76 PLP ;Pレジスタにスタックからプル $C2/4D77 PLB ;DBレジスタにスタックからプル $C2/4D78 RTS ;サブルーチン戻り $C2/016F JSR $0526 [$C2:0526] ;[$C2:0526]へジャンプ $C2/0526 PHP ;ステータスレジスタをスタックへプッシュ $C2/0527 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/0529 LDA $004200[$00:4200] ;Aに[$00:4200](割り込み有効化レジスタ)をロード $C2/052D BMI $0B [$053A] ;ネガティブフラグが立っているとき[$053A]分岐 $C2/052F JSR $4B00 [$C2:4B00] ;[$C2:4B00]へジャンプ
まず、[$00:057A]に注目していただきたい。
[$00:057A]はメニュー画面を開いた時に初期化され$00が入る。
$C2/4D5Dのゼロフラグ判定で、[$00:057A]と$02の論理積が0でない場合、後述のGDMA転送チャネル1の設定をしない($C2/4D76にジャンプ)。
一方、ゼロフラグが立っていたら、GDMA転送チャネル1の設定をして、$C2/4D74で[$00:057A]と$02で論理和を取り、[$00:057A]に書き戻す。
つまり、[$00:057A]の下から2ビット目はGDMA転送チャネル1を行うかどうかのフラグを担当していて、GDMA転送チャネル1を設定したら下から2ビット目に1が立つ。
その、GDMA転送チャネル1の設定$C2/4D5F~$C2/4D74は以下の通り。
$C2/4D6AのMVNで、以下のようにGDMA転送チャネル1の設定をしている。
| アドレス | 内容 | 値 |
|---|---|---|
[$00:4310] | GDMA/HDMA設定レジスタ | $02 |
[$00:4311] | 転送先指定 | $22=CGRAM |
[$00:4313][$00:4312] | データ元アドレス(開始位置) | $8C00 |
[$00:4314] | データ元バンク | $7E |
[$00:4316][$00:4315] | 転送するデータ量 | $0200 |
つまり、$7E:8C00から$0200バイトのデータをCGRAMに転送する、という処理である。
だが、その後、実際の転送処理までの間に他処理をしている。
$C2/0529で、[$00:4200](割り込み有効化レジスタ)のネガティブフラグ分岐を行っている。
つまり、[$00:4200]の一番上のビットが1なら$C2/053Aへ、0なら$C2/052F→$C2:4B00ということになる。
割り込み有効化レジスタの一番上のビットは「VBlank NMI 有効化フラグ」の値が入る。
これに0が入っているとVBlank時、NMIでの割り込みが発生しない(VBlankの間に別処理、だいたいの場合で画像関係の処理をしない)。
おそらくこの後にVBlankかの判定→NMIで割り込んでCGRAMへの転送、という処理をしている(と思う)。
その、NMIでの割り込み後の処理が以下である。
;*NMI割り込み $00/00F8 JMP $C24DB0[$C2:4DB0] ;[$C2:4DB0]へジャンプ(レジスタをスタックに積まない) $C2/4DB0 REP #$30 ;Aを16bit幅に変更、X/Yレジスタを16bit幅に変更 $C2/4DB2 PHA ;Aレジスタをスタックにプッシュ $C2/4DB3 PHP ;ステータスレジスタをスタックへプッシュ $C2/4DB4 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4DB6 LDA $004210[$00:4210] ;Aに[$00:4210](NMIフラグ)をロード $C2/4DBA LDA #$00 ;Aに$00をロード $C2/4DBC STA $00420C[$00:420C] ;Aを[$00:420C](HDMAチャネルレジスタ)に書き込み $C2/4DC0 STA $002102[$00:2102] ;Aを[$00:2102](OAMアドレス(下位8bit))に書き込み $C2/4DC4 LDA #$80 ;Aに$80をロード $C2/4DC6 STA $002103[$00:2103] ;Aを[$00:2103](OAMアドレス(上位1bit))に書き込み $C2/4DCA LDA $00057A[$00:057A] ;Aに[$00:057A]をロード $C2/4DCE BEQ $0A [$4DDA] ;ゼロフラグが立っているとき[$4DDA]分岐 ;ゼロフラグOFF(GDMA転送を行う) $C2/4DD0 STA $00420B[$00:420B] ;Aを[$00:420B](GDMAチャネルレジスタ)に書き込み
$C2/4DCA~$C2/4DD0が処理にあたるが、ここで[$00:057A]に入っている値を[$00:420B](GDMAチャネルレジスタ)に書き込んでいる。
先ほどのGDMA転送チャネル1の設定時に$02との論理和を取ったから、下から2ビット目に1が立っているはずであり(%0000 0010)、[$00:420B]のGDMAチャネルレジスタは、1が立っているビットに対応したチャネルのGDMA転送を実行する。
よって、この時点で、チャネル1のGDMA転送を行い、CGRAMにカラーパレットのデータが送られたことになる。
なお、$C2/4D55~$C2/052FのGDMA転送チャネル1の設定前、$C2/4D35~$C2/4D54でGDMA転送チャネル0の転送設定をしており、後述するがそちらはOAMへの転送設定である。
$C2/4D35~$C2/4D54でGDMA転送チャネル0の転送設定をした場合は、[$00:057A]と$01の論理和を[$00:057A]に書き戻すという処理をする。
このため、上の$C2/4DCA~$C2/4DD0で[$00:057A]に$03(%0000 0011)が入ることになり、GDMAのチャネル0とチャネル1を実行することになる。
では、そのOAM転送について、最後に説明する。
OAMのデータは、$00:0700~$00:091Fにコピーされ、まとめてOAMに送っている。
メニュー画面を開いた時点では、$00:0700~$00:0703にカーソルのスプライト、$00:0710~に味方キャラのスプライトのデータ(各4バイト)、$00:0900以降に各スプライトの2ビットのデータが入って、そのままOAMに送る、という仕組みになっている。
転送前にまず、$00:0700~$00:091Fの値をリセットする。
$C2/04FE PHP ;ステータスレジスタをスタックへプッシュ
$C2/04FF REP #$20 ;Aを16bit幅に変更、Mフラグをクリア
$C2/0501 LDX $00 [$00:0500] ;Xに[$00:0501][$00:0500]をロード
$C2/0503 LDA #$E0FF ;Aに$E0FFをロード
;ループ処理開始
$C2/0506 STA $0700,x ;Aを[$7E:0701][$7E:0700]
;~[$7E:08FD][$7E:08FC]に書き込み
$C2/0509 STZ $0702,x ;[$7E:0703][$7E:0702]
;~[$7E:08FF][$7E:08FE]に$00を書き込み
$C2/050C INX ;Xをインクリメント +1
$C2/050D INX ;Xをインクリメント +1
$C2/050E INX ;Xをインクリメント +1
$C2/050F INX ;Xをインクリメント +1
$C2/0510 CPX #$0200 ;Xと$0200を減算比較(ステータスレジスタ変更のみ)
$C2/0513 BMI $F1 [$0506] ;ネガティブフラグが立っているとき[$0506]分岐
;ループ処理ここまで
$C2/0515 LDX $00 [$00:0500] ;Xに[$00:0501][$00:0500]をロード
$C2/0517 LDA #$0000 ;Aに$0000をロード
;ループ処理開始
$C2/051A STA $0900,x ;Aを[$7E:0901][$7E:0900]
;~[$7E:091F][$7E:091E]に書き込み
$C2/051D INX ;Xをインクリメント +1
$C2/051E INX ;Xをインクリメント +1
$C2/051F CPX #$0020 ;Xと$0020を減算比較(ステータスレジスタ変更のみ)
$C2/0522 BMI $F6 [$051A] ;ネガティブフラグが立っているとき[$051A]分岐
;ループ処理ここまで
$C2/0524 PLP ;Pレジスタにスタックからプル
$C2/0525 RTS ;サブルーチン戻り
$00:0700~$00:08FFは、「FF E0 00 00」と4バイトごとに入り、$00:0900~$00:091Fはすべて$00で埋まる。
スプライトは最大128個表示させることができるが、使用しないスプライトは画面外に設置して非表示とするため、X座標に$FF (255)、Y座標に$E0 (224)を指定している。これにより、画面右下にはみ出した位置に非表示のスプライトを設置している。
リセット後、まず、味方キャラのスプライトのデータを1人ずつ書き込む。
下は味方キャラ1人目のデータ(今回は近未来編・アキラ)のデータ。
$C2/35F5 ASL A ;Aを算術左シフト *2 $C2/35F6 PHA ;Aレジスタをスタックにプッシュ $C2/35F7 INX ;Xをインクリメント +1 $C2/35F8 PHX ;Xレジスタをスタックにプッシュ $C2/35F9 TAX ;Aの値をXレジスタに転送 $C2/35FA LDA $C275DF,x[$C2:75E9] ;Aに[$C2:75E9]をロード $C2/35FE PLX ;Xレジスタにスタックからプル $C2/35FF CMP #$0000 ;Aと$0000を減算比較(ステータスレジスタ変更のみ) $C2/3602 BEQ $09 [$360D] ;ゼロフラグが立っているとき[$360D]分岐 $C2/3604 DEC A ;をデクリメント -1 $C2/3605 LDY #$0691 ;Yに$0691をロード $C2/3608 PHB ;DBレジスタをスタックにプッシュ $C2/3609 MVN C2 00 ;データ転送 ; ;A:転送データサイズ-1 $0005 ;X:転送元アドレス $7D2E ;Y:転送先アドレス $0691 ;オペランド:転送元と転送先バンク C2 -> 00 ;$C2:7D2E -> $00:0691 +6バイト ; $C2/360C PLB ;DBレジスタにスタックからプル $C2/360D PLA ;Aレジスタにスタックからプル $C2/360E RTS ;サブルーチン戻り $C2/35C3 PHX ;Xレジスタをスタックにプッシュ $C2/35C4 TAX ;Aの値をXレジスタに転送 $C2/35C5 JSR ($75F3,x)[$C2:3789] ;[$C2:3789]へジャンプ $C2/3789 JSR $36CF [$C2:36CF] ;[$C2:36CF]へジャンプ $C2/36CF PHP ;ステータスレジスタをスタックへプッシュ $C2/36D0 LDA $0691 [$7E:0691] ;Aに[$7E:0692][$7E:0691]をロード $C2/36D3 AND #$007F ;Aと$007Fで論理積 $C2/36D6 ASL A ;Aを算術左シフト *2 $C2/36D7 ASL A ;Aを算術左シフト *2 $C2/36D8 TAX ;Aの値をXレジスタに転送 $C2/36D9 LDA $0692 [$7E:0692] ;Aに[$7E:0693][$7E:0692]をロード $C2/36DC STA $0700,x[$7E:0710] ;Aを[$7E:0711][$7E:0710]に書き込み $C2/36DF LDA $0694 [$7E:0694] ;Aに[$7E:0695][$7E:0694]をロード $C2/36E2 STA $0702,x[$7E:0712] ;Aを[$7E:0713][$7E:0712]に書き込み $C2/36E5 LDA $0691 [$7E:0691] ;Aに[$7E:0692][$7E:0691]をロード $C2/36E8 AND #$007F ;Aと$007Fで論理積 $C2/36EB LSR A ;Aを論理右シフト (/2) $C2/36EC LSR A ;Aを論理右シフト (/2) $C2/36ED TAY ;Aの値をYレジスタに転送 $C2/36EE LDA $0691 [$7E:0691] ;Aに[$7E:0692][$7E:0691]をロード $C2/36F1 AND #$0003 ;Aと$0003で論理積 $C2/36F4 TAX ;Aの値をXレジスタに転送 $C2/36F5 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/36F7 LDA $0691 [$7E:0691] ;Aに[$7E:0691]をロード $C2/36FA BMI $0B [$3707] ;ネガティブフラグが立っているとき[$3707]分岐 $C2/3707 LDA $C2761B,x[$C2:761B] ;Aに[$C2:761B]をロード $C2/370B ORA $0900,y[$7E:0901] ;Aと[$7E:0901]で論理和 $C2/370E STA $0900,y[$7E:0901] ;Aを[$7E:0901]に書き込み $C2/3711 PLP ;Pレジスタにスタックからプル $C2/3712 RTS ;サブルーチン戻り
$C2/3609~$C2/360CのMVNで、
$C2:7D2E~$C2:7D34→$00:0691~$00:0697
と、コピーされる。
さらに、$C2/36CF~から、[$7E:0710]以降へのコピーが行われるので、整理すると、
| コピー元 | コピー先 |
|---|---|
$C2:7D2F | $7E:0710 |
$C2:7D30 | $7E:0711 |
$C2:7D31 | $7E:0712 |
$C2:7D32 | $7E:0713 |
$C2:761B | $7E:0901(と論理和) |
$7E:0710~$7E:0713はOAM下位4バイトで元のデータは$C2:7D2F~$C2:7D32。
$7E:0901はOAM上位2ビットに対応で元のデータは$C2:761B、ということがわかる。
$7E:0901の論理和は、データが1つのスプライトにつき2ビットだけなので、8ビットの情報を書き込める2バイト分のアドレスに値を書き込む際、他スプライトの情報が既に入っているなら論理和を取らないと前のデータが上書きされて消えてしまうため。
以上の処理を味方全員分行った後、カーソルの情報も追加する。
その先はGDMA転送チャネル0の設定まで続くため、一緒に紹介する。
$C2/0782 REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/0784 LDA $7E8E02,x[$7E:8E02] ;Aに[$7E:8E03][$7E:8E02]をロード $C2/0788 STA $000700[$00:0700] ;Aを[$00:0701][$00:0700]に書き込み $C2/078C LDA $34 [$00:0534] ;Aに[$00:0535][$00:0534]をロード $C2/078E STA $000702[$00:0702] ;Aを[$00:0703][$00:0702]に書き込み $C2/0792 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/0794 LDA $7E8E00,x[$7E:8E00] ;Aに[$7E:8E00]をロード $C2/0798 STA $42 [$00:0542] ;Aを[$00:0542]に書き込み $C2/079A LDA $7E8E01,x[$7E:8E01] ;Aに[$7E:8E01]をロード $C2/079E STA $44 [$00:0544] ;Aを[$00:0544]に書き込み $C2/07A0 JSR $4D35 [$C2:4D35] ;[$C2:4D35]へジャンプ $C2/4D35 PHB ;DBレジスタをスタックにプッシュ $C2/4D36 PHP ;ステータスレジスタをスタックへプッシュ $C2/4D37 SEP #$20 ;MフラグON Aレジスタは8bit幅 $C2/4D39 LDA #$01 ;Aに$01をロード $C2/4D3B AND $7A [$00:057A] ;Aと[$00:057A]で論理積 $C2/4D3D BNE $13 [$4D52] ;ゼロフラグが立っていないとき[$4D52]分岐 $C2/4D3F REP #$20 ;Aを16bit幅に変更、Mフラグをクリア $C2/4D41 LDX #$790A ;Xに$790Aをロード $C2/4D44 LDY #$4300 ;Yに$4300をロード $C2/4D47 LDA #$0007 ;Aに$0007をロード $C2/4D4A MVN C2 00 ;データ転送 ; ;A:転送データサイズ-1 $0007 ;X:転送元アドレス $790A ;Y:転送先アドレス $4300 ;オペランド:転送元と転送先バンク C2 -> 00 ;$C2:790A -> $00:4300 +8バイト ;GDMA転送チャネル0の設定になる ; $C2/4D4D LDA #$0001 ;Aに$0001をロード $C2/4D50 TSB $7A [$00:057A] ;Aと[$00:057A]で論理和を取り、メモリに書き戻し $C2/4D52 PLP ;Pレジスタにスタックからプル $C2/4D53 PLB ;DBレジスタにスタックからプル $C2/4D54 RTS ;サブルーチン戻り
$C2/0782~$C2/07A0はカーソルのOAMデータを$00:0700~$00:0703へ書き込む処理。詳細は省略する。
$C2/4D35から、OAMへの転送のため、GDMA転送チャネル0の設定を行っている。
[$00:057A]の値の処理も行っている(GDMA転送チャネル0の設定が行われていないことを確認してから、GDMA転送チャネル0設定をし、$01を書きこむ)。
GDMA転送チャネル0設定は以下のとおり。
| アドレス | 内容 | 値 |
|---|---|---|
[$00:4300] | GDMA/HDMA設定レジスタ | $02 |
[$00:4301] | 転送先指定 | $04=OAM |
[$00:4303][$00:4302] | データ元アドレス(開始位置) | $0700 |
[$00:4304] | データ元バンク | $00 |
[$00:4306][$00:4305] | 転送するデータ量 | $0220 |
$00:0700~$00:0920をOAMへ転送する処理になる。
実際の転送処理は、上のCGRAM(カラーパレット)転送の最後に記したとおり。
転送の優先順位は、GDMA転送チャネルの低い順のため、GDMA転送チャネル0(OAM)→GDMA転送チャネル1(CGRAM)、と行われる。