Infocom's games of 1979–89 were written in a language called ZIL, the Zork Implementation Language. At first sight this is outlandishly unlike Inform, but appearances are deceptive. The following source code describes toy boats in Kensington Park, from a game widely considered a masterpiece: ‘Trinity’ (1986), by Brian Moriarty.
<OBJECT BOAT (LOC ROUND-POND) (DESC "toy boats") (FLAGS TRYTAKE NODESC PLURAL) (SYNONYM BOAT BOATS TOYS) (ADJECTIVE TOY) (ACTION BOAT-F)> <ROUTINE BOAT-F () <COND (<VERB? EXAMINE WATCH> <TELL CTHEO " are crafted of paper and sticks. They bob freely among the " D ,POND-BIRDS ", who can barely conceal their outrage." CR> <RTRUE>) (<VERB? SWIM DIVE WALK-TO FOLLOW SIT LIE-DOWN ENTER> <DO-WALK ,P?IN> <RTRUE>) (<INTBL? ,PRSA ,TOUCHVERBS ,NTOUCHES> <TELL CTHE ,BOAT " are far out of reach." CR> <RTRUE>) (T <RFALSE>)>>
Inform and ZIL each have objects, with properties and
attributes. They're called different things, true: ZIL has its dictionary
words in SYNONYM
and ADJECTIVE
where Inform
uses name
, ZIL calls attributes “flags” and
has NODESC where Inform would have scenery
, but the similarity
is striking. Both languages have routines with a tendency to return
true
and false
, too.
The underlying similarity is the Z-machine, which both languages make use of. The Z-machine is an imaginary computer: created on Joel Berez's mother's coffee table in Pittsburgh in 1979, it has never existed as circuitry. Instead, almost every real computer built in the 1980s and 1990s has been taught to emulate the Z-machine, and so to run story files.
This chapter contains what the advanced Inform programmer needs, from time to time, to know about the Z-machine. The reader who only wants to get at fairly easy screen effects like coloured text may want to turn straight to the references to §42, where a number of convenient library extensions are listed.
In any case this chapter is by no means the full story, which is contained in The Z-Machine Standards Document. It seems nonetheless appropriate to acknowledge here the work of the Z-machine's architects: Joel Berez, Marc Blank, P. David Lebling, Tim Anderson and others.
· · · · ·
The Z-machine as conceived in 1979 is now known as “version 1”, and there have been seven subsequent versions to date. Inform normally produces version 5 story files, but this can be controlled with the -v switch: so -v6 compiles a version 6 story file, for instance. Briefly, the versions are:
Versions 1 and 2. Early draft designs by Infocom, used only in the first release of the ‘Zork’ trilogy. Inform cannot produce them.
Version 3. The standard Infocom design, limited in various ways: to 255 objects, 32 attributes, at most 4 entries in a property array, at most 3 arguments in a routine call and a story file at most 128K in size. Inform can produce version 3 files but this is not recommended and some advanced features of the language, such as message-sending, will not work.
Version 4. A partial upgrade, now seldom used.
Version 5. The advanced Infocom design and the one normally used by Inform. Limits are raised to 65,535 objects, 48 attributes, 32 entries in a property array, 7 arguments in a routine call and a story file at most 256K in size.
Version 6. Similar, though not identical, in architecture to Version 5, but offering support for pictures. Inform will compile to this, but there are two further obstructions: you need a way to package up the sounds and images to go with the story file (see §43), and then players need an interpreter able to make use of them.
Version 7. An intermediate version which has never proved useful, and whose use is now deprecated.
Version 8. Identical to version 5 except that it allows story files up to 512K long. Most of the largest Inform games use version 8.
· · · · ·
The native language of the Z-machine is neither Inform nor ZIL, but an intermediate-level code which we'll call “assembly language”. It's tiresome to write a program of any complexity in assembly language. For instance, here are two equivalent pieces of Inform: first, a statement in ordinary code:
"The answer is ", 3*subtotal + 1;
Secondly, assembly language which achieves the same end:
@print "The answer is "; @mul 3 subtotal -> x; @add x 1 -> x; @print_num x; @new_line; @rtrue;
(Here we've used a variable called x
.)
Inform allows you to mix assembly language and ordinary Inform source
code freely, but all commands in assembly language, called “opcodes”,
are written with an @
sign in front, to distinguish them. The values supplied
to opcodes, such as 3
and subtotal
, are called “operands”.
The ->
arrow sign is read “store to” and
indicates that an answer is being stored somewhere. So, for instance, the line
@add x 1 -> x;
adds x
and 1, storing the result of this
addition back in x
. Operands can only be constants or
variables: so you can't write a compound expression like
my_array-->(d*4)
.
As can be seen above, some opcodes store values and some don't. Another important category are the “branch” opcodes, which result in execution jumping to a different place in the source code if some condition turns out to be true, and not otherwise. For instance:
@je x 1 ?Xisone; @print "x isn't equal to 1."; .Xisone;
Here, Xisone
is the name of a label,
marking a point in the source code which a branch opcode (or an Inform
jump
statement) might want to jump to. (A label can't
be the last thing in a function, but if you needed this, you could always
finish with a label plus a return
statement instead.) @je
means “jump
if equal”, so the code tests x
to see
if it's equal to 1 and jumps to Xisone
if so. Inform will
only allow branches to labels in the same routine. Note that inserting
a tilde,
@je x 1 ?~Xisntone;
reverses the condition, so this opcode branches if
x
is not equal to 1.
The full specification of Inform's assembly-language syntax is given in §14 of The Z-Machine Standards Document, but this will seldom if ever be needed, because the present chapter contains everything that can't be done more easily without assembly language anyway.
· · · · ·
▲
The rest of this section sketches the architecture of the Z-machine,
which many designers won't need to know about. Briefly, it contains memory
in three sections: readable and writeable memory at byte addresses 0 up
to S−1, read-only memory from S up to
P−1 and inaccessible memory from P upwards. (In
any story file, the Inform expression 0-->2
gives the
value of P and 0-->7
gives S.) The
read-write area contains everything that needs to change in play:
variables, object properties and attributes, arrays and certain other
tables; except for the stack and the “program counter”,
its marker as to which part of some routine it is currently running.
The beginning of the read-write area is a 64-byte “header”.
Byte 0 of this header, and so of an entire story file, contains the
version number of the Z-machine for which it is written. (The expression
0->0
evaluates to this.)
The read-only area contains tables which the Inform parser needs to make detailed use of but never alters: the grammar lines and the dictionary, for instance. The “inaccessible” area contains routines and static (that is, unalterable) strings of text. These can be called or printed out, which is access of a sort, but you can't write code which will examine them one byte at a time.
In addition to local and global variables, the Z-machine
contains a “stack”, which is accessed with the name sp
for “stack pointer”. The stack is a pile of values. Each
time sp
is written to, a new value is placed on top of the
pile. Each time it is read, the value being read is taken off the pile.
At the start of a routine, the stack is always empty.
There is no access to the outside world except by
using certain opcodes. For instance, @read
and @read_char
allow use of the keyboard, whereas @print
and
@draw_picture
allow use of the screen. (The screen's image
is not stored anywhere in memory, and nor is the state of the keyboard.)
Conversely, hardware can cause the Z-machine to “interrupt”,
that is, to make a spontaneous call to a particular routine, interrupting
what it was previously working on. This happens only if the story file
has previously requested it: for example, by setting a sound effect playing
and asking for a routine to be called when it finishes; or by asking
for an interrupt if thirty seconds pass while the player is thinking
what to type.