I wrote some support functions for filling a VRAM update 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 .
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). Generally, you only need to set_vram_buffer() once, at the top of the main() function, and never turn it off, just clear it every frame…
You should use clear_vram_buffer() at the top of the game loop, after the ppu_wait_nmi().
When you are writing to the buffer, it remembers where it’s at, and it continues to grow. 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. clear_vram_buffer() will reset the index to zero.
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);
If you want to send more than 1 byte to the PPU, use
multi_vram_buffer_horz(const char * data, unsigned char len, int ppu_address);
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.