1 It is assumed that the user has a good working knowledge of the Z-machine; this
2 document is not meant as a tutorial, nor is this assembler meant to be useful
3 for anything beyond the testing of interpreters.
5 All Z-machine instructions for all Z-machine versions are implemented. However,
6 there are some major features of the Z-machine that are not currently
7 implemented. These include (but surely are not limited to) objects, the
8 dictionary, and abbreviations. There is no direct access to the Unicode table,
9 but if you use UTF-8 characters in a string, they will automatically be added to
12 Diagnostics are generally useful, but might sometimes be cryptic. If all else
13 fails, look at the source code. This assembler is quite rough around the edges.
15 Source files must be encoded in UTF-8. If you're only using ASCII, that will be
18 In this file, the term "C-style constant" refers to a number as written in C: a
19 leading 0x means the number is hexadecimal, a leading 0 means octal, otherwise,
22 Instruction names are identical to those given in ยง15 of the Z-machine standard.
23 Unlike Inform, instructions are not prefixed with @.
25 Comments are introduced with the # character and extend to the end of the line.
27 A label is introduced with the "label" directive:
30 An aligned label (which is the same as calling "align" then "label") has the
34 A routine is introduced with the "routine" directive, and includes the number of
35 locals the routune has (this cannot be omitted even if there are no locals):
38 If a version 1-4 story file is being created, initial values can be given to
39 each local variable by listing their values after the number of locals. The
40 following gives the value 1 to L00, 2 to L01, 3 to L02 and, because values were
41 omitted, L03 and L04 are set to zero:
42 routune RoutineName 5 1 2 3
44 An arbitrary byte sequence is introduced with the "byte" directive, each byte
45 specified in hex, separated by space:
48 The "align" directive, when given no arguments, forces routine alignment,
49 ensuring that the next instruction is on a 2-, 4-, or 8-byte boundardy,
50 depending on the Z-Machine version (1-3, 4-7, and 8, respectively). When given
51 a C-style constant as an argument, align to that value instead:
55 The "include" directive causes the contents of another file to be included for
56 assemby, analogous to #include in C (recursion is not detected):
59 The "seek" directive inserts the specified number of zeroes into the output
60 file; the argument is a C-style constant:
63 The "seeknop" directive is identical to "seek", except instead of zeroes, nop
64 instructions are inserted.
66 The "status" directive, available only in V3 stories, indicates whether this is
67 a "score game" or a "time game". The syntax is:
71 The "status" directive may be specified at any point in the source file any
72 number of times. The last takes precedence. The default game type is a score
73 game. Although objects are not supported by this assembler, object 1 is created
74 so that interpreters can properly display the status bar. This object has the
75 name "Default object" and no properties.
77 The instructions "print" and "print_ret" take a string as their argument; this
78 should not be enclosed in quotes unless you want to print out quotes. However,
79 a ^ character does indicate newline as in Inform:
80 print "Try" our hot dog's^Try "our" hot dog's^
82 In order to allow strings to start with space, the two printing instructions are
83 tokenized slightly different from other instructions. Rather than skipping all
84 whicespace after the instruction name, a single space is skipped; and this must
85 be a space character, not a tab. So these could be thought of as the "print "
86 and "print_ret " instructions, in a sense:
89 This will print " Foo" because there are two spaces; one to separate the
90 arguments from the instruction name, and then one before the 'F' of "Foo".
92 The directive "string " is treated in the same manner. This directive will
93 directly insert an encoded string, suitable for use with print_paddr.
95 The instruction "print_char" cannot be used with literal characters, but instead
96 must be supplied with a ZSCII value:
99 The "aread" and "sread" opcodes are just called "read" in this assembler.
101 If you are in a routine, local variables can be accessed as L00, L01, ... L15.
102 The numbers are decimal.
104 Global variables are available in G00, G01, ... Gef. The numbers are hex.
106 The stack pointer is called sp or SP.
108 For instructions that require a store, use -> to indicate that:
109 call_1s Routine -> sp
111 Literal numbers are stored appropriately, meaning as a small constant for values
112 that fit into 8 bits, a large constant otherwise. Only values that will fit
113 into a 16-bit integer (signed or unsigned) are allowed, meaning -32768 to 65535.
114 Numbers are parsed as C-style constants.
116 The "start" directive must be used once, before any opcodes are used. Until
117 "start" is seen, the assembler writes to dynamic memory. Once "start" is
118 encountered, static memory begins, and the starting program counter is set to
119 that address. The reason for this is chiefly to allow arrays to be placed in
121 # Combine label and seek to produce arrays.
124 # Execution will begin here.
126 storew &Array 0 0x1234
128 In V6, the starting point of the story must be a routine, not an instruction.
129 To accomplish this, "start" has a special syntax for V6, which looks identical
130 to the syntax for "routine":
133 This causes a routine called "main" with zero locals to be created, and uses
134 this as the entry point. These arguments are allowed in non-V6 games, but are
138 Labels in the Z-Machine are slightly convoluted. One might think that a label
139 could be both branched to and called, but this is not exactly the case. When
140 using a call instruction, the address of the routine is packed, meaning (in V8)
141 that it is divided by 8 and stored in a 16-bit word. When branching, the
142 address is stored either as a 14-bit signed offset or a 6-bit unsigned offset.
143 And, finally, the @jump instruction takes a 16-bit signed offset.
145 Above it was noted that routune labels are produced differently than "normal"
146 labels; this is because routunes start with a byte that specifies the number of
147 local variables, and this byte needs to be jumped past when calling the routine.
148 Branching to a routine label would make no sense, just as calling a normal label
151 How to encode a label depends entirely on how it's going to be used: routine
152 call (or print_paddr), branch, or jump.
154 The type of a label name could be inferred from how it was defined, but due to a
155 design choice in this assembler, when branching to a label, a prefix is
156 required. So as not to deviate from Inform's syntax too much, this prefix is a
161 This will branch to the label Label if 0 is equal to 0. As with Inform a prefix
162 of ?~ will invert the test. This stores a 14-bit offset. By using % instead of
163 ?, an unsigned 6-bit offset can be chosen, which means one fewer byte used in
164 the output. Of course, this also means that you can only jump about 63 bytes
165 forward. The assembler will tell you if you're trying to jump too far.
167 Branching instructions can, instead of branching when the test passes, return
168 true or false from the current routine. This can be accomplished with ?1 and
171 When a packed address is desired (e.g. when you are trying to call a routine),
172 an exclamation point should be used:
178 This syntax should also be used when a packed string is required for use with
179 the print_paddr opcode:
184 string This is a string.
186 Note that routine and string offsets (for V6 and V7 games) are not supported, so
187 the packing of strings and routines at the top of memory (beyond 256K) will
188 fail. This should not be an issue unless you deliberately seek this far before
189 creating a string or routine.
191 The actual address of a label can be retrieved with the ampersand prefix. This
192 means either an unpacked routine, or when used with a label, the absolute
193 address of the label, not an offset. So to print the address of an instruction:
197 Or to use an array (as seen in the "start" directive above):
201 storew &Array 0 0x1234
203 Note that it is up to you to use the proper prefix with labels. The assembler
204 will cheerfully let you call a label, or jump to a routine, neither of which
205 makes sense. So the following assembles, but is nonsensical:
212 How should the jump instruction be used, since it is neither a branch nor a
213 routine call? This instruction is special-cased by pretending that it is a
214 branch instruction. Before this nefarious lie can cause problems, however, the
215 assembler steps and and writes a proper jump offset. So:
220 Here is a full working sample:
229 print This will not be seen.^
233 print This will not be seen either.^
236 print The main routine is:
239 print Packed, the main routine is:
242 print The Equal label is: