01. Our first program

01. Our first program

The most basic thing you can do is writing to the bg while the screen is off.

ppu_off(); vram_adr(address); vram_put(tile); ppu_on_all();

Let’s go over these functions.

  • ppu_off(); to turn the screen off (which resets the bits xxx1 1xxx of the PPU mask register 2001 to zero.) This frees the PPU to do whatever you want.

Then, set an address to set the start position for writing.

  • vram_adr(NTADR_A(x,y)); This pushes 2 bytes to ppu address register 2006, first the high byte and then the low byte. It sets a location on the screen for writing.

We want to write to the #0 nametable, which is between $2000 and $23ff in the PPUs RAM. Nametable just means tilemap, or background screen.

This little macro will generate a correct address at compile time.

#define NTADR_A(x,y) (NAMETABLE_A|(((y)<<5)|(x)))

X and Y are tile positions. X from 0 to 31, Y from 0 to 29.

Then we can start sending data to the PPU DATA register $2007, 1 byte at a time.

The most obvious way to do that is with vram_put(tile) function. Just loop until all the data has been sent. If you want to fill a large area of the screen with the same tile, you could use vram_fill(tile, length).

The NES PPU will automatically increment the PPU address as each data byte is sent. So, each byte of data will go in 1 to the right of the last one, wrapping around to the next line.

Then turn the screen on (which flips the xxx1 1xxx bits ON in register 2001).

  • ppu_on_all();

What we are doing is putting values on a tile map, which tells the NES which tiles to draw on the screen. Like arranging puzzle pieces on a grid. I made the tileset to look like letters. I positioned them the same as ASCII map does, so I can call them like ‘A’ or “ABC” and it matches the graphics.

This file is called Alpha.chr. I have “incbin” -ed the graphics at the end of crt0.s and put in a “CHR” segment, which the linker is directed to put at the end of the file. Our linker configuration is nrom_32k_vert.cfg, which makes sure that the file is organized in a way that emulators will know how to run it.

See hello.c for our code -> link

Download the source code inside the cc65 main folder. compile.bat sets a relative path to cc65 home that is up one directory. set CC65_HOME=..\

So it should look like /cc65/01_Hello/

Or, you could change the path to cc65 home, if you want to put your dev code elsewhere.

On a sidenote. When I was first starting programming NES games in ASM, I tried to write to the screen, and was confused because what I wrote would only show up in the upper left of the screen. Due to the strange way the PPU works, writing an address (2006) overwrites the scroll registers (2005). After writing to the screen, it is important to write to 2000 once and 2005 twice (or 2006 twice and 2005 twice) to realign the screen.

In many commercial games you will see this after a write to the PPU…

lda #0 sta $2006 sta $2006 sta $2005 sta $2005

neslib does this automatically in the background. If you look near the bottom of the nmi code in neslib.s, you see it does exactly what I described, just before the screen comes back on.

lda #0 sta PPU_ADDR ;2006 sta PPU_ADDR ;2006 lda <SCROLL_X sta PPU_SCROLL ;2005 lda <SCROLL_Y sta PPU_SCROLL ;2005 lda <PPU_CTRL_VAR sta PPU_CTRL ;2000

The 2000 write sets the proper nametable.

Last updated