Z-Machine Standard1.1

14. Complete table of opcodes

Two-operand opcodes 2OP
StBrOpcodeHexVInform name and syntaxZIL nameLink
------0------
*2OP:11 je a b c d ?(label)EQUAL?je
*2OP:22 jl a b ?(label)LESS?jl
*2OP:33jg a b ?(label) GRTR?jg
*2OP:44dec_chk (variable) value ?(label) DLESS?dec_chk
*2OP:55inc_chk (variable) value ?(label) IGRTR?inc_chk
*2OP:66jin obj1 obj2 ?(label) IN?jin
*2OP:77test bitmap flags ?(label) BTSTtest
*2OP:88or a b -> (result) BORor
*2OP:99and a b -> (result) BANDand
*2OP:10Atest_attr object attribute ?(label) FSET?test_attr
2OP:11Bset_attr object attribute FSETset_attr
2OP:12Cclear_attr object attribute FCLEARclear_attr
2OP:13Dstore (variable) value SETstore
2OP:14Einsert_obj object destination MOVEinsert_obj
*2OP:15Floadw array word-index -> (result) GETloadw
*2OP:1610loadb array byte-index -> (result) GETBloadb
*2OP:1711get_prop object property -> (result) GETPget_prop
*2OP:1812get_prop_addr object property -> (result) GETPTget_prop_addr
*2OP:1913get_next_prop object property -> (result) NEXTPget_next_prop
*2OP:2014add a b -> (result) ADDadd
*2OP:2115sub a b -> (result) SUBsub
*2OP:2216mul a b -> (result) MULmul
*2OP:2317div a b -> (result) DIVdiv
*2OP:2418mod a b -> (result) MODmod
*2OP:25194call_2s routine arg1 -> (result) CALL2call_2s
2OP:261A5call_2n routine arg1 ICALL2call_2n
2OP:271B5set_colour foreground background COLORset_colour
6set_colour foreground background window COLORset_colour
2OP:281C5/6throw value stack-frame THROWthrow
------1D------
------1E------
------1F------
Opcode numbers 32 to 127: other forms of 2OP with different types.
One-operand opcodes 1OP
StBrOpcodeHexVInform name and syntaxZIL nameLink
*1OP:1280jz a ?(label) ZERO?jz
**1OP:1291get_sibling object -> (result) ?(label) NEXT?get_sibling
**1OP:1302get_child object -> (result) ?(label) FIRST?get_child
*1OP:1313get_parent object -> (result) LOCget_parent
*1OP:1324get_prop_len property-address -> (result) PTSIZEget_prop_len
1OP:1335inc (variable) INCinc
1OP:1346dec (variable) DECdec
1OP:1357print_addr byte-address-of-string PRINTBprint_addr
*1OP:13684call_1s routine -> (result) CALL1call_1s
1OP:1379remove_obj object REMOVEremove_obj
1OP:138Aprint_obj object PRINTDprint_obj
1OP:139Bret value RETURNret
1OP:140Cjump ?(label) JUMPjump
1OP:141Dprint_paddr packed-address-of-string PRINTprint_paddr
*1OP:142Eload (variable) -> (result) VALUEload
*1OP:143F1/4not value -> (result) BCOMnot
5call_1n routine ICALL1call_1n
Opcode numbers 144 to 175: other forms of 1OP with different types.
Zero-operand opcodes 0OP
StBrOpcodeHexVInform name and syntaxZIL nameLink
0OP:1760rtrue RTRUErtrue
0OP:1771rfalse RFALSErfalse
0OP:1782 print (literal-string)PRINTIprint
0OP:1793 print_ret (literal-string)PRINTRprint_ret
0OP:18041/-nopNOOPnop
*0OP:18151 save ?(label)SAVEsave
*4save -> (result) SAVEsave
5[illegal]
*0OP:18261restore ?(label) RESTORErestore
*4restore -> (result) RESTORErestore
5[illegal]
0OP:1837restart RESTARTrestart
0OP:1848ret_popped RSTACKret_popped
0OP:18591pop FSTACKpop
*5/6catch -> (result) CATCHcatch
0OP:186Aquit QUITquit
0OP:187Bnew_line CRLFnew_line
0OP:188C3show_status USLshow_status
4[illegal]
*0OP:189D3verify ?(label) VERIFYverify
0OP:190E5[first byte of extended opcode] EXTOPextended
*0OP:191F5/-piracy ?(label) ORIGINAL?piracy
Opcode numbers 192 to 223: VAR forms of 2OP:0 to 2OP:31.
Variable-operand opcodes VAR
StBrOpcodeHexVInform name and syntaxZIL nameLink
*VAR:22401call routine ...0 to 3 args... -> (result) CALLcall
*4call_vs routine ...0 to 3 args... -> (result) CALLcall_vs
VAR:2251storew array word-index value PUTstorew
VAR:2262storeb array byte-index value PUTBstoreb
VAR:2273put_prop object property value PUTPput_prop
VAR:22841sread text-buffer parse-buffer READsread
4sread text-buffer parse-buffer time routine READsread
*5aread text-buffer parse-buffer time routine -> (result) READaread
VAR:2295print_char output-character-code PRINTCprint_char
VAR:2306print_num value PRINTNprint_num
*VAR:2317random range -> (result) RANDOMrandom
VAR:2328push value PUSHpush
VAR:23391pull (variable) POPpull
*6pull stack -> (result) POPpull
VAR:234A3split_window lines SPLITsplit_window
VAR:235B3set_window window SCREENset_window
*VAR:236C4call_vs2 routine ...0 to 7 args... -> (result) XCALLcall_vs2
VAR:237D4erase_window window CLEARerase_window
VAR:238E4/-erase_line value ERASEerase_line
6erase_line pixels ERASEerase_line
VAR:239F4set_cursor line column CURSETset_cursor
6set_cursor line column window CURSETset_cursor
VAR:240104/6get_cursor array CURGETget_cursor
VAR:241114set_text_style style HLIGHTset_text_style
VAR:242124buffer_mode flag BUFOUTbuffer_mode
VAR:243133output_stream number DIROUToutput_stream
3/4output_stream number table DIROUToutput_stream
6output_stream number table width DIROUToutput_stream
VAR:244143input_stream number DIRINinput_stream
VAR:245155/3sound_effect number effect volume SOUNDsound_effect
5sound_effect number effect volume routine SOUNDsound_effect
*VAR:246164read_char 1 time routine -> (result) INPUTread_char
**VAR:247174scan_table x table len form -> (result) ?(label) INTBL?scan_table
*VAR:248185/6not value -> (result) BCOMnot
VAR:249195call_vn routine ...up to 3 args... ICALLcall_vn
VAR:2501A5call_vn2 routine ...up to 7 args... IXCALLcall_vn2
VAR:2511B5tokenise text parse dictionary flag LEXtokenise
VAR:2521C5encode_text zscii-text length from coded-text ZWSTRencode_text
VAR:2531D5copy_table first second size COPYTcopy_table
VAR:2541E5print_table zscii-text width height skip PRINTTprint_table
*VAR:2551F5check_arg_count argument-number ?(label) ASSIGNED?check_arg_count
Extended opcodes EXT
StBrOpcodeHexVInform name and syntaxZIL nameLink
*EXT:005save table bytes name prompt -> (result) SAVEsave
*EXT:115restore table bytes name prompt -> (result) RESTORErestore
*EXT:225log_shift number places -> (result) SHIFTlog_shift
*EXT:335/-art_shift number places -> (result) ASHIFTart_shift
*EXT:445set_font font -> (result) FONTset_font
*6/-set_font font window -> (result) FONTset_font
EXT:556draw_picture picture-number y x DISPLAYdraw_picture
*EXT:666picture_data picture-number array ?(label) PICINFpicture_data
EXT:776erase_picture picture-number y x DCLEARerase_picture
EXT:886set_margins left right window MARGINset_margins
*EXT:995save_undo -> (result) ISAVEsave_undo
*EXT:10A5restore_undo -> (result) IRESTORErestore_undo
EXT:11B5/*print_unicode char-number PRINTUprint_unicode
*EXT:12C5/*check_unicode char-number -> (result) CHECKUcheck_unicode
EXT:13D5/*set_true_colour foreground background set_true_colour
6/*set_true_colour foreground background window set_true_colour
-------E------
-------F------
EXT:16106move_window window y x WINPOSmove_window
EXT:17116window_size window y x WINSIZEwindow_size
EXT:18126window_style window flags operation WINATTRwindow_style
*EXT:19136get_wind_prop window property-number -> (result) WINGETget_wind_prop
EXT:20146scroll_window window pixels SCROLLscroll_window
EXT:21156pop_stack items stack FSTACKpop_stack
EXT:22166read_mouse array MOUSE-INFOread_mouse
EXT:23176mouse_window window MOUSE-LIMITmouse_window
*EXT:24186push_stack value stack ?(label) XPUSHpush_stack
EXT:25196put_wind_prop window property-number value WINPUTput_wind_prop
EXT:261A6print_form formatted-table PRINTFprint_form
*EXT:271B6make_menu number table ?(label) MENUmake_menu
EXT:281C6picture_table table PICSETpicture_table
*EXT:291D6/*buffer_screen mode -> (result) buffer_screen

14.1 This table contains all 119 opcodes and, taken with the dictionary in § 15, describes exactly what each should do. In addition, it lists which opcodes are actually used in the known Infocom story files, and documents the Inform assembly language syntax.

14.2 Formally, it is illegal for a game to contain an opcode not specified for its version. An interpreter should normally halt with a suitable message.

14.2.1 However, extended opcodes in the range EXT:30 to EXT:255 should be simply ignored (perhaps with a warning message somewhere off-screen).

14.2.2 ***[1.0][1.1] EXT:11 and EXT:12 were opcodes added in Standard 1.0 and can be generated in code compiled by Inform 6.12 or later. EXT:13 and EXT:29 are new in Standard 1.1. EXT:14 to EXT:15, and EXT:30 to EXT:127, are reserved for future versions of this document to specify.

14.2.3 Designers who wish to create their own "new" opcodes, for one specific game only, are asked to use opcode numbers in the range EXT:128 to EXT:255. It is easy to modify Inform to name and assemble such opcodes. (Of course the game will then have to be circulated with a suitably modified interpreter to run it.)

14.2.4 Interpreter-writers should ideally make this easy by providing a routine which is called if EXT:128 to EXT:255 are found, so that the minimum possible modification to the interpreter is needed.

Reading the opcode tables

The two columns "St" and "Br" (store and branch) mark whether an instruction stores a result in a variable, and whether it must provide a label to jump to, respectively.

The "Opcode" is written TYPE:Decimal where the TYPE is the operand count (2OP, 1OP, 0OP or VAR) or else EXT for two-byte opcodes (where the first byte is (decimal) 190). The decimal number is the lowest possible decimal opcode value. The hex number is the opcode number within each TYPE.

The "V" column gives the Version information. If nothing is specified, the opcode is as stated from Version 1 onwards. Otherwise, it exists only from the version quoted onwards. Before this time, its use is illegal. Some opcodes change their meanings as the Version increases, and these have more than one line of specification. Others become illegal again, and these are marked [illegal].

In a few cases, the Version is given as "3/4" or some such. The first number is the Version number whose specification the opcode belongs to, and the second is the earliest Version in which the opcode is known actually to be used in an Infocom-produced story file. A dash means that it seems never to have been used (in any of Versions 1 to 6). The notation "5/*" or "6/*" means that the opcode was introduced in this Standards document long after the Infocom era.

The table explicitly marks opcodes which do not exist in any version of the Z-machine as ------: in addition, none of the extended set of codes after EXT:29 have been used.

Inform assembly language

This section documents Inform 6 assembly language, which is richer than that of Inform 5. The Inform 6 assembler can generate every legal opcode and automatically sets any consequent header bits (for instance, a usage of set_colour will set the "colours needed" bit).

One way to get a picture of Inform assembly language is to compile a short program with tracing switched on (using the -a or -t switches).

1. An Inform statement beginning with an @ is sent directly to the assembler. In the syntax below, (variable) and (result) must be variables (or sp, a special variable name available only in assembly language, and meaning the stack pointer); (label) a label (not a routine name).

(literal-string) must be literal text in quotation marks "thus".

routine should be the name of a routine (this assembles to its packed address). Otherwise any Inform constant term (such as '/' or 'beetle') can be given as an operand.

2. It is optional, but sensible, to place a -> sign before a store-variable. For example, in

    @mul a 56 -> sp;

("multiply variable a by 56, and put the result on the stack") the -> can be omitted, but should be included for clarity.

3. A label to branch to should be prefaced with a question mark ?, as in

    @je a b ?Equal;      ! Branch to Equal if a == b

(If the question mark is omitted, the branch is compiled in the short form, which will only work for very nearby labels and is very seldom useful in code written by hand.) Note that the effect of any branch instruction can be negated using a tilde ~:

    @je a b ?~Different; ! Branch to Different if a ~= b

4. Labels are assembled using full stops:

    .MyLabel;

All branches must be to such a label within the same routine. (The Inform assembler imposes the same-routine restriction.)

5. Most operands are assembled in the obvious way: numbers and constant values (like characters) as numbers, variables as variables, sp as the value on top of the stack. There are two exceptions. "Call" opcodes expect as first operand the name of a routine to call:

    @call_1n MyRoutine;

but one can also give an indirect address, as a constant or variable, using square brackets:

    @call_1n [x];        ! Call routine whose address is in x

Secondly, seven Z-machine opcodes access variables but by their numbers: thus one should write, say, the constant 0 instead of the variable sp. This is inconvenient, so the Inform assembler accepts variable names instead. The operands affected are those marked as (variable) in the syntax chart; Inform translates the variable name as a "small constant" operand with that variable's number as value. The affected opcodes are:

    inc,  dec,  inc_chk,  dec_chk,  store,  pull,  load.

This is useful, but there is another possibility, of genuinely giving a variable operand. The Inform notation for this involves square brackets again:

    @inc frog;          ! Increment var "frog"
    @inc [frog];        ! Increment var whose number is in "frog"

Infocom story files often use such instructions.

6. The Inform assembler is also written with possible extensions to the Z-machine instruction set in mind. (Of course these can only work if a customised interpreter is used.) Simply give a specification in double-quotes where you would normally give the opcode name. For example,

    @"1OP:4S" 34 -> i;
    @get_prop_len 34 -> i;

are equivalent instructions, since get_prop_len is instruction 4 in the 1OP (one-operand) set, and is a Store opcode. The syntax is:

    "  0OP       :  decimal-number  flags  "        (range 0 to 15)
       1OP                                                 0    15
       2OP                                                 0    15
       VAR                                                 32   63
       VAR_LONG                                            32   63
       EXT                                                 0    255
       EXT_LONG                                            0    255

(EXT_LONG is a logical possibility but has not been used in the Z-machine so far: the assembler provides it in case it might be useful in future.) The possible flags are:

       S    Store opcode
       B    Branch opcode
       T    Text in-line instead of operands
            (as with "print" and "print_ret")
       I    "Indirect addressing": first operand is a (variable)
       Fnn  Set bit nn in Flags 2 (signalling to the interpreter that an
            unusual feature has been called for): the number is in decimal

For example,

    "EXT:128BSF14"

is an exotic new opcode, number 128 in the extended range, which is both Branch and Store, and the assembly of which causes bit 14 to be set in "Flags 2". See § 14.2 below for rules on how to number newly created opcodes.

Remarks

The opcodes EXT:5 to EXT:8 were very likely in Infocom's own Version 5 specification (documentary records of which are lost): they seem to have been partially implemented in existing Infocom interpreters, but do not occur in any existing Version 5 story file. They are here left unspecified.

The notation "5/3" for sound_effect is because this plainly Version 5 feature was used also in one solitary Version 3 game, 'The Lurking Horror' (the sound version of which was the last Version 3 release, in September 1987).

The 2OP opcode 0 was possibly intended for setting break-points in debugging (and may be used for this again). It was not nop.

read_mouse and make_menu are believed to have been used only in 'Journey' (based on a check of 11 Version 6 story files).

picture_table is used once by 'Shogun' and several times by 'Zork Zero'.

The Z-Machine Standards Document, version 1.1 — 24 February 2014. Canonical copy at inform-fiction.org.