モード7

モード7で、BGを回転・拡大縮小します。

モード7の特徴

モード7では、BG1を1枚だけ、変換行列を使って回転・拡大縮小して表示します(BG)。ほかのモードとはデータの形式が大きく異なります。

項目内容
タイルマップ128×128タイル、1タイル1バイト(タイル番号のみ)
タイルデータ256枚、1ピクセル1バイト(256色)
VRAMの配置$0000~$3FFFの各ワードの下位バイトにタイルマップ、上位バイトにタイルデータ

データの転送

タイルマップとタイルデータは、同じワードの下位バイトと上位バイトに入るので、VMAINCの設定を変えて別々に転送します。

    SEP #$20
    REP #$10
    ; タイルマップ(16KB)を各ワードの下位バイトへ
    STZ $2115       ; VMAINC: $2118への書込後にアドレスを1増やす
    LDX.w #$0000
    STX $2116       ; VMADD = $0000
    STZ $4300       ; DMAP0: モード0(1レジスタ)
    LDA.b #$18
    STA $4301       ; BBAD0: VMDATAL($2118)
    LDX.w #Mode7Map&$FFFF
    STX $4302
    LDA.b #Mode7Map>>16
    STA $4304
    LDX.w #$4000
    STX $4305       ; 16384バイト
    LDA.b #$01
    STA $420B

    ; タイルデータ(16KB)を各ワードの上位バイトへ
    LDA.b #$80
    STA $2115       ; VMAINC: $2119への書込後にアドレスを1増やす
    LDX.w #$0000
    STX $2116       ; VMADD = $0000
    LDA.b #$19
    STA $4301       ; BBAD0: VMDATAH($2119)
    LDX.w #Mode7Tiles&$FFFF
    STX $4302
    LDA.b #Mode7Tiles>>16
    STA $4304
    LDX.w #$4000
    STX $4305
    LDA.b #$01
    STA $420B

設定

    LDA.b #$07
    STA $2105       ; BGMODE: モード7
    STZ $211A       ; M7SEL: 範囲外は繰り返し、反転なし
    LDA.b #$01
    STA $212C       ; TM: BG1を表示する

変換行列

変換行列の各要素(M7A~M7D)は8.8の固定小数点で、$0100が1倍です。回転の中心はM7X・M7Yで指定します。どれも下位バイト、上位バイトの順に2回書き込みます。

効果M7AM7BM7CM7D
そのまま$010000$0100
2倍に拡大$008000$0080
半分に縮小$020000$0200
角度θだけ回転cosθsinθ-sinθcosθ

値を小さくすると拡大、大きくすると縮小になることに注意してください。行列は「画面上の点が、BGのどの点を表示するか」を表しているためです。

回転させる

角度ごとのcos・sinの値(8.8の固定小数点)を表にしておき、NMI処理ルーチンで角度に応じた値を書き込みます。

    ; $30 の角度(0~255)で回転させる(NMI処理ルーチンの中で)
    REP #$30
    LDA $30
    AND.w #$00FF
    ASL A
    TAX
    LDA.l CosTable,X
    SEP #$20
    STA $211B       ; M7A 下位バイト
    XBA
    STA $211B       ; M7A 上位バイト
    XBA
    STA $211E       ; M7D 下位バイト(M7D = M7A)
    XBA
    STA $211E       ; M7D 上位バイト
    REP #$20
    LDA.l SinTable,X
    SEP #$20
    STA $211C       ; M7B 下位バイト
    XBA
    STA $211C       ; M7B 上位バイト
    XBA             ; 上位・下位を元の並びに戻す
    REP #$20
    EOR.w #$FFFF
    INC A           ; -sinθ
    SEP #$20
    STA $211D       ; M7C 下位バイト
    XBA
    STA $211D       ; M7C 上位バイト

    ; 回転の中心を画面の中央にする
    LDA.b #128
    STA $211F
    STZ $211F       ; M7X = 128
    LDA.b #112
    STA $2120
    STZ $2120       ; M7Y = 112

CosTable・SinTableは、角度1あたり 360°÷256 で、値を256倍した16ビットの数を256個ずつ並べた表です。ツールなどで計算して作っておきます。

このページのコードで使うMode7Map(mode7map.bin)・Mode7Tiles(mode7tiles.bin)・CosTable・SinTable(sincos.asm)は、サンプルの素材に入っています。

M7X・M7Yは、BGの座標で指定します。上の例はスクロール量(BG1HOFS・BG1VOFS)が0の場合で、BGの(128, 112)が画面の中央に来ています。スクロールしている場合は、画面の中央に来るBGの座標(スクロール量 + 128、スクロール量 + 112)をM7X・M7Yに書き込みます。

そのまま2倍に拡大回転(角度21、約30°)
変換行列を単位行列にしたモード7の画面M7AとM7Dを$0080にして2倍に拡大した画面上の回転のコードで角度21だけ回転させた画面

どれもスクロール量0、回転の中心はBGの(128, 112)です。画面写真は、サンプルの画面にこのページのコードを実行し、エミュレーターで撮ったものです。

奥行きのある床

ラインごとに拡大率を変えると、画面の奥へ向かって遠ざかる床のように見せられます。これには、M7AとM7Dの値をHDMAでラインごとに書き換えます(HDMAを使う)。M7A・M7Dは2回書き込みのレジスタなので、転送モード2を使います。

注意

  • モード7のスクロール量は、BG1HOFS・BG1VOFS($210D・$210E)に書き込みます。ほかのモードのBG1のスクロールと同じレジスタです。
  • 変換行列のレジスタは、モード7を使っていないときは符号付き乗算器としても使えます。モード7の表示中は、乗算器として使わないでください。

参照

BG
M7A~M7D レジスタ
M7X・M7Y レジスタ
M7SEL レジスタ
HDMAを使う