VOGONS


Reply 20 of 33, by Bisqwit

User metadata
Rank Newbie
Rank
Newbie
wd wrote:

For pmode/paging games you should try running them using the
dynamic core, as it avoids recursion for (most) pagefaults.

I'm not sure, but I think recursion is not a big problem. I changed Normal_Loop() such that it only invokes GFX_Events() (and along, MovieTick()) in the outermost recursion level of Normal_Loop(), and hence, savestates can also only occur at the outermost level; no recursion info needs to be saved.
Of course, if this limitation is lifted, then recursion needs to be accounted for.

x6425081281317.png

Reply 21 of 33, by wd

User metadata
Rank DOSBox Author
Rank
DOSBox Author

Yes but this way you can't save a state when something is in a 2nd shell,
in a keyboard input loop (int16) or a fullcore-handled pagefault.
But should be irrelevant for the problems you have with descent, right.

Reply 22 of 33, by Bisqwit

User metadata
Rank Newbie
Rank
Newbie
wd wrote:

Yes but this way you can't save a state when something is in a 2nd shell,
in a keyboard input loop (int16) or a fullcore-handled pagefault.
But should be irrelevant for the problems you have with descent, right.

Ah.. yes. Well, the aspect of "frame" is subject to debate. (I.e. what are the events where savestates can be made and loaded at.)
Currently I have defined it as "every N nonrecursed iterations of Normal_Loop()" where N is adjustable.
If it were defined as "any time user presses a key" or "every time the display card experiences vrefresh", things would be different.

x6425081281317.png

Reply 23 of 33, by Qbix

User metadata
Rank DOSBox Author
Rank
DOSBox Author

there is some time(null) localtime stuff in dos.cpp as well. perharps that has influence on the starcon2 stuff

Water flows down the stream
How to ask questions the smart way!

Reply 24 of 33, by frobme

User metadata
Rank Member
Rank
Member

Incidentally, the way we made save states endian-safe in Atari800 code base was to create serialization functions for all the fundamental data types (anything larger than a byte), which involved simply saving the low byte, shifting, saving again, etc. Since shifts are endian agnostic it works fine. You have to agree on a size for some types that may differ, like int - a natural choice is 4 - and if the native platform has a wider representation than that you have to save off the sign bit. When you read it in just read byte1, byte2, byte3, byte4 and value = (byte4 << 24) | (byte3 << 16) | (byte2 << 😎 | byte1 as an example.

You don't have to do this for general memory of course in the emulated machine, as that's all emulated and should be the same regardless of architecture, you just have to do it for all of your native variables - pointers, counters and the like - that exist in the host emulator.

It's cumbersome but completely doable. Helps when you are C++ as you have a lot more robust serialization support in the language, but it's doable even in C (which is what Atari800 is).

Reply 26 of 33, by Bisqwit

User metadata
Rank Newbie
Rank
Newbie
frobme wrote:

simply saving the low byte, shifting, saving again, etc.

True, this is no secret knowledge. However, the labour in saving every variable and every struct member individually is several magnitudes larger than saving each struct as a blob. (And also, pointers still need to be handled specially.)

Qbix: I already took care of that, and also, sc2 does not desync for me. Vasyl is yet to respond whether my instruction to use the included .conf file was of any help.

x6425081281317.png

Reply 27 of 33, by vasyl

User metadata
Rank Oldbie
Rank
Oldbie

I do use the included .conf. This is all very odd. There must be some factor that we are missing. BTW, I haven't seen any reports from anybody actually trying to run the recorded file. Looks like most people on TASVideos just played the AVI. I've checked the AVI and the star coordinates are very different from what I am getting. The oddest part is that I am getting the same (wrong) coordinates on very different systems: one is A64 with a lot of RAM, ATI video, and XP Pro, the other is old laptop with Centrino, embedded video, and XP Home. I was meaning to test on some more systems this morning but forgot to grab the USB stick from my desk when leaving home.

Reply 28 of 33, by frobme

User metadata
Rank Member
Rank
Member
Bisqwit wrote:
frobme wrote:

simply saving the low byte, shifting, saving again, etc.

True, this is no secret knowledge. However, the labour in saving every variable and every struct member individually is several magnitudes larger than saving each struct as a blob. (And also, pointers still need to be handled specially.)

Shrug, that's why C++ provided templates!

Struct packing is arbitrary and undefined in the language..if you don't decompose it for serialization, you do run the risk of a compiler a year from now not understanding what the heck you are doing. Most compilers are going to let you redefine the packing method, but there's no guarantee that will exist on all platforms. It's not a high probability failure, just saying.

Pointers are messy, but what we did with Atari800 was make sure that pointers were saved as an offset into the referenced emulated memory - and then, in a couple of edge cases, made sure we were saving out locally hosted memory and reference an offset to that (or make sure a table lookup or what not could be re-created at load time). It was nit pick code but surprisingly easy once we realized almost all our pointers referenced some location in the emulated memory area, which we were saving out already, woot!

-Frob

Reply 29 of 33, by wd

User metadata
Rank DOSBox Author
Rank
DOSBox Author

If it were defined as "any time user presses a key" or "every time
the display card experiences vrefresh", things would be different.

If something uses a 2nd shell you won't reach the outer recursion depth
for a very long time. But as already said that's nothing to bother at the
moment, yet that's the reason why savestates haven't been considered yet.

Reply 30 of 33, by Bisqwit

User metadata
Rank Newbie
Rank
Newbie
frobme wrote:

Shrug, that's why C++ provided templates!

I don't C++ templates provide any easy way to serialize/unserialize arbitrary structs.

frobme wrote:

Pointers are messy, but what we did with Atari800 was make sure that pointers were saved as an offset into the referenced emulated memory

How about pointers to the emulator's functions, pointers to consequential linked list members, etc? 😀

x6425081281317.png

Reply 32 of 33, by Qbix

User metadata
Rank DOSBox Author
Rank
DOSBox Author

It's not entirely necisairy to save the function addresses in the callback stuff. At least not when the machine would be rebuild using special constructors.

Nevertheless naming all callbacks isn't bad in itself (a lot are allready named as well) because it helps debugging.

Water flows down the stream
How to ask questions the smart way!

Reply 33 of 33, by frobme

User metadata
Rank Member
Rank
Member

I don't C++ templates provide any easy way to serialize/unserialize arbitrary structs.

In that you don't have to explicitly type each saved data element. foo.foo and foo.bar can both be passed into templated serialize_thing() - providing there is a template for each atomic data type, of course.

How about pointers to the emulator's functions, pointers to consequential linked list members, etc? 😀

As I said, in cases in which you reference offsets into local hosted memory tables that can't be rebuilt directly from the emulated memory dump, you'd have to save the table out and then the offset within that table.

In any case, I'm happy to see somebody working on saved states, an oft requested feature. I had fiddled with the start of it myself around december time frame, but work intervened and ate all my home programming time.

-frob