glulx_asm/
instr_def.rs

1// SPDX-License-Identifier: CC-BY-NC-SA-4.0 AND Apache-2.0 WITH LLVM-Exception
2// Copyright 2024 Daniel Fox Franke.
3// Glulx specification excerpts Copyright 2020-2022 by the Interactive Fiction
4// Technology Foundation.
5
6//! Definition of [`Instr`].
7//!
8//! Keep this definition in its own file separate from any `impl`s, so that it's
9//! easy to strip the CC-BY-NC-SA-4.0 stuff if needed.
10
11use crate::operands::{LoadOperand, StoreOperand};
12
13/// Representation of a Glulx instruction.
14///
15/// **License note**: the rustdoc comments on this enumeration incorporate
16/// material from the Glulx VM Specification version 3.1.3, Copyright 2020-2022
17/// by the Interactive Fiction Technology Foundation. The specification is
18/// licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0
19/// International License (SPDX-License-Identifier: CC-BY-NC-SA-4.0). Any
20/// redistribution of this crate with this documentation intact must abide by
21/// the terms of that license. Although the license is
22/// "sharealike/"infectious"/"copyleft", linking against this crate or
23/// distributing this crate in binary-only form generally will not bind you to
24/// it, since such products generally will not incorporate the documentation and
25/// so will not constitute works derived from it. Such activities are permitted
26/// subject only to the Apache-2.0 WITH LLVM-Exception license under which this
27/// crate's source code is provided. The specification's license does not infect
28/// code which merely implements the specification, since, in the spec's own
29/// words, "The virtual machine described by this document is an idea, not an
30/// expression of an idea, and is therefore not copyrightable. Anyone is free to
31/// write programs that run on the Glulx VM or make use of it, including
32/// compilers, interpreters, debuggers, and so on".
33#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
34pub enum Instr<L> {
35    /// No-op
36    Nop,
37
38    // INTEGER MATH
39    /// Add L1 and L2, using standard 32-bit addition. Truncate the result to 32
40    /// bits if necessary. Store the result in S1.
41    Add(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
42    /// Compute `(L1 - L2)`, and store the result in S1.
43    Sub(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
44    /// Compute `(L1 * L2)`, and store the result in S1. Truncate the result to 32
45    /// bits if necessary.
46    Mul(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
47    /// Compute `(L1 / L2)`, and store the result in S1. This is signed integer
48    /// division.
49    Div(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
50    /// Compute `(L1 % L2)`, and store the result in S1. This is the remainder
51    /// from signed integer division.
52    ///
53    /// In division and remainer, signs are annoying. Rounding is towards zero.
54    /// The sign of a remainder equals the sign of the dividend. It is always
55    /// true that `(A / B) * B + (A % B) == A`. Some examples (in decimal):
56    ///
57    /// ```text
58    /// 11 /  2 =  5
59    /// -11 /  2 = -5
60    /// 11 / -2 = -5
61    /// -11 / -2 =  5
62    /// 13 %  5 =  3
63    /// -13 %  5 = -3
64    /// 13 % -5 =  3
65    /// -13 % -5 = -3
66    /// ```
67    Mod(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
68    /// Compute the negative of L1.
69    Neg(LoadOperand<L>, StoreOperand<L>),
70    /// Compute the bitwise AND of L1 and L2.
71    Bitand(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
72    /// Compute the bitwise OR of L1 and L2.
73    Bitor(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
74    /// Compute the bitwise XOR of L1 and L2.
75    Bitxor(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
76    /// Compute the bitwise negation of L1.
77    Bitnot(LoadOperand<L>, StoreOperand<L>),
78    /// Shift the bits of L1 to the left (towards more significant bits) by L2
79    /// places. The bottom L2 bits are filled in with zeroes. If L2 is 32 or
80    /// more, the result is always zero.
81    Shiftl(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
82    /// Shift the bits of L1 to the right by L2 places. The top L2 bits are
83    /// filled in with zeroes. If L2 is 32 or more, the result is always zero.
84    Ushiftr(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
85    /// Shift the bits of L1 to the right by L2 places. The top L2 bits are
86    /// filled in with copies of the top bit of L1. If L2 is 32 or more, the
87    /// result is always zero or `FFFFFFFF`, depending on the top bit of L1.
88    Sshiftr(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
89
90    // BRANCHING
91    /// Branch unconditionally to offset L1.
92    Jump(LoadOperand<L>),
93    /// If L1 is equal to zero, branch to L2.
94    Jz(LoadOperand<L>, LoadOperand<L>),
95    /// If L1 is not equal to zero, branch to L2.
96    Jnz(LoadOperand<L>, LoadOperand<L>),
97    /// If L1 is equal to L2, branch to L3.
98    Jeq(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
99    /// If L1 is not equal to L2, branch to L3.
100    Jne(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
101    /// If L1 is less than L2, branch to L3. The values are compared as signed
102    /// 32-bit values.
103    Jlt(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
104    /// If L1 is less than or equal to L2, branch to L3. The values are compared
105    /// as signed 32-bit values.
106    Jle(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
107    /// If L1 is greater than L2, branch to L3. The values are compared as
108    /// signed 32-bit values.
109    Jgt(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
110    /// If L1 is greater than or equal to L2, branch to L3. The values are
111    /// compared as signed 32-bit values.
112    Jge(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
113    /// If L1 is less than L2, branch to L3. The values are compared as unsigned
114    /// 32-bit values.
115    Jltu(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
116    /// If L1 is less than or equal to L2, branch to L3. The values are compared
117    /// as unsigned 32-bit values.
118    Jleu(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
119    /// If L1 is greater than L2, branch to L3. The values are compared as
120    /// unsigned 32-bit values.
121    Jgtu(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
122    /// If L1 is greater than or equal to L2, branch to L3. The values are
123    /// compared as unsigned 32-bit values.
124    Jgeu(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
125    /// Branch unconditionally to address L1. Unlike the other branch opcodes,
126    /// this takes an absolute address, not an offset. The special cases 0 and 1
127    /// (for returning) do not apply; `jumpabs 0` would branch to memory address
128    /// 0, if that were ever a good idea, which it isn't.
129    Jumpabs(LoadOperand<L>),
130
131    // MOVING DATA
132    /// Read L1 and store it at S1, without change.
133    Copy(LoadOperand<L>, StoreOperand<L>),
134    /// Read a 16-bit value from L1 and store it at S1.
135    Copys(LoadOperand<L>, StoreOperand<L>),
136    /// Read an 8-bit value from L1 and store it at S1.
137    Copyb(LoadOperand<L>, StoreOperand<L>),
138    /// Sign-extend a value, considered as a 16-bit value. If the value's `8000`
139    /// bit is set, the upper 16 bits are all set; otherwise, the upper 16 bits
140    /// are all cleared.
141    Sexs(LoadOperand<L>, StoreOperand<L>),
142    /// Sign-extend a value, considered as an 8-bit value. If the value's 80 bit
143    /// is set, the upper 24 bits are all set; otherwise, the upper 24 bits are
144    /// all cleared.
145    Sexb(LoadOperand<L>, StoreOperand<L>),
146
147    // ARRAY DATA
148    /// Store L3 into the 32-bit field at main memory address `(L1+4*L2)`.
149    Astore(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
150    /// Load a 32-bit value from main memory address `(L1+4*L2)`, and store it in S1.
151    Aload(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
152    /// Store L3 into the 16-bit field at main memory address `(L1+4*L2)`.
153    Astores(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
154    /// Load a 16-bit value from main memory address `(L1+4*L2)`, and store it in S1.
155    Aloads(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
156    /// Store L3 into the 8-bit field at main memory address `(L1+4*L2)`.
157    Astoreb(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
158    /// Load a 8-bit value from main memory address `(L1+4*L2)`, and store it in S1.
159    Aloadb(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
160    /// Set or clear a single bit. This is bit number `(L2 mod 8)` of memory
161    /// address `(L1+L2/8)`. It is cleared if L3 is zero, set if nonzero.
162    Astorebit(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
163    /// Test a single bit, similarly. If it is set, 1 is stored at S1; if clear, 0 is stored.
164    Aloadbit(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
165
166    // THE STACK
167    /// Store a count of the number of values on the stack. This counts only
168    /// values above the current call-frame. In other words, it is always zero
169    /// when a C1 function starts executing, and `(numargs+1)` when a C0
170    /// function starts executing. It then increases and decreases thereafter as
171    /// values are pushed and popped; it is always the number of values that can
172    /// be popped legally. (If S1 uses the stack push mode, the count is done
173    /// before the result is pushed.)
174    Stkcount(StoreOperand<L>),
175
176    /// Peek at the L1'th value on the stack, without actually popping anything.
177    /// If L1 is zero, this is the top value; if one, it's the value below that;
178    /// etc. L1 must be less than the current stack-count. (If L1 or S1 use the
179    /// stack pop/push modes, the peek is counted after L1 is popped, but before
180    /// the result is pushed.)
181    Stkpeek(LoadOperand<L>, StoreOperand<L>),
182
183    /// Swap the top two values on the stack. The current stack-count must be at
184    /// least two.
185    Stkswap,
186
187    /// Peek at the top L1 values in the stack, and push duplicates onto the
188    /// stack in the same order. If L1 is zero, nothing happens. L1 must not be
189    /// greater than the current stack-count. (If L1 uses the stack pop mode,
190    /// the stkcopy is counted after L1 is popped.)
191    ///
192    /// An example of stkcopy, starting with six values on the stack:
193    ///
194    /// ```text
195    /// 5 4 3 2 1 0 <top>
196    /// stkcopy 3
197    /// 5 4 3 2 1 0 2 1 0 <top>
198    /// ```
199    Stkcopy(LoadOperand<L>),
200
201    /// Rotate the top L1 values on the stack. They are rotated up or down L2
202    /// places, with positive values meaning up and negative meaning down. The
203    /// current stack-count must be at least L1. If either L1 or L2 is zero,
204    /// nothing happens. (If L1 and/or L2 use the stack pop mode, the roll
205    /// occurs after they are popped.)
206    ///
207    /// An example of two stkrolls, starting with nine values on the stack:
208    ///
209    /// ```text
210    /// 8 7 6 5 4 3 2 1 0 <top>
211    /// stkroll 5 1
212    /// 8 7 6 5 0 4 3 2 1 <top>
213    /// stkroll 9 -3
214    /// 5 0 4 3 2 1 8 7 6 <top>
215    /// ```
216    ///
217    /// Note that stkswap is equivalent to stkroll 2 1, or for that matter
218    /// stkroll 2 -1. Also, stkcopy 1 is equivalent to stkpeek 0 sp.
219    ///
220    /// These opcodes can only access the values pushed on the stack above the
221    /// current call-frame. It is illegal to stkswap, stkpeek, stkcopy, or
222    /// stkroll values below that – i.e, the locals segment or any previous
223    /// function call frames.
224    Stkroll(LoadOperand<L>, LoadOperand<L>),
225
226    // FUNCTIONS
227    /// Call function whose address is L1, passing in L2 arguments, and store
228    /// the return result at S1.
229    ///
230    /// The arguments are taken from the stack. Before you execute the call
231    /// opcode, you must push the arguments on, in backward order (last argument
232    /// pushed first, first argument topmost on the stack.) The L2 arguments are
233    /// removed before the new function's call frame is constructed. (If L1, L2,
234    /// or S1 use the stack pop/push modes, the arguments are taken after L1 or
235    /// L2 is popped, but before the result is pushed.)
236    ///
237    /// Recall that all functions in Glulx have a single 32-bit return value. If
238    /// you do not care about the return value, you can use operand mode 0
239    /// ("discard value") for operand S1.
240    Call(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
241    /// Call function whose address is L1, passing no arguments. Store the
242    /// return result at S1.
243    Callf(LoadOperand<L>, StoreOperand<L>),
244    /// Call function whose address is L1, passing one argument as L2. Store the
245    /// return result at S1.
246    Callfi(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
247    /// Call function whose address is L1, passing two argument as L2/L3. Store
248    /// the return result at S1.
249    Callfii(
250        LoadOperand<L>,
251        LoadOperand<L>,
252        LoadOperand<L>,
253        StoreOperand<L>,
254    ),
255    /// Call function whose address is L1, passing three argument as L2/L3/L4.
256    /// Store the return result at S1.
257    Callfiii(
258        LoadOperand<L>,
259        LoadOperand<L>,
260        LoadOperand<L>,
261        LoadOperand<L>,
262        StoreOperand<L>,
263    ),
264    /// Return from the current function, with the given return value. If this
265    /// is the top-level function, Glulx execution is over.
266    Return(LoadOperand<L>),
267
268    ///Call function whose address is L1, passing in L2 arguments, and pass the
269    ///return result out to whoever called the current function.
270    ///
271    /// This destroys the current call-frame, as if a return had been executed,
272    /// but does not touch the call stub below that. It then immediately calls
273    /// L1, creating a new call-frame. The effect is the same as a call
274    /// immediately followed by a return, but takes less stack space.
275    ///
276    /// It is legal to use tailcall from the top-level function. L1 becomes the
277    /// top-level function.
278    Tailcall(LoadOperand<L>, LoadOperand<L>),
279
280    // CONTINUATIONS
281    /// Generates a "catch token", which can be used to jump back to this
282    /// execution point from a throw opcode. The token is stored in S1, and then
283    /// execution branches to offset L1. If execution is proceeding from this
284    /// point because of a throw, the thrown value is stored instead, and the
285    /// branch is ignored.
286    ///
287    /// Remember if the branch value is not 0 or 1, the branch is to to (Addr +
288    /// L1 - 2), where Addr is the address of the instruction after the catch.
289    /// If the value is 0 or 1, the function returns immediately, invalidating
290    /// the catch token.
291    ///
292    ///If S1 or L1 uses the stack push/pop modes, note that the precise order of
293    ///execution is: evaluate L1 (popping if appropriate); generate a call stub
294    ///and compute the token; store S1 (pushing if appropriate).
295    Catch(StoreOperand<L>, LoadOperand<L>),
296
297    /// Jump back to a previously-executed catch opcode, and store the value L1.
298    /// L2 must be a valid catch token.
299    ///
300    /// The exact catch/throw procedure is as follows:
301    ///
302    /// When catch is executed, a four-value call stub is pushed on the stack –
303    /// result destination, PC, and FramePtr. (See section 1.3.2, "Call Stubs".
304    /// The PC is the address of the next instruction after the catch.) The
305    /// catch token is the value of the stack pointer after these are pushed.
306    /// The token value is stored in the result destination, and execution
307    /// proceeds, branching to L1.
308    ///
309    /// When throw is executed, the stack is popped down until the stack pointer
310    /// equals the given token. Then the four values are read back off the
311    /// stack, the thrown value is stored in the destination, and execution
312    /// proceeds with the instruction after the catch.
313    ///
314    /// If the call stub (or any part of it) is removed from the stack, the
315    /// catch token becomes invalid, and must not be used. This will certainly
316    /// occur when you return from the function containing the catch opcode. It
317    /// will also occur if you pop too many values from the stack after
318    /// executing the catch. (You may wish to do this to "cancel" the catch; if
319    /// you pop and discard those four values, the token is invalidated, and it
320    /// is as if you had never executed the catch at all.) The catch token is
321    /// also invalidated if any part of the call stub is overwritten (e.g. with
322    /// stkswap or stkroll).
323    Throw(LoadOperand<L>, LoadOperand<L>),
324
325    // MEMORY MAP
326    /// Store the current size of the memory map. This is originally the ENDMEM
327    /// value from the header, but you can change it with the setmemsize opcode.
328    /// (The malloc and mfree opcodes may also cause this value to change; see
329    /// section 2.9, "Memory Allocation Heap".) It will always be greater than
330    /// or equal to ENDMEM, and will always be a multiple of 256.
331    Getmemsize(StoreOperand<L>),
332
333    /// Set the current size of the memory map. The new value must be a multiple
334    /// of 256, like all memory boundaries in Glulx. It must be greater than or
335    /// equal to ENDMEM (the initial memory-size value which is stored in the
336    /// header.) It does not have to be greater than the previous memory size.
337    /// The memory size may grow and shrink over time, as long as it never gets
338    /// smaller than the initial size.
339    ///
340    /// When the memory size grows, the new space is filled with zeroes. When it
341    /// shrinks, the contents of the old space are lost.
342    ///
343    /// If the allocation heap is active (see section 2.9, "Memory Allocation
344    /// Heap") you may not use setmemsize – the memory map is under the control
345    /// of the heap system. If you free all heap objects, the heap will then no
346    /// longer be active, and you can use setmemsize.
347    ///
348    /// Since memory allocation is never guaranteed, you must be prepared for
349    /// the possibility that setmemsize will fail. The opcode stores the value
350    /// zero if it succeeded, and 1 if it failed. If it failed, the memory size
351    /// is unchanged.
352    ///
353    /// Some interpreters do not have the capability to resize memory at all. On
354    /// such interpreters, setmemsize will always fail. You can check this in
355    /// advance with the ResizeMem gestalt selector.
356    ///
357    /// Note that the memory size is considered part of the game state. If you
358    /// restore a saved game, the current memory size is changed to the size
359    /// that was in effect when the game was saved. If you restart, the current
360    /// memory size is reset to its initial value.
361    Setmemsize(LoadOperand<L>, StoreOperand<L>),
362
363    /// Manage the memory allocation heap.
364    ///
365    /// Allocate a memory block of L1 bytes. (L1 must be positive.) This stores
366    /// the address of the new memory block, which will be within the heap and
367    /// will not overlap any other extant block. The interpreter may have to
368    /// extend the memory map (see section 2.8, "Memory Map") to accomodate the
369    /// new block.
370    ///
371    /// This operation does not change the contents of the memory block (or,
372    /// indeed, the contents of the memory map at all). If you want the memory
373    /// block to be initialized, you must do it yourself.
374    ///
375    /// If the allocation fails, this stores zero.
376    ///
377    /// Glulx is able to maintain a list of dynamically-allocated memory
378    /// objects. These objects exist in the memory map, above ENDMEM. The malloc
379    /// and mfree opcodes allow the game to request the allocation and
380    /// destruction of these objects.
381    ///
382    /// Some interpreters do not have the capability to manage an allocation
383    /// heap. On such interpreters, malloc will always fail. You can check this
384    /// in advance with the MAlloc gestalt selector.
385    ///
386    /// When you first allocate a block of memory, the heap becomes active. The
387    /// current end of memory – that is, the current getmemsize value – becomes
388    /// the beginning address of the heap. The memory map is then extended to
389    /// accomodate the memory block.
390    ///
391    /// Subsequent memory allocations and deallocations are done within the
392    /// heap. The interpreter may extend or reduce the memory map, as needed,
393    /// when allocations and deallocations occur. While the heap is active, you
394    /// may not manually resize the memory map with setmemsize; the heap system
395    /// is responsible for doing that.
396    ///
397    /// When you free the last extant memory block, the heap becomes inactive.
398    /// The interpreter will reduce the memory map size down to the heap-start
399    /// address. (That is, the getmemsize value returns to what it was before
400    /// you allocated the first block.) Thereafter, it is legal to call
401    /// setmemsize again.
402    ///
403    /// It is legitimate to read or write any memory address in the heap range
404    /// (from ENDMEM to the end of the memory map). You are not restricted to
405    /// extant blocks. [The VM's heap state is not stored in its own memory map.
406    /// So, unlike the familiar C heap, you cannot damage it by writing outside
407    /// valid blocks.]
408    ///
409    ///The heap state (whether it is active, its starting address, and the
410    ///addresses and sizes of all extant blocks) is part of the saved game
411    ///state.
412    Malloc(LoadOperand<L>, StoreOperand<L>),
413
414    /// Free the memory block at address L1. This must be the address of an
415    /// extant block – that is, a value returned by malloc and not previously
416    /// freed.
417    ///
418    /// This operation does not change the contents of the memory block (or,
419    /// indeed, the contents of the memory map at all).
420    Mfree(LoadOperand<L>),
421
422    // GAME STATE
423    /// Shut down the terp and exit. This is equivalent to returning from the
424    /// top-level function, or for that matter calling glk_exit().
425    ///
426    /// Note that (in the Glk I/O system) Glk is responsible for any "hit any
427    /// key to exit" prompt. It is safe for you to print a bunch of final text
428    /// and then exit immediately.
429    Quit,
430
431    /// Restore the VM to its initial state (memory, stack, and registers). Note
432    /// that the current memory size is reset, as well as the contents of
433    /// memory.
434    Restart,
435
436    /// Save the VM state to the output stream L1. It is your responsibility to
437    /// prompt the player for a filespec, open the stream, and then destroy
438    /// these objects afterward. S1 is set to zero if the operation succeeded, 1
439    /// if it failed, and -1 if the VM has just been restored and is continuing
440    /// from this instruction.
441
442    /// (In the Glk I/O system, L1 should be the ID of a writable Glk stream. In
443    /// other I/O systems, it will mean something different. In the "filter" and
444    /// "null" I/O systems, the save opcode is illegal, as the interpreter has
445    /// nowhere to write the state.)
446    Save(LoadOperand<L>, StoreOperand<L>),
447
448    /// Restore the VM state from the input stream L1. S1 is set to 1 if the
449    /// operation failed. If it succeeded, of course, this instruction never
450    /// returns a value.
451    Restore(LoadOperand<L>, StoreOperand<L>),
452
453    /// Save the VM state in a temporary location. The terp will choose a
454    /// location appropriate for rapid access, so this may be called once per
455    /// turn. S1 is set to zero if the operation succeeded, 1 if it failed, and
456    /// -1 if the VM state has just been restored.
457    Saveundo(StoreOperand<L>),
458
459    /// Restore the VM state from temporary storage. S1 is set to 1 if the
460    /// operation failed.
461    Restoreundo(StoreOperand<L>),
462
463    /// Test whether a VM state is available in temporary storage. S1 is set to
464    /// 0 if a state is available, 1 if not. If this returns 0, then restoreundo
465    /// is expected to succeed.
466    Hasundo(StoreOperand<L>),
467
468    /// Discard a VM state (the most recently saved) from temporary storage. If
469    /// none is available, this does nothing.
470    ///
471    /// The hasundo and discardundo opcodes were added in Glulx 3.1.3. You can
472    /// check for their existence with the ExtUndo gestalt selector.
473    Discardundo,
474
475    /// Protect a range of memory from restart, restore, restoreundo. The
476    /// protected range starts at address L1 and has a length of L2 bytes. This
477    /// memory is silently unaffected by the state-restoring operations.
478    /// (However, if the result-storage S1 is directed into the protected range,
479    /// that is not blocked.)
480    ///
481    /// When the VM starts up, there is no protection range. Only one range can
482    /// be protected at a time. Calling protect cancels any previous range. To
483    /// turn off protection, call protect with L1 and L2 set to zero.
484    ///
485    /// It is important to note that the protection range itself (its existence,
486    /// location, and length) is not part of the saved game state! If you save a
487    /// game, move the protection range to a new location, and then restore that
488    /// game, it is the new range that will be protected, and the range will
489    /// remain there afterwards.
490    Protect(LoadOperand<L>, LoadOperand<L>),
491
492    /// Perform sanity checks on the game file, using its length and checksum.
493    /// S1 is set to zero if everything looks good, 1 if there seems to be a
494    /// problem. (Many interpreters will do this automatically, before the game
495    /// starts executing. This opcode is provided mostly for slower
496    /// interpreters, where auto-verify might cause an unacceptable delay.)
497    Verify(StoreOperand<L>),
498
499    // OUTPUT
500    /// Return the current I/O system mode and rock.
501    ///
502    /// Due to a long-standing bug in the reference interpreter, the two store
503    /// operands must be of the same general type: both main-memory/global
504    /// stores, both local variable stores, or both stack pushes.
505    Getiosys(StoreOperand<L>, StoreOperand<L>),
506
507    /// Set the I/O system mode and rock. If the system L1 is not supported by
508    /// the interpreter, it will default to the "null" system (0).
509
510    /// These systems are currently defined:
511
512    /// * 0: The null system. All output is discarded. (When the Glulx machine
513    ///    starts up, this is the current system.)
514    ///
515    /// * 1: The filtering system. The rock (L2) value should be the address of a
516    ///    Glulx function. This function will be called for every character output
517    ///    (with the character value as its sole argument). The function's return
518    ///    value is ignored.
519    ///
520    /// * 2: The Glk system. All output will be handled through Glk function
521    ///    calls, sent to the current Glk stream.
522    ///
523    /// * 20: The FyreVM channel system. See section 0.2, "Glulx and Other IF
524    ///   Systems".
525    ///
526    /// The values 140-14F are reserved for extension projects by ZZO38. These
527    /// are not documented here.
528    ///
529    /// It is important to recall that when Glulx starts up, the Glk I/O system
530    /// is not set. And when Glk starts up, there are no windows and no current
531    /// output stream. To make anything appear to the user, you must first do
532    /// three things: select the Glk I/O system, open a Glk window, and set its
533    /// stream as the current one. (It is illegal in Glk to send output when
534    /// there is no stream set. Sending output to Glulx's "null" I/O system is
535    /// legal, but pointless.)
536    Setiosys(LoadOperand<L>, LoadOperand<L>),
537
538    /// Send L1 to the current stream. This sends a single character; the value L1 is truncated to eight bits.
539    Streamchar(LoadOperand<L>),
540
541    /// Send L1 to the current stream. This sends a single (32-bit) character.
542    ///
543    /// This opcode was added in Glulx version 3.0.
544    Streamunichar(LoadOperand<L>),
545
546    /// Send L1 to the current stream, represented as a signed decimal number in ASCII.
547    Streamnum(LoadOperand<L>),
548
549    /// Send a string object to the current stream. L1 must be the address of a
550    /// Glulx string object (type E0, E1, or E2.) The string is decoded and sent
551    /// as a sequence of characters.
552    ///
553    /// When the Glk I/O system is set, these opcodes are implemented using the
554    /// Glk API. You can bypass them and directly call glk_put_char(),
555    /// glk_put_buffer(), and so on. Remember, however, that glk_put_string()
556    /// only accepts unencoded string (E0) objects; glk_put_string_uni() only
557    /// accepts unencoded Unicode (E2) objects.
558    ///
559    /// Note that it is illegal to decode a compressed string (E1) if there is
560    /// no string-decoding table set.
561    Streamstr(LoadOperand<L>),
562
563    /// Return the address the terp is currently using for its string-decoding
564    /// table. If there is no table, set, this returns zero.
565    Getstringtbl(StoreOperand<L>),
566
567    /// Change the address the terp is using for its string-decoding table. This
568    /// may be zero, indicating that there is no table (in which case it is
569    /// illegal to print any compressed string). Otherwise, it must be the
570    /// address of a valid string-decoding table.
571    Setstringtbl(LoadOperand<L>),
572
573    /// Convert an integer value to the closest equivalent float. (That is, if
574    /// L1 is 1, then 3F800000 – the float encoding of 1.0 – will be stored in
575    /// S1.) Integer zero is converted to (positive) float zero.
576    ///
577    /// If the value is less than -1000000 or greater than 1000000 (hex), the
578    /// conversion may not be exact. (More specifically, it may round to a
579    /// nearby multiple of a power of 2.)
580    Numtof(LoadOperand<L>, StoreOperand<L>),
581
582    /// Convert a float value to an integer, rounding towards zero (i.e.,
583    /// truncating the fractional part). If the value is outside the 32-bit
584    /// integer range, or is NaN or infinity, the result will be 7FFFFFFF (for
585    /// positive values) or 80000000 (for negative values).
586    Ftonumz(LoadOperand<L>, StoreOperand<L>),
587
588    /// Convert a float value to an integer, rounding towards the nearest
589    /// integer. Again, overflows become 7FFFFFFF or 80000000.\
590    Ftonumn(LoadOperand<L>, StoreOperand<L>),
591
592    /// Floating point addition. Overflows produce infinite values (with the
593    /// appropriate sign); underflows produce zero values (ditto). 0/0 is NaN.
594    /// Inf/Inf, or Inf-Inf, is NaN. Any finite number added to infinity is
595    /// infinity. Any nonzero number divided by an infinity, or multiplied by
596    /// zero, is a zero. Any nonzero number multiplied by an infinity, or
597    /// divided by zero, is an infinity.
598    Fadd(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
599
600    ///  Floating pointing subtraction.
601    Fsub(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
602
603    ///  Floating pointing multiplication.
604    Fmul(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
605
606    ///  Floating pointing division.
607    Fdiv(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
608
609    ///  Perform a floating-point modulo operation. S1 is the remainder (or
610    ///  modulus); S2 is the quotient.
611    ///
612    /// S2 is L1/L2, rounded (towards zero) to an integral value. S1 is
613    /// L1-(S2*L2). Note that S1 always has the same sign as L1; S2 has the
614    /// appropriate sign for L1/L2.
615    ///
616    /// If L2 is 1, this gives you the fractional and integer parts of L1. If L1
617    /// is zero, both results are zero. If L2 is infinite, S1 is L1 and S2 is
618    /// zero. If L1 is infinite or L2 is zero, both results are NaN.
619    Fmod(
620        LoadOperand<L>,
621        LoadOperand<L>,
622        StoreOperand<L>,
623        StoreOperand<L>,
624    ),
625
626    /// Round L1 up (towards +Inf) to the nearest integral value. (The result is
627    /// still in float format, however.) These opcodes are idempotent.
628    ///
629    /// The result keeps the sign of L1; in particular, floor(0.5) is 0 and
630    /// ceil(−0.5) is −0. Rounding −0 up or down gives −0. Rounding an infinite
631    /// value gives infinity.
632    Ceil(LoadOperand<L>, StoreOperand<L>),
633
634    /// Round L1 down (toward -Inf) to the nearest integral value.
635    Floor(LoadOperand<L>, StoreOperand<L>),
636
637    /// Compute the square root of L1.
638    ///
639    /// sqrt(−0) is −0. sqrt returns NaN for all other negative values.
640    Sqrt(LoadOperand<L>, StoreOperand<L>),
641    /// Compute exp(L1).
642    ///
643    /// exp(+0) and exp(−0) are 1; exp(−Inf) is +0.
644    Exp(LoadOperand<L>, StoreOperand<L>),
645    /// Compute ln(L1).
646    ///
647    /// log(+0) and log(−0) are −Inf. log returns NaN for all other negative values.
648    Log(LoadOperand<L>, StoreOperand<L>),
649
650    /// Compute L1 raised to the L2 power.
651    Pow(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
652
653    /// Standard trigonometric sine function.
654    Sin(LoadOperand<L>, StoreOperand<L>),
655    /// Standard trigonometric cosine function.
656    Cos(LoadOperand<L>, StoreOperand<L>),
657    /// Standard trigonometric tangent function.
658    Tan(LoadOperand<L>, StoreOperand<L>),
659    /// Standard trigonometric arcsine function.
660    Asin(LoadOperand<L>, StoreOperand<L>),
661    /// Standard trigonometric arccosine function.
662    Acos(LoadOperand<L>, StoreOperand<L>),
663    /// Standard trigonometric arctangent function.
664    Atan(LoadOperand<L>, StoreOperand<L>),
665    /// Computes the arctangent of L1/L2, using the signs of both arguments to
666    /// determine the quadrant of the return value. (Note that the Y argument is
667    /// first and the X argument is second.)
668    Atan2(LoadOperand<L>, StoreOperand<L>),
669
670    /// Convert an integer value to the closest equivalent double. Integer zero
671    /// is converted to (positive) double zero. The result is stored as S2:S1.
672    Numtod(LoadOperand<L>, StoreOperand<L>, StoreOperand<L>),
673
674    /// Convert a double value L1:L2 to an integer, rounding towards zero (i.e.,
675    /// truncating the fractional part). If the value is outside the 32-bit
676    /// integer range, or is NaN or infinity, the result will be 7FFFFFFF (for
677    /// positive values) or 80000000 (for negative values).
678    Dtonumz(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
679
680    /// Convert a double value L1:L2 to an integer, rounding towards the nearest
681    /// integer. Again, overflows become 7FFFFFFF or 80000000.
682    Dtonumn(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
683
684    /// Convert a float value L1 to a double value, stored as S2:S1.
685    Ftod(LoadOperand<L>, StoreOperand<L>, StoreOperand<L>),
686
687    /// Convert a double value L1:L2 to a float value, stored as S1.
688    Dtof(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
689
690    /// Add doubles. The arguments are L1:L2 and L3:L4; the result is stored as S2:S1.
691    Dadd(
692        LoadOperand<L>,
693        LoadOperand<L>,
694        LoadOperand<L>,
695        LoadOperand<L>,
696        StoreOperand<L>,
697        StoreOperand<L>,
698    ),
699
700    /// Subtract doubles. The arguments are L1:L2 and L3:L4; the result is stored as S2:S1.
701    Dsub(
702        LoadOperand<L>,
703        LoadOperand<L>,
704        LoadOperand<L>,
705        LoadOperand<L>,
706        StoreOperand<L>,
707        StoreOperand<L>,
708    ),
709
710    /// Multiply doubles. The arguments are L1:L2 and L3:L4; the result is stored as S2:S1.
711    Dmul(
712        LoadOperand<L>,
713        LoadOperand<L>,
714        LoadOperand<L>,
715        LoadOperand<L>,
716        StoreOperand<L>,
717        StoreOperand<L>,
718    ),
719
720    /// Divide doubles. The arguments are L1:L2 and L3:L4; the result is stored as S2:S1.
721    Ddiv(
722        LoadOperand<L>,
723        LoadOperand<L>,
724        LoadOperand<L>,
725        LoadOperand<L>,
726        StoreOperand<L>,
727        StoreOperand<L>,
728    ),
729
730    /// Get the remainder of a floating point modulo operation. The arguments
731    /// are L1:L2 and L3:L4; the result is stored as S2:S1.
732    ///
733    /// Unlike fmod, there are separate opcodes to compute the remainder and
734    /// modulus.
735    Dmodr(
736        LoadOperand<L>,
737        LoadOperand<L>,
738        LoadOperand<L>,
739        LoadOperand<L>,
740        StoreOperand<L>,
741        StoreOperand<L>,
742    ),
743
744    /// Get the quotient of a float point modulo operation. The arguments are
745    /// L1:L2 and L3:L4; the result is stored as S2:S1.
746    ///
747    /// Unlike fmod, there are separate opcodes to compute the remainder and
748    /// modulus.
749    Dmodq(
750        LoadOperand<L>,
751        LoadOperand<L>,
752        LoadOperand<L>,
753        LoadOperand<L>,
754        StoreOperand<L>,
755        StoreOperand<L>,
756    ),
757
758    /// Round L1:L2 up (towards +Inf) to the nearest integral value. (The result
759    /// is still in double format, however.) The result is stored as S2:S1.
760    /// These opcodes are idempotent.
761    Dceil(
762        LoadOperand<L>,
763        LoadOperand<L>,
764        StoreOperand<L>,
765        StoreOperand<L>,
766    ),
767
768    /// Round L1:L2 down (towards −Inf) to the nearest integral value. (The
769    /// result is still in double format, however.) The result is stored as
770    /// S2:S1. These opcodes are idempotent.
771    Dfloor(
772        LoadOperand<L>,
773        LoadOperand<L>,
774        StoreOperand<L>,
775        StoreOperand<L>,
776    ),
777
778    /// Compute the square root of L1:L2.
779    Dsqrt(
780        LoadOperand<L>,
781        LoadOperand<L>,
782        StoreOperand<L>,
783        StoreOperand<L>,
784    ),
785
786    /// Compute exp(L1:L2).
787    Dexp(
788        LoadOperand<L>,
789        LoadOperand<L>,
790        StoreOperand<L>,
791        StoreOperand<L>,
792    ),
793
794    /// Compute ln(L1:L2).
795    Dlog(
796        LoadOperand<L>,
797        LoadOperand<L>,
798        StoreOperand<L>,
799        StoreOperand<L>,
800    ),
801
802    /// Compute L1:L2 raised to the L3:L4 power. The result is stored as S2:S1.
803    Dpow(
804        LoadOperand<L>,
805        LoadOperand<L>,
806        LoadOperand<L>,
807        LoadOperand<L>,
808        StoreOperand<L>,
809        StoreOperand<L>,
810    ),
811
812    /// Compute the standard trigonometric sine of (L1:L2).
813    Dsin(
814        LoadOperand<L>,
815        LoadOperand<L>,
816        StoreOperand<L>,
817        StoreOperand<L>,
818    ),
819    /// Compute the standard trigonometric cosine of (L1:L2).
820    Dcos(
821        LoadOperand<L>,
822        LoadOperand<L>,
823        StoreOperand<L>,
824        StoreOperand<L>,
825    ),
826    /// Compute the standard trigonometric tangent of (L1:L2).
827    Dtan(
828        LoadOperand<L>,
829        LoadOperand<L>,
830        StoreOperand<L>,
831        StoreOperand<L>,
832    ),
833    /// Compute the standard trigonometric arcsine of (L1:L2).
834    Dasin(
835        LoadOperand<L>,
836        LoadOperand<L>,
837        StoreOperand<L>,
838        StoreOperand<L>,
839    ),
840    /// Compute the standard trigonometric arccosine of (L1:L2).
841    Dacos(
842        LoadOperand<L>,
843        LoadOperand<L>,
844        StoreOperand<L>,
845        StoreOperand<L>,
846    ),
847    /// Compute the standard trigonometric arctangent of (L1:L2).
848    Datan(
849        LoadOperand<L>,
850        LoadOperand<L>,
851        StoreOperand<L>,
852        StoreOperand<L>,
853    ),
854
855    /// Computes the arctangent of L1:L2/L3:L4, using the signs of both
856    /// arguments to determine the quadrant of the return value. (Note that the
857    /// Y argument is first and the X argument is second.) The result is stored
858    /// as S2:S1.
859    Datan2(
860        LoadOperand<L>,
861        LoadOperand<L>,
862        LoadOperand<L>,
863        LoadOperand<L>,
864        StoreOperand<L>,
865        StoreOperand<L>,
866    ),
867
868    // FLOATING POINT COMPARISONS
869    /// Branch to L2 if the floating-point value L1 is a NaN value.
870    Jisnan(LoadOperand<L>, LoadOperand<L>),
871
872    /// Branch to L2 if the floating-point value L1 is an infinity (7F800000 or FF800000).
873    Jisinf(LoadOperand<L>, LoadOperand<L>),
874
875    /// Branch to L4 if the difference between L1 and L2 is less than or equal
876    /// to (plus or minus) L3. The sign of L3 is ignored.
877    ///
878    /// If any of the arguments are NaN, this will not branch. If L3 is
879    /// infinite, this will always branch – unless L1 and L2 are opposite
880    /// infinities. (Opposite infinities are never equal, regardless of L3.
881    /// Infinities of the same sign are always equal.)
882    ///
883    /// If L3 is (plus or minus) zero, this tests for exact equality. Note that
884    /// +0 is considered exactly equal to −0.
885    Jfeq(
886        LoadOperand<L>,
887        LoadOperand<L>,
888        LoadOperand<L>,
889        LoadOperand<L>,
890    ),
891
892    /// The reverse of jfeq. This will branch if any of the arguments is NaN.
893    Jfne(
894        LoadOperand<L>,
895        LoadOperand<L>,
896        LoadOperand<L>,
897        LoadOperand<L>,
898    ),
899
900    /// Branch to L3 if L1 is less than L2.
901    Jflt(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
902    /// Branch to L3 if L1 is less than or equal to L2.
903    Jfle(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
904    /// Branch to L3 if L1 is greater than L2.
905    Jfgt(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
906    /// Branch to L3 if L1 is greater than or equal to L2.
907    Jfge(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
908
909    // DOUBLE PRECISION COMPARISONS
910    /// Branch to L3 if the double value L1:L2 is a NaN value.
911    Jdisnan(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
912
913    /// Branch to L3 if the double value L1:L2 is an infinity.
914    Jdisinf(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
915
916    /// Branch to L7 if the difference between L1:L2 and L3:L4 is less than or
917    /// equal to (plus or minus) L5:L6. The sign of L5:L6 is ignored.
918    Jdeq(
919        LoadOperand<L>,
920        LoadOperand<L>,
921        LoadOperand<L>,
922        LoadOperand<L>,
923        LoadOperand<L>,
924        LoadOperand<L>,
925        LoadOperand<L>,
926    ),
927
928    /// The reverse of jdeq
929    Jdne(
930        LoadOperand<L>,
931        LoadOperand<L>,
932        LoadOperand<L>,
933        LoadOperand<L>,
934        LoadOperand<L>,
935        LoadOperand<L>,
936        LoadOperand<L>,
937    ),
938
939    /// Branch to L5 if L1:L2 is less than L3:L4.
940    Jdlt(
941        LoadOperand<L>,
942        LoadOperand<L>,
943        LoadOperand<L>,
944        LoadOperand<L>,
945        LoadOperand<L>,
946    ),
947    /// Branch to L5 if L1:L2 is less than or equal to L3:L4.
948    Jdle(
949        LoadOperand<L>,
950        LoadOperand<L>,
951        LoadOperand<L>,
952        LoadOperand<L>,
953        LoadOperand<L>,
954    ),
955    /// Branch to L5 if L1:L2 is greater than L3:L4.
956    Jdgt(
957        LoadOperand<L>,
958        LoadOperand<L>,
959        LoadOperand<L>,
960        LoadOperand<L>,
961        LoadOperand<L>,
962    ),
963    /// Branch to L5 if L1:L2 is greater than or equal to L3:L4.
964    Jdge(
965        LoadOperand<L>,
966        LoadOperand<L>,
967        LoadOperand<L>,
968        LoadOperand<L>,
969        LoadOperand<L>,
970    ),
971
972    // RANDOM NUMBER GENERATOR
973    /// Return a random number in the range 0 to (L1-1); or, if L1 is negative,
974    /// the range (L1+1) to 0. If L1 is zero, return a random number in the full
975    /// 32-bit integer range. (Remember that this may be either positive or
976    /// negative.)
977    Random(LoadOperand<L>, StoreOperand<L>),
978
979    /// Seed the random-number generator with the value L1. If L1 is zero,
980    /// subsequent random numbers will be as genuinely unpredictable as the terp
981    /// can provide; it may include timing data or other random sources in its
982    /// generation. If L1 is nonzero, subsequent random numbers will follow a
983    /// deterministic sequence, always the same for a given nonzero seed.
984    ///
985    /// The terp starts up in the "nondeterministic" mode (as if setrandom 0 had
986    /// been invoked.)
987    ///
988    /// The random-number generator is not part of the saved-game state.
989    Setrandom(LoadOperand<L>),
990
991    // BLOCK COPY AND CLEAR
992    /// Write L1 zero bytes, starting at address L2.
993    Mzero(LoadOperand<L>, LoadOperand<L>),
994
995    /// Copy L1 bytes from address L2 to address L3. It is safe to copy a block to an overlapping block.
996    Mcopy(LoadOperand<L>, LoadOperand<L>, LoadOperand<L>),
997
998    // SEARCHING
999    /// Accelerated linear search.
1000    ///
1001    /// * L1: Key
1002    /// * L2: KeySize
1003    /// * L3: Start
1004    /// * L4: StructSize
1005    /// * L5: NumStructs
1006    /// * L6: KeyOffset
1007    /// * L7: Options
1008    /// * S1: Result
1009    ///
1010    /// An array of data structures is stored in memory, beginning at `Start`,
1011    /// each structure being `StructSize` bytes. Within each struct, there is a
1012    /// key value `KeySize` bytes long, starting at position `KeyOffset` (from
1013    /// the start of the structure.) Search through these in order. If one is
1014    /// found whose key matches, return it. If `NumStructs` are searched with no
1015    /// result, the search fails.
1016    ///
1017    /// NumStructs may be -1 (`0xFFFFFFFF``) to indicate no upper limit to the
1018    /// number of structures to search. The search will continue until a match
1019    /// is found, or (if `ZeroKeyTerminates`` is used) a zero key.  
1020    ///
1021    /// The following options may be set in `L7`:
1022    /// * KeyIndirect (`0x01`): This flag indicates that the `Key`` argument passed
1023    ///   to the opcode is the address of the actual key. If this flag is not
1024    ///   used, the Key argument is the key value itself. (In this case, the
1025    ///   KeySize must be 1, 2, or 4 – the native sizes of Glulx values. If the
1026    ///   KeySize is 1 or 2, the lower bytes of the Key are used and the upper
1027    ///   bytes ignored.)
1028    /// * ZeroKeyTerminates (`0x02`): This flag indicates that the search should
1029    ///   stop (and return failure) if it encounters a structure whose key is
1030    ///   all zeroes. If the searched-for key happens to also be all zeroes, the
1031    ///   success takes precedence.
1032    /// * ReturnIndex (`0x04`): This flag indicates that search should return the
1033    ///   array index of the structure that it finds, or -1 (`0xFFFFFFFF`) for
1034    ///   failure. If this flag is not used, the search returns the address of
1035    ///   the structure that it finds, or 0 for failure.
1036    Linearsearch(
1037        LoadOperand<L>,
1038        LoadOperand<L>,
1039        LoadOperand<L>,
1040        LoadOperand<L>,
1041        LoadOperand<L>,
1042        LoadOperand<L>,
1043        LoadOperand<L>,
1044        StoreOperand<L>,
1045    ),
1046    /// Accelerated binary search.
1047    ///
1048    /// * L1: Key
1049    /// * L2: KeySize
1050    /// * L3: Start
1051    /// * L4: StructSize
1052    /// * L5: NumStructs
1053    /// * L6: KeyOffset
1054    /// * L7: Options
1055    /// * S1: Result
1056    ///
1057    /// An array of data structures is in memory, as above. However, the structs
1058    /// must be stored in forward order of their keys (taking each key to be a
1059    /// big-endian unsigned integer.) There can be no duplicate keys. `NumStructs``
1060    /// must indicate the exact length of the array; it cannot be -1.
1061    ///
1062    /// The `KeyIndirect`` and `ReturnIndex`` options may be used.
1063    Binarysearch(
1064        LoadOperand<L>,
1065        LoadOperand<L>,
1066        LoadOperand<L>,
1067        LoadOperand<L>,
1068        LoadOperand<L>,
1069        LoadOperand<L>,
1070        LoadOperand<L>,
1071        StoreOperand<L>,
1072    ),
1073    /// Accelerated linked-list search.
1074    ///
1075    /// * L1: Key
1076    /// * L2: KeySize
1077    /// * L3: Start
1078    /// * L4: KeyOffset
1079    /// * L5: NextOffset
1080    /// * L6: Options
1081    /// * S1: Result
1082    ///
1083    /// The structures need not be consecutive; they may be anywhere in memory,
1084    /// in any order. They are linked by a four-byte address field, which is
1085    /// found in each struct at position NextOffset. If this field contains
1086    /// zero, it indicates the end of the linked list.
1087    ///
1088    /// The KeyIndirect and ZeroKeyTerminates options may be used.
1089    Linkedsearch(
1090        LoadOperand<L>,
1091        LoadOperand<L>,
1092        LoadOperand<L>,
1093        LoadOperand<L>,
1094        LoadOperand<L>,
1095        LoadOperand<L>,
1096        StoreOperand<L>,
1097    ),
1098
1099    // ACCELERATED FUNCTIONS
1100    /// Request that the VM function with address L2 be replaced by the
1101    /// accelerated function whose number is L1. If L1 is zero, the acceleration
1102    /// for address L2 is cancelled.
1103    ///
1104    /// If the terp does not offer accelerated function L1, this does nothing.
1105    ///
1106    /// If you request acceleration at an address which is already accelerated,
1107    /// the previous request is cancelled before the new one is considered. If
1108    /// you cancel at an unaccelerated address, nothing happens.
1109    ///
1110    /// A given accelerated function L1 may replace several VM functions (at
1111    /// different addresses) at the same time. Each request is considered
1112    /// separate, and must be cancelled separately.
1113    Accelfunc(LoadOperand<L>, LoadOperand<L>),
1114    /// Store the value L2 in the parameter table at position L1. If the terp
1115    /// does not know about parameter L1, this does nothing.
1116    Accelparam(LoadOperand<L>, LoadOperand<L>),
1117
1118    // MISCELLANEOUS
1119    /// Test the Gestalt selector number L1, with optional extra argument L2,
1120    /// and store the result in S1. If the selector is not known, store zero.
1121
1122    /// The list of L1 selectors is as follows. Note that if a selector does not
1123    /// mention L2, you should always set that argument to zero.
1124
1125    /// * GlulxVersion (0): Returns the version of the Glulx spec which the
1126    ///   interpreter implements. The upper 16 bits of the value contain a major
1127    ///   version number; the next 8 bits contain a minor version number; and
1128    ///   the lowest 8 bits contain an even more minor version number, if any.
1129    ///   This specification is version 3.1.3, so a terp implementing it would
1130    ///   return 0x00030103. Future Glulx specs will try to maintain the
1131    ///   convention that minor version changes are backwards compatible, and
1132    ///   subminor version changes are backwards and forwards compatible.
1133    /// * TerpVersion (1): Returns the version of the interpreter. The format is
1134    ///   the same as the GlulxVersion. [Each interpreter has its own version
1135    ///   numbering system, defined by its author, so this information is not
1136    ///   terribly useful. But it is convenient for the game to be able to
1137    ///   display it, in case the player is capturing version information for a
1138    ///   bug report.]
1139    /// * ResizeMem (2): Returns 1 if the terp has the potential to resize the
1140    ///   memory map, with the setmemsize opcode. If this returns 0, setmemsize
1141    ///   will always fail. [But remember that setmemsize might fail in any
1142    ///   case.]
1143    /// * Undo (3): Returns 1 if the terp has the potential to undo. If this
1144    ///   returns 0, saveundo, restoreundo, and hasundo will always fail.
1145    /// * IOSystem (4): Returns 1 if the terp supports the I/O system given in
1146    ///   L2. (The constants are the same as for the setiosys opcode: 0 for
1147    ///   null, 1 for filter, 2 for Glk, 20 for FyreVM. 0 and 1 will always
1148    ///   succeed.)
1149    /// * Unicode (5): Returns 1 if the terp supports Unicode operations. These
1150    ///   are: the E2 Unicode string type; the 04 and 05 string node types (in
1151    ///   compressed strings); the streamunichar opcode; the type-14 call stub.
1152    ///   If the Unicode selector returns 0, encountering any of these will
1153    ///   cause a fatal interpreter error.
1154    /// * MemCopy (6): Returns 1 if the interpreter supports the mzero and mcopy
1155    ///   opcodes. (This must true for any terp supporting Glulx 3.1.)
1156    /// * MAlloc (7): Returns 1 if the interpreter supports the malloc and mfree
1157    ///   opcodes. (If this is true, MemCopy and ResizeMem must also both be
1158    ///   true, so there is no need to check all three.)
1159    /// * MAllocHeap (8): Returns the start address of the heap. This is the
1160    ///   value that getmemsize had when the first memory block was allocated.
1161    ///   If the heap is not active (no blocks are extant), this returns zero.
1162    /// * Acceleration (9): Returns 1 if the interpreter supports the accelfunc
1163    ///   and accelparam opcodes. (This must true for any terp supporting Glulx
1164    ///   3.1.1.)
1165    /// * AccelFunc (10): Returns 1 if the terp implements the accelerated
1166    ///   function given in L2.
1167    /// * Float (11): Returns 1 if the interpreter supports the floating-point
1168    ///   arithmetic opcodes.
1169    /// * ExtUndo (12): Returns 1 if the interpreter supports the hasundo and
1170    ///   discardundo opcodes.
1171    /// * Double (13): Returns 1 if the interpreter supports the
1172    ///   double-precision floating-point arithmetic opcodes.
1173    ///
1174    /// Selectors 0x1000 to 0x10FF are reserved for use by FyreVM. Selectors
1175    /// 0x1100 to 0x11FF are reserved for extension projects by Dannii Willis.
1176    /// Selectors 0x1200 to 0x12FF are reserved for iOS extension features by
1177    /// Andrew Plotkin. Selectors 0x1400 to 0x14FF are reserved for iOS
1178    /// extension features by ZZO38.
1179    Gestalt(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
1180
1181    /// Interrupt execution to do something interpreter-specific with L1. If the
1182    /// interpreter has nothing in mind, it should halt with a visible error
1183    /// message.
1184    Debugtrap(LoadOperand<L>),
1185
1186    /// Call the Glk API function whose identifier is L1, passing in L2
1187    /// arguments. The return value is stored at S1. (If the Glk function has no
1188    /// return value, zero is stored at S1.)
1189
1190    /// The arguments are passed on the stack, last argument pushed first, just
1191    /// as for the call opcode.
1192
1193    /// Arguments should be represented in the obvious way. Integers and
1194    /// character are passed as integers. Glk opaque objects are passed as
1195    /// integer identifiers, with zero representing NULL. Strings and Unicode
1196    /// strings are passed as the addresses of Glulx string objects (see section
1197    /// 1.6.1, "Strings".) References to values are passed by their addresses.
1198    /// Arrays are passed by their addresses; note that an array argument,
1199    /// unlike a string argument, is always followed by an array length
1200    /// argument.
1201
1202    /// Reference arguments require more explanation. A reference to an integer
1203    /// or opaque object is the address of a 32-bit value (which, being in main
1204    /// memory, does not have to be aligned, but must be big-endian.)
1205    /// Alternatively, the value -1 (FFFFFFFF) may be passed; this is a special
1206    /// case, which means that the value is read from or written to the stack.
1207    /// Arguments are always evaluated left to right, which means that input
1208    /// arguments are popped from the stack first-topmost, but output arguments
1209    /// are pushed on last-topmost.
1210
1211    /// A reference to a Glk structure is the address of an array of 32-bit
1212    /// values in main memory. Again, -1 means that all the values are written
1213    /// to the stack. Also again, an input structure is popped off
1214    /// first-topmost, and an output structure is pushed on last-topmost.
1215
1216    /// All stack input references (-1 addresses) are popped after the Glk
1217    /// argument list is popped. [This should be obvious, since the -1 occurs in
1218    /// the Glk argument list.] Stack output references are pushed after the Glk
1219    /// call, but before the S1 result value is stored.
1220    Glk(LoadOperand<L>, LoadOperand<L>, StoreOperand<L>),
1221}