MMC3(マッパー4)でラスタスクロール


ギコ猫でもわかるファミコンプログラミング - ラスタースクロール を MMC3 で書き換えたもの。画面走査のタイミングのプログラムだなんて、ファミコンはおっかない世界だねぇ。
参考文書

;;; MMC3 でラスタスクロールするテスト
;;; http://gikofami.fc2web.com/nes/nes015.html の改変
;;; giko3.pal, giko3.bkg は上のページから拾ってね

        .inesprg 1              ; プログラムバンク数
        .ineschr 1              ; CHR バンク数
        .inesmir 0              ; 水平ミラーリング
        .inesmap 4              ; mapper #4 (MMC3)

        .bank   0
        .org    $C000

SIZE:   .equ    3               ; スクロールする領域の数
lines:  .byte   120, 50         ; スクロールする領域の高さ。スキャンライン数
speeds: .byte   0, 1, 2         ; スクロールスピード

target: .equ    $00             ; 次の IRQ でこのインデックスのスクロール領域を設定する
scrolls: .equ   $01             ; 現在のスクロールX位置. ここから SIZE bytes

;;; スタート/リセット時に呼ばれる
start:
        ;; 初期化中は割り込み禁止
        sei                     ; IRQ
        lda #%00001000          ; NMI
        sta $2000
        
        cld                     ; デシマルモードクリア
        ldx     #$ff            ; スタック初期化
        txs

        jsr     vwait           ; VBlank待ち

        lda     #%00000110      ; 初期化中はスプライトとBGをOFF
        sta     $2001

        lda     #$40            ; frame counter off
        sta     $4017           ; (mapper のみ IRQ 発行)

        lda     #$01
        sta     $a000           ; MMC3 水平ミラーリング
        lda     #$00
        sta     $a001           ; これなに?

        ;; ゼロページ初期化
        lda     #$00
        ldx     #$00
.initZeroPage:
        sta     <$00, x
        inx
        bne     .initZeroPage


        ;; パレット初期化
        ldx     #$3F            ; PPU $3F00
        stx     $2006
        ldx     #$00
        stx     $2006
.initPalette
        lda     PALETTE_DATA, x
        sta     $2007
        inx
        cpx     #$20
        bne     .initPalette

        ;; ネームテーブル生成 (空と地面を480こずつ)
        lda     #$20            ; PPU $2000
        sta     $2006
        lda     #$00
        sta     $2006

        lda     #$00            ; 空
        ldx     #250
        jsr     writeVRAM
        ldx     #230
        jsr     writeVRAM

        lda     #$01            ; 地面
        ldx     #250
        jsr     writeVRAM
        ldx     #230
        jsr     writeVRAM


        lda     #%00011110      ; スプライトとBGの表示をONにする
        sta     $2001

        lda     #%10001000      ; NMI割り込み許可
        sta     $2000

        cli                     ; IRQ割り込み許可
.loop
        jmp     .loop


;;; VBlank のタイミングで呼ばれる
nmi:
        lda     #$00
        sta     target

        jsr     scroll

        rti

;;; scroll でセットしたスキャンラインのタイミングで呼ばれる
irq:
        ;; レジスタをスタックに退避
        pha
        txa
        pha
        tya
        pha

        jsr     scroll

        ;; レジスタを IRQ 前の状態に復帰
        pla
        tay
        pla
        tax
        pla

        rti

;;; target 番目のスクロールをセット
;;; target のインクリメントと IRQ のセットもする
scroll:
        lda     $2002           ; スクロールクリア

        ldx     target

        lda     scrolls, x
        sta     $2005           ; set scroll X
        lda     #$00
        sta     $2005           ; Y スクロールは固定

        lda     scrolls, x        
        clc
        adc     speeds, x
        sta     scrolls, x      ; increment scroll

        ;; target をインクリメント
        inx
        cpx     #SIZE
        beq     .quit

        ;; 次の IRQ セット
        dex
        lda     lines, x        ; IRQ を呼ぶまでのスキャンライン
        sta     $c000
        sta     $c001
        lda     #$01
        sta     $e000           ; acknowledge the IRQ
        sta     $e001           ; enable the IRQ
        inx
        stx     target
        rts
        
.quit
        ;; 最後なら IRQ 終わり
        lda     #$01
        sta     $e000
        ldx     #$00
        stx     target
        rts


;;; VBlank 待ち
vwait:
.wait:
        lda     $2002
        bpl     .wait
        rts

;;; a を $2007 に x 回書き込む
writeVRAM:
.write:
        sta     $2007
        dex
        bne     .write
        rts

PALETTE_DATA:
        .incbin "giko3.pal"


        .bank   1
        .org $fffa
        .dw nmi, start, irq


        .bank 2
        .incbin "giko3.bkg"