【主角DMA?】

是的,主角DMA。你还记得我们怎么利用$2003和$2004寄存器写入SPR-RAM(OAM)的吗?实际上真实系统中这种做法是不可靠的。我们应该利用内存作为OAM,然后向一个寄存器写入值,所有内容都自动拷贝到真实OAM。如果你不懂,那我们做一遍就懂了。

【什么内存?】

就像我上面说的,我们需要使用“变量”内存来复制一份OAM。本教程使用$0300用于复制的OAM。注意NES上只有64个主角(占用64*4B=256B,0x100)。所以基本上你自己定义的变量尽可能放在$0000-$0200内。从$0300-$0400的布局应该同OAM完全一致。

另外记住,$300是一个内存地址,而不像内存变量。我们必须在每次读写后将地址加一,而不是持续读写同一个地址的内容。

我们用偶数在百位用于我们OAM拷贝。我会告诉你为什么的。

好了,希望你理解了上述理论,下面看汇编代码。

;;--- 代码开始 ---;;

.inesmap 0 ;

.inesprg 1 ;

.ineschr 1 ;

.inesmir 1 ;

.bank 1

.org $FFFA

.dw 0

.dw Start

.dw 0

.bank 0 ; 代码段

.org $0000 ;

; 普通变量在这里定义

.org $0300 ; OAM镜像从这里开始

Sprite1_Y: .db 0 ; 1号主角的纵坐标

Sprite1_T: .db 0 ; 1号主角的瓷砖编号

Sprite1_S: .db 0 ; 1号主角的特殊待遇

Sprite1_X: .db 0 ; 1号主角的横坐标

Sprite2_Y: .db 0 ; 2号主角的纵坐标

Sprite2_T: .db 0 ; 不用说了吧?

Sprite2_S: .db 0 ;

Sprite2_X: .db 0 ;

; 依此类推去吧。。。

.org $8000 ; 代码开始

Start:

;

; 先卖个关子

; 后面会给出详细代码

infin:

jmp infin ; 死循环

;;--- 代码结束 ---;;

如果你不懂,email我告诉我你到底哪不懂。

【DMA寄存器】

DMA寄存器地址是$4014,我们需要写3进去。为什么是3?因为我们内存OAM在$300处。你向$4014写入n,那么就从$0n00处拷贝内容到真正OAM。也就是说,如果从$0400开始那就写4,如果从$0500开始那就写5,明白?

下面看怎么搬运我们的OAM到真实OAM中:

lda #$3 ; 也可以写成 #3, 因为显然3的十进制和十六进制表示是相同的

sta $4014 ; 一旦写入, 拷贝就开始

就是这样,比我们老办法可靠多了,而且更简单!

【怎样按照上述方法修改第九天的代码?】

我们需要做几件事。

首先,拷贝 .org $0300和后面那堆东西到我们旧的变量区。我们再也不用那些变量了,因为我们的主角X,Y都使用OAM拷贝。

其次,使用查找替换功能吧所有X_Pos和Y_Pos改为Sprite1_X和Sprite1_Y。

再次,找到写$2003的代码块,替换为:

lda #$3 ;

sta $4014 ;

就是它了!我们用这种方法也节省了几个字节代码空间。

【今日回顾】

希望你喜欢主角DMA,我已经将它尽可能口语化了。我们今天学到了更好的写OAM数据的方法,明天还要看下更好的方法来捕获VBlank。接下来是。。。。。中断!

希望你的代码没有bug。