§42   Devices and opcodes

This section covers the only opcodes which designers are likely to have occasional need of: those which drive powerful and otherwise inaccessible features of the Z-machine's “hardware”, such as sound, graphics, menus and the mouse. There's no need to be fluent in assembly language to use these opcodes, which work just as well if used as incantations from a unfamiliar tongue.

WARNING
Some of these incantations may not work well if a story file is played on old interpreters which do not adhere to the Z-Machine Standard. Standard interpreters are very widely available, but if seriously worried you can test in an Initialise routine whether your game is running on a good interpreter, as in the following code.

if (standard_interpreter == 0) {
    print "This game must be played on an interpreter obeying the
           Z-Machine Standard.^";
    @quit;
}

The library variable standard_interpreter holds the version number of the standard obeyed, with the upper byte holding the major and the lower byte the minor version number, or else zero if the interpreter isn't standard-compliant. Thus $002 means 0.2 and $100 means 1.0. Any standard interpreter will carry out the opcodes in this chapter correctly, or else provide fair warning that they cannot. (For instance, an interpreter running on a palm-top personal organiser without a loudspeaker cannot provide sound effects.) Here is how to tell whether a standard interpreter can or can't provide the feature you need.

FeatureVersionsAvailable if
auxiliary files5,6,8(true)
coloured text5,6,8((0->1) & 1 ~= 0)
input streams5,6,8(true)
menus6(($10-->0) & 256 ~= 0)
mouse5,6(($10-->0) & 32 ~= 0)
output streams5,6,8(true)
pictures6(($10-->0) & 8 ~= 0)
sounds5,6,8(($10-->0) & 128 ~= 0)
throw/catch stack frames5,6,8(true)
timed keyboard interrupts5,6,8((0->1) & 128 ~= 0)

For instance, if coloured text is essential (for instance if red and black letters have to look different because it's a vital clue to some puzzle), you may want to add a test like the following to your Initialise routine:

if ((0->1) & 1 == 0)
    print "*** This game is best appreciated on an interpreter
           capable of displaying colours, unlike the present
           one. Proceed at your own risk! ***^";

· · · · ·

Text flows in and out of the Z-machine continuously: the player's commands flow in, responses flow out. Commands can come in from two different “input streams”, only one of which is selected at any given time: stream 0 is the keyboard and stream 1 is a file on the host computer. The stream is selected with:

@input_stream number

The Inform debugging verb “replay” basically does no more than switch input to stream 1.

There are four output streams for text, numbered 1 to 4. These are: (1) the screen, (2) the transcript file, (3) an array in memory and (4) a file of commands on the host computer. These can be active in any combination, except that at all times either stream 1 or stream 3 is active and not both. Inform uses stream 3 when the message print_to_array is sent to a string, and streams 2 and 4 in response to commands typed by the player: “script on” switches stream 2 on, “script on” switches it off; “recording on” and “off” switch stream 4 on and off. The relevant opcode is:

@output_stream number arr

If number is 0 this does nothing. +n switches stream n on, -n switches it off. The arr operand is omitted except for stream 3, when it's a table array holding the text printed: that is, arr-->0 contains the number of characters printed and the text printed is stored as ZSCII characters in arr->2, arr->3, …

As the designer, you cannot choose the filename of the file of commands used by input stream 1 or output stream 4. Whoever is playing the story file will choose this: perhaps after being prompted by the interpreter, perhaps through a configuration setting on that interpreter.

▲▲ EXERCISE 122
Implement an Inform version of the standard ‘C’ routine printf, taking the form

printf(format, arg1, ...)

to print out the format string but with escape sequences like %d replaced by the arguments (printed in various ways). For example,

printf("The score is %e out of %e.", score, MAX_SCORE);

should print something like “The score is five out of ten.”

In Version 6 story files, only, @output_stream can take an optional third operand when output stream 3 is being enabled. That is:

@output_stream 3 arr width

If width is positive, the text streamed into the array will be word-wrapped as if it were on a screen width characters wide; if width is negative, then as if on a screen -width pixels wide. The text going into arr is in the form of a sequence of lines, each consisting of a word containing the number of characters and then the ZSCII characters themselves in bytes. The sequence of lines finishes with a zero word. Such an array is exactly what is printed out by the opcode @print_form arr.

· · · · ·

The Z-machine has two kinds of “screen model”, or specification for what can and can't be done to the contents of the screen. Version 6 has an advanced graphical model, whereas other versions have much simpler textual arrangements. Early versions of the Z-machine are generally less capable here, so this section will document only the Version 5 and Version 6 models. (Versions 7 and 8 have the same model as Version 5.)

The version 5 screen model. The screen is divided into an upper window, normally used for a status line and sometimes also for quotations or menus, and a lower window, used for ordinary text. At any given time the upper window has a height H, which is a whole number of lines: and H can be zero, making the upper window invisible. (The story file can vary H from time to time and many do.) When text in the upper and lower windows occupy the same screen area, it's the upper window text that's visible. This often happens when quotation boxes are displayed.

@split_window H

Splits off an upper-level window of the given number of lines H in height from the main screen. Be warned that the upper window doesn't scroll, so you need to make H large enough for all the text you need to fit at once.

@set_window window

Selects which window text is to be printed into: (0) the lower one or (1) the upper one. Printing on the upper window overlies printing on the lower, is always done in a fixed-pitch font and does not appear in a printed transcript of the game.

@set_cursor line column

Places the cursor inside the upper window, where (1, 1) is the top left character.

@buffer_mode flag

This turns on (flag==true) or off (flag==false) word-breaking for the current window: that is, the practice of printing new-lines only at the ends of words, so that text is neatly formatted.

@erase_window window

Blanks out window 0 (lower), window 1 (upper) or the whole screen (if window=-1).

Using fixed-pitch measurements, the screen has dimensions X characters across by Y characters down, where X and Y are stored in bytes $21 and $20 of the header respectively. It's sometimes useful to know this when formatting tables:

print "My screen has ", 0->$20, " rows and ", 0->$21, " columns.^";

Be warned: it might be 80 × 210 or then again it might be 7 × 40. Text printing has a given foreground and background colour at all times. The standard stock of colours is:

0current colour5yellow
1default colour6blue
2black7magenta
3red8cyan
4green9white

@set_colour foreground background

If coloured text is available, this opcode sets text to be foreground against background. (But bear in mind that not all interpreters can display coloured text, and not all players enjoy reading it.) Even in a monochrome game, text can be set to print in “reverse colours”: background on foreground rather than vice versa. Status lines are almost always printed in reverse-colour, but this is only a convention and is not required by the Z-machine. Reverse is one of five possible text styles: roman, bold, underline (which many interpreters will render with italic), reverse and fixed-pitch. (Inform's style statement chooses between these.)

EXERCISE 123
Design a title page for ‘Ruins’, displaying a more or less apposite quotation and waiting for a key to be pressed. (For this last part, see below.)

EXERCISE 124
Change the status line so that it has the usual score/moves appearance except when a variable invisible_status is set to true, when it's invisible.

EXERCISE 125
Alter the ‘Advent’ example game to display the number of treasures found instead of the score and turns on the status line.

EXERCISE 126
(From code by Joachim Baumann.) Put a compass rose on the status line, displaying the directions in which the room can be left.

▲▲ EXERCISE 127
(Cf. ‘Trinity’.) Make the status line consist only of the name of the current location, centred in the top line of the screen.

The version 6 screen model. We are now in the realm of graphics, and the screen is considered to be a grid of pixels: coordinates are usually given in the form (y,x), with (1,1) at the top left. y and x are measured in units known, helpfully enough, as “units”. The interpreter decides how large “1 unit” is, and it's not safe to assume that 1 unit equals 1 pixel. All you can tell is what the screen dimensions are, in units:

print "The screen measures ", $22-->0, " units across and ",
      $22-->1, " units down.^";

There are eight windows, numbered 0 to 7, which text and pictures can currently be printing to: what actually appears on the screen is whatever shows through the boundaries of the window at the time the printing or plotting happens. Window number −3 means “the current one”. Windows have no visible borders and usually lie on top of each other. Subsequent movements of the window do not move what was printed and there is no sense in which characters or graphics “belong” to any particular window once printed. Each window has a position (in units), a size (in units), a cursor position within it (in units, relative to its own origin), a number of flags called “attributes” and a number of variables called “properties”. If you move a window so that the cursor is left outside, the interpreter automatically moves the cursor back to the window's new top left. If you only move the cursor, it's your responsibility to make sure it doesn't leave the window.

The attributes are (0) “wrapping”, (1) “scrolling”, (2) “copy text to output stream 2 if active” and (3) “buffer printing”. Wrapping means that when text reaches the right-hand edge it continues from the left of the next line down. Scrolling means scrolling the window upwards when text printing reaches the bottom right corner, to make room for more. Output stream 2 is the transcript file, so the question here is whether you want text in the given window to appear in a transcript: for instance, for a status line the answer is probably “no”, but for normal conversation it would be “yes”. Finally, buffering is a more sophisticated form of wrapping, which breaks lines of text in between words, but which (roughly speaking) means that no line is printed until complete. Note that ordinary printing in the lower window has all four of these attributes.

@window_style window attrs operation

Changes window attributes. attrs is a bitmap in which bit 0 means “wrapping”, bit 1 means “scrolling”, etc. operation is 0 to set to these settings, 1 to set only those attributes which you specify in the bitmap, 2 to clear only those and 3 to reverse them. For instance,

@window_style 2 $$1011 0

sets window 2 to have wrapping, scrolling and buffering but not to be copied to output stream 2, and

@window_style 1 $$1000 2

clears the buffer printing attribute of window 1.

Windows have 16he text you need to fit at once.

@set_window window

Selects which window text is to be printed into: (0) the lower one or (1) the upper one. Printing on the upper window overlies printing on the lower, is always done in a fixed-pitch font and does not appear in a printed transcript of the game.

@set_cursor line column

Places the cursor inside the upper window, where (1, 1) is the top left character.

@buffer_mode flag

This turns on (flag==true) or off (flag==false) word-breaking for the current window: that is, the practice of printing new-lines only at the ends of words, so that text is neatly formatted.

@erase_window window

Blanks out window 0 (lower), window 1 (upper) or the whole screen (if window=-1).

Using fixed-pitch measurements, the screen has dimensions X characters across by Y characters down, where X and Y are stored in bytes $21 and $20 of the header respectively. It's sometimes useful to know this when formatting tables:

print "My screen has ", 0->$20, " rows and ", 0->$21, " columns.^";

Be warned: it might be 80 × 210 or then again it might be 7 × 40. Text printing has a given foreground and background colour at all times. The standard stock of colours is:

0current colour5yellow
1default colour6blue
2black7magenta
3red8cyan
4green9white

@set_colour foreground background

If coloured text is available, this opcode sets text to be foreground against background. (But bear in mind that not all interpreters can display coloured text, and not all players enjoy reading it.) Even in a monochrome game, text can be set to print in “reverse colours”: background on foreground rather than vice versa. Status lines are almost always printed in reverse-colour, but this is only a convention and is not required by the Z-machine. Reverse is one of five possible text styles: roman, bold, underline (which many interpreters will render with italic), reverse and fixed-pitch. (Inform's style statement chooses between these.)

EXERCISE 123
Design a title page for ‘Ruins’, displaying a more or less apposite quotation and waiting for a key to be pressed. (For this last part, see below.)

EXERCISE 124
Change the status line so that it has the usual score/moves appearance except when a variable invisible_status is set to true, when it's invisible.

EXERCISE 125
Alter the ‘Advent’ example game to display the number of treasures found instead of the score and turns on the status line.

EXERCISE 126
(From code by Joachim Baumann.) Put a compass rose on the status line, displaying the directions in which the room can be left.

▲▲ EXERCISE 127
(Cf. ‘Trinity’.) Make the status line consist only of the name of the current location, centred in the top line of the screen.

The version 6 screen model. We are now in the realm of graphics, and the screen is considered to be a grid of pixels: coordinates are usually given in the form (y,x), with (1,1) at the top left. y and x are measured in units known, helpfully enough, as “units”. The interpreter decides how large “1 unit” is, and it's not safe to assume that 1 unit equals 1 pixel. All you can tell is what the screen dimensions are, in units:

print "The screen measures ", $22-->0, " units across and ",
      $22-->1, " units down.^";

There are eight windows, numbered 0 to 7, which text and pictures can currently be printing to: what actually appears on the screen is whatever shows through the boundaries of the window at the time the printing or plotting happens. Window number −3 means “the current one”. Windows have no visible borders and usually lie on top of each other. Subsequent movements of the window do not move what was printed and there is no sense in which characters or graphics “belong” to any particular window once printed. Each window has a position (in units), a size (in units), a cursor position within it (in units, relative to its own origin), a number of flags called “attributes” and a number of variables called “properties”. If you move a window so that the cursor is left outside, the interpreter automatically moves the cursor back to the window's new top left. If you only move the cursor, it's your responsibility to make sure it doesn't leave the window.

The attributes are (0) “wrapping”, (1) “scrolling”, (2) “copy text to output stream 2 if active” and (3) “buffer printing”. Wrapping means that when text reaches the right-hand edge it continues from the left of the next line down. Scrolling means scrolling the window upwards when text printing reaches the bottom right corner, to make room for more. Output stream 2 is the transcript file, so the question here is whether you want text in the given window to appear in a transcript: for instance, for a status line the answer is probably “no”, but for normal conversation it would be “yes”. Finally, buffering is a more sophisticated form of wrapping, which breaks lines of text in between words, but which (roughly speaking) means that no line is printed until complete. Note that ordinary printing in the lower window has all four of these attributes.

@window_style window attrs operation

Changes window attributes. attrs is a bitmap in which bit 0 means “wrapping”, bit 1 means “scrolling”, etc. operation is 0 to set to these settings, 1 to set only those attributes which you specify in the bitmap, 2 to clear only those and 3 to reverse them. For instance,

@window_style 2 $$1011 0

sets window 2 to have wrapping, scrolling and buffering but not to be copied to output stream 2, and

@window_style 1 $$1000 2

clears the buffer printing attribute of window 1.

Windows have 16he text you need to fit at once.

@set_window window

Selects which window text is to be printed into: (0) the lower one or (1) the upper one. Printing on the upper window overlies printing on the lower, is always done in a fixed-pitch font and does not appear in a printed transcript of the game.

@set_cursor line column

Places the cursor inside the upper window, where (1, 1) is the top left character.

@buffer_mode flag

This turns on (flag==true) or off (flag==false) word-breaking for the current window: that is, the practice of printing new-lines only at the ends of words, so that text is neatly formatted.

@erase_window window

Blanks out window 0 (lower), window 1 (upper) or the whole screen (if window=-1).

Using fixed-pitch measurements, the screen has dimensions X characters across by Y characters down, where X and Y are stored in bytes $21 and $20 of the header respectively. It's sometimes useful to know this when formatting tables:

print "My screen has ", 0->$20, " rows and ", 0->$21, " columns.^";

Be warned: it might be 80 × 210 or then again it might be 7 × 40. Text printing has a given foreground and background colour at all times. The standard stock of colours is:

0current colour5yellow
1default colour6blue
2black7magenta
3red8cyan
4green9white

@set_colour foreground background

If coloured text is available, this opcode sets text to be foreground against background. (But bear in mind that not all interpreters can display coloured text, and not all players enjoy reading it.) Even in a monochrome game, text can be set to print in “reverse colours”: background on foreground rather than vice versa. Status lines are almost always printed in reverse-colour, but this is only a convention and is not required by the Z-machine. Reverse is one of five possible text styles: roman, bold, underline (which many interpreters will render with italic), reverse and fixed-pitch. (Inform's style statement chooses between these.)

EXERCISE 123
Design a title page for ‘Ruins’, displaying a more or less apposite quotation and waiting for a key to be pressed. (For this last part, see below.)

EXERCISE 124
Change the status line so that it has the usual score/moves appearance except when a variable invisible_status is set to true, when it's invisible.

EXERCISE 125
Alter the ‘Advent’ example game to display the number of treasures found instead of the score and turns on the status line.

EXERCISE 126
(From code by Joachim Baumann.) Put a compass rose on the status line, displaying the directions in which the room can be left.

▲▲ EXERCISE 127
(Cf. ‘Trinity’.) Make the status line consist only of the name of the current location, centred in the top line of the screen.

The version 6 screen model. We are now in the realm of graphics, and the screen is considered to be a grid of pixels: coordinates are usually given in the form (y,x), with (1,1) at the top left. y and x are measured in units known, helpfully enough, as “units”. The interpreter decides how large “1 unit” is, and it's not safe to assume that 1 unit equals 1 pixel. All you can tell is what the screen dimensions are, in units:

print "The screen measures ", $22-->0, " units across and ",
      $22-->1, " units down.^";

There are eight windows, numbered 0 to 7, which text and pictures can currently be printing to: what actually appears on the screen is whatever shows through the boundaries of the window at the time the printing or plotting happens. Window number −3 means “the current one”. Windows have no visible borders and usually lie on top of each other. Subsequent movements of the window do not move what was printed and there is no sense in which characters or graphics “belong” to any particular window once printed. Each window has a position (in units), a size (in units), a cursor position within it (in units, relative to its own origin), a number of flags called “attributes” and a number of variables called “properties”. If you move a window so that the cursor is left outside, the interpreter automatically moves the cursor back to the window's new top left. If you only move the cursor, it's your responsibility to make sure it doesn't leave the window.

The attributes are (0) “wrapping”, (1) “scrolling”, (2) “copy text to output stream 2 if active” and (3) “buffer printing”. Wrapping means that when text reaches the right-hand edge it continues from the left of the next line down. Scrolling means scrolling the window upwards when text printing reaches the bottom right corner, to make room for more. Output stream 2 is the transcript file, so the question here is whether you want text in the given window to appear in a transcript: for instance, for a status line the answer is probably “no”, but for normal conversation it would be “yes”. Finally, buffering is a more sophisticated form of wrapping, which breaks lines of text in between words, but which (roughly speaking) means that no line is printed until complete. Note that ordinary printing in the lower window has all four of these attributes.

@window_style window attrs operation

Changes window attributes. attrs is a bitmap in which bit 0 means “wrapping”, bit 1 means “scrolling”, etc. operation is 0 to set to these settings, 1 to set only those attributes which you specify in the bitmap, 2 to clear only those and 3 to reverse them. For instance,

@window_style 2 $$1011 0

sets window 2 to have wrapping, scrolling and buffering but not to be copied to output stream 2, and

@window_style 1 $$1000 2

clears the buffer printing attribute of window 1.

Windows have 16he text you need to fit at once.

@set_window window

Selects which window text is to be printed into: (0) the lower one or (1) the upper one. Printing on the upper window overlies printing on the lower, is always done in a fixed-pitch font and does not appear in a printed transcript of the game.

@set_cursor line column

Places the cursor inside the upper window, where (1, 1) is the top left character.

@buffer_mode flag

This turns on (flag==true) or off (flag==false) word-breaking for the current window: that is, the practice of printing new-lines only at the ends of words, so that text is neatly formatted.

@erase_window window

Blanks out window 0 (lower), window 1 (upper) or the whole screen (if window=-1).

Using fixed-pitch measurements, the screen has dimensions X characters across by Y characters down, where X and Y are stored in bytes $21 and $20 of the header respectively. It's sometimes useful to know this when formatting tables:

print "My screen has ", 0->$20, " rows and ", 0->$21, " columns.^";

Be warned: it might be 80 × 210 or then again it might be 7 × 40. Text printing has a given foreground and background colour at all times. The standard stock of colours is:

0current colour5yellow
1default colour6blue
2black7magenta
3red8cyan
4green9white

@set_colour foreground background

If coloured text is available, this opcode sets text to be foreground against background. (But bear in mind that not all interpreters can display coloured text, and not all players enjoy reading it.) Even in a monochrome game, text can be set to print in “reverse colours”: background on foreground rather than vice versa. Status lines are almost always printed in reverse-colour, but this is only a convention and is not required by the Z-machine. Reverse is one of five possible text styles: roman, bold, underline (which many interpreters will render with italic), reverse and fixed-pitch. (Inform's style statement chooses between these.)

EXERCISE 123
Design a title page for ‘Ruins’, displaying a more or less apposite quotation and waiting for a key to be pressed. (For this last part, see below.)

EXERCISE 124
Change the status line so that it has the usual score/moves appearance except when a variable invisible_status is set to true, when it's invisible.

EXERCISE 125
Alter the ‘Advent’ example game to display the number of treasures found instead of the score and turns on the status line.

EXERCISE 126
(From code by Joachim Baumann.) Put a compass rose on the status line, displaying the directions in which the room can be left.

▲▲ EXERCISE 127
(Cf. ‘Trinity’.) Make the status line consist only of the name of the current location, centred in the top line of the screen.

The version 6 screen model. We are now in the realm of graphics, and the screen is considered to be a grid of pixels: coordinates are usually given in the form (y,x), with (1,1) at the top left. y and x are measured in units known, helpfully enough, as “units”. The interpreter decides how large “1 unit” is, and it's not safe to assume that 1 unit equals 1 pixel. All you can tell is what the screen dimensions are, in units:

print "The screen measures ", $22-->0, " units across and ",
      $22-->1, " units down.^";

There are eight windows, numbered 0 to 7, which text and pictures can currently be printing to: what actually appears on the screen is whatever shows through the boundaries of the window at the time the printing or plotting happens. Window number −3 means “the current one”. Windows have no visible borders and usually lie on top of each other. Subsequent movements of the window do not move what was printed and there is no sense in which characters or graphics “belong” to any particular window once printed. Each window has a position (in units), a size (in units), a cursor position within it (in units, relative to its own origin), a number of flags called “attributes” and a number of variables called “properties”. If you move a window so that the cursor is left outside, the interpreter automatically moves the cursor back to the window's new top left. If you only move the cursor, it's your responsibility to make sure it doesn't leave the window.

The attributes are (0) “wrapping”, (1) “scrolling”, (2) “copy text to output stream 2 if active” and (3) “buffer printing”. Wrapping means that when text reaches the right-hand edge it continues from the left of the next line down. Scrolling means scrolling the window upwards when text printing reaches the bottom right corner, to make room for more. Output stream 2 is the transcript file, so the question here is whether you want text in the given window to appear in a transcript: for instance, for a status line the answer is probably “no”, but for normal conversation it would be “yes”. Finally, buffering is a more sophisticated form of wrapping, which breaks lines of text in between words, but which (roughly speaking) means that no line is printed until complete. Note that ordinary printing in the lower window has all four of these attributes.

@window_style window attrs operation

Changes window attributes. attrs is a bitmap in which bit 0 means “wrapping”, bit 1 means “scrolling”, etc. operation is 0 to set to these settings, 1 to set only those attributes which you specify in the bitmap, 2 to clear only those and 3 to reverse them. For instance,

@window_style 2 $$1011 0

sets window 2 to have wrapping, scrolling and buffering but not to be copied to output stream 2, and

@window_style 1 $$1000 2