Updating the Z-Machine Standard Documents

I think version 4 is underspecified with regard to sound_effect.

The standard makes clear what is expected for V3 and V5, but leaves V4 unmentioned.
The sound_effect opcode works in V4, e.g. in AMFV, but only bleeps are used and no samples.

I see 4 possibilities for sound_effect behavior in V4:

  1. It is completely undefined for anything beyond bleeps. (This is logical, but it bugs me)
  2. It follows V3 behavior - externally defined looping, and no interrupts. (Yuck)
  3. Hybrid behavior - No externally defined looping which allows repeats via operand like V5, but still no interrupts. (Meh)
  4. Full V5 behavior. (Too much?)

I agree with this, technically the standard implies that version 4 has no capacity for sound. I think, logically, V4 should be the same as V3, even though the V3 method is ugly and weird.

1 Like

Given a choice Iā€™d prefer V4 be limited to bleeps vs. use the V3 model, provided no post-Infocom V4 games use samples.

It may not make sense as a chronological evolution (thatā€™s the part that bugs me), but we know sampled sound was essentially backported to The Lurking Horror from V5 and Infocomā€™s V4 terps did not support anything except bleeps.

1 Like

Looking back at my current code: I am allowing V4 to play sounds and set the count via the high byte of the third operand, but not allowing V5 style interrupts. Thatā€™s equivalent to #3 - hybrid behavior.

Options 2, 3, and 4 are all possible in my existing code without adding additional version checks - just altering the existing ones. Option 1 would require new checksā€¦sigh.

I think option 2 is the simplest to explain in the standard, technically easy for interpreters to handle, and what most game writers would expect from a version between 3 and 5.

Option 2 or option 4 are probably what Infocom would have done if theyā€™d ever decided to hack sound back into an ezip game.

I tried to check what Frotz does and from what I can tell, it hard-codes the loops for Lurking Horror and otherwise just lets any version game use V5 behaviour. So that wasnā€™t very helpful.

1 Like

Heh, I just noticed that Blorb specifies that the looping chunk not be used in games which are not V3 Z-machine games. So option 2 would require a small update to the Blorb standard. :sweat_smile:

Another area where the standard could use some clarification is regarding sound volume.

Section 9 has:
Sound effects (other than bleeps) can be played at any volume level from 1 to 8 (8 being loudest of these). The volume level -1 should be implemented as ā€œloudest possibleā€.

while Section 15 has:
The low byte of volume holds the volume level, the high byte the number of repeats. (The value 255 means ā€œloudest possibleā€ and ā€œforeverā€ respectively.)

Obviously in a byte 255 unsigned = -1 signed, but the reader is left to figure that out. Also, it isnā€™t clearly explained if volume 8 should be equal to 255 or not, and what to do with all the numbers from 9 to 254.

Interestingly, Infocom described volume as being from 0 minimum to 8 maximum. The value 255 (or -1 if you prefer) used the MIDI volume (from file?). Count was similarly weird: 0 used the MIDI count (from file?), while 1-254 were finite and 255 was infinite. That would fit with the count byte always being zero in TLH because it would result in using the counts from the files.

What I ended up doing is treat all volumes from 8 to 255 as equal and zero as equal to one.

As long as weā€™re on the topic of soundā€¦

Zen Speaks! was brought up in a related thread as a non-Infocom game that uses sound. I decided to try it with Bocfel/Gargoyle, and as soon as you command the door to open, it hangs the interpreter.

The code is effectively doing this:

Global sound_finished = 0;

[SoundRoutine;
    sound_finished = 1;
];

[Main;
    @sound_effect 13 2 $01ff SoundRoutine;

    while (~~sound_finished) {
    }
];

The problem in Bocfel is that it uses Glk, and the ā€œend of soundā€ event is only delivered whenever events are explicitly requested, which, in general, is whenever there is input. During that while loop, thereā€™s no input, so no events are handled, so even though the sound effect stops, the routineā€™s never called, and the loop never ends.

I played around with calling glk_select_poll() periodically but itā€™s so incredibly heavyweight, relatively speaking, that itā€™s just not feasible; and Iā€™d really prefer not to jam things up with code like ā€œif a sound routine is expected and there have been at least 10000 instructions, call glk_select_poll(), otherwise reset the instruction counterā€.

This code is sort of obliquely a violation of the standard, anyway:

The intention is that this routine may implement effects such as fading in and out, by replaying the sound effect at a different volume. (A game should not place any important code in such a routine.)

I could argue that even something as simple as this routine is ā€œimportantā€ since itā€™s required in order to move the game forward.

As such, if the standard does ever get around to being cleaned up, Iā€™d recommend stating that interpreters should provide a best effort to ensure the routine is called, but thereā€™s no guarantee it ever will be; perhaps with a remark that itā€™s possible the routine wonā€™t be called until the next input instruction. Maybe thatā€™s too specific to my particular interpreter, but itā€™s not unlikely that itā€™d affect other Glk-based interpreters that support sound: Nitfol, for example, appears to have the same behavior.

2 Likes

I donā€™t know whether you should even expect glk_select_poll() to work. It was a half-assed idea to begin with and never got, er, re-assed.

1 Like

Thanks for pointing out this demo. It doesnā€™t hang my interpreter. :partying_face:

Yeah, I think the intent was the game would continue to be playable even if the interrupt didnā€™t happen. Still, I think it is desirable to be able to react to sounds between player inputs.

Okay, so Iā€™ve gone through this thread and added issues to the issue tracker on github for everything that has come up so far. Iā€™ll try to keep this updated. Each issue has a comment linking back to the relevant post (or posts) here.

6 Likes

According to Stefan Jokischā€™s information about Infocomā€™s sound format (https://ifarchive.org/if-archive/infocom/info/sound_format.txt), Infocomā€™s v4 Amiga interpreter supports sound, which is a strong argument in favour of some form of sound support for v4 in the standard. I will try to investigate further.

1 Like

Youā€™re right. Iā€™d missed this.

Looks like it is implemented with four operands like this:
sound_effect SOUND ACTION VOLUME COUNT

I will try to do some testing on an amiga emulator, but I havenā€™t done that in a long time, so might take some time.

1 Like

For what itā€™s worth the Infocom EZIP spec documents the SOUND opcode, with bleep-only support.

And the Infocom XZIP spec says:

In XZIP (and the Amiga version of ZIP), a new sound
specification exists. SOUND takes the same sound-identifier
argument as before, but adds a sound-operation argument as
well. Currently, there are only three operations defined:

op	meaning
1	initialize specified sound
2	start specified sound
3	stop specified sound
4	clean up buffers from specified sound

So the Amiga ZIP interpreter had slightly more robust sound support than others, even if no released V3 games ever used it. Probably a forward-looking development that made its way ā€œofficiallyā€ into later versions.

At this point it looks like sound was sort of half-specified vs what we actually see in Infocom games.

This matches with Infocomā€™s YZIP documentation, which is of course at odds with Graham Nelsonā€™s standard, and at odds with Sherlock, given those have the combined volume/repeats plus routine.

Maybe this is already well-known, but it seems that the sound opcode was modified after this version of the YZIP documentation was produced, probably to accommodate the need for fading sounds in Sherlock. Though that is kind of upended by the fact that the sound-enabled Sherlockā€™s serial is 880127, and the YZIP spec we have is dated November 30, 1988.

ETA: I actually never looked carefully before, but there are differing dates in the YZIP docs. The PDF we have says ā€œ3/23/89ā€ while the text file has ā€œ11/30/88ā€, but then it also has ā€œ1/13/89ā€. Well, no matter, theyā€™re all about a year after Sherlock in any case.

Finally, I just tested the V4 DOS interpreter for Trinity (from Masterpieces), and it supports bleeps.

2 Likes

Iā€™ve noticed that in the yzip docs as well - no interrupts and repeats specified in the fourth operand. I think what isnā€™t clear is the timing vs. features in Sherlock, as youā€™ve mentioned.

V4 definitely supports bleeps, in Trinity, Bureaucracy, and AMFV but it is less clear what else might have been supported in the various interpreters, even if it went unused.

On a different note, Z-Machine screen units. The standard says that v5 and later games can implement screen units as pixels or characters (or anything they like, technically).

8.4.2

Screen dimensions are measured in notional "units". In Versions 1 to 4, one 
unit is simply the height or width of one character. In Version 5 and later, 
the interpreter is free to implement units as anything from character sizes 
down to individual pixels. 

The remarks recommend that v5 (and by implication v7/v8) interpreters always use character size for units.

It's recommended that a Version 5 interpreter always use units to 
correspond to characters: that is, characters occupy 1x1 units. 'Beyond Zork' 
was written in the expectation that it could be using either 1x1 or 8x8, and 
contains correct code to calculate screen positions whatever units are used. 
(Infocom's Version 5 interpreter for MSDOS could either run in a text mode, 
1x1, or a graphics mode, 8x8.) However, the German translation of 'Zork I' 
contains incorrect code to calculate screen positions unless 1x1 units are 
used. 

As far as Iā€™m aware, most if not all post-Infocom (non-v6) games assume that units are characters which an interpreter that is technically Standard but uses pixels for units sizes will break a whole of Inform produced games.

None of this is a major problem, because interpreters donā€™t do that. But Iā€™d like to fix it anyway.

I believe the ā€˜graphicsā€™ bit in Flags 2 was used in Beyond Zork (some versions at least) to indicate that the game wanted to use a pixel display, in order to display the fancy font. If the Standard was edited to say that screen units must be characters unless this bit is set (by the game), I believe this would fix the issue.

1 Like

There is correction needed for the output_stream opcode.
In section 14, the table for output_stream is incorrect:

VAR:243 13
3 output_stream number
5 output_stream number table
6 output_stream number table width

It should read:

VAR:243 13
3 output_stream number
3 output_stream number table
6 output_stream number table width

Almost a dozen of Infocomā€™s V3 games use output_stream to manipulate stream 4. In fact manipulating stream 4 is the only usage of output_stream in any Infocom V3 game. A number of Infocomā€™s V4 games manipulate all four streams via output_stream.

It is tempting to blame Standard 1.1 as there was a time that the Standard 1.1. proposal had the following (incorrect) text:

Section 7.1.2 is incorrect - output streams 3 and 4 are present only in
Version 5 and later.

ā€¦but the mistake in section 14 predates 1.1 and in fact dates back at least as far as Standard 0.2.

One could possibly argue that only control of stream 4 is supported in V3, and the others were added in V4, but then Section 7 would need changing:

7.1.2

Versions 3 and later supply these and two other output streams, numbered 3 (Z-machine memory) and 4 (a script file of the playerā€™s whole commands and of individual keypresses as read by read_char).

Regardless, claiming that any stream requires V5 is incorrect.

Do Infocomā€™s Z3 interpreters support the table parameter?

Iā€™m not certain, but their lzip/ezip (Z4) interpreters definitely do. The table parameter is required for all usages of stream 3, and both games and interpreters for V4 use it.

No Infocom V3 games utilize stream 3 (they only use stream 4), so itā€™s harder to tell what was supported in their interpreters. Thatā€™s why I said you could make the argument that it requires V4, but it would be odd to require V3 support stream 4 and not stream 3. So it may be V3 or V4, but it definitely isnā€™t V5.

The notes for the screen model states

Due to a bug or an oversight, the V6 story files for all 
interpreters use buffer_mode once: to remove buffering while 
printing "Please wait..."

In fact, the games print ā€œ[Please be patientā€¦ā€.

Not a major issue as it has no impact on any interpreter behaviour, but it did cause me difficulty last night when looking for this in the source code.

3 Likes