Day 10 - Sprite DMA
Sprite DMA?
Yep, Sprite DMA. You remember how we wrote to SPR-RAM (OAM) with $2003
and $2004? Well, this is actually unrealiable on the real system (or so I've heard).
What we actually need to do is utalize (utallise?) memory as OAM, and then put a
number into a register and everything is copied for us automatically into the actual
OAM. If you didn't get that it'll (hopefully) become clearer as we do it.
What Memory?
As I said above, we need to use our "variable" memory to make a sort of
duplicate of OAM. In this tutorial we will use $0300 for our OAM Copy if you will,
and you will, 'cause I will/am... nevermind. ok... Note that there are only something
like 64 sprites on the NES so this only really takes (64*4(4bytes for each entry)=256
bytes aways from what we can use. So basically, leave all your "variables" at $0000-$0200
and only go over $0500 with your "variables" if you have to.
Now when I say that we'll be using $0300 for an OAM Copy, I mean that it will
(actually HAS to) be the same layout as the regular OAM. Also, try to remember that
$0300 is a memory address, which is somewhat different from a memory register
as in we have to increment the address every write/store ourselfs rather than just keep giving
values to a (memory) register.
Also (alot of alsos, I know... I just want to make this as clear as possible),
there is a reason we use an even number in the hundreds for our OAM Copy. I'll tell
you in the next section.
Alright, hopefully you get the theory. Now let's see some assembler!
Here goes:
;;--- START OF CODE ---;;
.inesmap 0 ;
.inesprg 1 ; I do these in different orders sometimes, it doesn't matter
.ineschr 1 ; as long as you keep the 1s with the right ones. I hope that
.inesmir 1 ; didn't confuse you as much as I just confused myself. ;)
.bank 1
.org $FFFA
.dw 0
.dw Start
.dw 0
.bank 0 ; code bank
.org $0000 ; variable ORG location $0000
; normally variables defined here
.org $0300 ; OAM Copy location $0300
Sprite1_Y: .db 0 ; sprite #1's Y value
Sprite1_T: .db 0 ; sprite #1's Tile Number
Sprite1_S: .db 0 ; sprite #1's special byte
Sprite1_X: .db 0 ; sprite #1's X value
Sprite2_Y: .db 0 ; same thing, same order for sprite #2
Sprite2_T: .db 0 ; note that I numbered 1 2 ...
Sprite2_S: .db 0 ; some people may actually prefer starting
Sprite2_X: .db 0 ; the count at 0, but it doesn't really matter.
; this would just go on and on for however many sprites you have
.org $8000 ; code ORG location $8000
Start:
; code goes here, the code that goes here or atleast the code to get the
; above sprite definitions into the actual OAM, will be given in the next
; section.
infin:
jmp infin ; infinite loop
;;--- END OF CODE FILE ---;;
If you don't get it, email me telling exactly what you don't get.
The DMA Register
The DMA Register is $4014. We want to write a 3 to it. Why 3? Because
our OAM Copy is at $0300. Writing to the register causes the stuff at
$0n00 (where 'n' is the number we wrote) to be copied into the actual OAM.
So if we had our stuff at $0400 we would write a 4, if it was at $0500 then
write a 5. Get it? I sure hope so. If not tell me please, I will not hesitate
to help you.
So, now to get our values in the actual OAM assuming we put the sprite's info
in the Sprite#-whatever "variables", we just go like this:
lda #$3 ; could be #3, 3 is the same hex and decimal (obviously).
sta $4014 ; when we write to it, the copy from $0300+ to actual OAM, is carried out.
That's it! It's not only more reliable than our old method, it's easier!
How to modify Day 9's code to use this method.
Well, we need to do several things. First, copy the .org $0300 and stuff over
our old variables section. We don't need the variables any more (for sprite info
that is) because we're using the OAM Copy itself for the sprite's X and Y.
Second, use a find and replace function to replace all the 'X_Pos' and 'Y_Pos' with
'Sprite1_X' and 'Sprite1_Y' respectively.
Third find the block of code that loads $2003 with a value and then proceeds to
write to $2004 several times. Find that block of code and replace it with:
lda #3
sta $4014
That should be it! We also save several bytes of code with this method.
This Day In Review
Hope you enjoy Sprite DMA, I tried to make it as painless as possible.
Now that we have a better way to put data in OAM, we can look at a better way to
capture VBlank tomorrow. On to interrupts...
May Your Programs Run Without Bugs,
-Mike H a.k.a GbaGuy
Intro - Day 11