SeaGtGruff, on Thu Jul 14, 2011 3:33 AM, said:
Thomas Jentzsch, on Wed Jul 13, 2011 1:59 PM, said:
It seems like maybe to do this properly-- ideally, being able to step back an instruction at a time-- you'd need to save the status register after every instruction, not to mention the state of any RAM, the RIOT timer, all the CPU/TIA/RIOT registers, bank selections, input ports, collisions, etc.
I see what you mean about maybe just saving one snapshot of everything at the beginning of each frame, so you could at least back up one frame and start going forward again from there. But if you don't yet know what caused the error, could you rule out things like "the joystick was pressed in direction A at the wrong time"? If it were possible to save an actual cycle-by-cycle stream of all the data needed to truly "rewind" a program one cycle at a time, including rewinding any input, that would be awesome, but probably too resource-intensive-- unless you could perhaps save the stream to a "RAM disk" or memory block (rather than, say, writing it to the hard drive), but have a set length for how much you could save before the stream "wrapped around" and started to overwrite itself-- like you could always rewind up to a maximum of one or two frames, maybe depending on how much memory you've told Stella to allocate for your "virtual tape recorder" (or "virtual state recorder"?
) data stream.
I'm back! Well not yet really, it will be another week or two before I start coding again
WRT rewind, this has been a frequently requested feature, and one that I've been considering for some time. Your description here is exactly what Stella currently does. When entering the debugger, each step performed (whether by a single instruction, a frame, or a scanline) saves the entire state of the emulation in a memory-backed data stream. That is, it's never written to disk, and recycles when it reaches the end (so the oldest state is thrown away). Then, each time you do a rewind, you simply step back one in the state queue and do a state load. It's actually a very simple concept. The main problem right now is that it's only done when you enter the debugger, usually after
a bug has been triggered.
To have it work in the general case, you'd need to save states at each appropriate interval, or what I call the 'resolution' of the states. If you want to be able to step back a single instruction at a time, then obviously you need instruction-resolution. This one is the most intensive; it's basically a memory-based state save each time the PC is executed. Scanline and frame resolution follow the same idea, but are done less regularly. To be honest, I've never actually tried it, so my thoughts that it is intensive are just a gut feeling at this point. On a fast enough PC with enough memory, it might not even be an issue.
Note that this brings together several other ideas, and also some problems. For example, being able to rewind a frame at a time (or perhaps a second/60 frames) in the general case is what other emulators do when you rewind while playing a game. That is, you come to a hard spot in the game, rewind a little, and try it again. However, there are also some problems, particularly with viewing the contents of the TV screen after a rewind. This is because there's no actual framebuffer, so if we rewind and play from that point, at that instant there's nothing on the screen! The screen won't be drawn fully until at least one frame later, so if you're rewinding a single instruction at a time and then inspecting what that instruction would do when you step ahead, you won't necessarily see anything. BTW, this is also why I have difficulty implementing another feature; toggling TIA objects while the game is paused. You can change the object easily enough, but you won't necessarily see the change until the next
I get around this in the debugger by saving the TIA virtual framebuffer as part of the state file. I cheat in the general case (ie, a normal state save) by leaving off the TIA framebuffer, because I know that once the state is loaded, the emulation loop is running and it will quickly regenerate the display. I suspect that if you were to load a normal state file while in the debugger, you'd see what I mean. Since it doesn't contain the TIA framebuffer, the display would go blank for awhile.
Anyway, the reason I bring this up is that saving the TIA framebuffer adds perhaps 50KB to the state file, which would normally be 1KB or less. So there are potential performance issues, and almost definitely memory issues. More feedback and testing is required for this ...
FInally, I assume you haven't seen the Stella code, and if not, good job on independently coming up with the state load/save algorithm that Stella is using
EDIT: OK, now you've got me started
Another option I've considered is not saving the state at all, or at least not the full state every single time. If we could instead save a full state at some larger resolution (say each frame) and then save events (with timestamps), a rewind could theoretically be done at any resolution. That is, if you want to rewind to the previous frame, just back up and you're done. If you want to rewind to the previous instruction, back up to the previous frame and then play
until you reach the CPU cycle that you're looking for. The same is true for a rewinding by one scanline; back up the entire frame and then play until just before the scanline starts.
I didn't go with this idea for two reasons. First, saving the entire state each time in a brute-force fashion is much easier to understand and implement, and second, I wasn't entirely sure that it is deterministic (that is, if you rewind and play forward, will it always give the same results).
Now that I think of it, this would probably solve the TIA framebuffer issue too, since it would only be saved each frame, and not each instruction. And then pause could work too, since we could just rewind one frame, change the TIA objects, and then play ahead one frame. You'd be in the same place as before, but with the objects changed
Finally, the idea of logging events with timestamps is something I toyed with at one point. There's code in Stella save a stream of events (basically, the state of the controller pins in each frame), and then load that data. On load, it does a full state load, then updates the controller pins accordingly. This was going to be the basis for 'recording' a movie, except that the movie wouldn't be an actual video, but a file that one could open and let Stella play
it for you. It worked well enough in initial testing, but I abandoned it because of lack of time, as usual.