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}