03. VRAM buffer

I wrote some support functions for filling a VRAM buffer. This is an automatic system where you write changes to the screen in a buffer. The system then copies these changes to the PPU during the v-blank period. It runs smoothly without ever having to turn the screen off.

I’m using some behind the scenes code, and I’ve defined a VRAM_BUF in crt0.s to be at $700. Notice, that this technically shares the 700-7ff space with the C stack. They could potentially collide. If you are worried about this, put the VRAM buffer at 600-6ff. But, you shouldn’t be putting more than 74 bytes of writes to the VRAM buffer, so this should never get bigger than 77 bytes. The C stack grows down from 7ff. And, the C stack only needs a dozen or so bytes, if you program like I do, and don’t use local variables, and only pass a few at a time to a function (preferably none), and don’t use recursion. If you do those things, you’ll be fine. But, I thought I should let you know.

To use my system, you need to point some internal pointer to my system. This requires no passed value, that address is defined by VRAM_BUF in crt0.s .

set_vram_buffer()

This is kind of like set_vram_update(). To point the PPU update to another data set, you would do it with set_vram_update(&data), and to turn it off you can do set_vram_update(NULL).

.

The other thing you need to do is reset the buffer every frame.

clear_vram_buffer()

When you are writing to the buffer, it remembers where it’s at, and it continues to grow. clear_vram_buffer() will reset the index to zero. The data is automatically transferred between frames, so if this comes after ppu_wait_nmi(), we know our data has been sent already, and it’s ok to clear it.

You can buffer a single tile with

one_vram_buffer(tile, ppu_address)

You just need the tile number and an address. This macro can get us the address NTADR_A(x,y). X and Y are tile positions. X from 0 to 31, Y from 0 to 29.

You could also use a function I wrote that calculates the address at run time, using pixel values 0-255 X, 0-239 Y. NT is which nametable, 0-3.

get_ppu_addr(char nt, char x, char y);

and you can write a contiguous run with

multi_vram_buffer_horz(const char * data, unsigned char len, int ppu_address);

or

multi_vram_buffer_vert(const char * data, unsigned char len, int ppu_address);

Horz goes left to right. Vert goes top to bottom.

.

So the address and data and an eof are copied to the vram buffer. One of the pluses here is that the eof is auto adjusted as you keep writing to the buffer.

One warning, though. It doesn’t keep track of how big you make the buffer, and if you aren’t careful, you could put too much in, and bad things might happen. (garbled graphics, misaligned screens, or crashed games.)

I wrote multiple variations of how you could write to the screen, and this all is transferred in 1 v-blank. This is pretty much the maximum number of changes you can do in one frame.

https://github.com/nesdoug/03_Hello3/blob/master/hello3.c

https://github.com/nesdoug/03_Hello3

Last updated