piot wrote:
Should I poll for the vertical blank?
You can poll a flag that you set in the NMI, but not $2002. Reading bit 7 of $2002 is not advised is because when the register is read at the exact same time the flag gets set by the PPU, the flag gets cleared immediately and never returns a 1. This particular alignment has reasonably high chances of happening, since the register is read in a loop. This will cause you to miss VBlanks from time to time, which will affect the smoothness of animations and sounds.
Quote:
Or do the rendering in some other way?
The absolute simplest way is to keep NMIs always enabled and have the NMI handler only change a variable, nothing more:
Code:
NMI:
inc FrameCount
rti
Then, whenever you want to wait for Vblank, you do this:
Code:
;wait for VBlank
lda FrameCount
Wait:
cmp FrameCount
beq Wait
The idea is that you put the current frame counter into the accumulator and keep comparing it to the memory location it was loaded from. The values will remain equal until the NMI fires and modifies the variable in RAM, which will cause the loop to end.
NMIs are 100% reliable, and you won't miss VBlanks with this technique as long as your game logic doesn't take longer than a full NES frame to execute.
The simplest NES game structure that's still versatile for a wide variaty of game types goes something like this:
Code:
InitializeModule:
;SET UP EVERYTHING FOR THE FOLLOWING LOOP;
ProcessFrame:
;READ CONTROLLERS;
;GAME LOGIC (PHYSICS, AI, SCROLLING, ETC.);
;WAIT FOR VBLANK;
;WRITE DATA TO THE PPU, RESET THE SCROLL, ETC;
;CALL MUSIC DRIVER;
jmp ProcessFrame
You can have multiple blocks like this in your NES program, one for each different "module". For example, the title screen and the menus would use one loop like this where the logic is basically moving cursors around and updating options on screen according to user input. Once the game starts, another loop like this will take care of updating backgrounds, moving objects around and so on. You can freely navigate from module to module by simply jumping to the initialization code of another module at any time.