Update interpreters to latest Garglk codebase
[projects/chimara/chimara.git] / interpreters / git / terp.c
1 // $Id: terp.c,v 1.42 2004/12/22 14:33:40 iain Exp $
2 // Interpreter engine.
3
4 #include "git.h"
5 #include <assert.h>
6 #include <math.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <time.h>
11
12 // -------------------------------------------------------------
13 // Global variables
14
15 git_sint32* gStackPointer;
16
17 #ifdef USE_DIRECT_THREADING
18 Opcode* gOpcodeTable;
19 #endif
20
21 // -------------------------------------------------------------
22 // Useful macros for manipulating the stack
23
24 #define LOCAL(n)   (locals[(n)])
25
26 #define PUSH(n)    *sp++ = (n)
27 #define POP        (*--sp)
28 #define READ_PC    ((git_uint32)(*pc++))
29
30 #define CHECK_FREE(n) if ((top - sp) < (n)) goto stack_overflow
31 #define CHECK_USED(n) if ((sp - values) < (n)) goto stack_underflow
32
33 // -------------------------------------------------------------
34 // Floating point support
35
36 #define ENCODE_FLOAT(f) (* (git_uint32*) &f)
37 #define DECODE_FLOAT(n) (* (git_float*) &n)
38
39 int floatCompare(git_sint32 L1, git_sint32 L2, git_sint32 L3)
40 {
41   git_float F1, F2;
42
43   if (((L3 & 0x7F800000) == 0x7F800000) && ((L3 & 0x007FFFFF) != 0))
44     return 0;
45   if ((L1 == 0x7F800000 || L1 == 0xFF800000) && (L2 == 0x7F800000 || L2 == 0xFF800000))
46     return (L1 == L2);
47
48   F1 = DECODE_FLOAT(L2) - DECODE_FLOAT(L1);
49   F2 = fabs(DECODE_FLOAT(L3));
50   return ((F1 <= F2) && (F1 >= -F2));
51 }
52
53 #ifdef USE_OWN_POWF
54 float git_powf(float x, float y)
55 {
56   if (x == 1.0f)
57     return 1.0f;
58   else if ((y == 0.0f) || (y == -0.0f))
59     return 1.0f;
60   else if ((x == -1.0f) && isinf(y))
61     return 1.0f;
62   return powf(x,y);
63 }
64 #endif
65
66 // -------------------------------------------------------------
67 // Functions
68
69 void startProgram (size_t cacheSize, enum IOMode ioMode)
70 {
71     Block pc; // Program counter (pointer into dynamically generated code)
72
73     git_sint32 L1=0, L2=0, L3=0, L4=0, L5=0, L6=0, L7=0;
74 #define S1 L1
75 #define S2 L2
76     git_float F1=0.0f, F2=0.0f, F3=0.0f, F4=0.0f;
77
78     git_sint32* base;   // Bottom of the stack.
79     git_sint32* frame;  // Bottom of the current stack frame.
80     git_sint32* locals; // Start of the locals section of the current frame.
81     git_sint32* values; // Start of the values section of the current frame.
82     git_sint32* sp;     // Next free stack slot.
83     git_sint32* top;    // The top of the stack -- that is, the first unusable slot.
84
85     git_sint32 args [64]; // Array of arguments. Count is stored in L2.
86     git_uint32 runCounter = 0;
87
88     git_uint32 ioRock = 0;
89
90     git_uint32 stringTable = memRead32(28);    
91     git_uint32 startPos    = memRead32(24);
92     git_uint32 stackSize   = memRead32(20);
93
94     git_uint32 protectPos = 0;
95     git_uint32 protectSize = 0;
96     
97     git_uint32 glulxPC = 0;
98     git_uint32 glulxOpcode = 0;
99
100     acceleration_func accelfunc;
101
102     // Initialise the code cache.
103
104 #ifdef USE_DIRECT_THREADING
105     static Opcode opcodeTable [] = {
106 #define LABEL(label) &&do_ ## label,
107 #include "labels.inc"
108     NULL};
109     gOpcodeTable = opcodeTable;
110 #endif    
111
112     initCompiler (cacheSize);
113
114     // Initialise the random number generator.
115     srand (time(NULL));
116
117     // Set up the stack.
118
119     base = malloc (stackSize);
120     if (base == NULL)
121         fatalError ("Couldn't allocate stack");
122         
123     top = base + (stackSize / 4);
124     frame = locals = values = sp = base;
125
126     // Call the first function.
127
128     L1 = startPos; // Initial PC.
129     L2 = 0; // No arguments.
130     goto do_enter_function_L1;
131
132 #ifdef USE_DIRECT_THREADING
133 #define NEXT do { ++runCounter; goto **(pc++); } while(0)
134 #else
135 #define NEXT goto next
136 //#define NEXT do { CHECK_USED(0); CHECK_FREE(0); goto next; } while (0)
137 next:
138     ++runCounter;
139     switch (*pc++)
140     {
141 #define LABEL(foo) case label_ ## foo: goto do_ ## foo;
142 #include "labels.inc"
143     default: fatalError("exec: bad opcode");
144     }
145 #endif
146
147 do_debug_step:
148     // This opcode lets us keep track of how the compiled
149     // code relates to the original glulx code.
150     glulxPC = READ_PC;     // Glulx program counter.
151     glulxOpcode = READ_PC; // Glulx opcode number.
152 //    fprintf (stdout, "\nPC: 0x%08x\nOpcode: 0x%04x\n", glulxPC, glulxOpcode);
153 //    fprintf (stdout, "Stack:");
154 //    for (L7 = 0 ; L7 < (sp - base) ; ++L7)
155 //        fprintf (stdout," 0x%x", base[L7]);
156 //    fprintf (stdout, "\n");
157     NEXT;
158
159 #define LOAD_INSTRUCTIONS(reg)                                  \
160     do_ ## reg ## _const:   reg = READ_PC; NEXT;                \
161     do_ ## reg ## _stack:   CHECK_USED(1); reg = POP; NEXT;     \
162     do_ ## reg ## _addr:    reg = memRead32 (READ_PC); NEXT;    \
163     do_ ## reg ## _local:   reg = LOCAL (READ_PC); NEXT
164
165     LOAD_INSTRUCTIONS(L1);
166     LOAD_INSTRUCTIONS(L2);
167     LOAD_INSTRUCTIONS(L3);
168     LOAD_INSTRUCTIONS(L4);
169     LOAD_INSTRUCTIONS(L5);
170     LOAD_INSTRUCTIONS(L6);
171     LOAD_INSTRUCTIONS(L7);
172
173 #define STORE_INSTRUCTIONS(reg)                                 \
174     do_ ## reg ## _stack:   CHECK_FREE(1); PUSH(reg); NEXT;     \
175     do_ ## reg ## _addr:    memWrite32 (READ_PC, reg); NEXT;    \
176     do_ ## reg ## _local:   LOCAL (READ_PC) = reg; NEXT
177
178     STORE_INSTRUCTIONS(S1);
179     STORE_INSTRUCTIONS(S2);
180
181 #define DOUBLE_LOAD(mode2) \
182     do_L1_const_L2_ ## mode2: L1 = READ_PC;             goto do_L2_ ## mode2; \
183     do_L1_stack_L2_ ## mode2: CHECK_USED(1); L1 = POP;  goto do_L2_ ## mode2; \
184     do_L1_local_L2_ ## mode2: L1 = LOCAL (READ_PC);     goto do_L2_ ## mode2; \
185     do_L1_addr_L2_ ## mode2:  L1 = memRead32 (READ_PC); goto do_L2_ ## mode2
186
187     DOUBLE_LOAD(const);
188     DOUBLE_LOAD(stack);
189     DOUBLE_LOAD(local);
190     DOUBLE_LOAD(addr);
191
192 #undef LOAD_INSTRUCTIONS
193 #undef STORE_INSTRUCTIONS
194 #undef DOUBLE_LOAD
195
196 do_L1_addr16: L1 = memRead16 (READ_PC); NEXT; 
197 do_L1_addr8:  L1 = memRead8 (READ_PC); NEXT;
198 do_S1_addr16: memWrite16 (READ_PC, S1); NEXT;
199 do_S1_addr8:  memWrite8 (READ_PC, S1); NEXT;
200
201 #define UL7 ((git_uint32)L7)
202
203 do_recompile:
204     gBlockHeader->runCounter = runCounter;
205     pc = compile (READ_PC);
206     runCounter = 0;
207         NEXT;
208         
209 do_jump_abs_L7:
210     gBlockHeader->runCounter = runCounter;
211     pc = getCode (UL7);
212     runCounter = gBlockHeader->runCounter;
213     NEXT;
214
215 do_enter_function_L1: // Arg count is in L2.
216
217     // Check for an accelerated function
218     accelfunc = accel_get_func(L1);
219     if (accelfunc) {
220         S1 = accelfunc(L2, (glui32 *) args);
221         goto do_pop_call_stub;
222     }
223
224     frame = sp;
225     // Read the function type.
226     L7 = memRead8(L1++);
227     // Parse the local variables descriptor.
228     L6 = L5 = L4 = 0;
229     do
230     {
231         L6 = memRead8(L1++); // LocalType
232         L5 = memRead8(L1++); // LocalCount
233         if (L6 != 4 && L6 != 0) // We only support 4-byte locals.
234         {
235             if (L6 == 1 || L6 == 2)
236                 fatalError("Short local variables are not supported, use Glulxe");
237             else
238                 fatalError("Local variable wasn't 4 bytes wide");
239         }
240         L4 += L5; // Cumulative local count.
241     }
242     while (L5 != 0);
243
244     // Write out the stack frame.
245     // Recall that the number of locals is stored in L4.
246
247     CHECK_FREE(3 + L4);
248     
249     PUSH (L4*4 + 12); // FrameLen
250     PUSH (12);        // LocalsPos
251     if (L4 == 0)
252         L6 = 0;
253     else
254         L6 = (4 << 24) | (L4 << 16);
255     PUSH (L6);         // format of locals
256
257     // This is where the local variables start, so:
258     locals = sp;
259     
260     // Read the arguments, based on the function type.
261     switch (L7)
262     {
263         case 0xC0: // arguments should be placed on the stack.
264             // argc is in L2; we'll randomly use L5 as scratch.
265             CHECK_FREE(L5 + 1);
266             // Initialise the local variables.
267             for ( ; L4 > 0 ; --L4)
268                 PUSH (0);
269             // This is where the temporary values start, so:
270             values = sp;
271             // Push the args onto the stack.
272             for (L5 = 0 ; L5 < L2 ; ++L5)
273                 PUSH (args [L5]);
274             // Push the arg count.
275             PUSH (L2);
276             break;
277     
278         case 0xC1: // arguments should be written into locals.
279             // argc is in L2, num locals is in L4.
280             // Stuff as many locals as possible with arguments.
281             for (L5 = 1 ; L5 <= L2 && L4 > 0 ; ++L5, --L4)
282                 PUSH (args [L2 - L5]);
283             // Initialise any remaining locals.
284             for ( ; L4 > 0 ; --L4)
285                 PUSH (0);
286             // This is where the temporary values start, so:
287             values = sp;
288             break;
289     
290         default:
291             // This isn't a function!
292             fatalError("Not a function");
293             break;
294     }
295         
296     // Start executing the function.
297     L7 = L1;
298     goto do_jump_abs_L7;
299
300     do_nop:     NEXT;
301
302 #define PEEPHOLE_STORE(tag, code)                     \
303     do_ ## tag ## _discard:  code; NEXT;              \
304     do_ ## tag ## _S1_stack: code; goto do_S1_stack;  \
305     do_ ## tag ## _S1_local: code; goto do_S1_local;  \
306     do_ ## tag ## _S1_addr:  code; goto do_S1_addr
307
308     PEEPHOLE_STORE(add,     S1 = L1 + L2);
309     PEEPHOLE_STORE(sub,     S1 = L1 - L2);
310     PEEPHOLE_STORE(mul,     S1 = L1 * L2);
311     PEEPHOLE_STORE(div,     if (L2 == 0) fatalError ("Divide by zero"); S1 = L1 / L2);
312     PEEPHOLE_STORE(mod,     if (L2 == 0) fatalError ("Divide by zero"); S1 = L1 % L2);
313
314     PEEPHOLE_STORE(neg,     S1 = -L1);
315     PEEPHOLE_STORE(bitnot,  S1 = ~L1);
316
317     PEEPHOLE_STORE(bitand,  S1 = L1 & L2);
318     PEEPHOLE_STORE(bitor,   S1 = L1 | L2);
319     PEEPHOLE_STORE(bitxor,  S1 = L1 ^ L2);
320
321     PEEPHOLE_STORE(shiftl,  if (L2 > 31 || L2 < 0) S1 = 0; else S1 = L1 << ((git_uint32) L2));
322     PEEPHOLE_STORE(sshiftr, if (L2 > 31 || L2 < 0) L2 = 31; S1 = ((git_sint32) L1) >> ((git_uint32) L2));
323     PEEPHOLE_STORE(ushiftr, if (L2 > 31 || L2 < 0) S1 = 0; else S1 = ((git_uint32) L1) >> ((git_uint32) L2));
324
325     PEEPHOLE_STORE(aload,   S1 = memRead32 (L1 + (L2<<2)));
326     PEEPHOLE_STORE(aloads,  S1 = memRead16 (L1 + (L2<<1)));
327     PEEPHOLE_STORE(aloadb,  S1 = memRead8  (L1 + L2));
328     PEEPHOLE_STORE(aloadbit,S1 = (memRead8 (L1 + (L2>>3)) >> (L2 & 7)) & 1);
329
330     PEEPHOLE_STORE(copys,   S1 = L1 & 0xFFFF);
331     PEEPHOLE_STORE(copyb,   S1 = L1 & 0x00FF);
332     PEEPHOLE_STORE(sexs,    S1 = (git_sint32)((signed short)(L1 & 0xFFFF)));
333     PEEPHOLE_STORE(sexb,    S1 = (git_sint32)((signed char)(L1 & 0x00FF)));
334
335     PEEPHOLE_STORE(fadd,    F1 = DECODE_FLOAT(L1) + DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1));
336     PEEPHOLE_STORE(fsub,    F1 = DECODE_FLOAT(L1) - DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1));
337     PEEPHOLE_STORE(fmul,    F1 = DECODE_FLOAT(L1) * DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1));
338     PEEPHOLE_STORE(fdiv,    F1 = DECODE_FLOAT(L1) / DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1));
339
340 #define PEEPHOLE_LOAD(tag,reg) \
341     do_ ## tag ## _ ## reg ## _const: reg = READ_PC; goto do_ ## tag; \
342     do_ ## tag ## _ ## reg ## _stack: CHECK_USED(1); reg = POP; goto do_ ## tag; \
343     do_ ## tag ## _ ## reg ## _local: reg = LOCAL(READ_PC); goto do_ ## tag; \
344     do_ ## tag ## _ ## reg ## _addr:  reg = memRead32(READ_PC); goto do_ ## tag
345
346     PEEPHOLE_LOAD (return, L1);
347     PEEPHOLE_LOAD (astore, L3);
348     PEEPHOLE_LOAD (astores, L3);
349     PEEPHOLE_LOAD (astoreb, L3);
350     PEEPHOLE_LOAD (astorebit, L3);
351
352 #undef PEEPHOLE_STORE
353
354     do_astore:    memWrite32 (L1 + (L2<<2), L3); NEXT;
355     do_astores:   memWrite16 (L1 + (L2<<1), L3); NEXT;
356     do_astoreb:   memWrite8  (L1 + L2, L3); NEXT;
357     do_astorebit:
358         L4 = memRead8(L1 + (L2>>3));
359         if (L3 == 0)
360             L4 &= ~(1 << (L2 & 7));
361         else
362             L4 |= (1 << (L2 & 7));
363         memWrite8(L1 + (L2>>3), L4);
364         NEXT;
365
366 #define DO_JUMP(tag, reg, cond) \
367     do_ ## tag ## _var:     L7 = READ_PC; if (cond) goto do_goto_ ## reg ## _from_L7; NEXT; \
368     do_ ## tag ## _const:   L7 = READ_PC; if (cond) goto do_jump_abs_L7; NEXT;              \
369     do_ ## tag ## _by:      L7 = READ_PC; if (cond) pc += L7; NEXT;                         \
370     do_ ## tag ## _return0: if (cond) { L1 = 0; goto do_return; } NEXT;                     \
371     do_ ## tag ## _return1: if (cond) { L1 = 1; goto do_return; } NEXT
372     
373     DO_JUMP(jump,   L1, 1 == 1);
374     DO_JUMP(jz,     L2, L1 == 0);
375     DO_JUMP(jnz,    L2, L1 != 0);
376     DO_JUMP(jeq,    L3, L1 == L2);
377     DO_JUMP(jne,    L3, L1 != L2);
378     DO_JUMP(jlt,    L3, L1 < L2);
379     DO_JUMP(jge,    L3, L1 >= L2);
380     DO_JUMP(jgt,    L3, L1 > L2);
381     DO_JUMP(jle,    L3, L1 <= L2);
382     DO_JUMP(jltu,   L3, ((git_uint32)L1 < (git_uint32)L2));
383     DO_JUMP(jgeu,   L3, ((git_uint32)L1 >= (git_uint32)L2));
384     DO_JUMP(jgtu,   L3, ((git_uint32)L1 > (git_uint32)L2));
385     DO_JUMP(jleu,   L3, ((git_uint32)L1 <= (git_uint32)L2));
386     DO_JUMP(jisnan, L2, (((L1 & 0x7F800000) == 0x7F800000) && ((L1 & 0x007FFFFF) != 0)));
387     DO_JUMP(jisinf, L2, ((L1 == 0x7F800000) || (L1 == 0xFF800000)));
388     DO_JUMP(jflt,   L3, DECODE_FLOAT(L1) < DECODE_FLOAT(L2));
389     DO_JUMP(jfge,   L3, DECODE_FLOAT(L1) >= DECODE_FLOAT(L2));
390     DO_JUMP(jfgt,   L3, DECODE_FLOAT(L1) > DECODE_FLOAT(L2));
391     DO_JUMP(jfle,   L3, DECODE_FLOAT(L1) <= DECODE_FLOAT(L2));
392     DO_JUMP(jfeq,   L4, floatCompare(L1, L2, L3) != 0);
393     DO_JUMP(jfne,   L4, floatCompare(L1, L2, L3) == 0);
394
395 #undef DO_JUMP
396
397     do_jumpabs: L7 = L1; goto do_jump_abs_L7; NEXT;
398
399     do_goto_L4_from_L7: L1 = L4; goto do_goto_L1_from_L7;
400     do_goto_L3_from_L7: L1 = L3; goto do_goto_L1_from_L7;
401     do_goto_L2_from_L7: L1 = L2; goto do_goto_L1_from_L7;
402     do_goto_L1_from_L7:
403         if (L1 == 0 || L1 == 1) goto do_return;
404         L7 = L7 + L1 - 2; goto do_jump_abs_L7;
405
406     do_args_stack:
407         // The first argument is topmost in the stack; the count is in L2.
408         CHECK_USED(L2);
409         // We want to store the arguments in 'args' in the same order.
410         for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
411             args [L3] = POP;
412         NEXT;
413
414     // Specialised versions of above:
415     do_args_stack_call_stub_discard:
416         CHECK_USED(L2);
417         for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
418             args [L3] = POP;
419         goto do_call_stub_discard;
420         
421     do_args_stack_call_stub_addr:
422         CHECK_USED(L2);
423         for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
424             args [L3] = POP;
425         goto do_call_stub_addr;
426
427     do_args_stack_call_stub_local:
428         CHECK_USED(L2);
429         for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
430             args [L3] = POP;
431         goto do_call_stub_local;
432
433     do_args_stack_call_stub_stack:
434         CHECK_USED(L2);
435         for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
436             args [L3] = POP;
437         goto do_call_stub_stack;
438
439     do_args_3:
440         args [0] = L4;
441         args [1] = L3;
442         args [2] = L2;
443         L2 = 3;
444         NEXT;
445
446     do_args_2:
447         args [0] = L3;
448         args [1] = L2;
449         L2 = 2;
450         NEXT;
451
452     do_args_1:
453         args [0] = L2;
454         L2 = 1;
455         NEXT;
456
457     do_args_0:
458         L2 = 0;
459         NEXT;
460
461     do_undo_stub_discard:
462         CHECK_FREE(4);
463         PUSH (0); // DestType
464         PUSH (0); // DestAddr
465         goto finish_undo_stub;
466
467     do_undo_stub_addr:
468         CHECK_FREE(4);
469         PUSH (1);       // DestType
470         PUSH (READ_PC); // DestAddr
471         goto finish_undo_stub;
472
473     do_undo_stub_local:
474         CHECK_FREE(4);
475         PUSH (2);       // DestType
476         PUSH (READ_PC); // DestAddr
477         goto finish_undo_stub;
478
479     do_undo_stub_stack:
480         CHECK_FREE(4);
481         PUSH (3); // DestType
482         PUSH (0); // DestAddr
483         goto finish_undo_stub;
484
485 finish_undo_stub:
486         PUSH (READ_PC);             // PC
487         PUSH ((frame - base) * 4);  // FramePtr
488         saveUndo (base, sp);
489         S1 = 0;
490         goto do_pop_call_stub;
491
492     do_restoreundo:
493         if (restoreUndo (base, protectPos, protectSize) == 0)
494         {
495             sp = gStackPointer;
496             S1 = -1;
497             goto do_pop_call_stub;
498         }
499         S1 = 1;
500         NEXT;
501
502     do_save_stub_discard:
503         CHECK_FREE(4);
504         PUSH (0); // DestType
505         PUSH (0); // DestAddr
506         goto finish_save_stub;
507
508     do_save_stub_addr:
509         CHECK_FREE(4);
510         PUSH (1);       // DestType
511         PUSH (READ_PC); // DestAddr
512         goto finish_save_stub;
513
514     do_save_stub_local:
515         CHECK_FREE(4);
516         PUSH (2);       // DestType
517         PUSH (READ_PC); // DestAddr
518         goto finish_save_stub;
519
520     do_save_stub_stack:
521         CHECK_FREE(4);
522         PUSH (3); // DestType
523         PUSH (0); // DestAddr
524         goto finish_save_stub;
525
526 finish_save_stub:
527         PUSH (READ_PC);                        // PC
528         PUSH ((frame - base) * 4);  // FramePtr
529         if (ioMode == IO_GLK)
530             S1 = saveToFile (base, sp, L1);
531         else
532             S1 = 1;
533         goto do_pop_call_stub;
534
535     do_restore:
536         if (ioMode == IO_GLK
537          && restoreFromFile (base, L1, protectPos, protectSize) == 0)
538         {
539             sp = gStackPointer;
540             S1 = -1;
541             goto do_pop_call_stub;
542         }
543         S1 = 1;
544         NEXT;
545
546     do_catch_stub_discard:
547         CHECK_FREE(4);
548         L7 = 0;
549         PUSH (0); // DestType
550         goto finish_catch_stub_addr_L7;
551
552     do_catch_stub_addr:
553         CHECK_FREE(4);
554         L7 = READ_PC;
555         memWrite32(L7, (sp-base+4)*4);
556         PUSH (1);       // DestType
557         goto finish_catch_stub_addr_L7;
558
559     do_catch_stub_local:
560         CHECK_FREE(4);
561         L7 = READ_PC;
562         LOCAL(L7 / 4) = (sp-base+4)*4;
563         PUSH (2);       // DestType
564         goto finish_catch_stub_addr_L7;
565
566     do_catch_stub_stack:
567         CHECK_FREE(5);
568         PUSH (3);                  // DestType
569         PUSH (0);                  // DestAddr
570         PUSH (READ_PC);            // PC
571         PUSH ((frame - base) * 4); // FramePtr
572         L7 = (sp - base)*4;        // Catch token.
573             PUSH (L7);
574         NEXT;
575
576 finish_catch_stub_addr_L7:
577         PUSH (L7);                 // DestAddr
578         PUSH (READ_PC);            // PC
579         PUSH ((frame - base) * 4); // FramePtr
580         NEXT;
581
582     do_throw:
583         if (L2 < 16 || L2 > ((sp-base)*4))
584             fatalError ("Invalid catch token in throw");
585         sp = base + L2 / 4;
586         goto do_pop_call_stub;
587     
588 do_call_stub_discard:
589         CHECK_FREE(4);
590         PUSH (0); // DestType
591         PUSH (0); // DestAddr
592         goto finish_call_stub;
593
594     do_call_stub_addr:
595         CHECK_FREE(4);
596         PUSH (1);       // DestType
597         PUSH (READ_PC); // DestAddr
598         goto finish_call_stub;
599
600     do_call_stub_local:
601         CHECK_FREE(4);
602         PUSH (2);       // DestType
603         PUSH (READ_PC); // DestAddr
604         goto finish_call_stub;
605
606     do_call_stub_stack:
607         CHECK_FREE(4);
608         PUSH (3); // DestType
609         PUSH (0); // DestAddr
610         goto finish_call_stub;
611
612 finish_call_stub:
613         PUSH (READ_PC);             // PC
614         PUSH ((frame - base) * 4);  // FramePtr
615         goto do_enter_function_L1;
616     
617 do_tailcall:
618         // Zap the current stack frame, down to its call stub.
619         sp = frame;
620         // Call the function!
621         goto do_enter_function_L1;
622     
623     do_return:
624         sp = frame;
625         // ...
626         // fall through
627         // ...
628     do_pop_call_stub:// L1 holds the return value.
629         if (sp - base < 4)
630         {
631             if (sp == base)
632                 // We just exited the top-level function.
633                 goto finished;
634             else
635                 // Something nasty happened.
636                 goto stack_underflow;
637         }
638         L2 = POP;    // FramePtr
639         L7 = POP;    // PC
640         L6 = POP;    // DestAddr
641         switch (POP) // DestType
642         {
643             case 0: // Do not store.
644                 frame = base + L2 / 4;
645                 locals = frame + frame[1]/4;
646                 values = frame + frame[0]/4;
647                 break;
648
649             case 1: // Store in main memory.
650                 frame = base + L2 / 4;
651                 locals = frame + frame[1]/4;
652                 values = frame + frame[0]/4;
653                 memWrite32 (L6, L1);
654                 break;
655
656             case 2: // Store in local variable.
657                 frame = base + L2 / 4;
658                 locals = frame + frame[1]/4;
659                 values = frame + frame[0]/4;
660                 LOCAL(L6/4) = L1;
661                 break;
662
663             case 3: // Push on stack.
664                 frame = base + L2 / 4;
665                 locals = frame + frame[1]/4;
666                 values = frame + frame[0]/4;
667                 PUSH (L1);
668                 break;
669             
670             case 10: // Resume printing a compressed (E1) string.
671                 frame = base + L2 / 4;
672                 locals = frame + frame[1]/4;
673                 values = frame + frame[0]/4;
674                 goto resume_compressed_string_L7_bit_L6;
675                 
676             case 11: // Resume executing function code after a string completes.
677                 // Don't restore the frame pointer.
678                 break;
679                 
680             case 12: // Resume printing a signed decimal integer.
681                 frame = base + L2 / 4;
682                 locals = frame + frame[1]/4;
683                 values = frame + frame[0]/4;
684                 goto resume_number_L7_digit_L6;
685                 
686             case 13: // Resume printing a C-style (E0) string.
687                 frame = base + L2 / 4;
688                 locals = frame + frame[1]/4;
689                 values = frame + frame[0]/4;
690                 goto resume_c_string_L7;
691                 
692             case 14: // Resume printing a Unicode (E2) string.
693                 frame = base + L2 / 4;
694                 locals = frame + frame[1]/4;
695                 values = frame + frame[0]/4;
696                 goto resume_uni_string_L7;
697
698             default:
699                 fatalError("Bad call stub");
700         }
701         // Restore the PC.
702         goto do_jump_abs_L7;
703
704     do_stkcount:
705         S1 = sp - values; NEXT;
706     
707     do_stkpeek:
708         if (L1 < 0 || L1 > (sp - values))
709             fatalError("Out of bounds in stkpeek");
710         S1 = sp[-1 - L1]; NEXT;
711
712     do_stkswap:
713         CHECK_USED(2);
714         L1 = POP; L2 = POP; PUSH(L1); PUSH(L2); NEXT;
715
716     do_stkcopy:
717         CHECK_USED(L1);
718         for (L2 = L1 ; L2 > 0 ; --L2)
719         {
720             L3 = sp[-L1];
721             PUSH (L3);
722         }
723         NEXT;
724
725     resume_number_L7_digit_L6:
726     {
727         char buffer [16];
728         
729         // If the IO mode is 'null', do nothing.
730         if (ioMode == IO_NULL)
731             goto do_pop_call_stub;
732
733         // Write the number into the buffer.
734         L1 = (L7 < 0) ? -L7 : L7; // Absolute value of number.
735         L2 = 0;                   // Current buffer position.
736         do
737         {
738             buffer [L2++] = '0' + (L1 % 10);
739             L1 /= 10;
740         }
741         while (L1 > 0);
742
743         if (L7 < 0)
744             buffer [L2++] = '-';
745         
746         if (L6 >= L2)
747             goto do_pop_call_stub; // We printed the whole number already.
748
749         // If we're in filter mode, push a call stub
750         // and filter the next character.
751         if (ioMode == IO_FILTER)
752         {
753             // Store the next character in the args array.
754             args[0] = buffer [L2 - L6 - 1];
755             ++L6;
756             
757             // Push a call stub to print the next character.
758             CHECK_FREE(4);
759             PUSH(12); // DestType
760             PUSH(L6); // DestAddr (next digit)
761             PUSH(L7); // PC       (number to print)
762             PUSH ((frame - base) * 4); // FramePtr
763
764             // Call the filter function.
765             L1 = ioRock;
766             L2 = 1;
767             goto do_enter_function_L1;
768         }
769         else
770         {
771             // We're in Glk mode. Just print all the characters.
772             for ( ; L6 < L2 ; ++L6)
773                 glk_put_char (buffer [L2 - L6 - 1]);
774         }
775     }
776         goto do_pop_call_stub;
777
778     resume_c_string_L7:
779         // If the IO mode is 'null', or if we've reached the
780         // end of the string, do nothing.
781         L2 = memRead8(L7++);
782         if (L2 == 0 || ioMode == IO_NULL)
783             goto do_pop_call_stub;
784         // Otherwise we're going to have to print something,
785         // If the IO mode is 'filter', filter the next char.
786         if (ioMode == IO_FILTER)
787         {
788             // Store this character in the args array.
789             args [0] = L2;
790             // Push a call stub.
791             CHECK_FREE(4);
792             PUSH(13); // DestType (resume C string)
793             PUSH(L6); // DestAddr (ignored)
794             PUSH(L7); // PC       (next char to print)
795             PUSH ((frame - base) * 4); // FramePtr
796             // Call the filter function.
797             L1 = ioRock;
798             L2 = 1;
799             goto do_enter_function_L1;
800         }
801         // We're in Glk mode. Just print all the characters.
802         while (L2 != 0)
803         {
804             glk_put_char ((unsigned char) L2);
805             L2 = memRead8(L7++);
806         }
807         goto do_pop_call_stub;
808
809     resume_uni_string_L7:
810         // If the IO mode is 'null', or if we've reached the
811         // end of the string, do nothing.
812         L2 = memRead32(L7);
813         L7 += 4;
814         if (L2 == 0 || ioMode == IO_NULL)
815             goto do_pop_call_stub;
816         // Otherwise we're going to have to print something,
817         // If the IO mode is 'filter', filter the next char.
818         if (ioMode == IO_FILTER)
819         {
820             // Store this character in the args array.
821             args [0] = L2;
822             // Push a call stub.
823             CHECK_FREE(4);
824             PUSH(14); // DestType (resume Unicode string)
825             PUSH(L6); // DestAddr (ignored)
826             PUSH(L7); // PC       (next char to print)
827             PUSH ((frame - base) * 4); // FramePtr
828             // Call the filter function.
829             L1 = ioRock;
830             L2 = 1;
831             goto do_enter_function_L1;
832         }
833         // We're in Glk mode. Just print all the characters.
834         while (L2 != 0)
835         {
836 #ifdef GLK_MODULE_UNICODE
837             glk_put_char_uni ((glui32) L2);
838 #else
839             unsigned char c = (L2 > 0 && L2 < 256) ? L2 : '?';
840             glk_put_char (c);
841 #endif // GLK_MODULE_UNICODE
842             L2 = memRead32(L7);
843             L7 += 4;
844         }
845         goto do_pop_call_stub;
846
847     resume_compressed_string_L7_bit_L6:
848         // Load the first string table node into L1.
849         // Its address is stored at stringTable + 8.
850         L1 = memRead32 (stringTable + 8);
851         // Load the node's type byte.
852         L2 = memRead8 (L1++);
853         // Is the root node a branch?
854         if (L2 == 0)
855         {
856             // We'll keep a reservoir of input bits in L5.
857             L5 = memRead8(L7);
858             // Keep following branch nodes until we hit a leaf node.
859             while (L2 == 0)
860             {
861                 // Read the next bit.
862                 L4 = (L5 >> L6) & 1;
863                 // If we're finished reading this byte,
864                 // move on to the next one.
865                 if (++L6 > 7)
866                 {
867                     L6 -= 8;
868                     L5 = memRead8(++L7);
869                 }
870                 // Follow the branch.
871                 L1 = memRead32(L1 + 4 * L4);
872                 L2 = memRead8 (L1++);
873             }
874         }
875         else if (L2 == 2 || L2 == 3)
876         {
877             // The root node prints a single character or a string.
878             // This will produce infinite output in the Null or Glk
879             // I/O modes, so we'll catch that here.
880
881             if (ioMode != IO_FILTER)
882                 fatalError ("String table prints infinite strings!");
883
884             // In Filter mode, the output will be sent to the current
885             // filter function, which can change the string table
886             // before returning, so we'll continue and see what happens.
887         }
888         // We're at a leaf node.
889         switch (L2)
890         {
891             case 1: // Terminator.
892                 goto do_pop_call_stub;
893
894             case 2: // Single char.
895                 if (ioMode == IO_NULL)
896                     { /* Do nothing */ }
897                 else if (ioMode == IO_GLK)
898                     glk_put_char ((unsigned char) memRead8(L1));
899                 else
900                 {
901                     // Store this character in the args array.
902                     args [0] = memRead8(L1);
903                     // Push a call stub.
904                     CHECK_FREE(4);
905                     PUSH(10); // DestType
906                     PUSH(L6); // DestAddr (bit number in string)
907                     PUSH(L7); // PC       (byte address in string)
908                     PUSH ((frame - base) * 4); // FramePtr
909                     // Call the filter function.
910                     L1 = ioRock;
911                     L2 = 1;
912                     goto do_enter_function_L1;
913                 }
914                 break;
915
916             case 3: // C string.
917                 // Push a 'resume compressed string' call stub.
918                 CHECK_FREE(4);
919                 PUSH (10); // DestType
920                 PUSH (L6); // DestAddr (bit number in string)
921                 PUSH (L7); // PC       (byte address in string)
922                 PUSH ((frame - base) * 4); // FramePtr
923                 // Print the C string.
924                 L7 = L1;
925                 goto resume_c_string_L7;
926                 
927             case 4: // Unicode char
928                 if (ioMode == IO_NULL)
929                     { /* Do nothing */ }
930                 else if (ioMode == IO_GLK)
931                 {
932 #ifdef GLK_MODULE_UNICODE
933                     glk_put_char_uni (memRead32(L1));
934 #else
935                     git_uint32 c = memRead32(L1);
936                     if (c > 255) c = '?';
937                     glk_put_char ((unsigned char) c);
938 #endif // GLK_MODULE_UNICODE
939                 }
940                 else
941                 {
942                     // Store this character in the args array.
943                     args [0] = memRead32(L1);
944                     // Push a call stub.
945                     CHECK_FREE(4);
946                     PUSH(10); // DestType
947                     PUSH(L6); // DestAddr (bit number in string)
948                     PUSH(L7); // PC       (byte address in string)
949                     PUSH ((frame - base) * 4); // FramePtr
950                     // Call the filter function.
951                     L1 = ioRock;
952                     L2 = 1;
953                     goto do_enter_function_L1;
954                 }
955                 break;
956
957             case 5: // Unicode string.
958                 // Push a 'resume compressed string' call stub.
959                 CHECK_FREE(4);
960                 PUSH (10); // DestType
961                 PUSH (L6); // DestAddr (bit number in string)
962                 PUSH (L7); // PC       (byte address in string)
963                 PUSH ((frame - base) * 4); // FramePtr
964                 // Print the Unicode string.
965                 L7 = L1;
966                 goto resume_uni_string_L7;
967
968             case 8:  // Indirect reference.
969                 L3 = memRead32(L1);
970                 L2 = 0; goto indirect_L3_args_L2;
971
972             case 9:  // Double-indirect reference.
973                 L3 = memRead32(L1); L3 = memRead32(L3);
974                 L2 = 0; goto indirect_L3_args_L2;
975
976             case 10: // Indirect reference with args.
977                 L3 = memRead32(L1);
978                 L2 = memRead32(L1 + 4); goto indirect_L3_args_L2;
979
980             case 11: // Double-indirect reference with args.
981                 L3 = memRead32(L1); L3 = memRead32(L3);
982                 L2 = memRead32(L1 + 4); goto indirect_L3_args_L2;
983
984             indirect_L3_args_L2:
985                 // Push a 'resume compressed string' call stub.
986                 CHECK_FREE(4);
987                 PUSH (10); // DestType
988                 PUSH (L6); // DestAddr (bit number in string)
989                 PUSH (L7); // PC       (byte address in string)
990                 PUSH ((frame - base) * 4); // FramePtr
991                 // Check the type of the embedded object.
992                 switch (memRead8(L3))
993                 {
994                     case 0xE0: // C string.
995                         L7 = L3 + 1;
996                         goto resume_c_string_L7;
997
998                     case 0xE1: // Compressed string.
999                         L7 = L3 + 1;
1000                         L6 = 0;
1001                         goto resume_compressed_string_L7_bit_L6;
1002                         
1003                     case 0xE2: // Unicode string.
1004                         L7 = L3 + 4; // Skip extra three padding bytes.
1005                         goto resume_uni_string_L7;
1006
1007                     case 0xC0: case 0xC1: // Function.
1008                         // Retrieve arguments.
1009                         for (L1 += 8, L4 = L2; L4 > 0 ; --L4, L1+=4)
1010                             args[L4-1] = memRead32(L1);
1011                         // Enter function.
1012                         L1 = L3;
1013                         goto do_enter_function_L1;
1014                     
1015                     default: fatalError ("Embedded object in string has unknown type");
1016                 }
1017                 break;
1018
1019             default: fatalError ("Unknown string table node type");
1020         }
1021         // Start back at the root node again.
1022         goto resume_compressed_string_L7_bit_L6;
1023
1024     do_streamstr:
1025         // Push a 'resume function' call stub.
1026         CHECK_FREE(4);
1027         PUSH (11);                            // DestType
1028         PUSH (0);                             // Addr
1029         PUSH (READ_PC);                       // PC
1030         PUSH ((frame - base) * 4); // FramePtr
1031
1032         // Load the string's type byte.
1033         L2 = memRead8(L1++);
1034         if (L2 == 0xE0)
1035         {
1036             // Uncompressed string.
1037             L7 = L1;
1038             goto resume_c_string_L7;
1039         }
1040         else if (L2 == 0xE1)
1041         {
1042             // Compressed string.
1043             L7 = L1;
1044             L6 = 0;
1045             goto resume_compressed_string_L7_bit_L6;
1046         }
1047         else if (L2 == 0xE2)
1048         {
1049             // Uncompressed Unicode string.
1050             L7 = L1 + 3; // Skip three padding bytes.
1051             goto resume_uni_string_L7;
1052         }
1053         else
1054         {
1055             fatalError ("Value used in streamstr was not a string");
1056             goto finished;
1057         }
1058
1059     do_streamchar:
1060         L7 = READ_PC;
1061         if (ioMode == IO_NULL)
1062             { /* Do nothing */ }
1063         else if (ioMode == IO_GLK)
1064         {
1065             unsigned char c = (L1 & 0xff);
1066             glk_put_char (c);
1067         }
1068         else
1069         {
1070             // Store this character in the args array.
1071             args [0] = (L1 & 0xff);
1072             // Push a 'resume function' call stub.
1073             CHECK_FREE(4);
1074             PUSH (0);                  // DestType
1075             PUSH (0);                  // Addr
1076             PUSH (L7);                 // PC
1077             PUSH ((frame - base) * 4); // FramePtr
1078             // Call the filter function.
1079             L1 = ioRock;
1080             L2 = 1;
1081             goto do_enter_function_L1;
1082         }
1083         NEXT;
1084
1085     do_streamunichar:
1086         L7 = READ_PC;
1087         if (ioMode == IO_NULL)
1088             { /* Do nothing */ }
1089         else if (ioMode == IO_GLK)
1090         {
1091 #ifdef GLK_MODULE_UNICODE
1092             glk_put_char_uni ((glui32) L1);
1093 #else
1094             unsigned char c = (L1 > 0 && L1 < 256) ? L1 : '?';
1095             glk_put_char (c);
1096 #endif // GLK_MODULE_UNICODE
1097         }
1098         else
1099         {
1100             // Store this character in the args array.
1101             args [0] = L1;
1102             // Push a 'resume function' call stub.
1103             CHECK_FREE(4);
1104             PUSH (0);                  // DestType
1105             PUSH (0);                  // Addr
1106             PUSH (L7);                 // PC
1107             PUSH ((frame - base) * 4); // FramePtr
1108             // Call the filter function.
1109             L1 = ioRock;
1110             L2 = 1;
1111             goto do_enter_function_L1;
1112         }
1113         NEXT;
1114
1115     do_streamnum:
1116         // Push a 'resume function' call stub.
1117         CHECK_FREE(4);
1118         PUSH (11);                            // DestType
1119         PUSH (0);                             // Addr
1120         PUSH (READ_PC);                       // PC
1121         PUSH ((frame - base) * 4); // FramePtr
1122
1123         // Print the number.
1124         L7 = L1;
1125         L6 = 0;
1126         goto resume_number_L7_digit_L6;
1127
1128     // Stub opcodes:
1129
1130     do_getmemsize:
1131         S1 = gEndMem;
1132         NEXT;
1133
1134     do_getiosys:
1135         S1 = ioMode;
1136         S2 = ioRock;
1137         NEXT;
1138
1139     do_setiosys:    
1140         switch (L1)
1141         {
1142             case IO_NULL:
1143             case IO_FILTER:
1144             case IO_GLK:
1145                 ioMode = (enum IOMode) L1;
1146                 ioRock = L2;
1147                 break;
1148             
1149             default:
1150                 fatalError ("Illegal I/O mode");
1151                 break;
1152         }
1153         NEXT;
1154
1155     do_quit:
1156         goto finished;
1157         
1158     do_restart:
1159         // Reset game memory to its initial state.
1160         resetMemory(protectPos, protectSize);
1161
1162         // Reset all the stack pointers.
1163         frame = locals = values = sp = base;
1164
1165         // Call the first function.
1166         L1 = startPos; // Initial PC.
1167         L2 = 0; // No arguments.
1168         goto do_enter_function_L1;        
1169
1170     do_verify:
1171         S1 = verifyMemory();
1172         NEXT;
1173
1174     do_random:
1175         if (L1 > 0)
1176             S1 = rand() % L1;
1177         else if (L1 < 0)
1178             S1 = -(rand() % -L1);
1179         else
1180         {
1181             // The parameter is zero, so we should generate a
1182             // random number in "the full 32-bit range". The rand()
1183             // function might not cover the entire range, so we'll
1184             // generate the number with several calls.
1185 #if (RAND_MAX < 0xffff)
1186             S1 = rand() ^ (rand() << 12) ^ (rand() << 24);
1187 #else
1188             S1 = (rand() & 0xffff) | (rand() << 16);
1189 #endif
1190         }
1191         NEXT;
1192
1193     do_setrandom:
1194         srand (L1 ? L1 : time(NULL));
1195         NEXT;
1196
1197     do_glk:
1198         // The first argument is topmost in the stack; count is in L2.
1199         CHECK_USED(L2);
1200         // We want to store the arguments in 'args' in the same order.
1201         for (L3 = 0 ; L3 < L2 ; ++L3)
1202             args [L3] = POP;
1203         gStackPointer = sp;
1204         S1 = git_perform_glk (L1, L2, (glui32*) args);
1205         sp = gStackPointer;
1206         NEXT;
1207
1208     do_binarysearch:
1209         S1 = git_binary_search (L1, L2, L3, L4, L5, L6, L7);
1210         NEXT;
1211
1212     do_linearsearch:
1213         S1 = git_linear_search (L1, L2, L3, L4, L5, L6, L7);
1214         NEXT;
1215
1216     do_linkedsearch:
1217         S1 = git_linked_search (L1, L2, L3, L4, L5, L6);
1218         NEXT;
1219
1220     do_gestalt:
1221         S1 = gestalt (L1, L2);
1222         NEXT;
1223
1224     do_getstringtbl: S1 = stringTable; NEXT;
1225     do_setstringtbl: stringTable = L1; NEXT;
1226         
1227     do_debugtrap:
1228         // TODO: do something useful here.
1229         NEXT;
1230
1231     do_stkroll:
1232         // We need to rotate the top L1 elements by L2 places.
1233         if (L1 < 0)
1234             fatalError ("Negative number of elements to rotate in stkroll");
1235         if (L1 > (sp - values))
1236             fatalError ("Tried to rotate too many elements in stkroll");
1237         if (L1 == 0)
1238             NEXT;
1239         // Now, let's normalise L2 into the range [0..L1).
1240         if (L2 >= 0)
1241             L2 = L2 % L1;
1242         else
1243             L2 = L1 - (-L2 % L1);
1244         // Avoid trivial cases.
1245         if (L2 == 0 || L2 == L1)
1246             NEXT;
1247         L2 = L1 - L2;
1248         // The problem is reduced to swapping elements [0..L2) with
1249         // elements [L2..L1). Let's call these two sequences A and B,
1250         // so we need to transform AB into BA. We do this sneakily
1251         // with reversals, as follows: AB -> A'B -> A'B' -> (A'B')',
1252         // where X' is the reverse of the sequence X.
1253 #define SWAP(x,y) \
1254         do { L4 = sp[(x)-L1];sp[(x)-L1]=sp[(y)-L1];sp[(y)-L1]=L4; } while (0)
1255
1256         // Reverse [0..L2).
1257         for (L3 = 0 ; L3 < L2/2 ; ++L3)
1258             SWAP (L3, L2-1-L3);
1259         // Reverse [L2..L1).
1260         for (L3 = L2 ; L3 < (L2 + (L1-L2)/2) ; ++L3)
1261             SWAP (L3, L1-1-(L3-L2));
1262         // Reverse [0..L1).
1263         for (L3 = 0 ; L3 < L1/2 ; ++L3)
1264             SWAP (L3, L1-1-L3);
1265
1266 #undef SWAP
1267         // And we're done!
1268         NEXT;
1269         
1270     do_setmemsize:
1271         S1 = resizeMemory (L1, 0);
1272         NEXT;
1273         
1274     do_protect:
1275         protectPos = L1;
1276         protectSize = L2;
1277         NEXT;
1278     
1279     // Memory management (new with glulx spec 3.1)
1280     
1281     do_mzero:
1282         if (L1 > 0) {
1283                         if (L2 < gRamStart || (L2 + L1) > gEndMem)
1284                                 memWriteError(L2);
1285                         memset(gRam + L2, 0, L1);
1286                 }
1287         NEXT;
1288         
1289     do_mcopy:
1290         if (L1 > 0) {
1291             if (L2 < 0 || (L2 + L1) > gEndMem)
1292                 memReadError(L2);
1293             if (L3 < gRamStart || (L3 + L1) > gEndMem)
1294                 memWriteError(L3);
1295             // ROM and ROM are stored separately, so this is a bit fiddly...
1296             if (L2 > gRamStart) {
1297                 // Only need to copy from RAM. Might be overlapping, so use memmove.
1298                 memmove(gRam + L3, gRam + L2, L1);
1299             } else if ((L2 + L1) <= gRamStart) {
1300                 // Only need to copy from ROM. Can't overlap, so memcpy is safe.
1301                 memcpy(gRam + L3, gRom + L2, L1);
1302             } else {
1303                 // Need to copy from both ROM and RAM.
1304                 L4 = (L2 + L1) - gRamStart; // Amount of ROM to copy.
1305                 memcpy(gRam + L3, gRom + L2, L4);
1306                 memmove(gRam + L3 + L4, gRam + L2 + L4, L1 - L4);
1307             }
1308         }
1309         NEXT;
1310         
1311     do_malloc:
1312         S1 = heap_alloc(L1);
1313         NEXT;
1314         
1315     do_mfree:
1316         heap_free(L1);
1317         NEXT;
1318         
1319     // Function acceleration (new with glulx spec 3.1.1)
1320         
1321     do_accelfunc:
1322         accel_set_func(L1, L2);
1323         NEXT;
1324         
1325     do_accelparam:
1326         accel_set_param(L1, L2);
1327         NEXT;
1328         
1329     // Floating point (new with glulx spec 3.1.2)
1330
1331     do_numtof:
1332         F1 = (git_float) L1;
1333         S1 = ENCODE_FLOAT(F1);
1334         NEXT;
1335
1336     do_ftonumz:
1337         F1 = DECODE_FLOAT(L1);
1338         if (!signbit(F1)) {
1339           if (isnan(F1) || isinf(F1) || (F1 > 2147483647.0))
1340             S1 = 0x7FFFFFFF;
1341           else
1342             S1 = (git_sint32) truncf(F1);
1343         } else {
1344           if (isnan(F1) || isinf(F1) || (F1 < -2147483647.0))
1345             S1 = 0x80000000;
1346           else
1347             S1 = (git_sint32) truncf(F1);
1348         }
1349         NEXT;
1350
1351     do_ftonumn:
1352         F1 = DECODE_FLOAT(L1);
1353         if (!signbit(F1)) {
1354           if (isnan(F1) || isinf(F1) || (F1 > 2147483647.0))
1355             S1 = 0x7FFFFFFF;
1356           else
1357             S1 = (git_sint32) roundf(F1);
1358         } else {
1359           if (isnan(F1) || isinf(F1) || (F1 < -2147483647.0))
1360             S1 = 0x80000000;
1361           else
1362             S1 = (git_sint32) roundf(F1);
1363         }
1364         NEXT;
1365
1366     do_ceil:
1367         F1 = ceilf(DECODE_FLOAT(L1));
1368         L2 = ENCODE_FLOAT(F1);
1369         if ((L2 == 0x0) || (L2 == 0x80000000))
1370           L2 = L1 & 0x80000000;
1371         S1 = L2;
1372         NEXT;
1373
1374     do_floor:
1375         F1 = floorf(DECODE_FLOAT(L1));
1376         S1 = ENCODE_FLOAT(F1);
1377         NEXT;
1378
1379     do_sqrt:
1380         F1 = sqrtf(DECODE_FLOAT(L1));
1381         S1 = ENCODE_FLOAT(F1);
1382         NEXT;
1383
1384     do_exp:
1385         F1 = expf(DECODE_FLOAT(L1));
1386         S1 = ENCODE_FLOAT(F1);
1387         NEXT;
1388
1389     do_log:
1390         F1 = logf(DECODE_FLOAT(L1));
1391         S1 = ENCODE_FLOAT(F1);
1392         NEXT;
1393
1394     do_pow:
1395 #ifdef USE_OWN_POWF
1396         F1 = git_powf(DECODE_FLOAT(L1), DECODE_FLOAT(L2));
1397 #else
1398         F1 = powf(DECODE_FLOAT(L1), DECODE_FLOAT(L2));
1399 #endif
1400         S1 = ENCODE_FLOAT(F1);
1401         NEXT;
1402
1403     do_atan2:
1404         F1 = atan2f(DECODE_FLOAT(L1), DECODE_FLOAT(L2));
1405         S1 = ENCODE_FLOAT(F1);
1406         NEXT;
1407
1408     do_fmod:
1409         F1 = DECODE_FLOAT(L1);
1410         F2 = DECODE_FLOAT(L2);
1411         F3 = fmodf(F1, F2);
1412         F4 = (F1 - F3) / F2;
1413         L4 = ENCODE_FLOAT(F4);
1414         if ((L4 == 0) || (L4 == 0x80000000))
1415           L4 = (L1 ^ L2) & 0x80000000;
1416         S1 = ENCODE_FLOAT(F3);
1417         S2 = L4;
1418         NEXT;
1419
1420     do_sin:
1421         F1 = sinf(DECODE_FLOAT(L1));
1422         S1 = ENCODE_FLOAT(F1);
1423         NEXT;
1424
1425     do_cos:
1426         F1 = cosf(DECODE_FLOAT(L1));
1427         S1 = ENCODE_FLOAT(F1);
1428         NEXT;
1429
1430     do_tan:
1431         F1 = tanf(DECODE_FLOAT(L1));
1432         S1 = ENCODE_FLOAT(F1);
1433         NEXT;
1434
1435     do_asin:
1436         F1 = asinf(DECODE_FLOAT(L1));
1437         S1 = ENCODE_FLOAT(F1);
1438         NEXT;
1439
1440     do_acos:
1441         F1 = acosf(DECODE_FLOAT(L1));
1442         S1 = ENCODE_FLOAT(F1);
1443         NEXT;
1444
1445     do_atan:
1446         F1 = atanf(DECODE_FLOAT(L1));
1447         S1 = ENCODE_FLOAT(F1);
1448         NEXT;
1449
1450     // Special Git opcodes
1451     
1452     do_git_setcacheram:
1453         gCacheRAM = (L1 == 0) ? 0 : 1;
1454         NEXT;
1455         
1456     do_git_prunecache:
1457         pruneCodeCache (L1, L2);
1458         NEXT;
1459     
1460     // Error conditions:
1461     
1462     do_error_bad_opcode:
1463         fatalError ("Illegal instruction");
1464         goto finished;
1465     
1466     stack_overflow:
1467         fatalError ("Stack overflow");
1468         goto finished;
1469     
1470     stack_underflow:
1471         fatalError ("Stack underflow");
1472         goto finished;
1473         
1474 // ---------------------------------
1475
1476 finished:
1477
1478     free (base);
1479     shutdownCompiler();
1480 }