Saturday, May 10, 2014

NES Hello Sprites 2

NES programs will often clone sprite memory in RAM to allow for DMA transfers. DMA, which stands for Direct Memory Access, is a rapid way of transferring blocks of memory to hardware. When a DMA transfer happens, the processor is temporarily halted while the memory is rapidly copied into PPU memory. While this still takes time, it is much faster than manually copying sprites. If you have a large number of sprites, it is far better to use DMA than to manually copy sprites.

Another convenience that the NES provides programmers is a VBlank Non-Maskable Interrupt (NMI). Interrupts cause the processor to stop what it is doing and immediately start running the subroutine pointed to by the interrupt vector. There are two types of interrupts. Your regular interrupts, or IRQs, can be disabled by the SEI instruction and enabled by the CLI instruction. NMIs are not affected by those instructions so will always occur. The PPU control register, however, does allow the VBlank to be disabled.

The advantage of using interrupts is that you do not have to worry about polling for the VBlank so you can process other game logic while the PPU is drawing the screen without having to worry about missing this important period. In this second sprite demo, all of our logic will be done in the interrupt handler, though this certainly does not have to be the case and in later projects we will be developing a state machine to handle complex processing.

The code for the second version of our sprite demo is very similar to the original version with only a few changes needed to support interrupts and DMA. Instead of copying the sprite data directly to the PPUs sprite memory, it is copied to a page of RAM. VBlank interrupts are enabled just before the radically changed main loop as follows:

LDA #%10000000 ; enable NMI
STA $2000

MainGameLoop:
NOP ; Right now we do nothing in the main loop.
JMP MainGameLoop

Yes, absolutely nothing is done in the main loop. Instead all work is done in the Interrupt. While the NOP instruction is not necessary, I have heard urban legends about loops causing hardware to catch fire. I find this highly suspect but spreading the loop over an extra instruction does give me an excuse to use my favorite 6502 instruction. The real logic happens in the Non-maskable interrupt routine. Though thanks to DMA hardware this is much simpler than the manual method of transferring sprite information.

NMI:
LDA #2
STA $4014
JSR MoveSprites
RTI

As you can see, DMA is very simple to use. You just store the page of memory you want to transfer to the PPU into $4014. The move sprites function is almost identical except it stores sprite positions to RAM instead of writing directly to the PPUs sprite memory which has the added benefit of being more compatible with emulators (always a benefit for home-brew games).

No comments: