| |
Contents Back Forward |
|
| |
Some dirty tricks require bypassing all of Inform's higher levels to program the Z-machine directly with assembly language. There is an element of danger in this, in that some combinations of unusual opcodes might look ugly on some incomplete or wrongly-written interpreters: so if you're doing anything complicated, test it as widely as possible. | |
| |
The best-researched and most reliable interpreters available by far are Mark
Howell's Zip and Stefan Jokisch's Frotz: they are also faster than their
only serious rival, the InfoTaskForce, a historically important work which
is fairly thorough (and should give little trouble in practice) but which was
written when the format was a little less well understood. In some ports,
ITF gets rarer screen effects wrong, and it lacks an "undo'' feature, so the
Inform "undo'' verb won't work under ITF.
(The other two publically-available interpreters are pinfocom and zterp,
but these are unable to run Advanced games. In the last resort, sometimes it's
possible to use one of Infocom's own supplied interpreters with a different game
from that it came with; but only sometimes, as they may have inconvenient
filenames 'wired into them'.)
Interpreters conforming to the Z-Machine Standard, usually but not always derived from Frotz or Zip, are reliable and widely available. But remember that one source of unportability is inevitable. Your game may be running on a screen which is anything from a 64 characters by 9 pocket organiser LCD display, up to a 132 by 48 window on a 21-inch monitor. Anyone wanting to really push the outer limits (say, by implementing Space Invaders or NetHack) will need to refer to The Z-Machine Standards Document. This is much more detailed (the definition of aread alone runs for two pages) and covers the whole range of assembly language. However, this section does document all those features which can't be better obtained with higher-level code. Lines of assembly language must begin with an @ character and then the name of the "opcode'' (i.e., assembly language statement). A number of arguments, or "operands'' follow (how many depends on the opcode): these may be any Inform constants, local or global variables or the stack pointer sp, but may not be compound expressions. sp does not behave like a variable: writing a value to it pushes that value onto the stack, whereas reading the value of it (for instance, by giving it as an operand) pulls the top value off the stack. Don't use sp unless you have to. After the operands, some opcodes require a variable (or sp) to write a result into. The opcodes documented in this section are as follows: @split_window lines @set_window window @set_cursor line column @buffer_mode flag @erase_window window @set_colour foreground background @aread text parse time function <result> @read_char 1 time function <result> @tokenise text parse dictionary @encode_text ascii-text length from coded-text @output_stream number table @input_stream number @catch <result> @throw value stack-frame @save buffer length filename <result> @restore buffer length filename <result>
@split_window linesSplits off an upper-level window of the given number of lines in height from the main screen. This upper window usually holds the status line and can be resized at any time: nothing visible happens until the window is printed to. Warning: make the upper window tall enough to include all the lines you want to write to it, as it should not be allowed to scroll. @set_window windowThe text part of the screen (the lower window) is "window 0'', the status line (the upper one) is window 1; this opcode selects which one text is to be printed into. Each window has a "cursor position'' at which text is being printed, though it can only be set for the upper window. 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. Note that before printing to the upper window, it is wise to use @buffer_mode to turn off word-breaking. @set_cursor line columnPlaces the cursor inside the upper window, where $(1,1)$ is the top left character. @buffer_mode flagThis turns on (flag=1) or off (flag=0) 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). It is wise to turn off word-breaking while printing to the upper window. @erase_window windowThis opcode is unfortunately incorrectly implemented on some interpreters and so it can't safely be used to erase individual windows. However, it can be used with window=-1, and then clears the entire screen. Don't do this in reverse video mode, as a bad interpreter may (incorrectly) wipe the entire screen in reversed colours. @set_colour foreground backgroundIf coloured text is available, set text to be foreground-against-background. The colour numbers are borrowed from the IBM PC: 2 = black, 3 = red, 4 = green, 5 = yellow, 6 = blue, 7 = magenta, 8 = cyan, 9 = white 0 = the current setting, 1 = the default.On many machines coloured text is not available: the opcode will then do nothing. @aread text parse time function <result>The keyboard can be read in remarkably flexible ways. This opcode reads a line of text from the keyboard, writing it into the text string array and 'tokenising' it into a word stream, with details stored in the parse string array (unless this is zero, in which case no tokenisation happens). (See the end of Section 27 for the format of text and parse.) While it is doing this, it calls function(time) every time tenths of a second while the user is thinking: the process ends if ever this function returns true. <result> is to be a variable, but the value written in it is only meaningful if you're using a "terminating characters table''. Thus (by Replaceing the Keyboard routine in the library files) you could, say, move around all the characters every ten seconds of real time. Warning: not every interpreter supports this real-time feature, and most of those that do count in seconds instead of tenths of seconds.
@read_char 1 time function <result>results in the ASCII value of a single keypress. Once again, the function is called every time tenths of a second and may stop this process early. Function keys return special values from 129 onwards, in the order: cursor up, down, left, right, function key f1, ..., f12, keypad digit 0, ..., 9. The first operand must be 1 (used by Infocom as a device number to identify the keyboard). @tokenise text parse dictionaryThis takes the text in the text buffer (in the format produced by aread) and tokenises it (i.e. breaks it up into words, finds their addresses in the dictionary) into the parse buffer in the usual way but using the given dictionary instead of the game's usual one. (See the Z-Machine Standards Document for the dictionary format.) @encode_text ascii-text length from coded-textTranslates an ASCII word to the internal (Z-encoded) text format suitable for use in a @tokenise dictionary. The text begins at from in the ascii-text and is length characters long, which should contain the right length value (though in fact the interpreter translates the word as far as a 0 terminator). The result is 6 bytes long and usually represents between 1 and 9 letters. @output_stream number tableText can be output to a variety of different 'streams', possibly simultaneously. If number is 0 this does nothing. $+n$ switches stream n on, $-n$ switches it off. The output streams are: 1 (the screen), 2 (the game transcript), 3 (memory) and 4 (script of player's commands). The table can be omitted except for stream 3, when it's a table array holding the text printed; printing to this stream is never word-broken, whatever the state of @buffer_mode. @input_stream numberSwitches the 'input stream' (the source of the player's commands). 0 is the keyboard, and 1 a command file (the idea is that a list of commands produced by output_stream 4 can be fed back in again). @catch <result>The opposite of throw, catch preserves the "stack frame'' of the current routine: meaning, roughly, the current position of which routine is being run and which ones have called it so far. @throw value stack-frameThis causes the program to execute a return with value, but as if it were returning from the routine which was running when the stack-frame was caught (see catch). Any routines which were called in the mean time and haven't returned yet (because each one called the next) are forgotten about. This is useful to get the program out of large recursive tangles in a hurry. @save buffer length filename <result>Saves the byte array buffer (of size length) to a file, whose (default) name is given in the filename (a string array). Afterwards, result holds 1 on success, 0 on failure.
@restore buffer length filename <result>Loads in the byte array buffer (of size length) from a file, whose (default) name is given in the filename (a string array). Afterwards, result holds the number of bytes successfully read.
| |
WARNING: Some of these features may not work well on obsolete interpreters which do not adhere to the Z-Machine Standard. Standard interpreters are widely available, but if seriously worried you can test whether your game is running on a good interpreter: if (standard_interpreter == 0) { print "This game must be played on an interpreter obeying the Z-Machine Standard.^"; @quit; }
| |
EXERCISE 90: (link to the answer) | |
In a role-playing game campaign, you might want several
scenarios, each implemented as a separate Inform game. How could
the character from one be saved and loaded into another?
| |
EXERCISE 91: (link to the answer) | |
Design a title page for 'Ruins', displaying
a more or less apposite quotation and waiting for a key to be
pressed.
| |
EXERCISE 92: (link to the answer) | |
Change the status line so that it has the usual
score/moves appearance except when a variable invisible_status
is set, when it's invisible.
| |
EXERCISE 93: (link to the answer) | |
Alter the 'Advent' example game to display the number
of treasures found instead of the score and turns on the status
line.
| |
EXERCISE 94: (link to the answer) | |
(From code by Joachim Baumann.) Put a compass rose
on the status line, displaying the directions in which the room can be
left.
| |
EXERCISE 95: (link to the answer) | |
(Cf.
'Trinity'.)
Make the status line consist only of the name of the current
location, centred in the top line of the
screen.
| |
EXERCISE 96: (link to the answer) | |
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.''
| |
REFERENCES: The assembly-language connoisseur will appreciate 'Freefall' by Andrew Plotkin and 'Robots' by Torbjorn Andersson although the present lack of on-line hints make these difficult games to win. |