跳转到内容

超级任天堂编程/动画精灵

来自维基教科书,开放世界开放书籍

本文档面向略微高级的 ASM 程序员,比本网站其他内容更高级。

默认情况下,SNES 的精灵模式限制为 16kB,相当于:512 个 8x8 精灵 128 个 16x16 精灵 32 个 32x32 精灵 8 个 64x64 精灵

如您所见,动画精灵会很快消耗掉可用内存。这就是 DMA 的用武之地。唯一的问题是 DMA 并不快。您只有在垂直消隐期间最多有时间进行 6kB 的 DMA 传输,如果考虑滚动和 OAM 更新,时间甚至更短。为了本文的目的,我们假设为 4kB。

如何无论我们向屏幕发送什么数据,都防止 DMA 传输超过 4kB?我们需要让系统知道何时停止,并在下一帧继续 DMA 加载。怎么做?我的解决方案是将 vram 分割成 1kB 的插槽。我选择 1kB,因为 1kB 恰好是一行 8 个 16x16 的精灵。

!apple = "$0000"
!banana = "$0001"
!table = "$0080"
!update_flag = "$0100"
!vram = "$0101"
!source = "$0102"
;================================================================
macro change_vram(source, vram)
php
rep #$20
sep #$10
lda <source>
ldy <vram>
jsr change_slot
plp
endmacro
;================================================================
change_slot:
cmp !table,y
beq redundant_vram_load
sta !table,y
ldx !apple                        ;; apple is just a stupid name I chose for a temporary register
sta !source,x
phy
ldy #$01
phy
pla
sta !update_flag,x
inx #4
stx !apple
redundant_vram_load:
rts
;=============================================================
update_vram_slots:
php
sep #$30
ldx !banana                       ;; banana is just another stupid name I chose
ldy #$04                          ;; to name another temporary register
lda #$80
sta $2115
lda #$01
sta $4300
lda #$18
sta $4301
vram_loop:
lda !update_flag,x
beq finish
stz $2116
lda !vram,x
sta $2117
stz $4302
lda !source,x
sta $4303
lda !source+1,x
sta $4304
stz $4305
lda #$04
sta $4306
lda #$01
sta $420b
lda #$00
sta !update_flag,x
inx #4
dey
bne vram_loop
finish:
stx !banana
sep #$30
lda !apple
sec
sbc !banana
cmp #$10
bcc lag
jmp end_of_game_code
lag:
plp
rts

您只需在垂直消隐期间跳转到 "update_vram_slot" 子例程,并在需要更新动画插槽时使用 "change_vram" 宏。

例如,如果您想从地址 $98a300 更新 vram 中的插槽 $0400,请使用此宏

%change_vram(#$98a3,#$04)

此代码不仅限于精灵动画,还可以用于背景动画和滚动。

华夏公益教科书