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