What good is a video game unless we can actually play it? In this lesson, we’re going to cover capturing input from standard controllers and acting on that input every frame to move a sprite around the screen. Check out the finished product to see it in action.
Firstly, we’re going to need to add some new sprites to our pattern table.
A great tool for editing NES tilesets is YY-CHR which is a Windows program but runs decently in wine on Linux and OS X.
It comes in two flavors, the newer .NET version and the older C++ version.
I find that the .NET version is generally your best bet, though copy/paste seems to work better in the C++ version when running in
Let’s add some background tiles to make a border around the screen and a tiny one-tile character (we’ll get into using metasprites to make a multi-tile character in a later post).
Remember how to draw background tiles to the pattern table? Let’s do that again. First, we’ll create some handy aliases for the tile indices representing our background (and the sprite that we’ll use later).
We’ll also create a background palette (we’ll only need one for this lesson) that will color our border tiles and a sprite palette (again, we’ll just need one) to use for later.
After writing the palette data to the PPU, we’ll set the
PPU_ADDRESS to the beginning of the nametable and then loop through and fill in our border tiles:
Since we’re only using one background palette, and it’s palette 0, we don’t have to do anything with the attribute table. Now let’s move on to something new!
Information about sprites on the NES is stored in a special 256-byte chunk of memory in the PPU called OAM, or Object Attribute Memory. Each sprite is represented by four bytes, which means that we can display up to 64 sprites on the screen. The PPU can only render eight sprites on the same scan line; those nearer the beginning of OAM taking precedence over those near the end.
We can write data to OAM very similarly to how we write to the rest of the PPU using the
OAM_DATA registers, but there’s actually a more efficient way to do it.
The NES actually has a DMA (Direct Memory Access) mechanism to write an entire page of RAM (256 bytes) directly into the PPU’s OAM much more quickly than we could looping through and writing one byte at a time into
If you remember from the introduction to vblank in the last post, we’re limited to writing to PPU memory (including OAM) during the (very, very short) vblank period, so we want to save as many cycles as we can.
Firstly, we’re going to set aside a page of RAM to be used as a buffer that we’ll write to OAM every frame:
define = yes for the OAM segment defines the linker symbol
__OAM_LOAD__ (it will be
0x0200 corresponding to the start address we set for the memory section).
We can now very easily modify our NMI handler to write the buffer to OAM every frame via DMI.
We do this by setting
0 (start writing to the beginning of OAM) and then writing the high byte of the RAM address (
Now we can update our buffer anytime we want and any changes will be pushed to the PPU during the next vblank.
To make our code more readable, we’ll define a struct to represent a sprite in our buffer:
And we’ll create just one sprite for our player at the beginning of our OAM buffer, then initialize it before enabling the PPU:
Now we have our player smack dab in the middle of the screen. Let’s make it move!
We’ll want to poll the input from the controllers with every frame, but we want to make sure we’re not wasting valuable vblank time.
To achieve this, we’ll create a new method,
WaitFrame that will spin idly until our NMI handler completes, then carry out some non-PPU tasks that we want to do every frame, like reading input, then yield back to our game loop:
We’ve written this in assembly because it’s super simple and we can loop more efficiently.
Notice the use of a new zero page variable,
frame_done, which we won’t need to export to C as it’s only used internally.
A call to
WaitFrame will set this flag to a non-zero value (via
inc) then wait for it to be zero, which will happen at the end of our NMI handler:
As you can guess by the lack of a leading underscore, we’ll also write
UpdateInput in assembly and won’t export it to C, though we will expose the values that we’ve read from the controllers.
The code to read the controller values is fairly straightforward:
First, we need to strobe the controller by writing a
1 followed by a
0 to a memory-mapped register at
Strobe is a term in electronics used to refer to a signal which helps synchronize the data in a bus when the components (here the CPU and the controller) have no common clock.
1 we’re telling the controller to start filling its internal shift register with button states.
We have to write a
0 before we start to read those states or else we’ll always be reading the state of the first button (the A button).
Each time we read from the controllers shift register (via the memory-mapped
0x4017), we get the state of one button in the following order: A, B, Select, Start, Up, Down, Left, Right.
After reading a button state, we only care about the first two bits, so we’ll perform a logical
and on the value read with the mask
cmp instruction will set the carry flag to
1 if the button is pressed (i.e. the result of the
0 if it isn’t.
rol (rotate left) the carry bit onto the
After doing this eight times, we’ll end up with a byte for each controller port where each of the eight bits represent the state of a button on that controller.
All we need are some masks to be able to test for the states of individual buttons:
You’ll notice that
UpdateInput actually calls
ReadInput twice and compares the values from each read, returning only if they are equal.
This has to do with a conflict with the APU (Audio Processing Unit) that can sometimes interfere with reading the controller input on NTSC systems.
If we read the same values twice in a row, we can be reasonably sure that those are the correct values and no interference has occurred.
Now we’ll just export the necessary symbols:
And make them easily available in C:
Finally, we can make our sprite move every frame by updating its coordinates in our OAM buffer based on which buttons are currently pressed:
We left an 8px (
SPRITE_HEIGHT) buffer so that we don’t overlap with the border that we drew in the background.
The coordinates are for the top-left corner of the sprite, so we have to take into account the height of the sprite itself in addition to the height of the border tile for the bottom boundary, hence the
(2 * SPRITE_HEIGHT).
Now take a few minutes and enjoy commanding your character to explore the vast expanse of the screen.
In the next post, we’ll look at more efficient ways to create and draw static (non-scrolling) backgrounds, using metasprites to represent multi-tile sprites, and animating our sprites.
While poking around nes.h, you might have noticed some funky stuff going on with NTSC vs. PAL.
Due to overscan, NES games played on an NTSC TV will often have the first and last eight pixels (which is also the first and last row in the nametable) hidden by the border of the TV.
Many emulators therefore will only render lines 8-231 when playing an NTSC ROM, so we’ll create some handy constants that take this all into account and make our loops (like those in
DrawBackground) easier to write.