I'm working on a Lua script for FCEUX and I just realized, that there are some details, regarding the FrameAdvance() function, that I don't know.
First of all, what does the function exactly do? From what I understand it unpauses the emulator for 1/60 seconds, so that one frame can play out and then it pauses it again. Is that just it, or is there a bit more?
When, for example, you use the sound.get() function in between frames, does that access the state of the APU from the frame that was just played, or from the frame that is about to play, when using the FrameAdvance() function? Does any of the game code, executed during VBlank come into play?
I hope someone can tell me. It'll be very useful.
Thanks.
I don't know the exact details to answer your question, but when I investigated NES sound using FCEUX's lua, I copied the method the included scripts SoundDisplay.lua and SoundDisplay2.lua use -- they set up a function that gets called every frame using emu.registerafter.
While using those scripts, if I pause the emulation and step it a frame at a time, I can hear a new sound start and the corresponding display change at the same time, so that's good enough for me for now.
But if anyone else has more details about the exact timing positions of FCEUX's emu.frameadvance (and emu.registerafter, emu.registerbefore, and gui.register), I'd be interested to learn more.
Unless I'm mistaken, I believe it runs until the next NMI. i.e. when control returns to the lua script, the PPU is about to generate an NMI signal for a new frame.
(I could be wrong, but I don't know any other place that would be sensible to break.)
All accesses to memory or the APU, etc. should be consistent with this timing.
Well, after playing around with the various functions, including frameadvance(), it seems that they won't do good and I'll have to have the script pause the emulator manually. Basically, I need the script to execute code every time a VBlank has ended and the PPU is about to draw the screen again. Most games update their sound during VBlank and I need to catch the updates at the right time. Because frameadvance() seems to pause the emulator, when an NMI occurs, there is a one frame delay between when the game updates its sound and when the script reads the state of the APU. Check out
this thread for more information.
Many games will finish their sound update past the end of vblank. It's usually consistently timed from the start of NMI, but there's no constraints in there that require the APU to be operated within vblank, only the PPU stuff needs to finish by then.
I don't think there's a consistent thing you could do to trap the end of APU updates, it's going to be very different, game to game.
You could try to trap it at the RTI from NMI, and this would be good for games that do sound last in their NMI, but not good for any game that does its game logic or raster effects in the NMI routine, or games that do music outside of NMI. Optimally, you need to analyze each game and figure out where the sound routine ends; put a trap there.
Pragmatically, maybe just do your test 30 scanlines or so into the picture. You'd probably be early enough to avoid the first frame counter clock (1/4 frame), so the APU state should still be "fresh", and you'd probably be late enough that most music engines are done by then.
Well, at first I wanted to pause the emulator at the RTI, but considering all you've mentioned and that frameadvance() won't do much good anyway, and that the FCEUX.pause() function pauses the emulator until you manually unpause it, I've decided to just put an event listener for when the necessary writes happen to the APU. That way it wouldn't matter whether they're happening during VBlank or outside of it, the code will be executed immediately after the writes.