For my first demonstration of sprites I decided to go with your typical bouncing sprite. Instead of using a ball, the text message "Hello Sprites!" Is used. Due to the NES limitation of only allowing 8 sprites on a line, the message is broken into two lines of text. This version manually manipulates sprite memory instead of using the more common DMA method that will be covered next. To my surprise, this actually lead to issues with the emulator if ran with the default fast PPU emulation though worked fine when ran with the slower PPU emulator. This is a good reminder that emulators are not always accurate, though developing with them is much faster than developing on real hardware.
The complete source code for this project is at http://blazinggames.com/books/NES/. Only the most relevant sections of code are included.
The program starts out with pretty standard initialization code. The screen is then filled with a checkerboard pattern instead of blank spaces so it will be clear that the sprites are appearing above the screen image (background). Next we have the task of setting up the sprites. In our case we are just copying this info from a table in ROM into the PPU Sprite Memory. Variables for holding the top corner of the block of sprites and the movement directions are then set up. The main loop simply waits for the VBlank flag to be set and then calls the moveSprite function which has all the movement logic.
; initialize the sprites
; fill sprite memory with data from ROM
; fill rest of sprite memory with 255 so that it will not be visible
; set sprite variables
The MoveSprites function is fairly large but also simple. The first chunk of code shows how the coordinates are adjusted. Each frame the block's top corner is adjusted by the current direction variables. Hits against the screen bounds are then performed with hits resulting in the direction being changed. What may be confusing, and something I will definitely be writing about in the future, 255 is used for -1.
; First adjust top corner of sprite block
; Has hit left edge?
; if so set horizontal adjust to +1
; if has hit right edge?
; if hit right edge, set horizontal adjust to -1
LDA #$FF ; -1
; vertical tests similar to above and not included here, see source file for full code.
Once we know the coordinates for the top of the block of sprites, we can calculate the position of the sprites. As the bottom calculations are the more complex ones, here is the code for setting the bottom line of sprites.
; we reach here when top row done, so prepare bottom row
STA TEMP_Y ; Y coordinate to use for bottom row of sprites
STA TEMP_X ; X coordinate starts at block x coordinate
; Y register already correct so no need to set
LDX #8 ; 8 sprites in bottom row need to be counted
STA $2004 ; write adjusted sprite Y coordinate
INY ; Adjust index to point to sprite X data
STY $2003 ; tell PPU we want to write sprite X
STA $2004 ; Write sprite X coordinate
ADC #8 ; add 8 to this coordinate for next sprite
INY ; set up index for next sprite
DEX ; and adjust countdown
As you can see it is fairly simple. Most NES programs, however, do not manipulate sprites directly as we are doing so we will rewrite this program to take advantage of interrupts and DMA,as well as explain what interrupts and DMA shortly but next week will be a postmortem possibly followed by something else.