Interaction between @save_undo and bit 4 of Flags 2?

Some questions about ZMS 1.1:

  1. Per 6.1.4: “In Versions 5 and later, an interpreter unable to save the game state into internal memory (for ‘undo’ purposes) must clear bit 4 of ‘Flags 2’ in the header.” When exactly is this clearing of the bit supposed to occur?

  2. Per 6.1.2: “On a ‘restore’ or ‘undo’ (which restores a game saved into internal memory), the entire state of play is written back except that ‘Flags 2’ in the header is preserved. (This information includes whether the game is being transcribed to printer and whether a fixed-pitch font is being used.)” But per 6.1.2.2: “After a ‘restore’ or ‘undo’, an interpreter should reset the header values marked Rst in the header table of S 11. (It should not be assumed that the game was saved by the same interpreter.)” Bit 4 of Flags 2 is marked Rst in the header table chart in Section 11. Is this correct? If so, how is the “original” value to be written determined, i.e. is it the state of the flag at VM initialization, after Initialise(), at the time of @save_undo, or some other time?

  3. The implication of 6.1.4 is that the game should be allowed to set bit 4 of Flags 2 at will. Is the game allowed to try to clear that flag? If so, does any part of the standard specify the interpreter’s response?

  4. The opcode dictionary entry for @save_undo says: “If the interpreter is unable to provide this feature, it must return -1: otherwise it returns the save return value.” What is the meaning of “unable” in this context, i.e. temporarily unable due to resource constraints, permanently unable due to initialization parameters, both? Under what circumstances is a result of 0 preferable to a result of -1?

  5. Under what circumstances, if any, should an interpreter be modifying (either setting or clearing) bit 4 of Flags 2 aside from clearing it to signal that undo is not supported?

These are practical questions; I am trying to get a particular effect and am noticing some disagreement in behavior among interpreters.

If there is pre-existing discussion on these, please feel free to point me to it.

1 Like

I’ve struggled with some of these in the past, and there is a little bit of inconsistency in the standard as well. I’ll answer to the best of my knowledge:

I’ll use the terms “interpreter” and “game” as the term “program” is ambiguous.

  1. The bit should be cleared or set by the interpreter at startup and after a restore or restart to indicate the interpreter’s support for UNDO opcodes. Presumably the game will check it before trying to use them.

  2. There is some inconsistency here. Due to the possibility of a game being restored on an intepreter with different capabilities than the one on which it was saved, I think it is best to fully reset these flags on restores or restarts except for the transcript bit. My rough procedure for all of this during a restore or restart is this:

  • Capture current transcript status.
  • Load the game’s memory from the story or restore file.
  • Set all flags2 capability bits to match the interpreter’s capabilities (regardless of whther the game has it set or not, as they may have been cleared by a less capable interpreter and then saved).
  • Reset the transcript bit to match actual status.
  1. I believe the idea here is that the bit (as with the other flag bits indicating capabilities) should already be set in the story file, not actively changed by the game during runtime since this bit is not marked as changeable by the game. The interpreter clears (or sets: see #2 above) this to indicate to the game what it is capable of. While the game is likely physically capable of changing this bit at any time (pretty much any bit actually, despite what the standard says), its sole use is to indicate to the game what the interpreter can do, so there wouldn’t be much point to change it any other time.

  2. 0 for failure, -1 if the interpreter can’t. Presumably a game seeing a -1 will stop trying to use the UNDO opcodes. Use 0 to represent any transient failure, such as out of memory, etc.

  3. See #2. Change it on startup, restart, restore. That should be sufficient.

Thank you for the reply. Good point about the ambiguity of “program” – I’ve updated the original question accordingly.

Regarding the game setting bit 4 of Flags 2: I’m looking at the section titled “Inform Assembly Language” at the end of section 14, where it states in regard to extended opcodes that the purpose of flag “Fnn” is:

Set bit nn in Flags 2 (signalling to the interpreter that an unusual feature has been called for): the number is in decimal

and also the explanatory text for bit 4, which states its purpose as:

If set, game wants to use the UNDO opcodes

I recognize that the first quote comes from a discussion about “possible extensions to the Z-machine instruction set,” but note that on the preceding page it calls out that @set_colour sets Flags 2 bit 6, so it seems logical to expect that @save_undo sets bit 4 (though I don’t have any confirmation of this). The wording here (as the effect of an opcode!) strongly suggests that this “signal” is intended to be something that the game can send at runtime. Also the “wants to use” terminology can be taken as implying a communication of volition of the game’s (and by extension, the author’s) will about use of undo, which conceivably could change one or more times during execution.

Also, it’s not clear how the setting of this flag can even be accomplished at compile time, short of use of a hex editor post-compilation. (Is there an I6 compiler option for this that I’ve somehow missed?)

Regarding resetting this flag on restore or restart: Doing so will invariably (and, if set only at compile time, irrevocably) destroy any compile-time signal from the game that it wants to use undo capability. Is it the responsibility of the game to reset this flag on restore or restart to resend the signal? Is it the responsibility of the interpreter to note the setting at a specific point and keep it stored elsewhere (e.g. as part of a save game file)?

Regarding -1 meaning that the interpreter “can’t” support undo – I assume that you mean that it categorically can’t because it lacks that feature entirely. What about cases where the interpreter is configured to not use undo for the session through player volition, e.g. via frotz -u 0 (which indicates zero turns of undo capability available)? Should the interpreter pretend that it categorically can’t support undo and cause @save_undo to return -1, or should it just cause @save_undo to fail with result zero every time it is executed during the session?

I’m not very familiar with Inform. My tinkering is restricted to the z-machine itself.

My impression is that Inform sets the relevant flags in the header at compilation time (I could certainly be mistaken about this, but it’s my assumption). I certainly don’t believe that the execution of the opcode would set the bit.

So a story should come with all the bits it wants to use already set. An interpreter that can’t provide those capabilities should clear them, as I read the standard.

I might be rethinking the process I listed earlier, so bear with me. If you load a game and those bits are set, restoring a game where they have been cleared shouldn’t matter if you preserve the values your interpreter already set. It got the compile time information when the story was first loaded. The same would hold for any subsequent interpreter that loads a save.

So perhaps a better process would be:

  • Load the story memory.
  • Clear any capability flags you can’t support.
  • On restart or restore simply leave these flags as-is, i.e. don’t overwrite them with the story file (in the case of restart) or the save’s data (in the case of restore).
  • Optionally - Perhaps you want your interpreter to be able dynamically change it’s capabilities. Then I would re-load the original flags from the story and re-apply the interpreter’s capabilities.

The only issue I see with that process is if the story file doesn’t have all the intended capability bits set for any reason. In that case, it might be worth setting capability bits to match the interpreter regardless of the story file’s original state.

Edit: Another issue with the above procedure is it really makes no sense why bit 1 or 2 should survive a restart or restore. In fact, I would say bit 1 shouldn’t.

As for the UNDO, I’d use -1 for lack of capability, even selectively disabled capability. The result 0 is for unexpected failure.

The flags should be updated after restoring because it could be a different VM with different capabilities.

Edit: Until my last post, I would’ve completely agreed with what you said, but the more I think about it, the more I think it isn’t needed.

You have to load the story first, right? That’d mean the interpreter has access to the original flags at that time and can simply ignore any bits set or cleared during a restore. Or am I off base?

Maybe an example:

Interpeter A loads a game that wants to use sound. Sound is not supported so the bit is cleared by the interpreter. Game is then saved and the cleared bit is in the save file.

Interpreter B then loads the game. This interpreter supports sound, so the bit already set in the story is left alone. The save game from Interpreter A is then loaded. The current flags are preserved, and the sound flag stays enabled.

Ah, well whether you update the flags or just preserve them through a restore is an implementation detail. Updating them seems simpler to me.

Implementation detail - true.

Of course preserving naturally matches the behavior the transcript bit needs anyway.

But as I said bits 1 and 2 are weird. I really don’t know the correct behavior there.

Thinking out loud:

Bit 1 - The game forcing fixed-pitch seems like something that should be set to the story file value on a restart and the save file value on a restore, i.e. never preserved.

Bit 2 - Set by interpreter and cleared by game. I would guess that since restarting resets the display, it should be cleared, but restoring doesn’t reset the display, so maybe this bit’s behavior should be conditional?

To be clear, what I’m asking here is:

  1. Are there explicit definitions to serve as answers for these questions, based on the ZMS 1.1 specifications? Failing that, are there accepted or consensus definitions among those actively writing and maintaining Z-machine interpreters?
  2. If the answer is yes to the above, what are those definitions with respect to my original questions about Flags 2 bit 4 and the return values for the @save_undo opcode?

I’m asking from the perspective of someone writing a game, who is trying to anticipate the interpreter environment(s) on which it will be played.

Dannii makes a good point about the possibility of loading a save game from interpreter A into another interpreter B with different capabilities. From the functional perspective of the game, this is equivalent to saying that the interpreter is free to change its mind during execution about whether or not it supports undo capabilities. There is a relevant passage in the remarks at the end of section 6:

Given the existence of Quetzal, a portable saved file format, it is quite possible that after loading, the game may be running on a different interpreter to that on which the game started. As a result, it is strongly advisable for games to recheck any interpreter capabilities (eg Standard version, Unicode support, etc) after loading.

However, note that a preceding passage in the same section basically disclaims save game portability as a feature of the standard:

These issues are taken up in great detail in Martin Frost’s Quetzal standard for saved game files, created to allow different interpreters to exchange saved games. This Standard doesn’t require compliance with Quetzal, but interpreter writers are urged to consider it: it can only help authors if players can send them saved games where bugs seem to have appeared.

I do appreciate your responses, @Mike_G, as they are very informative. However, I’m still looking for concrete answers, and it’s not encouraging that you’ve already proposed two different possible models of interpreter response. The purpose of a standard is to create predictability from both sides of an interface, so shouldn’t it have definite answers here?

As a broader question, there seem to be real questions about the purpose(s) which are served by Flags 2 bit 4. Competing concepts include:

  1. How the flag is to be set by the game:
    1a. It’s to be set in the game by the compiler at compile time only, to signify that the game wishes to use undo capabilities at some point or always during execution.
    1b. It’s to be set by the game via opcode at any point during run time, to signify that it wishes to use undo capabilities from that point onward during execution. (And the game may attempt to send this signal more than once.)

  2. How the flag is to be cleared by the interpreter:
    2a. It’s to be cleared by the interpreter only at VM initialization, to indicate that it cannot or will not support undo capabilities at any point during that run of the game (regardless of future changes in interpreter or interpreter settings across play sessions).
    2b. It’s to be cleared by the interpreter at VM initialization and upon restoration of a saved game, to indicate that it cannot or will not support undo capabilities during that particular play session of the game.
    2c. It’s to be cleared by the interpreter frequently (i.e. at least per turn) during execution, to indicate that it cannot or will not support undo capabilities at that point during a particular play session of the game.

  3. How the flag is to be set by the interpreter:
    3a. It should never be set by the interpreter. (This looks like the best interpretation of what’s actually in the standard.)
    3b. It should be set by the interpreter at VM initialization, to signal to the game that it is willing and able to support undo capability through that run of the game (regardless of future changes in interpreter or interpreter settings across play sessions).
    3c. It should be set by the interpreter at VM initialization and upon restoration of a saved game, to indicate that it is willing and able to support undo capabilities during that particular play session of the game.

  4. How the flag is to be cleared by the game:
    4a. It’s never supposed to be cleared by the game.
    4b. It should be cleared at compile time to indicate that the game doesn’t care whether undo capabilities are available or not. (This seems supported by the standard as written.)
    4c. It should be cleared at any point during run time to signal to the interpreter that the game does not want undo capability to be present. (This isn’t supported by the standard, but it puts the game on equal footing with the interpreter about being able to change its mind during execution.)

It seems as though the bit is intended to serve more than one purpose: first as a signal from game to interpreter, second as a signal from interpreter to game. I haven’t found anything about timing requirements that will let the signal flow both ways unambiguously. It’s also complicated by the fact that, as written, each side is only empowered to say one specific thing in the process. The game says “I would like to use undo” and the interpreter (observing the bit as set at one or more unspecified points) either responds explicitly by clearing the bit to say “Too bad, you can’t.” or implicitly by leaving it set to say “OK, that will be fine.”

The implication is that, after a single initial exchange or at any point in a continuing conversation, the state of bit 4 can be read by the game as “Undo capability is available now.” That seems to be the usage intended by the standard, but the details about the nature of the conversation between game and interpreter to create this meaning aren’t clear.

To add more complexity, the result of @save_undo serves as a separate method of communication between game and interpreter about the interpreter’s willingness/capability to provide undo, and there doesn’t seem to be firm information in the standard about whether and how those signals from the interpreter side should correlate.

I assume that you meant that the interpreter should update the flags after restoring, along the lines of 3c above – is that correct? What, if any, is the game’s duty here? Is it supposed to attempt to signal its wishes in the matter somehow at restoration, or is the game’s intent only to be read from the setting in the original story file? Should the interpreter set the bit even if it’s unset in the original story file?

All of my responses (and indeed what I have implemeneted in my interpreters over the years) have been under the assumption that the game never sets these bits during runtime and are statically set in the story file. I think that issue of static vs dynamic goes to the heart of your questions. The static view is much bolstered by the fact the standard lists all flag2 bits except 0,1, and 2 as unchangeable by the game.

My procedures - as pointed out by Dannii, are essentially equivalent approaches. The interpreter either clears bits at startup and thereafter preserves on restores, or clears/sets as needed on each restore.

If the static case is true, which I believe it is, then the game need only check at startup and after restores. The interpreter isn’t going to change inbetween turns without a restore taking place.

With regard to Quetzal: Most interpreters support it so although it is not a part of the z-machine standard, save game portability is a common thing among z-machine implementations.

OK, yes, perfect! There is explicit information in the standard:

“Dyn” means that the byte or bit may legally be changed by the game during play; “Int” means that the interpreter may change it; “Rst” means that the interpreter must set it correctly after loading the game, after a restore or after a restart.

Flags 2 Bit 4 is not marked Dyn. By implication, an attempt by the game to change it at run time is illegal and should not be done. Thank you – that is a definite anchor.

So this implies that what the remarks cited above concerning what Inform does with @set_colour is illegal behavior, then? (Or does that passage mean that the compiler notes at compile time that @set_colour was used, and statically sets bit 4 in the header accordingly when generating the story file?)

Although a number of questions are eliminated by that clarification, there is still:

  • Should the interpreter ever be setting bit 4? (The standard implies no, unless there’s something else that I’ve missed.)(EDIT: No, actually, it clearly says yes because that bit is marked Int, so forget this question.)(EDIT 2: On third thought, the Dyn indicator means that the interpreter can change the flag at run time, but the other parts indicate that it should only ever change the flag by clearing it which could be the meaning of “setting it correctly” in the above passage, so don’t forget this question after all.)
  • Can the game rely on the status of bit 4 as an indicator of current undo capability, or should it rely solely on responses from @save_undo?
  • Should the game be trying to reconcile conflicting signals from bit 4 and @save_undo results? If so, how can it do so?

As a first pass, based on your input above, it would seem that the way for the game to read the situation is:

[EDIT: first draft table removed, see revised draft in post below]

Is that correct?

I believe static setting is the case, but someone with more knowledge of Inform than me will have to confirm.

With regard to the table of flags/UNDO results:
I never really understood why the -1 return value was deemed useful, let alone necessary, since it would seem to answer the same question as the flag. Hopefully all interpreters produce consistent results here. I would think if the flag is 0, the game shouldn’t be using the UNDO opcodes and would never see a -1 result.

An interpreter that has 1 for the flag, but returns -1 on the instruction would have to be considered broken, in my mind.

Likewise an interpreter that has 0 for the flag and returns anything other than -1 should also be considered broken, although succeeding when you claim you can’t is perhaps less bothersome than failing when you claim you can. Also, the game should be considered broken too, for not respecting the flag in the first place.

Bear in mind that Graham Nelson’s Z-machine standard is based a lot on reverse-engineering and conjecture; there are conflicts with Infocom’s own standards, and, as you’re discovering, some odd cases.

Infocom’s on YZip documentation has this to say about the flag in the header:

Bit #4 (%FUNDO) should be set at compile time by games which will try to
use ISAVE and IRESTORE. The interpreter should examine this bit in
marginal memory size cases to determine how many swapping pages to
allocate.

It does not say the interpreter clears the bit; it does indicate some other flags (such as the sound bit) are cleared if the interpreter can’t support it, so it seems plain that Infocom meant this bit not to be cleared, but only as a hint to the interpreter that it may need to allocate extra space for undo.

Modern interpreters probably follow Nelson’s standard, and clear the bit, but any reasonable interpreter will properly return a failure value of one kind or another from @save_undo, so I think it’s prudent for a game to simply try the call. I don’t think there’s any practical difference between testing the flag and checking the return value of @save_undo.

I did a cursory check for Infocom’s usage of @save_undo in their games, and they mostly just check whether 2 is returned, ignoring other values. Beyond Zork does look like it compares the result to 0 at one point, considering 0 to mean “no undo available”; but that just indicates that it doesn’t take into account that -1 might be returned.

In short, I’d recommend just calling @save_undo, treating 1/2 as success and any other value as failure. Don’t bother with the header flag.

An interesting question might be whether any original Infocom interpreters ever return -1 for the UNDO opcodes to indicate lack of support. Perhaps if their interpreters didn’t clear the flag, then the -1 return value would make sense from a historical perspective.

Yes, that’s correct. The documentation may be a bit ambiguous, but it’s just noting that if you use a feature which has a corresponding flag, that flag will be set when the story is compiled, i.e. “baked in” to the resulting file. It doesn’t update the flag at runtime.

1 Like

I thought that to be the case. I first learned much of this so long ago that some of the things I believe I have trouble tracing back to actual evidence. :grinning:

To make this more clear, Infocom’s approach appears to be to ignore both success and failure of @save_undo, which is reasonable: if it succeeds, you want to be quiet. If it fails, you also want to be quiet, since it would run every turn; but you’ll learn about the failure when you try to undo.

@cas: I very much appreciate any citations from primary Infocom documentation; they are on point in terms of original intent at Infocom but (regrettably) to some extent moot with respect to my questions because the questions are ultimately about how a game should deal with the widely-used interpreters as they exist today. (Your quote on %FUNDO certainly has implications in that regard, but only if the interpreter maintainers change their code in response.)

Do you have any input on the three remaining questions (especially on whether or not interpreters should ever set bit 4) and the hazy spots on the chart of game-observable bit 4 values/@save_undo results that I laid out above?

@Dannii: Same questions to you, since you chimed in briefly earlier and also are an interpreter author.

@Mike_G: I regret that I don’t know which interpreter is yours. Will you tell me?

The majority of the ones I’ve written have never been publicly released. I write them for fun and education when learning a new programming language.

I had one publicly available many years ago called Grue, written in C#. It died when Codeplex did. I am working on a new z-machine library written in Rust to be used by a separate frontend. But it isn’t at a point I want to make it public. Honestly I’ve been distracted with other things lately and haven’t been working on it like I should.