【主角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。