§41   Architecture and assembly

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.

        (LOC ROUND-POND)
        (DESC "toy boats")
        (ACTION BOAT-F)>
                <TELL CTHEO
" are crafted of paper and sticks. They bob freely among the "
D ,POND-BIRDS ", who can barely conceal their outrage." CR>
                <DO-WALK ,P?IN>
                <TELL CTHE ,BOAT " are far out of reach." CR>

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;

(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.";

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.