Day 5 - Displaying a Sprite

Published: 2005-07-22
Updated: 2010-12-19
Author: Mike Huber

Sprites

You would think between backgrounds and sprites, that backgrounds would be easier. Wrong! Sprites are so much easier than backgrounds. To do sprites, we just need to give a few pieces of info to SPR-RAM (sprite x,y positions, tile#, etc.). Well I guess we need to draw the sprite's picture first. So let's go! :)

Tile Layer Pro

You might be asking how much space we have for sprites. Answer: 4 KiloBytes. Alright, download Tile Layer here. Download an empty 4kb file here. Run TLP.exe and open MT.spr . Now click the View menu and set the "Format" to NES by hovering the mouse over "Format" and clicking "NES". Click the first square in the upper-left of the large grid-window. Now click the farthest right rectangle in the "Pallete Editor Window". That's right if you noticed, we can only use 4 colors. Now draw a smiley face or something in the "Tile Editor Window". Save the file as "our.spr". Good we have a sprite! Sorry if I rambled.

If you haven't noticed, spr means SPRite and bkg means BacKGround.

If you couldn't do that, click here to download mine! (UPDATE: I think I may have put the sprite in tile zero, the sprite should be in tile 1. If this isn't done the rest of the sprites will all have data X=0,Y=0,Tile num=0, so will all show up in top-left corner. Updating the source and editing the sprite file is left to you.)

Also, download again and name it "our.bkg" we won't use our.bkg today, but we do need it right now. Just put our.bkg and our.spr in the same folder that you are going to use for you code file.

Bank 2 Becoming More Important

In a previous Day, we told the PPU that we'll use $0000 in our Bank 2 as background picture data, and $1000 as sprite picture data. We will have Bank 2 being 8kb which is $2000 bytes. 4kb is 4096 bytes or $1000 in hex. So if we put the .bkg 4kb file first in Bank 2 and the 4kb file second, we will end-up having the picture data in perfectly the right place where we told Mr. PPU the stuff should be.

In short: Bank 2 has 2 binary files included in this order: our.bkg and our.spr, this order is exactly how we told the PPU it would be and it works our because each file is 4kb and all together we need 8kb. 4*2=8, got it?

Here's how our Bank 2 code looks now:

.bank 2 .org $0000 .incbin "our.bkg" .incbin "our.spr" .incbin includes a binary file.

Writing To SPR-RAM

SPR-RAM or otherwise known as "Place for Sprite Info", is written to by the two registers: $2003 and $2004. You write 2 times to $2003 giving it the address you want to start at and you keep giving $2004 the info until your done.

Remember that we do loads of one byte at a time, so you need to give $2003 the two (2) pieces of the address. Like this:

lda #$00 sta $2003 sta $2003

Since the address we want is $0000, we just store $00 twice. If we wanted say $00A9 for example, we'd give the first 2 digits first and the second two second like so:

;(NOTE: this is just an example, we'll start at $0000 for SPR-RAM writing) lda #$00 sta $2003 lda #$A9 sta $2003

That's how we give a 16bit address to somewhere. Note that we also did this when loading the palette, except we used a different memory register.

Actually Writing to SPR-RAM

Now that we've given $2003 what address to start at ($0000), we can just give $2004 the info. What info you ask? Each sprite has 4 bytes of info, they are:

Y
- vertical position on the screen.
N
- 8x8 tile number in the .spr file we include.
C
- Some funky special stuff I'm not covering yet.
X
- horizontal position on the screen.

Each of these is one byte, if you didn't notice. :).

There can be 64 sprites' info stored here. We'll display our sprite at 20,50 and our sprite should be the first tile, so the other two bytes can be 0.

Here goes: lda #50 ; note we load a decimal number sta $2004 ; store Y value lda #00 ; tile number is 0 for the first sprite sta $2004 ; store tile number. sta $2004 ; load that 0 again because we don't need any special stuff right now. lda #20 sta $2004 ; store X value.

And boom! The sprite appears. (hopefully)

Note that when we give a memory register an address and then write to it's brother register repetedly, that brother register puts the number in the right place for us, incrementing the address we gave the first register. All we have to do is keep stuffing the second one.

VBlank

Ideally, we really should write to SPR-RAM during the time when the little electric-beam gun in the TV has just finished the frame and is moving back to the top to start drawing the screen again. That's called VBlank. I'm just going to show you the code now and not explain it as our main focus here is sprites. Here goes:

waitblank: lda $2002 ; load A with value at location $2002 bpl waitblank ; if bit 7 is not set (not VBlank) keep checking

Oh well, I explained it alittle. Don't worry about this. It'll be in the source we put together now, but just remember that it makes the SPR-RAM update safe.

Putting Together Our First Code File

Alright, create a file called "our.asm" and open it in MS Notepad. Copy in the following. And yes it will be commented. Indentation is important.

;;--- CODE START ---;;

; INES header stuff
.inesprg 1   ; 1 bank of code
.ineschr 1   ; 1 bank of spr/bkg data
.inesmir 1   ; something always 1
.inesmap 0   ; we use mapper 0

.bank 1   ; following goes in bank 1
.org $FFFA  ; start at $FFFA
.dw 0    ; dw stands for Define Word and we give 0 as address for NMI routine
.dw Start ; give address of start of our code for execution on reset of NES.
.dw 0   ; give 0 for address of VBlank interrupt handler, we tell PPU not to
; make an interrupt for VBlank.

.bank 0   ; bank 0 - our place for code.
.org $8000  ; code starts at $8000

Start: lda #%00001000 ; do the setup of PPU sta $2000 ; that we lda #%00011110 ; talked about sta $2001 ; on a previous day

ldx #$00    ; clear X

lda #$3F    ; have $2006 tell
sta $2006   ; $2007 to start
lda #$00    ; at $3F00 (palette).
sta $2006

loadpal: ; this is a freaky loop lda tilepal, x ; that gives 32 numbers sta $2007 ; to $2007, ending when inx ; X is 32, meaning we cpx #32 ; are done. bne loadpal ; if X isn’t =32, goto “loadpal:” line.

waitblank: ; this is the wait for VBlank code from above lda $2002 ; load A with value at location $2002 bpl waitblank ; if bit 7 is not set (not VBlank) keep checking

lda #$00   ; these lines tell $2003
sta $2003  ; to tell
lda #$00   ; $2004 to start
sta $2003  ; at $0000.

lda #50  ; load Y value
sta $2004 ; store Y value
lda #$00  ; tile number 0
sta $2004 ; store tile number
lda #$00 ; no special junk
sta $2004 ; store special junk
lda #20  ; load X value
sta $2004 ; store X value
; and yes, it MUST go in that order.

infin: jmp infin ; JuMP to infin. note that this loop never ends. :)

tilepal: .incbin “our.pal” ; include and label our palette

.bank 2   ; switch to bank 2
.org $0000  ; start at $0000
.incbin "our.bkg"  ; empty background first
.incbin "our.spr"  ; our sprite pic data
; note these MUST be in that order.

;;--- WERE DONE / CODE END ---;; </code>

I realize that maybe you hoped for something smaller. You may want to research this for a day or 2 cross checking with my previous articles to find the inside scoop on what each individual thing does. If you have questions, ask me.

Assembling

Put everything (our.pal, our.bkg, our.spr, nesasm.exe, your code file) in the same folder and open up DOS. CD to your folder and type:

nesasm Your_Filename.asm

nesasm.exe will make a .nes file in that folder. Run it in an emulator to see the results.

This Day In Review

Wow, that was alot! Don't worry, it looks like alot, but once you know the NES, it'll just come to you. I guess it's on to backgrounds tomorrow maybe.

Sleep well :),
-Mike H a.k.a GbaGuy