After seeing a random Facebook comment from a friend, I spent quite a few hours this weekend working on my NES emulator to get the game “Low G Man” working. Turns out the issue was related to the fact that an NMI (Non-maskable interrupt) can occur mid-cycle, and that particular game waits around for the VBLANK (which is what generates the NMI) to occur (other games do this as well). Unfortunately, because my emulator doesn’t really support intra-instruction events (the emulator is single-threaded), the loop waiting for the VBLANK would never see it.
Essentially what I think was happening was
; wait for VBLANK
(let PPU run... VBLANK occurs... causing NMI)
(NMI interrupt handler runs... which reads $2002, clearing it)
LDA $2002; this now doesn't see the VBLANK because the NMI handler cleared it
I added in a modification that the PPU (or anyone) could tell the CPU “hey, this NMI actually occurred a litte later than you think”, allowing for one extra instruction to execute. Which, in this case, is enough to get it out of the infinite loop.
So, effectively it now does
(notify PPU to do work, generating VBLANK + NMI)
LDA $2002; see the VBLANK
(NMI handler runs, but the LDA has already had a chance to run)
; yay, outside loop!
I had previously done a hack of setting the “sign” flag (which is what the BNE is actually looking for, because it is that highest bit) in the CPU NMI callback, based on a recommendation of something I found. But this felt like a horrible hack. I’m not certain that the new way isn’t a hack as well, but I think it is at least more “realistic”.
On another note, I’ve put the emulator into github:
I couldn’t think of a name (JNES is already taken), so I just did Quay’s Java NES emulator (qjnes). Note that this is still technically both a C64 as well as an NES emulator.
If you do happen to download it, you can fire it up with ant:
ant nes -Drom=
While you can play a lot of games with it (Super Mario Brothers 3 plays well, Kirby’s Dreamland played well last time I check)… there is no sound and many glitches (it is remarkably how ‘perfect’ some games require the system to be). This is not the emulator you want to get if you actually want it for “fun” (Nestopia and the likes are for that). It was purely so I could “see if I could”.
It doesn’t do anything cool like bytecode manipulation and JIT compiling… because it doesn’t need to on a modern computer. I finally added code to sleep, because on my Macbook PRO it runs well over full speed, so I now try to keep it around 60fps.