1 // $Id: terp.c,v 1.42 2004/12/22 14:33:40 iain Exp $
11 // -------------------------------------------------------------
14 git_sint32* gStackPointer;
16 #ifdef USE_DIRECT_THREADING
20 // -------------------------------------------------------------
21 // Useful macros for manipulating the stack
23 #define LOCAL(n) (locals[(n)])
25 #define PUSH(n) *sp++ = (n)
27 #define READ_PC ((git_uint32)(*pc++))
29 #define CHECK_FREE(n) if ((top - sp) < (n)) goto stack_overflow
30 #define CHECK_USED(n) if ((sp - values) < (n)) goto stack_underflow
32 // -------------------------------------------------------------
35 void startProgram (size_t cacheSize, enum IOMode ioMode)
37 Block pc; // Program counter (pointer into dynamically generated code)
39 git_sint32 L1=0, L2=0, L3=0, L4=0, L5=0, L6=0, L7=0;
43 git_sint32* base; // Bottom of the stack.
44 git_sint32* frame; // Bottom of the current stack frame.
45 git_sint32* locals; // Start of the locals section of the current frame.
46 git_sint32* values; // Start of the values section of the current frame.
47 git_sint32* sp; // Next free stack slot.
48 git_sint32* top; // The top of the stack -- that is, the first unusable slot.
50 git_sint32 args [64]; // Array of arguments. Count is stored in L2.
51 git_uint32 runCounter = 0;
53 git_uint32 ioRock = 0;
55 git_uint32 stringTable = memRead32(28);
56 git_uint32 startPos = memRead32(24);
57 git_uint32 stackSize = memRead32(20);
59 git_uint32 protectPos = 0;
60 git_uint32 protectSize = 0;
62 git_uint32 glulxPC = 0;
63 git_uint32 glulxOpcode = 0;
65 acceleration_func accelfunc;
67 // Initialise the code cache.
69 #ifdef USE_DIRECT_THREADING
70 static Opcode opcodeTable [] = {
71 #define LABEL(label) &&do_ ## label,
74 gOpcodeTable = opcodeTable;
77 initCompiler (cacheSize);
79 // Initialise the random number generator.
84 base = malloc (stackSize);
86 fatalError ("Couldn't allocate stack");
88 top = base + (stackSize / 4);
89 frame = locals = values = sp = base;
91 // Call the first function.
93 L1 = startPos; // Initial PC.
94 L2 = 0; // No arguments.
95 goto do_enter_function_L1;
97 #ifdef USE_DIRECT_THREADING
98 #define NEXT do { ++runCounter; goto **(pc++); } while(0)
100 #define NEXT goto next
101 //#define NEXT do { CHECK_USED(0); CHECK_FREE(0); goto next; } while (0)
106 #define LABEL(foo) case label_ ## foo: goto do_ ## foo;
107 #include "labels.inc"
108 default: fatalError("exec: bad opcode");
113 // This opcode lets us keep track of how the compiled
114 // code relates to the original glulx code.
115 glulxPC = READ_PC; // Glulx program counter.
116 glulxOpcode = READ_PC; // Glulx opcode number.
117 // fprintf (stdout, "\nPC: 0x%08x\nOpcode: 0x%04x\n", glulxPC, glulxOpcode);
118 // fprintf (stdout, "Stack:");
119 // for (L7 = 0 ; L7 < (sp - base) ; ++L7)
120 // fprintf (stdout," 0x%x", base[L7]);
121 // fprintf (stdout, "\n");
124 #define LOAD_INSTRUCTIONS(reg) \
125 do_ ## reg ## _const: reg = READ_PC; NEXT; \
126 do_ ## reg ## _stack: CHECK_USED(1); reg = POP; NEXT; \
127 do_ ## reg ## _addr: reg = memRead32 (READ_PC); NEXT; \
128 do_ ## reg ## _local: reg = LOCAL (READ_PC); NEXT
130 LOAD_INSTRUCTIONS(L1);
131 LOAD_INSTRUCTIONS(L2);
132 LOAD_INSTRUCTIONS(L3);
133 LOAD_INSTRUCTIONS(L4);
134 LOAD_INSTRUCTIONS(L5);
135 LOAD_INSTRUCTIONS(L6);
136 LOAD_INSTRUCTIONS(L7);
138 #define STORE_INSTRUCTIONS(reg) \
139 do_ ## reg ## _stack: CHECK_FREE(1); PUSH(reg); NEXT; \
140 do_ ## reg ## _addr: memWrite32 (READ_PC, reg); NEXT; \
141 do_ ## reg ## _local: LOCAL (READ_PC) = reg; NEXT
143 STORE_INSTRUCTIONS(S1);
144 STORE_INSTRUCTIONS(S2);
146 #define DOUBLE_LOAD(mode2) \
147 do_L1_const_L2_ ## mode2: L1 = READ_PC; goto do_L2_ ## mode2; \
148 do_L1_stack_L2_ ## mode2: CHECK_USED(1); L1 = POP; goto do_L2_ ## mode2; \
149 do_L1_local_L2_ ## mode2: L1 = LOCAL (READ_PC); goto do_L2_ ## mode2; \
150 do_L1_addr_L2_ ## mode2: L1 = memRead32 (READ_PC); goto do_L2_ ## mode2
157 #undef LOAD_INSTRUCTIONS
158 #undef STORE_INSTRUCTIONS
161 do_L1_addr16: L1 = memRead16 (READ_PC); NEXT;
162 do_L1_addr8: L1 = memRead8 (READ_PC); NEXT;
163 do_S1_addr16: memWrite16 (READ_PC, S1); NEXT;
164 do_S1_addr8: memWrite8 (READ_PC, S1); NEXT;
166 #define UL7 ((git_uint32)L7)
169 gBlockHeader->runCounter = runCounter;
170 pc = compile (READ_PC);
175 gBlockHeader->runCounter = runCounter;
177 runCounter = gBlockHeader->runCounter;
180 do_enter_function_L1: // Arg count is in L2.
182 // Check for an accelerated function
183 accelfunc = accel_get_func(L1);
185 S1 = accelfunc(L2, (glui32 *) args);
186 goto do_pop_call_stub;
190 // Read the function type.
192 // Parse the local variables descriptor.
196 L6 = memRead8(L1++); // LocalType
197 L5 = memRead8(L1++); // LocalCount
198 if (L6 != 4 && L6 != 0) // We only support 4-byte locals.
199 fatalError("Local variable wasn't 4 bytes wide");
200 L4 += L5; // Cumulative local count.
204 // Write out the stack frame.
205 // Recall that the number of locals is stored in L4.
209 PUSH (L4*4 + 12); // FrameLen
210 PUSH (12); // LocalsPos
214 L6 = (4 << 24) | (L4 << 16);
215 PUSH (L6); // format of locals
217 // This is where the local variables start, so:
220 // Read the arguments, based on the function type.
223 case 0xC0: // arguments should be placed on the stack.
224 // argc is in L2; we'll randomly use L5 as scratch.
226 // Initialise the local variables.
227 for ( ; L4 > 0 ; --L4)
229 // This is where the temporary values start, so:
231 // Push the args onto the stack.
232 for (L5 = 0 ; L5 < L2 ; ++L5)
234 // Push the arg count.
238 case 0xC1: // arguments should be written into locals.
239 // argc is in L2, num locals is in L4.
240 // Stuff as many locals as possible with arguments.
241 for (L5 = 1 ; L5 <= L2 && L4 > 0 ; ++L5, --L4)
242 PUSH (args [L2 - L5]);
243 // Initialise any remaining locals.
244 for ( ; L4 > 0 ; --L4)
246 // This is where the temporary values start, so:
251 // This isn't a function!
252 fatalError("Not a function");
256 // Start executing the function.
262 #define PEEPHOLE_STORE(tag, code) \
263 do_ ## tag ## _discard: code; NEXT; \
264 do_ ## tag ## _S1_stack: code; goto do_S1_stack; \
265 do_ ## tag ## _S1_local: code; goto do_S1_local; \
266 do_ ## tag ## _S1_addr: code; goto do_S1_addr
268 PEEPHOLE_STORE(add, S1 = L1 + L2);
269 PEEPHOLE_STORE(sub, S1 = L1 - L2);
270 PEEPHOLE_STORE(mul, S1 = L1 * L2);
271 PEEPHOLE_STORE(div, if (L2 == 0) fatalError ("Divide by zero"); S1 = L1 / L2);
272 PEEPHOLE_STORE(mod, if (L2 == 0) fatalError ("Divide by zero"); S1 = L1 % L2);
274 PEEPHOLE_STORE(neg, S1 = -L1);
275 PEEPHOLE_STORE(bitnot, S1 = ~L1);
277 PEEPHOLE_STORE(bitand, S1 = L1 & L2);
278 PEEPHOLE_STORE(bitor, S1 = L1 | L2);
279 PEEPHOLE_STORE(bitxor, S1 = L1 ^ L2);
281 PEEPHOLE_STORE(shiftl, if (L2 > 31) S1 = 0; else S1 = L1 << ((git_uint32) L2));
282 PEEPHOLE_STORE(sshiftr, if (L2 > 31) L2 = 31; S1 = ((git_sint32) L1) >> ((git_uint32) L2));
283 PEEPHOLE_STORE(ushiftr, if (L2 > 31) S1 = 0; else S1 = ((git_uint32) L1) >> ((git_uint32) L2));
285 PEEPHOLE_STORE(aload, S1 = memRead32 (L1 + (L2<<2)));
286 PEEPHOLE_STORE(aloads, S1 = memRead16 (L1 + (L2<<1)));
287 PEEPHOLE_STORE(aloadb, S1 = memRead8 (L1 + L2));
288 PEEPHOLE_STORE(aloadbit,S1 = (memRead8 (L1 + (L2>>3)) >> (L2 & 7)) & 1);
290 PEEPHOLE_STORE(copys, S1 = L1 & 0xFFFF);
291 PEEPHOLE_STORE(copyb, S1 = L1 & 0x00FF);
292 PEEPHOLE_STORE(sexs, S1 = (git_sint32)((signed short)(L1 & 0xFFFF)));
293 PEEPHOLE_STORE(sexb, S1 = (git_sint32)((signed char)(L1 & 0x00FF)));
295 #define PEEPHOLE_LOAD(tag,reg) \
296 do_ ## tag ## _ ## reg ## _const: reg = READ_PC; goto do_ ## tag; \
297 do_ ## tag ## _ ## reg ## _stack: CHECK_USED(1); reg = POP; goto do_ ## tag; \
298 do_ ## tag ## _ ## reg ## _local: reg = LOCAL(READ_PC); goto do_ ## tag; \
299 do_ ## tag ## _ ## reg ## _addr: reg = memRead32(READ_PC); goto do_ ## tag
301 PEEPHOLE_LOAD (return, L1);
302 PEEPHOLE_LOAD (astore, L3);
303 PEEPHOLE_LOAD (astores, L3);
304 PEEPHOLE_LOAD (astoreb, L3);
305 PEEPHOLE_LOAD (astorebit, L3);
307 #undef PEEPHOLE_STORE
309 do_astore: memWrite32 (L1 + (L2<<2), L3); NEXT;
310 do_astores: memWrite16 (L1 + (L2<<1), L3); NEXT;
311 do_astoreb: memWrite8 (L1 + L2, L3); NEXT;
313 L4 = memRead8(L1 + (L2>>3));
315 L4 &= ~(1 << (L2 & 7));
317 L4 |= (1 << (L2 & 7));
318 memWrite8(L1 + (L2>>3), L4);
321 #define DO_JUMP(tag, reg, cond) \
322 do_ ## tag ## _var: L7 = READ_PC; if (cond) goto do_goto_ ## reg ## _from_L7; NEXT; \
323 do_ ## tag ## _const: L7 = READ_PC; if (cond) goto do_jump_abs_L7; NEXT; \
324 do_ ## tag ## _by: L7 = READ_PC; if (cond) pc += L7; NEXT; \
325 do_ ## tag ## _return0: if (cond) { L1 = 0; goto do_return; } NEXT; \
326 do_ ## tag ## _return1: if (cond) { L1 = 1; goto do_return; } NEXT
328 DO_JUMP(jump, L1, 1 == 1);
329 DO_JUMP(jz, L2, L1 == 0);
330 DO_JUMP(jnz, L2, L1 != 0);
331 DO_JUMP(jeq, L3, L1 == L2);
332 DO_JUMP(jne, L3, L1 != L2);
333 DO_JUMP(jlt, L3, L1 < L2);
334 DO_JUMP(jge, L3, L1 >= L2);
335 DO_JUMP(jgt, L3, L1 > L2);
336 DO_JUMP(jle, L3, L1 <= L2);
337 DO_JUMP(jltu, L3, ((git_uint32)L1 < (git_uint32)L2));
338 DO_JUMP(jgeu, L3, ((git_uint32)L1 >= (git_uint32)L2));
339 DO_JUMP(jgtu, L3, ((git_uint32)L1 > (git_uint32)L2));
340 DO_JUMP(jleu, L3, ((git_uint32)L1 <= (git_uint32)L2));
344 do_jumpabs: L7 = L1; goto do_jump_abs_L7; NEXT;
346 do_goto_L3_from_L7: L1 = L3; goto do_goto_L1_from_L7;
347 do_goto_L2_from_L7: L1 = L2; goto do_goto_L1_from_L7;
349 if (L1 == 0 || L1 == 1) goto do_return;
350 L7 = L7 + L1 - 2; goto do_jump_abs_L7;
353 // The first argument is topmost in the stack; the count is in L2.
355 // We want to store the arguments in 'args' in the same order.
356 for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
360 // Specialised versions of above:
361 do_args_stack_call_stub_discard:
363 for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
365 goto do_call_stub_discard;
367 do_args_stack_call_stub_addr:
369 for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
371 goto do_call_stub_addr;
373 do_args_stack_call_stub_local:
375 for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
377 goto do_call_stub_local;
379 do_args_stack_call_stub_stack:
381 for (L3 = L2 - 1 ; L3 >= 0 ; --L3)
383 goto do_call_stub_stack;
407 do_undo_stub_discard:
409 PUSH (0); // DestType
410 PUSH (0); // DestAddr
411 goto finish_undo_stub;
415 PUSH (1); // DestType
416 PUSH (READ_PC); // DestAddr
417 goto finish_undo_stub;
421 PUSH (2); // DestType
422 PUSH (READ_PC); // DestAddr
423 goto finish_undo_stub;
427 PUSH (3); // DestType
428 PUSH (0); // DestAddr
429 goto finish_undo_stub;
432 PUSH (READ_PC); // PC
433 PUSH ((frame - base) * 4); // FramePtr
436 goto do_pop_call_stub;
439 if (restoreUndo (base, protectPos, protectSize) == 0)
443 goto do_pop_call_stub;
448 do_save_stub_discard:
450 PUSH (0); // DestType
451 PUSH (0); // DestAddr
452 goto finish_save_stub;
456 PUSH (1); // DestType
457 PUSH (READ_PC); // DestAddr
458 goto finish_save_stub;
462 PUSH (2); // DestType
463 PUSH (READ_PC); // DestAddr
464 goto finish_save_stub;
468 PUSH (3); // DestType
469 PUSH (0); // DestAddr
470 goto finish_save_stub;
473 PUSH (READ_PC); // PC
474 PUSH ((frame - base) * 4); // FramePtr
475 if (ioMode == IO_GLK)
476 S1 = saveToFile (base, sp, L1);
479 goto do_pop_call_stub;
483 && restoreFromFile (base, L1, protectPos, protectSize) == 0)
487 goto do_pop_call_stub;
492 do_catch_stub_discard:
495 PUSH (0); // DestType
496 goto finish_catch_stub_addr_L7;
501 memWrite32(L7, (sp-base+4)*4);
502 PUSH (1); // DestType
503 goto finish_catch_stub_addr_L7;
508 memWrite32(L7, (sp-base+4)*4);
509 PUSH (2); // DestType
510 goto finish_catch_stub_addr_L7;
514 PUSH (3); // DestType
515 PUSH (0); // DestAddr
516 PUSH (READ_PC); // PC
517 PUSH ((frame - base) * 4); // FramePtr
518 L7 = (sp - base)*4; // Catch token.
522 finish_catch_stub_addr_L7:
523 PUSH (L7); // DestAddr
524 PUSH (READ_PC); // PC
525 PUSH ((frame - base) * 4); // FramePtr
529 if (L2 < 16 || L2 > ((sp-base)*4))
530 fatalError ("Invalid catch token in throw");
532 goto do_pop_call_stub;
534 do_call_stub_discard:
536 PUSH (0); // DestType
537 PUSH (0); // DestAddr
538 goto finish_call_stub;
542 PUSH (1); // DestType
543 PUSH (READ_PC); // DestAddr
544 goto finish_call_stub;
548 PUSH (2); // DestType
549 PUSH (READ_PC); // DestAddr
550 goto finish_call_stub;
554 PUSH (3); // DestType
555 PUSH (0); // DestAddr
556 goto finish_call_stub;
559 PUSH (READ_PC); // PC
560 PUSH ((frame - base) * 4); // FramePtr
561 goto do_enter_function_L1;
564 // Zap the current stack frame, down to its call stub.
566 // Call the function!
567 goto do_enter_function_L1;
574 do_pop_call_stub:// L1 holds the return value.
578 // We just exited the top-level function.
581 // Something nasty happened.
582 goto stack_underflow;
584 L2 = POP; // FramePtr
586 L6 = POP; // DestAddr
587 switch (POP) // DestType
589 case 0: // Do not store.
590 frame = base + L2 / 4;
591 locals = frame + frame[1]/4;
592 values = frame + frame[0]/4;
595 case 1: // Store in main memory.
596 frame = base + L2 / 4;
597 locals = frame + frame[1]/4;
598 values = frame + frame[0]/4;
602 case 2: // Store in local variable.
603 frame = base + L2 / 4;
604 locals = frame + frame[1]/4;
605 values = frame + frame[0]/4;
609 case 3: // Push on stack.
610 frame = base + L2 / 4;
611 locals = frame + frame[1]/4;
612 values = frame + frame[0]/4;
616 case 10: // Resume printing a compressed (E1) string.
617 frame = base + L2 / 4;
618 locals = frame + frame[1]/4;
619 values = frame + frame[0]/4;
620 goto resume_compressed_string_L7_bit_L6;
622 case 11: // Resume executing function code after a string completes.
623 // Don't restore the frame pointer.
626 case 12: // Resume printing a signed decimal integer.
627 frame = base + L2 / 4;
628 locals = frame + frame[1]/4;
629 values = frame + frame[0]/4;
630 goto resume_number_L7_digit_L6;
632 case 13: // Resume printing a C-style (E0) string.
633 frame = base + L2 / 4;
634 locals = frame + frame[1]/4;
635 values = frame + frame[0]/4;
636 goto resume_c_string_L7;
638 case 14: // Resume printing a Unicode (E2) string.
639 frame = base + L2 / 4;
640 locals = frame + frame[1]/4;
641 values = frame + frame[0]/4;
642 goto resume_uni_string_L7;
645 fatalError("Bad call stub");
651 S1 = sp - values; NEXT;
654 if (L1 < 0 || L1 > (sp - values))
655 fatalError("Out of bounds in stkpeek");
656 S1 = sp[-1 - L1]; NEXT;
660 L1 = POP; L2 = POP; PUSH(L1); PUSH(L2); NEXT;
664 for (L2 = L1 ; L2 > 0 ; --L2)
671 resume_number_L7_digit_L6:
675 // If the IO mode is 'null', do nothing.
676 if (ioMode == IO_NULL)
677 goto do_pop_call_stub;
679 // Write the number into the buffer.
680 L1 = (L7 < 0) ? -L7 : L7; // Absolute value of number.
681 L2 = 0; // Current buffer position.
684 buffer [L2++] = '0' + (L1 % 10);
693 goto do_pop_call_stub; // We printed the whole number already.
695 // If we're in filter mode, push a call stub
696 // and filter the next character.
697 if (ioMode == IO_FILTER)
699 // Store the next character in the args array.
700 args[0] = buffer [L2 - L6 - 1];
703 // Push a call stub to print the next character.
705 PUSH(12); // DestType
706 PUSH(L6); // DestAddr (next digit)
707 PUSH(L7); // PC (number to print)
708 PUSH ((frame - base) * 4); // FramePtr
710 // Call the filter function.
713 goto do_enter_function_L1;
717 // We're in Glk mode. Just print all the characters.
718 for ( ; L6 < L2 ; ++L6)
719 glk_put_char (buffer [L2 - L6 - 1]);
722 goto do_pop_call_stub;
725 // If the IO mode is 'null', or if we've reached the
726 // end of the string, do nothing.
728 if (L2 == 0 || ioMode == IO_NULL)
729 goto do_pop_call_stub;
730 // Otherwise we're going to have to print something,
731 // If the IO mode is 'filter', filter the next char.
732 if (ioMode == IO_FILTER)
734 // Store this character in the args array.
738 PUSH(13); // DestType (resume C string)
739 PUSH(L6); // DestAddr (ignored)
740 PUSH(L7); // PC (next char to print)
741 PUSH ((frame - base) * 4); // FramePtr
742 // Call the filter function.
745 goto do_enter_function_L1;
747 // We're in Glk mode. Just print all the characters.
750 glk_put_char ((unsigned char) L2);
753 goto do_pop_call_stub;
755 resume_uni_string_L7:
756 // If the IO mode is 'null', or if we've reached the
757 // end of the string, do nothing.
760 if (L2 == 0 || ioMode == IO_NULL)
761 goto do_pop_call_stub;
762 // Otherwise we're going to have to print something,
763 // If the IO mode is 'filter', filter the next char.
764 if (ioMode == IO_FILTER)
766 // Store this character in the args array.
770 PUSH(14); // DestType (resume Unicode string)
771 PUSH(L6); // DestAddr (ignored)
772 PUSH(L7); // PC (next char to print)
773 PUSH ((frame - base) * 4); // FramePtr
774 // Call the filter function.
777 goto do_enter_function_L1;
779 // We're in Glk mode. Just print all the characters.
782 #ifdef GLK_MODULE_UNICODE
783 glk_put_char_uni ((glui32) L2);
785 unsigned char c = (L2 > 0 && L2 < 256) ? L2 : '?';
787 #endif // GLK_MODULE_UNICODE
791 goto do_pop_call_stub;
793 resume_compressed_string_L7_bit_L6:
794 // Load the first string table node into L1.
795 // Its address is stored at stringTable + 8.
796 L1 = memRead32 (stringTable + 8);
797 // Load the node's type byte.
798 L2 = memRead8 (L1++);
799 // Is the root node a branch?
802 // We'll keep a reservoir of input bits in L5.
804 // Keep following branch nodes until we hit a leaf node.
807 // Read the next bit.
809 // If we're finished reading this byte,
810 // move on to the next one.
816 // Follow the branch.
817 L1 = memRead32(L1 + 4 * L4);
818 L2 = memRead8 (L1++);
821 else if (L2 == 2 || L2 == 3)
823 // The root node prints a single character or a string.
824 // This will produce infinite output in the Null or Glk
825 // I/O modes, so we'll catch that here.
827 if (ioMode != IO_FILTER)
828 fatalError ("String table prints infinite strings!");
830 // In Filter mode, the output will be sent to the current
831 // filter function, which can change the string table
832 // before returning, so we'll continue and see what happens.
834 // We're at a leaf node.
837 case 1: // Terminator.
838 goto do_pop_call_stub;
840 case 2: // Single char.
841 if (ioMode == IO_NULL)
843 else if (ioMode == IO_GLK)
844 glk_put_char ((unsigned char) memRead8(L1));
847 // Store this character in the args array.
848 args [0] = memRead8(L1);
851 PUSH(10); // DestType
852 PUSH(L6); // DestAddr (bit number in string)
853 PUSH(L7); // PC (byte address in string)
854 PUSH ((frame - base) * 4); // FramePtr
855 // Call the filter function.
858 goto do_enter_function_L1;
863 // Push a 'resume compressed string' call stub.
865 PUSH (10); // DestType
866 PUSH (L6); // DestAddr (bit number in string)
867 PUSH (L7); // PC (byte address in string)
868 PUSH ((frame - base) * 4); // FramePtr
869 // Print the C string.
871 goto resume_c_string_L7;
873 case 4: // Unicode char
874 if (ioMode == IO_NULL)
876 else if (ioMode == IO_GLK)
878 #ifdef GLK_MODULE_UNICODE
879 glk_put_char_uni (memRead32(L1));
881 git_uint32 c = memRead32(L1);
882 if (c > 255) c = '?';
883 glk_put_char ((unsigned char) c);
884 #endif // GLK_MODULE_UNICODE
888 // Store this character in the args array.
889 args [0] = memRead32(L1);
892 PUSH(10); // DestType
893 PUSH(L6); // DestAddr (bit number in string)
894 PUSH(L7); // PC (byte address in string)
895 PUSH ((frame - base) * 4); // FramePtr
896 // Call the filter function.
899 goto do_enter_function_L1;
903 case 5: // Unicode string.
904 // Push a 'resume compressed string' call stub.
906 PUSH (10); // DestType
907 PUSH (L6); // DestAddr (bit number in string)
908 PUSH (L7); // PC (byte address in string)
909 PUSH ((frame - base) * 4); // FramePtr
910 // Print the Unicode string.
912 goto resume_uni_string_L7;
914 case 8: // Indirect reference.
916 L2 = 0; goto indirect_L3_args_L2;
918 case 9: // Double-indirect reference.
919 L3 = memRead32(L1); L3 = memRead32(L3);
920 L2 = 0; goto indirect_L3_args_L2;
922 case 10: // Indirect reference with args.
924 L2 = memRead32(L1 + 4); goto indirect_L3_args_L2;
926 case 11: // Double-indirect reference with args.
927 L3 = memRead32(L1); L3 = memRead32(L3);
928 L2 = memRead32(L1 + 4); goto indirect_L3_args_L2;
931 // Push a 'resume compressed string' call stub.
933 PUSH (10); // DestType
934 PUSH (L6); // DestAddr (bit number in string)
935 PUSH (L7); // PC (byte address in string)
936 PUSH ((frame - base) * 4); // FramePtr
937 // Check the type of the embedded object.
938 switch (memRead8(L3))
940 case 0xE0: // C string.
942 goto resume_c_string_L7;
944 case 0xE1: // Compressed string.
947 goto resume_compressed_string_L7_bit_L6;
949 case 0xE2: // Unicode string.
950 L7 = L3 + 4; // Skip extra three padding bytes.
951 goto resume_uni_string_L7;
953 case 0xC0: case 0xC1: // Function.
954 // Retrieve arguments.
955 for (L1 += 8, L4 = 0; L4 < L2 ; ++L4, L1+=4)
956 args[L4] = memRead32(L1);
959 goto do_enter_function_L1;
961 default: fatalError ("Embedded object in string has unknown type");
965 default: fatalError ("Unknown string table node type");
967 // Start back at the root node again.
968 goto resume_compressed_string_L7_bit_L6;
971 // Push a 'resume function' call stub.
973 PUSH (11); // DestType
975 PUSH (READ_PC); // PC
976 PUSH ((frame - base) * 4); // FramePtr
978 // Load the string's type byte.
982 // Uncompressed string.
984 goto resume_c_string_L7;
988 // Compressed string.
991 goto resume_compressed_string_L7_bit_L6;
995 // Uncompressed Unicode string.
996 L7 = L1 + 3; // Skip three padding bytes.
997 goto resume_uni_string_L7;
1001 fatalError ("Value used in streamstr was not a string");
1007 if (ioMode == IO_NULL)
1008 { /* Do nothing */ }
1009 else if (ioMode == IO_GLK)
1011 unsigned char c = (L1 & 0xff);
1016 // Store this character in the args array.
1017 args [0] = (L1 & 0xff);
1018 // Push a 'resume function' call stub.
1020 PUSH (0); // DestType
1023 PUSH ((frame - base) * 4); // FramePtr
1024 // Call the filter function.
1027 goto do_enter_function_L1;
1033 if (ioMode == IO_NULL)
1034 { /* Do nothing */ }
1035 else if (ioMode == IO_GLK)
1037 #ifdef GLK_MODULE_UNICODE
1038 glk_put_char_uni ((glui32) L1);
1040 unsigned char c = (L1 > 0 && L1 < 256) ? L1 : '?';
1042 #endif // GLK_MODULE_UNICODE
1046 // Store this character in the args array.
1048 // Push a 'resume function' call stub.
1050 PUSH (0); // DestType
1053 PUSH ((frame - base) * 4); // FramePtr
1054 // Call the filter function.
1057 goto do_enter_function_L1;
1062 // Push a 'resume function' call stub.
1064 PUSH (11); // DestType
1066 PUSH (READ_PC); // PC
1067 PUSH ((frame - base) * 4); // FramePtr
1069 // Print the number.
1072 goto resume_number_L7_digit_L6;
1091 ioMode = (enum IOMode) L1;
1096 fatalError ("Illegal I/O mode");
1105 // Reset game memory to its initial state.
1106 resetMemory(protectPos, protectSize);
1109 // Reset all the stack pointers.
1110 frame = locals = values = sp = base;
1112 // Call the first function.
1113 L1 = startPos; // Initial PC.
1114 L2 = 0; // No arguments.
1115 goto do_enter_function_L1;
1118 S1 = verifyMemory();
1125 S1 = -(rand() % -L1);
1128 // The parameter is zero, so we should generate a
1129 // random number in "the full 32-bit range". The rand()
1130 // function might not cover the entire range, so we'll
1131 // generate the high 16 bits and low 16 bits separately.
1132 S1 = (rand() & 0xffff) | (rand() << 16);
1137 srand (L1 ? L1 : time(NULL));
1141 // The first argument is topmost in the stack; count is in L2.
1143 // We want to store the arguments in 'args' in the same order.
1144 for (L3 = 0 ; L3 < L2 ; ++L3)
1147 S1 = git_perform_glk (L1, L2, (glui32*) args);
1152 S1 = git_binary_search (L1, L2, L3, L4, L5, L6, L7);
1156 S1 = git_linear_search (L1, L2, L3, L4, L5, L6, L7);
1160 S1 = git_linked_search (L1, L2, L3, L4, L5, L6);
1164 S1 = gestalt (L1, L2);
1167 do_getstringtbl: S1 = stringTable; NEXT;
1168 do_setstringtbl: stringTable = L1; NEXT;
1171 // TODO: do something useful here.
1175 // We need to rotate the top L1 elements by L2 places.
1177 fatalError ("Negative number of elements to rotate in stkroll");
1178 if (L1 > (sp - values))
1179 fatalError ("Tried to rotate too many elements in stkroll");
1180 // Now, let's normalise L2 into the range [0..L1).
1184 L2 = L1 - (-L2 % L1);
1185 // Avoid trivial cases.
1186 if (L1 == 0 || L2 == 0 || L2 == L1)
1189 // The problem is reduced to swapping elements [0..L2) with
1190 // elements [L2..L1). Let's call these two sequences A and B,
1191 // so we need to transform AB into BA. We do this sneakily
1192 // with reversals, as follows: AB -> A'B -> A'B' -> (A'B')',
1193 // where X' is the reverse of the sequence X.
1195 do { L4 = sp[(x)-L1];sp[(x)-L1]=sp[(y)-L1];sp[(y)-L1]=L4; } while (0)
1198 for (L3 = 0 ; L3 < L2/2 ; ++L3)
1200 // Reverse [L2..L1).
1201 for (L3 = L2 ; L3 < (L2 + (L1-L2)/2) ; ++L3)
1202 SWAP (L3, L1-1-(L3-L2));
1204 for (L3 = 0 ; L3 < L1/2 ; ++L3)
1212 S1 = resizeMemory (L1, 0);
1220 // Memory management (new with glulx spec 3.1)
1224 if (L2 < gRamStart || (L2 + L1) > gEndMem)
1226 memset(gRam + L2, 0, L1);
1232 if (L2 < 0 || (L2 + L1) > gEndMem)
1234 if (L3 < gRamStart || (L3 + L1) > gEndMem)
1236 // ROM and ROM are stored separately, so this is a bit fiddly...
1237 if (L2 > gRamStart) {
1238 // Only need to copy from RAM. Might be overlapping, so use memmove.
1239 memmove(gRam + L3, gRam + L2, L1);
1240 } else if ((L2 + L1) <= gRamStart) {
1241 // Only need to copy from ROM. Can't overlap, so memcpy is safe.
1242 memcpy(gRam + L3, gRom + L2, L1);
1244 // Need to copy from both ROM and RAM.
1245 L4 = (L2 + L1) - gRamStart; // Amount of ROM to copy.
1246 memcpy(gRam + L3, gRom + L2, L4);
1247 memmove(gRam + L3 + L4, gRam + L2 + L4, L1 - L4);
1253 S1 = heap_alloc(L1);
1260 // Function acceleration (new with glulx spec 3.1.1)
1263 accel_set_func(L1, L2);
1267 accel_set_param(L1, L2);
1270 // Special Git opcodes
1273 gCacheRAM = (L1 == 0) ? 0 : 1;
1277 pruneCodeCache (L1, L2);
1280 // Error conditions:
1282 do_error_bad_opcode:
1283 fatalError ("Illegal instruction");
1287 fatalError ("Stack overflow");
1291 fatalError ("Stack underflow");
1294 // ---------------------------------