Hey guys,
I just need some general information about how the NES communicates with the standard controllers.
Are the gamepads polled in most games (i.e. within the gameloop) or do they generate an interrupt to the CPU (like the V-blanking period)?
I have been looking at various spec sheets, and they explain that the data from the gamepads can be read from by strobing addresses $4016 and $4017. It would seem that doing this, they could be read from at any time, suggesting that they are polled. However, this would make periodic communication between the gamepads and the CPU very unlikely.
Polled. Usually in the NMI or the frame handler, at 60 Hz (NTSC) or 50 Hz (PAL).
thefox wrote:
Polled. Usually in the NMI or the frame handler, at 60 Hz (NTSC) or 50 Hz (PAL).
Ah, I see. So it's handled every time the frame is updated. Is sound done the same way?
xgamer wrote:
thefox wrote:
Polled. Usually in the NMI or the frame handler, at 60 Hz (NTSC) or 50 Hz (PAL).
Ah, I see. So it's handled every time the frame is updated. Is sound done the same way?
Yes.
The joypad can be polled at any time, you could even poll it 50,000 times a second if you wanted to.
Some games poll multiple times in a frame to seed a random number generator.
The controller is just a 4021 8-bit serial-out shift register that the NES strobes then clocks several times under software control.
Sorry to cross-post, but I have a question about timing in general.
If sound engines are generally run within the NMI routines, along with the drawing subroutines and the controller handlers, wouldn't that implicate that the sound plays slower than if it was the only thing being handled?
How does the NES play songs at a constant rate even though there are multiple things being handled in the NMI routine?
Because the NMI is fired up at a constant rate of about 60 (50 for PAL systems) times per second, so unless occasionally your per-frame update of stuff (graphics updating, controller reading, sound) goes too long and gets past the duration of a frame(thus causing slow-downs) the sound should be updated at a constant rate.
Gilbert wrote:
so unless occasionally your per-frame update of stuff (graphics updating, controller reading, sound) goes too long and gets past the duration of a frame(thus causing slow-downs) the sound should be updated at a constant rate.
That said, I probably shouldn't have a lot of overhead in my update code, right?
The part that writes to VRAM must finish within about 2200 cycles. The rest of the NMI handler can take any of the remaining 27000-odd cycles; it only means that the "main" thread (the reset handler) won't run as long. In fact, some games run entirely in their NMI handler, and the reset handler falls into an endless JMP loop right after setting up the hardware and turning on NMIs. Super Mario Bros. is an example of a game that does this:
Code:
forever: jmp forever
tepples wrote:
The part that writes to VRAM must finish within about 2200 cycles. The rest of the NMI handler can take any of the remaining 27000-odd cycles; it only means that the "main" thread (the reset handler) won't run as long. In fact, some games run entirely in their NMI handler, and the reset handler falls into an endless JMP loop right after setting up the hardware and turning on NMIs. Super Mario Bros. is an example of a game that does this:
Code:
forever: jmp forever
How in the world does THAT work? It's crazy how a 2 MHz cpu can transfer almost 1000 bytes from RAM to VRAM and still be able to play music and sound effects at a ridiculous rate, read the controller, and still have time for other things.
Easy, they make the vblank interrupt last the entire frame... Remember the interrupt triggers when vblank starts and ends only when the code feels like it.
Thinking of it as a high-level thing is what's confusing. Really, NMI just forces the CPU to approximately do a PHP then JSR to your NMI handler. If you don't care about what's on the stack, you can just treat it like a JMP. So you're in a JMP loop that is broken out of by a JMP to your NMI handler. It does a frame of work, then waits in a loop. Hardly different than a loop that does a frame of work, then waits in a loop for the VBL to begin, then jumps back to the beginning (but of course there's no reliable way to poll the PPU directly for VBL, so this is out).
A simpler explanation is that, you have to make sure that critical operations that must be done within VBLANK (mainly graphical updates) be done within that period, i.e. perform such operations at the very beginning of the NMI routine and perform the less critical ones such as controller status reading, sound engine updates and game logic afterwards, so these less critical operations may be executed past the VBLANK time.
SMB1 style loop structure, in pseudocode:
Code:
def nmi_handler():
global updates_ready, missed_updates
if updates_ready:
copy_background_updates()
copy_oam_updates()
updates_ready = False
set_scroll_position()
# Everything before this point MUST complete in about 2270 cycles
update_sound()
# Omitted: If you have a top status bar, this is where you
# wait for sprite 0 hit and set scroll position again
if updates_ready:
# Prevent most of the logic from running if game logic runs
# overtime. This prevents a crash but creates slowdown.
updates_ready = False
missed_updates = 0
read_controller()
run_game_logic()
calculate_scroll_position()
prepare_background_updates()
prepare_oam_updates()
updates_ready = True
else:
# Use this value for debugging or to adjust movement speed
# if you anticipate slowdown in certain areas
missed_updates += 1