aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/glulxe/exec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/glulxe/exec.cpp')
-rw-r--r--engines/glk/glulxe/exec.cpp2015
1 files changed, 998 insertions, 1017 deletions
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index f314ca43e2..55cd918ebb 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -26,1042 +26,1023 @@ namespace Glk {
namespace Glulxe {
void Glulxe::execute_loop() {
- bool done_executing = false;
- int ix;
- uint opcode;
- const operandlist_t *oplist;
- oparg_t inst[MAX_OPERANDS];
- uint value, addr, val0, val1;
- int vals0, vals1;
- uint *arglist;
- uint arglistfix[3];
+ bool done_executing = false;
+ int ix;
+ uint opcode;
+ const operandlist_t *oplist;
+ oparg_t inst[MAX_OPERANDS];
+ uint value, addr, val0, val1;
+ int vals0, vals1;
+ uint *arglist;
+ uint arglistfix[3];
#ifdef FLOAT_SUPPORT
- gfloat32 valf, valf1, valf2;
+ gfloat32 valf, valf1, valf2;
#endif /* FLOAT_SUPPORT */
- while (!done_executing) {
-
- profile_tick();
- debugger_tick();
- /* Do OS-specific processing, if appropriate. */
- glk_tick();
-
- /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */
- prevpc = pc;
-
- /* Fetch the opcode number. */
- opcode = Mem1(pc);
- pc++;
- if (opcode & 0x80) {
- /* More than one-byte opcode. */
- if (opcode & 0x40) {
- /* Four-byte opcode */
- opcode &= 0x3F;
- opcode = (opcode << 8) | Mem1(pc);
- pc++;
- opcode = (opcode << 8) | Mem1(pc);
- pc++;
- opcode = (opcode << 8) | Mem1(pc);
- pc++;
- }
- else {
- /* Two-byte opcode */
- opcode &= 0x7F;
- opcode = (opcode << 8) | Mem1(pc);
- pc++;
- }
- }
-
- /* Now we have an opcode number. */
-
- /* Fetch the structure that describes how the operands for this
- opcode are arranged. This is a pointer to an immutable,
- static object. */
- if (opcode < 0x80)
- oplist = fast_operandlist[opcode];
- else
- oplist = lookup_operandlist(opcode);
-
- if (!oplist)
- fatal_error_i("Encountered unknown opcode.", opcode);
-
- /* Based on the oplist structure, load the actual operand values
- into inst. This moves the PC up to the end of the instruction. */
- parse_operands(inst, oplist);
-
- /* Perform the opcode. This switch statement is split in two, based
- on some paranoid suspicions about the ability of compilers to
- optimize large-range switches. Ignore that. */
-
- if (opcode < 0x80) {
-
- switch (opcode) {
-
- case op_nop:
- break;
-
- case op_add:
- value = inst[0].value + inst[1].value;
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_sub:
- value = inst[0].value - inst[1].value;
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_mul:
- value = inst[0].value * inst[1].value;
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_div:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals1 == 0)
- fatal_error("Division by zero.");
- /* Since C doesn't guarantee the results of division of negative
- numbers, we carefully convert everything to positive values
- first. They have to be unsigned values, too, otherwise the
- 0x80000000 case goes wonky. */
- if (vals0 < 0) {
- val0 = (-vals0);
- if (vals1 < 0) {
- val1 = (-vals1);
- value = val0 / val1;
- }
- else {
- val1 = vals1;
- value = -(int)(val0 / val1);
- }
- }
- else {
- val0 = vals0;
- if (vals1 < 0) {
- val1 = (-vals1);
- value = -(int)(val0 / val1);
- }
- else {
- val1 = vals1;
- value = val0 / val1;
- }
- }
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_mod:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals1 == 0)
- fatal_error("Division by zero doing remainder.");
- if (vals1 < 0) {
- val1 = -vals1;
- }
- else {
- val1 = vals1;
- }
- if (vals0 < 0) {
- val0 = (-vals0);
- value = -(int)(val0 % val1);
- }
- else {
- val0 = vals0;
- value = val0 % val1;
- }
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_neg:
- vals0 = inst[0].value;
- value = (-vals0);
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
-
- case op_bitand:
- value = (inst[0].value & inst[1].value);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_bitor:
- value = (inst[0].value | inst[1].value);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_bitxor:
- value = (inst[0].value ^ inst[1].value);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_bitnot:
- value = ~(inst[0].value);
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
-
- case op_shiftl:
- vals0 = inst[1].value;
- if (vals0 < 0 || vals0 >= 32)
- value = 0;
- else
- value = ((uint)(inst[0].value) << (uint)vals0);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_ushiftr:
- vals0 = inst[1].value;
- if (vals0 < 0 || vals0 >= 32)
- value = 0;
- else
- value = ((uint)(inst[0].value) >> (uint)vals0);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_sshiftr:
- vals0 = inst[1].value;
- if (vals0 < 0 || vals0 >= 32) {
- if (inst[0].value & 0x80000000)
- value = 0xFFFFFFFF;
- else
- value = 0;
- }
- else {
- /* This is somewhat foolhardy -- C doesn't guarantee that
- right-shifting a signed value replicates the sign bit.
- We'll assume it for now. */
- value = ((int)(inst[0].value) >> (int)vals0);
- }
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
-
- case op_jump:
- value = inst[0].value;
- /* fall through to PerformJump label. */
-
- PerformJump: /* goto label for successful jumping... ironic, no? */
- if (value == 0 || value == 1) {
- /* Return from function. This is exactly what happens in
- return_op, but it's only a few lines of code, so I won't
- bother with a "goto". */
- leave_function();
- if (stackptr == 0) {
- done_executing = true;
- break;
- }
- pop_callstub(value); /* zero or one */
- }
- else {
- /* Branch to a new PC value. */
- pc = (pc + value - 2);
- }
- break;
-
- case op_jz:
- if (inst[0].value == 0) {
- value = inst[1].value;
- goto PerformJump;
- }
- break;
- case op_jnz:
- if (inst[0].value != 0) {
- value = inst[1].value;
- goto PerformJump;
- }
- break;
- case op_jeq:
- if (inst[0].value == inst[1].value) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jne:
- if (inst[0].value != inst[1].value) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jlt:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals0 < vals1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jgt:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals0 > vals1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jle:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals0 <= vals1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jge:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals0 >= vals1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jltu:
- val0 = inst[0].value;
- val1 = inst[1].value;
- if (val0 < val1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jgtu:
- val0 = inst[0].value;
- val1 = inst[1].value;
- if (val0 > val1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jleu:
- val0 = inst[0].value;
- val1 = inst[1].value;
- if (val0 <= val1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jgeu:
- val0 = inst[0].value;
- val1 = inst[1].value;
- if (val0 >= val1) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
-
- case op_call:
- value = inst[1].value;
- arglist = pop_arguments(value, 0);
- push_callstub(inst[2].desttype, inst[2].value);
- enter_function(inst[0].value, value, arglist);
- break;
- case op_return:
- leave_function();
- if (stackptr == 0) {
- done_executing = true;
- break;
- }
- pop_callstub(inst[0].value);
- break;
- case op_tailcall:
- value = inst[1].value;
- arglist = pop_arguments(value, 0);
- leave_function();
- enter_function(inst[0].value, value, arglist);
- break;
-
- case op_catch:
- push_callstub(inst[0].desttype, inst[0].value);
- value = inst[1].value;
- val0 = stackptr;
- store_operand(inst[0].desttype, inst[0].value, val0);
- goto PerformJump;
- break;
- case op_throw:
- profile_fail("throw");
- value = inst[0].value;
- stackptr = inst[1].value;
- pop_callstub(value);
- break;
-
- case op_copy:
- value = inst[0].value;
+ while (!done_executing) {
+
+ profile_tick();
+ debugger_tick();
+ /* Do OS-specific processing, if appropriate. */
+ glk_tick();
+
+ /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */
+ prevpc = pc;
+
+ /* Fetch the opcode number. */
+ opcode = Mem1(pc);
+ pc++;
+ if (opcode & 0x80) {
+ /* More than one-byte opcode. */
+ if (opcode & 0x40) {
+ /* Four-byte opcode */
+ opcode &= 0x3F;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ } else {
+ /* Two-byte opcode */
+ opcode &= 0x7F;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ }
+ }
+
+ /* Now we have an opcode number. */
+
+ /* Fetch the structure that describes how the operands for this
+ opcode are arranged. This is a pointer to an immutable,
+ static object. */
+ if (opcode < 0x80)
+ oplist = fast_operandlist[opcode];
+ else
+ oplist = lookup_operandlist(opcode);
+
+ if (!oplist)
+ fatal_error_i("Encountered unknown opcode.", opcode);
+
+ /* Based on the oplist structure, load the actual operand values
+ into inst. This moves the PC up to the end of the instruction. */
+ parse_operands(inst, oplist);
+
+ /* Perform the opcode. This switch statement is split in two, based
+ on some paranoid suspicions about the ability of compilers to
+ optimize large-range switches. Ignore that. */
+
+ if (opcode < 0x80) {
+
+ switch (opcode) {
+
+ case op_nop:
+ break;
+
+ case op_add:
+ value = inst[0].value + inst[1].value;
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_sub:
+ value = inst[0].value - inst[1].value;
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_mul:
+ value = inst[0].value * inst[1].value;
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_div:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals1 == 0)
+ fatal_error("Division by zero.");
+ /* Since C doesn't guarantee the results of division of negative
+ numbers, we carefully convert everything to positive values
+ first. They have to be unsigned values, too, otherwise the
+ 0x80000000 case goes wonky. */
+ if (vals0 < 0) {
+ val0 = (-vals0);
+ if (vals1 < 0) {
+ val1 = (-vals1);
+ value = val0 / val1;
+ } else {
+ val1 = vals1;
+ value = -(int)(val0 / val1);
+ }
+ } else {
+ val0 = vals0;
+ if (vals1 < 0) {
+ val1 = (-vals1);
+ value = -(int)(val0 / val1);
+ } else {
+ val1 = vals1;
+ value = val0 / val1;
+ }
+ }
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_mod:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals1 == 0)
+ fatal_error("Division by zero doing remainder.");
+ if (vals1 < 0) {
+ val1 = -vals1;
+ } else {
+ val1 = vals1;
+ }
+ if (vals0 < 0) {
+ val0 = (-vals0);
+ value = -(int)(val0 % val1);
+ } else {
+ val0 = vals0;
+ value = val0 % val1;
+ }
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_neg:
+ vals0 = inst[0].value;
+ value = (-vals0);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_bitand:
+ value = (inst[0].value & inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_bitor:
+ value = (inst[0].value | inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_bitxor:
+ value = (inst[0].value ^ inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_bitnot:
+ value = ~(inst[0].value);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_shiftl:
+ vals0 = inst[1].value;
+ if (vals0 < 0 || vals0 >= 32)
+ value = 0;
+ else
+ value = ((uint)(inst[0].value) << (uint)vals0);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_ushiftr:
+ vals0 = inst[1].value;
+ if (vals0 < 0 || vals0 >= 32)
+ value = 0;
+ else
+ value = ((uint)(inst[0].value) >> (uint)vals0);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_sshiftr:
+ vals0 = inst[1].value;
+ if (vals0 < 0 || vals0 >= 32) {
+ if (inst[0].value & 0x80000000)
+ value = 0xFFFFFFFF;
+ else
+ value = 0;
+ } else {
+ /* This is somewhat foolhardy -- C doesn't guarantee that
+ right-shifting a signed value replicates the sign bit.
+ We'll assume it for now. */
+ value = ((int)(inst[0].value) >> (int)vals0);
+ }
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_jump:
+ value = inst[0].value;
+ /* fall through to PerformJump label. */
+
+PerformJump: /* goto label for successful jumping... ironic, no? */
+ if (value == 0 || value == 1) {
+ /* Return from function. This is exactly what happens in
+ return_op, but it's only a few lines of code, so I won't
+ bother with a "goto". */
+ leave_function();
+ if (stackptr == 0) {
+ done_executing = true;
+ break;
+ }
+ pop_callstub(value); /* zero or one */
+ } else {
+ /* Branch to a new PC value. */
+ pc = (pc + value - 2);
+ }
+ break;
+
+ case op_jz:
+ if (inst[0].value == 0) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jnz:
+ if (inst[0].value != 0) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jeq:
+ if (inst[0].value == inst[1].value) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jne:
+ if (inst[0].value != inst[1].value) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jlt:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 < vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jgt:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 > vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jle:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 <= vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jge:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 >= vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jltu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 < val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jgtu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 > val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jleu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 <= val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jgeu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 >= val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+
+ case op_call:
+ value = inst[1].value;
+ arglist = pop_arguments(value, 0);
+ push_callstub(inst[2].desttype, inst[2].value);
+ enter_function(inst[0].value, value, arglist);
+ break;
+ case op_return:
+ leave_function();
+ if (stackptr == 0) {
+ done_executing = true;
+ break;
+ }
+ pop_callstub(inst[0].value);
+ break;
+ case op_tailcall:
+ value = inst[1].value;
+ arglist = pop_arguments(value, 0);
+ leave_function();
+ enter_function(inst[0].value, value, arglist);
+ break;
+
+ case op_catch:
+ push_callstub(inst[0].desttype, inst[0].value);
+ value = inst[1].value;
+ val0 = stackptr;
+ store_operand(inst[0].desttype, inst[0].value, val0);
+ goto PerformJump;
+ break;
+ case op_throw:
+ profile_fail("throw");
+ value = inst[0].value;
+ stackptr = inst[1].value;
+ pop_callstub(value);
+ break;
+
+ case op_copy:
+ value = inst[0].value;
#ifdef TOLERATE_SUPERGLUS_BUG
- if (inst[1].desttype == 1 && inst[1].value == 0)
- inst[1].desttype = 0;
+ if (inst[1].desttype == 1 && inst[1].value == 0)
+ inst[1].desttype = 0;
#endif /* TOLERATE_SUPERGLUS_BUG */
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_copys:
- value = inst[0].value;
- store_operand_s(inst[1].desttype, inst[1].value, value);
- break;
- case op_copyb:
- value = inst[0].value;
- store_operand_b(inst[1].desttype, inst[1].value, value);
- break;
-
- case op_sexs:
- val0 = inst[0].value;
- if (val0 & 0x8000)
- val0 |= 0xFFFF0000;
- else
- val0 &= 0x0000FFFF;
- store_operand(inst[1].desttype, inst[1].value, val0);
- break;
- case op_sexb:
- val0 = inst[0].value;
- if (val0 & 0x80)
- val0 |= 0xFFFFFF00;
- else
- val0 &= 0x000000FF;
- store_operand(inst[1].desttype, inst[1].value, val0);
- break;
-
- case op_aload:
- value = inst[0].value;
- value += 4 * inst[1].value;
- val0 = Mem4(value);
- store_operand(inst[2].desttype, inst[2].value, val0);
- break;
- case op_aloads:
- value = inst[0].value;
- value += 2 * inst[1].value;
- val0 = Mem2(value);
- store_operand(inst[2].desttype, inst[2].value, val0);
- break;
- case op_aloadb:
- value = inst[0].value;
- value += inst[1].value;
- val0 = Mem1(value);
- store_operand(inst[2].desttype, inst[2].value, val0);
- break;
- case op_aloadbit:
- value = inst[0].value;
- vals0 = inst[1].value;
- val1 = (vals0 & 7);
- if (vals0 >= 0)
- value += (vals0 >> 3);
- else
- value -= (1 + ((-1 - vals0) >> 3));
- if (Mem1(value) & (1 << val1))
- val0 = 1;
- else
- val0 = 0;
- store_operand(inst[2].desttype, inst[2].value, val0);
- break;
-
- case op_astore:
- value = inst[0].value;
- value += 4 * inst[1].value;
- val0 = inst[2].value;
- MemW4(value, val0);
- break;
- case op_astores:
- value = inst[0].value;
- value += 2 * inst[1].value;
- val0 = inst[2].value;
- MemW2(value, val0);
- break;
- case op_astoreb:
- value = inst[0].value;
- value += inst[1].value;
- val0 = inst[2].value;
- MemW1(value, val0);
- break;
- case op_astorebit:
- value = inst[0].value;
- vals0 = inst[1].value;
- val1 = (vals0 & 7);
- if (vals0 >= 0)
- value += (vals0 >> 3);
- else
- value -= (1 + ((-1 - vals0) >> 3));
- val0 = Mem1(value);
- if (inst[2].value)
- val0 |= (1 << val1);
- else
- val0 &= ~((uint)(1 << val1));
- MemW1(value, val0);
- break;
-
- case op_stkcount:
- value = (stackptr - valstackbase) / 4;
- store_operand(inst[0].desttype, inst[0].value, value);
- break;
- case op_stkpeek:
- vals0 = inst[0].value * 4;
- if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase))
- fatal_error("Stkpeek outside current stack range.");
- value = Stk4(stackptr - (vals0+4));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_stkswap:
- if (stackptr < valstackbase+8) {
- fatal_error("Stack underflow in stkswap.");
- }
- val0 = Stk4(stackptr-4);
- val1 = Stk4(stackptr-8);
- StkW4(stackptr-4, val1);
- StkW4(stackptr-8, val0);
- break;
- case op_stkcopy:
- vals0 = inst[0].value;
- if (vals0 < 0)
- fatal_error("Negative operand in stkcopy.");
- if (vals0 == 0)
- break;
- if (stackptr < valstackbase+vals0*4)
- fatal_error("Stack underflow in stkcopy.");
- if (stackptr + vals0*4 > stacksize)
- fatal_error("Stack overflow in stkcopy.");
- addr = stackptr - vals0*4;
- for (ix=0; ix<vals0; ix++) {
- value = Stk4(addr + ix*4);
- StkW4(stackptr + ix*4, value);
- }
- stackptr += vals0*4;
- break;
- case op_stkroll:
- vals0 = inst[0].value;
- vals1 = inst[1].value;
- if (vals0 < 0)
- fatal_error("Negative operand in stkroll.");
- if (stackptr < valstackbase+vals0*4)
- fatal_error("Stack underflow in stkroll.");
- if (vals0 == 0)
- break;
- /* The following is a bit ugly. We want to do vals1 = vals0-vals1,
- because rolling down is sort of easier than rolling up. But
- we also want to take the result mod vals0. The % operator is
- annoying for negative numbers, so we need to do this in two
- cases. */
- if (vals1 > 0) {
- vals1 = vals1 % vals0;
- vals1 = (vals0) - vals1;
- }
- else {
- vals1 = (-vals1) % vals0;
- }
- if (vals1 == 0)
- break;
- addr = stackptr - vals0*4;
- for (ix=0; ix<vals1; ix++) {
- value = Stk4(addr + ix*4);
- StkW4(stackptr + ix*4, value);
- }
- for (ix=0; ix<vals0; ix++) {
- value = Stk4(addr + (vals1+ix)*4);
- StkW4(addr + ix*4, value);
- }
- break;
-
- case op_streamchar:
- profile_in(0xE0000001, stackptr, false);
- value = inst[0].value & 0xFF;
- (this->*stream_char_handler)(value);
- profile_out(stackptr);
- break;
- case op_streamunichar:
- profile_in(0xE0000002, stackptr, false);
- value = inst[0].value;
- (this->*stream_unichar_handler)(value);
- profile_out(stackptr);
- break;
- case op_streamnum:
- profile_in(0xE0000003, stackptr, false);
- vals0 = inst[0].value;
- stream_num(vals0, false, 0);
- profile_out(stackptr);
- break;
- case op_streamstr:
- profile_in(0xE0000004, stackptr, false);
- stream_string(inst[0].value, 0, 0);
- profile_out(stackptr);
- break;
-
- default:
- fatal_error_i("Executed unknown opcode.", opcode);
- }
- }
- else {
-
- switch (opcode) {
-
- case op_gestalt:
- value = do_gestalt(inst[0].value, inst[1].value);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
-
- case op_debugtrap:
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_copys:
+ value = inst[0].value;
+ store_operand_s(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_copyb:
+ value = inst[0].value;
+ store_operand_b(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_sexs:
+ val0 = inst[0].value;
+ if (val0 & 0x8000)
+ val0 |= 0xFFFF0000;
+ else
+ val0 &= 0x0000FFFF;
+ store_operand(inst[1].desttype, inst[1].value, val0);
+ break;
+ case op_sexb:
+ val0 = inst[0].value;
+ if (val0 & 0x80)
+ val0 |= 0xFFFFFF00;
+ else
+ val0 &= 0x000000FF;
+ store_operand(inst[1].desttype, inst[1].value, val0);
+ break;
+
+ case op_aload:
+ value = inst[0].value;
+ value += 4 * inst[1].value;
+ val0 = Mem4(value);
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+ case op_aloads:
+ value = inst[0].value;
+ value += 2 * inst[1].value;
+ val0 = Mem2(value);
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+ case op_aloadb:
+ value = inst[0].value;
+ value += inst[1].value;
+ val0 = Mem1(value);
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+ case op_aloadbit:
+ value = inst[0].value;
+ vals0 = inst[1].value;
+ val1 = (vals0 & 7);
+ if (vals0 >= 0)
+ value += (vals0 >> 3);
+ else
+ value -= (1 + ((-1 - vals0) >> 3));
+ if (Mem1(value) & (1 << val1))
+ val0 = 1;
+ else
+ val0 = 0;
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+
+ case op_astore:
+ value = inst[0].value;
+ value += 4 * inst[1].value;
+ val0 = inst[2].value;
+ MemW4(value, val0);
+ break;
+ case op_astores:
+ value = inst[0].value;
+ value += 2 * inst[1].value;
+ val0 = inst[2].value;
+ MemW2(value, val0);
+ break;
+ case op_astoreb:
+ value = inst[0].value;
+ value += inst[1].value;
+ val0 = inst[2].value;
+ MemW1(value, val0);
+ break;
+ case op_astorebit:
+ value = inst[0].value;
+ vals0 = inst[1].value;
+ val1 = (vals0 & 7);
+ if (vals0 >= 0)
+ value += (vals0 >> 3);
+ else
+ value -= (1 + ((-1 - vals0) >> 3));
+ val0 = Mem1(value);
+ if (inst[2].value)
+ val0 |= (1 << val1);
+ else
+ val0 &= ~((uint)(1 << val1));
+ MemW1(value, val0);
+ break;
+
+ case op_stkcount:
+ value = (stackptr - valstackbase) / 4;
+ store_operand(inst[0].desttype, inst[0].value, value);
+ break;
+ case op_stkpeek:
+ vals0 = inst[0].value * 4;
+ if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase))
+ fatal_error("Stkpeek outside current stack range.");
+ value = Stk4(stackptr - (vals0 + 4));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_stkswap:
+ if (stackptr < valstackbase + 8) {
+ fatal_error("Stack underflow in stkswap.");
+ }
+ val0 = Stk4(stackptr - 4);
+ val1 = Stk4(stackptr - 8);
+ StkW4(stackptr - 4, val1);
+ StkW4(stackptr - 8, val0);
+ break;
+ case op_stkcopy:
+ vals0 = inst[0].value;
+ if (vals0 < 0)
+ fatal_error("Negative operand in stkcopy.");
+ if (vals0 == 0)
+ break;
+ if (stackptr < valstackbase + vals0 * 4)
+ fatal_error("Stack underflow in stkcopy.");
+ if (stackptr + vals0 * 4 > stacksize)
+ fatal_error("Stack overflow in stkcopy.");
+ addr = stackptr - vals0 * 4;
+ for (ix = 0; ix < vals0; ix++) {
+ value = Stk4(addr + ix * 4);
+ StkW4(stackptr + ix * 4, value);
+ }
+ stackptr += vals0 * 4;
+ break;
+ case op_stkroll:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 < 0)
+ fatal_error("Negative operand in stkroll.");
+ if (stackptr < valstackbase + vals0 * 4)
+ fatal_error("Stack underflow in stkroll.");
+ if (vals0 == 0)
+ break;
+ /* The following is a bit ugly. We want to do vals1 = vals0-vals1,
+ because rolling down is sort of easier than rolling up. But
+ we also want to take the result mod vals0. The % operator is
+ annoying for negative numbers, so we need to do this in two
+ cases. */
+ if (vals1 > 0) {
+ vals1 = vals1 % vals0;
+ vals1 = (vals0) - vals1;
+ } else {
+ vals1 = (-vals1) % vals0;
+ }
+ if (vals1 == 0)
+ break;
+ addr = stackptr - vals0 * 4;
+ for (ix = 0; ix < vals1; ix++) {
+ value = Stk4(addr + ix * 4);
+ StkW4(stackptr + ix * 4, value);
+ }
+ for (ix = 0; ix < vals0; ix++) {
+ value = Stk4(addr + (vals1 + ix) * 4);
+ StkW4(addr + ix * 4, value);
+ }
+ break;
+
+ case op_streamchar:
+ profile_in(0xE0000001, stackptr, false);
+ value = inst[0].value & 0xFF;
+ (this->*stream_char_handler)(value);
+ profile_out(stackptr);
+ break;
+ case op_streamunichar:
+ profile_in(0xE0000002, stackptr, false);
+ value = inst[0].value;
+ (this->*stream_unichar_handler)(value);
+ profile_out(stackptr);
+ break;
+ case op_streamnum:
+ profile_in(0xE0000003, stackptr, false);
+ vals0 = inst[0].value;
+ stream_num(vals0, false, 0);
+ profile_out(stackptr);
+ break;
+ case op_streamstr:
+ profile_in(0xE0000004, stackptr, false);
+ stream_string(inst[0].value, 0, 0);
+ profile_out(stackptr);
+ break;
+
+ default:
+ fatal_error_i("Executed unknown opcode.", opcode);
+ }
+ } else {
+
+ switch (opcode) {
+
+ case op_gestalt:
+ value = do_gestalt(inst[0].value, inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_debugtrap:
#if VM_DEBUGGER
- /* We block and handle debug commands, but only if the
- library has invoked debug features. (Meaning, has
- the cycle handler ever been called.) */
- if (debugger_ever_invoked()) {
- debugger_block_and_debug("user debugtrap, pausing...");
- break;
- }
+ /* We block and handle debug commands, but only if the
+ library has invoked debug features. (Meaning, has
+ the cycle handler ever been called.) */
+ if (debugger_ever_invoked()) {
+ debugger_block_and_debug("user debugtrap, pausing...");
+ break;
+ }
#endif /* VM_DEBUGGER */
- fatal_error_i("user debugtrap encountered.", inst[0].value);
-
- case op_jumpabs:
- pc = inst[0].value;
- break;
-
- case op_callf:
- push_callstub(inst[1].desttype, inst[1].value);
- enter_function(inst[0].value, 0, arglistfix);
- break;
- case op_callfi:
- arglistfix[0] = inst[1].value;
- push_callstub(inst[2].desttype, inst[2].value);
- enter_function(inst[0].value, 1, arglistfix);
- break;
- case op_callfii:
- arglistfix[0] = inst[1].value;
- arglistfix[1] = inst[2].value;
- push_callstub(inst[3].desttype, inst[3].value);
- enter_function(inst[0].value, 2, arglistfix);
- break;
- case op_callfiii:
- arglistfix[0] = inst[1].value;
- arglistfix[1] = inst[2].value;
- arglistfix[2] = inst[3].value;
- push_callstub(inst[4].desttype, inst[4].value);
- enter_function(inst[0].value, 3, arglistfix);
- break;
-
- case op_getmemsize:
- store_operand(inst[0].desttype, inst[0].value, endmem);
- break;
- case op_setmemsize:
- value = change_memsize(inst[0].value, false);
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
-
- case op_getstringtbl:
- value = stream_get_table();
- store_operand(inst[0].desttype, inst[0].value, value);
- break;
- case op_setstringtbl:
- stream_set_table(inst[0].value);
- break;
-
- case op_getiosys:
- stream_get_iosys(&val0, &val1);
- store_operand(inst[0].desttype, inst[0].value, val0);
- store_operand(inst[1].desttype, inst[1].value, val1);
- break;
- case op_setiosys:
- stream_set_iosys(inst[0].value, inst[1].value);
- break;
-
- case op_glk:
- profile_in(0xF0000000+inst[0].value, stackptr, false);
- value = inst[1].value;
- arglist = pop_arguments(value, 0);
- val0 = perform_glk(inst[0].value, value, arglist);
+ fatal_error_i("user debugtrap encountered.", inst[0].value);
+
+ case op_jumpabs:
+ pc = inst[0].value;
+ break;
+
+ case op_callf:
+ push_callstub(inst[1].desttype, inst[1].value);
+ enter_function(inst[0].value, 0, arglistfix);
+ break;
+ case op_callfi:
+ arglistfix[0] = inst[1].value;
+ push_callstub(inst[2].desttype, inst[2].value);
+ enter_function(inst[0].value, 1, arglistfix);
+ break;
+ case op_callfii:
+ arglistfix[0] = inst[1].value;
+ arglistfix[1] = inst[2].value;
+ push_callstub(inst[3].desttype, inst[3].value);
+ enter_function(inst[0].value, 2, arglistfix);
+ break;
+ case op_callfiii:
+ arglistfix[0] = inst[1].value;
+ arglistfix[1] = inst[2].value;
+ arglistfix[2] = inst[3].value;
+ push_callstub(inst[4].desttype, inst[4].value);
+ enter_function(inst[0].value, 3, arglistfix);
+ break;
+
+ case op_getmemsize:
+ store_operand(inst[0].desttype, inst[0].value, endmem);
+ break;
+ case op_setmemsize:
+ value = change_memsize(inst[0].value, false);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_getstringtbl:
+ value = stream_get_table();
+ store_operand(inst[0].desttype, inst[0].value, value);
+ break;
+ case op_setstringtbl:
+ stream_set_table(inst[0].value);
+ break;
+
+ case op_getiosys:
+ stream_get_iosys(&val0, &val1);
+ store_operand(inst[0].desttype, inst[0].value, val0);
+ store_operand(inst[1].desttype, inst[1].value, val1);
+ break;
+ case op_setiosys:
+ stream_set_iosys(inst[0].value, inst[1].value);
+ break;
+
+ case op_glk:
+ profile_in(0xF0000000 + inst[0].value, stackptr, false);
+ value = inst[1].value;
+ arglist = pop_arguments(value, 0);
+ val0 = perform_glk(inst[0].value, value, arglist);
#ifdef TOLERATE_SUPERGLUS_BUG
- if (inst[2].desttype == 1 && inst[2].value == 0)
- inst[2].desttype = 0;
+ if (inst[2].desttype == 1 && inst[2].value == 0)
+ inst[2].desttype = 0;
#endif /* TOLERATE_SUPERGLUS_BUG */
- store_operand(inst[2].desttype, inst[2].value, val0);
- profile_out(stackptr);
- break;
-
- case op_random:
- vals0 = inst[0].value;
- if (vals0 == 0)
- value = glulx_random();
- else if (vals0 >= 1)
- value = glulx_random() % (uint)(vals0);
- else
- value = -(int)(glulx_random() % (uint)(-vals0));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_setrandom:
- glulx_setrandom(inst[0].value);
- break;
-
- case op_verify:
- value = perform_verify();
- store_operand(inst[0].desttype, inst[0].value, value);
- break;
-
- case op_restart:
- profile_fail("restart");
- vm_restart();
- break;
-
- case op_protect:
- val0 = inst[0].value;
- val1 = val0 + inst[1].value;
- if (val0 == val1) {
- val0 = 0;
- val1 = 0;
- }
- protectstart = val0;
- protectend = val1;
- break;
-
- case op_save:
- push_callstub(inst[1].desttype, inst[1].value);
- value = perform_save(find_stream_by_id(inst[0].value));
- pop_callstub(value);
- break;
-
- case op_restore:
- value = perform_restore(find_stream_by_id(inst[0].value), false);
- if (value == 0) {
- /* We've succeeded, and the stack now contains the callstub
- saved during saveundo. Ignore this opcode's operand. */
- value = (uint)-1;
- pop_callstub(value);
- }
- else {
- /* We've failed, so we must store the failure in this opcode's
- operand. */
- store_operand(inst[1].desttype, inst[1].value, value);
- }
- break;
-
- case op_saveundo:
- push_callstub(inst[0].desttype, inst[0].value);
- value = perform_saveundo();
- pop_callstub(value);
- break;
-
- case op_restoreundo:
- value = perform_restoreundo();
- if (value == 0) {
- /* We've succeeded, and the stack now contains the callstub
- saved during saveundo. Ignore this opcode's operand. */
- value = (uint)-1;
- pop_callstub(value);
- }
- else {
- /* We've failed, so we must store the failure in this opcode's
- operand. */
- store_operand(inst[0].desttype, inst[0].value, value);
- }
- break;
-
- case op_quit:
- done_executing = true;
- break;
-
- case op_linearsearch:
- value = linear_search(inst[0].value, inst[1].value, inst[2].value,
- inst[3].value, inst[4].value, inst[5].value, inst[6].value);
- store_operand(inst[7].desttype, inst[7].value, value);
- break;
- case op_binarysearch:
- value = binary_search(inst[0].value, inst[1].value, inst[2].value,
- inst[3].value, inst[4].value, inst[5].value, inst[6].value);
- store_operand(inst[7].desttype, inst[7].value, value);
- break;
- case op_linkedsearch:
- value = linked_search(inst[0].value, inst[1].value, inst[2].value,
- inst[3].value, inst[4].value, inst[5].value);
- store_operand(inst[6].desttype, inst[6].value, value);
- break;
-
- case op_mzero: {
- uint lx;
- uint count = inst[0].value;
- addr = inst[1].value;
- for (lx=0; lx<count; lx++, addr++) {
- MemW1(addr, 0);
- }
- }
- break;
- case op_mcopy: {
- uint lx;
- uint count = inst[0].value;
- uint addrsrc = inst[1].value;
- uint addrdest = inst[2].value;
- if (addrdest < addrsrc) {
- for (lx=0; lx<count; lx++, addrsrc++, addrdest++) {
- value = Mem1(addrsrc);
- MemW1(addrdest, value);
- }
- }
- else {
- addrsrc += (count-1);
- addrdest += (count-1);
- for (lx=0; lx<count; lx++, addrsrc--, addrdest--) {
- value = Mem1(addrsrc);
- MemW1(addrdest, value);
- }
- }
- }
- break;
- case op_malloc:
- value = heap_alloc(inst[0].value);
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_mfree:
- heap_free(inst[0].value);
- break;
-
- case op_accelfunc:
- accel_set_func(inst[0].value, inst[1].value);
- break;
- case op_accelparam:
- accel_set_param(inst[0].value, inst[1].value);
- break;
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ profile_out(stackptr);
+ break;
+
+ case op_random:
+ vals0 = inst[0].value;
+ if (vals0 == 0)
+ value = glulx_random();
+ else if (vals0 >= 1)
+ value = glulx_random() % (uint)(vals0);
+ else
+ value = -(int)(glulx_random() % (uint)(-vals0));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_setrandom:
+ glulx_setrandom(inst[0].value);
+ break;
+
+ case op_verify:
+ value = perform_verify();
+ store_operand(inst[0].desttype, inst[0].value, value);
+ break;
+
+ case op_restart:
+ profile_fail("restart");
+ vm_restart();
+ break;
+
+ case op_protect:
+ val0 = inst[0].value;
+ val1 = val0 + inst[1].value;
+ if (val0 == val1) {
+ val0 = 0;
+ val1 = 0;
+ }
+ protectstart = val0;
+ protectend = val1;
+ break;
+
+ case op_save:
+ push_callstub(inst[1].desttype, inst[1].value);
+ value = perform_save(find_stream_by_id(inst[0].value));
+ pop_callstub(value);
+ break;
+
+ case op_restore:
+ value = perform_restore(find_stream_by_id(inst[0].value), false);
+ if (value == 0) {
+ /* We've succeeded, and the stack now contains the callstub
+ saved during saveundo. Ignore this opcode's operand. */
+ value = (uint) - 1;
+ pop_callstub(value);
+ } else {
+ /* We've failed, so we must store the failure in this opcode's
+ operand. */
+ store_operand(inst[1].desttype, inst[1].value, value);
+ }
+ break;
+
+ case op_saveundo:
+ push_callstub(inst[0].desttype, inst[0].value);
+ value = perform_saveundo();
+ pop_callstub(value);
+ break;
+
+ case op_restoreundo:
+ value = perform_restoreundo();
+ if (value == 0) {
+ /* We've succeeded, and the stack now contains the callstub
+ saved during saveundo. Ignore this opcode's operand. */
+ value = (uint) - 1;
+ pop_callstub(value);
+ } else {
+ /* We've failed, so we must store the failure in this opcode's
+ operand. */
+ store_operand(inst[0].desttype, inst[0].value, value);
+ }
+ break;
+
+ case op_quit:
+ done_executing = true;
+ break;
+
+ case op_linearsearch:
+ value = linear_search(inst[0].value, inst[1].value, inst[2].value,
+ inst[3].value, inst[4].value, inst[5].value, inst[6].value);
+ store_operand(inst[7].desttype, inst[7].value, value);
+ break;
+ case op_binarysearch:
+ value = binary_search(inst[0].value, inst[1].value, inst[2].value,
+ inst[3].value, inst[4].value, inst[5].value, inst[6].value);
+ store_operand(inst[7].desttype, inst[7].value, value);
+ break;
+ case op_linkedsearch:
+ value = linked_search(inst[0].value, inst[1].value, inst[2].value,
+ inst[3].value, inst[4].value, inst[5].value);
+ store_operand(inst[6].desttype, inst[6].value, value);
+ break;
+
+ case op_mzero: {
+ uint lx;
+ uint count = inst[0].value;
+ addr = inst[1].value;
+ for (lx = 0; lx < count; lx++, addr++) {
+ MemW1(addr, 0);
+ }
+ }
+ break;
+ case op_mcopy: {
+ uint lx;
+ uint count = inst[0].value;
+ uint addrsrc = inst[1].value;
+ uint addrdest = inst[2].value;
+ if (addrdest < addrsrc) {
+ for (lx = 0; lx < count; lx++, addrsrc++, addrdest++) {
+ value = Mem1(addrsrc);
+ MemW1(addrdest, value);
+ }
+ } else {
+ addrsrc += (count - 1);
+ addrdest += (count - 1);
+ for (lx = 0; lx < count; lx++, addrsrc--, addrdest--) {
+ value = Mem1(addrsrc);
+ MemW1(addrdest, value);
+ }
+ }
+ }
+ break;
+ case op_malloc:
+ value = heap_alloc(inst[0].value);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_mfree:
+ heap_free(inst[0].value);
+ break;
+
+ case op_accelfunc:
+ accel_set_func(inst[0].value, inst[1].value);
+ break;
+ case op_accelparam:
+ accel_set_param(inst[0].value, inst[1].value);
+ break;
#ifdef FLOAT_SUPPORT
- case op_numtof:
- vals0 = inst[0].value;
- value = encode_float((gfloat32)vals0);
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_ftonumz:
- valf = decode_float(inst[0].value);
- if (!signbit(valf)) {
- if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
- vals0 = 0x7FFFFFFF;
- else
- vals0 = (int)(truncf(valf));
- }
- else {
- if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
- vals0 = 0x80000000;
- else
- vals0 = (int)(truncf(valf));
- }
- store_operand(inst[1].desttype, inst[1].value, vals0);
- break;
- case op_ftonumn:
- valf = decode_float(inst[0].value);
- if (!signbit(valf)) {
- if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
- vals0 = 0x7FFFFFFF;
- else
- vals0 = (int)(roundf(valf));
- }
- else {
- if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
- vals0 = 0x80000000;
- else
- vals0 = (int)(roundf(valf));
- }
- store_operand(inst[1].desttype, inst[1].value, vals0);
- break;
-
- case op_fadd:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- value = encode_float(valf1 + valf2);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_fsub:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- value = encode_float(valf1 - valf2);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_fmul:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- value = encode_float(valf1 * valf2);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
- case op_fdiv:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- value = encode_float(valf1 / valf2);
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
-
- case op_fmod:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- valf = fmodf(valf1, valf2);
- val0 = encode_float(valf);
- val1 = encode_float((valf1-valf) / valf2);
- if (val1 == 0x0 || val1 == 0x80000000) {
- /* When the quotient is zero, the sign has been lost in the
- shuffle. We'll set that by hand, based on the original
- arguments. */
- val1 = (inst[0].value ^ inst[1].value) & 0x80000000;
- }
- store_operand(inst[2].desttype, inst[2].value, val0);
- store_operand(inst[3].desttype, inst[3].value, val1);
- break;
-
- case op_floor:
- valf = decode_float(inst[0].value);
- value = encode_float(floorf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_ceil:
- valf = decode_float(inst[0].value);
- value = encode_float(ceilf(valf));
- if (value == 0x0 || value == 0x80000000) {
- /* When the result is zero, the sign may have been lost in the
- shuffle. (This is a bug in some C libraries.) We'll set the
- sign by hand, based on the original argument. */
- value = inst[0].value & 0x80000000;
- }
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
-
- case op_sqrt:
- valf = decode_float(inst[0].value);
- value = encode_float(sqrtf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_log:
- valf = decode_float(inst[0].value);
- value = encode_float(logf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_exp:
- valf = decode_float(inst[0].value);
- value = encode_float(expf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_pow:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- value = encode_float(glulx_powf(valf1, valf2));
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
-
- case op_sin:
- valf = decode_float(inst[0].value);
- value = encode_float(sinf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_cos:
- valf = decode_float(inst[0].value);
- value = encode_float(cosf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_tan:
- valf = decode_float(inst[0].value);
- value = encode_float(tanf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_asin:
- valf = decode_float(inst[0].value);
- value = encode_float(asinf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_acos:
- valf = decode_float(inst[0].value);
- value = encode_float(acosf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_atan:
- valf = decode_float(inst[0].value);
- value = encode_float(atanf(valf));
- store_operand(inst[1].desttype, inst[1].value, value);
- break;
- case op_atan2:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- value = encode_float(atan2f(valf1, valf2));
- store_operand(inst[2].desttype, inst[2].value, value);
- break;
-
- case op_jisinf:
- /* Infinity is well-defined, so we don't bother to convert to
- float. */
- val0 = inst[0].value;
- if (val0 == 0x7F800000 || val0 == 0xFF800000) {
- value = inst[1].value;
- goto PerformJump;
- }
- break;
- case op_jisnan:
- /* NaN is well-defined, so we don't bother to convert to
- float. */
- val0 = inst[0].value;
- if ((val0 & 0x7F800000) == 0x7F800000 && (val0 & 0x007FFFFF) != 0) {
- value = inst[1].value;
- goto PerformJump;
- }
- break;
-
- case op_jfeq:
- if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) {
- /* The delta is NaN, which can never match. */
- val0 = 0;
- }
- else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000)
- && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) {
- /* Both are infinite. Opposite infinities are never equal,
- even if the difference is infinite, so this is easy. */
- val0 = (inst[0].value == inst[1].value);
- }
- else {
- valf1 = decode_float(inst[1].value) - decode_float(inst[0].value);
- valf2 = fabs(decode_float(inst[2].value));
- val0 = (valf1 <= valf2 && valf1 >= -valf2);
- }
- if (val0) {
- value = inst[3].value;
- goto PerformJump;
- }
- break;
- case op_jfne:
- if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) {
- /* The delta is NaN, which can never match. */
- val0 = 0;
- }
- else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000)
- && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) {
- /* Both are infinite. Opposite infinities are never equal,
- even if the difference is infinite, so this is easy. */
- val0 = (inst[0].value == inst[1].value);
- }
- else {
- valf1 = decode_float(inst[1].value) - decode_float(inst[0].value);
- valf2 = fabs(decode_float(inst[2].value));
- val0 = (valf1 <= valf2 && valf1 >= -valf2);
- }
- if (!val0) {
- value = inst[3].value;
- goto PerformJump;
- }
- break;
-
- case op_jflt:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- if (valf1 < valf2) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jfgt:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- if (valf1 > valf2) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jfle:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- if (valf1 <= valf2) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
- case op_jfge:
- valf1 = decode_float(inst[0].value);
- valf2 = decode_float(inst[1].value);
- if (valf1 >= valf2) {
- value = inst[2].value;
- goto PerformJump;
- }
- break;
+ case op_numtof:
+ vals0 = inst[0].value;
+ value = encode_float((gfloat32)vals0);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_ftonumz:
+ valf = decode_float(inst[0].value);
+ if (!signbit(valf)) {
+ if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
+ vals0 = 0x7FFFFFFF;
+ else
+ vals0 = (int)(truncf(valf));
+ } else {
+ if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+ vals0 = 0x80000000;
+ else
+ vals0 = (int)(truncf(valf));
+ }
+ store_operand(inst[1].desttype, inst[1].value, vals0);
+ break;
+ case op_ftonumn:
+ valf = decode_float(inst[0].value);
+ if (!signbit(valf)) {
+ if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
+ vals0 = 0x7FFFFFFF;
+ else
+ vals0 = (int)(roundf(valf));
+ } else {
+ if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+ vals0 = 0x80000000;
+ else
+ vals0 = (int)(roundf(valf));
+ }
+ store_operand(inst[1].desttype, inst[1].value, vals0);
+ break;
+
+ case op_fadd:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 + valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_fsub:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 - valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_fmul:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 * valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_fdiv:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 / valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_fmod:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ valf = fmodf(valf1, valf2);
+ val0 = encode_float(valf);
+ val1 = encode_float((valf1 - valf) / valf2);
+ if (val1 == 0x0 || val1 == 0x80000000) {
+ /* When the quotient is zero, the sign has been lost in the
+ shuffle. We'll set that by hand, based on the original
+ arguments. */
+ val1 = (inst[0].value ^ inst[1].value) & 0x80000000;
+ }
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ store_operand(inst[3].desttype, inst[3].value, val1);
+ break;
+
+ case op_floor:
+ valf = decode_float(inst[0].value);
+ value = encode_float(floorf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_ceil:
+ valf = decode_float(inst[0].value);
+ value = encode_float(ceilf(valf));
+ if (value == 0x0 || value == 0x80000000) {
+ /* When the result is zero, the sign may have been lost in the
+ shuffle. (This is a bug in some C libraries.) We'll set the
+ sign by hand, based on the original argument. */
+ value = inst[0].value & 0x80000000;
+ }
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_sqrt:
+ valf = decode_float(inst[0].value);
+ value = encode_float(sqrtf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_log:
+ valf = decode_float(inst[0].value);
+ value = encode_float(logf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_exp:
+ valf = decode_float(inst[0].value);
+ value = encode_float(expf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_pow:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(glulx_powf(valf1, valf2));
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_sin:
+ valf = decode_float(inst[0].value);
+ value = encode_float(sinf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_cos:
+ valf = decode_float(inst[0].value);
+ value = encode_float(cosf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_tan:
+ valf = decode_float(inst[0].value);
+ value = encode_float(tanf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_asin:
+ valf = decode_float(inst[0].value);
+ value = encode_float(asinf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_acos:
+ valf = decode_float(inst[0].value);
+ value = encode_float(acosf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_atan:
+ valf = decode_float(inst[0].value);
+ value = encode_float(atanf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_atan2:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(atan2f(valf1, valf2));
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_jisinf:
+ /* Infinity is well-defined, so we don't bother to convert to
+ float. */
+ val0 = inst[0].value;
+ if (val0 == 0x7F800000 || val0 == 0xFF800000) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jisnan:
+ /* NaN is well-defined, so we don't bother to convert to
+ float. */
+ val0 = inst[0].value;
+ if ((val0 & 0x7F800000) == 0x7F800000 && (val0 & 0x007FFFFF) != 0) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+
+ case op_jfeq:
+ if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) {
+ /* The delta is NaN, which can never match. */
+ val0 = 0;
+ } else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000)
+ && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) {
+ /* Both are infinite. Opposite infinities are never equal,
+ even if the difference is infinite, so this is easy. */
+ val0 = (inst[0].value == inst[1].value);
+ } else {
+ valf1 = decode_float(inst[1].value) - decode_float(inst[0].value);
+ valf2 = fabs(decode_float(inst[2].value));
+ val0 = (valf1 <= valf2 && valf1 >= -valf2);
+ }
+ if (val0) {
+ value = inst[3].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfne:
+ if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) {
+ /* The delta is NaN, which can never match. */
+ val0 = 0;
+ } else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000)
+ && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) {
+ /* Both are infinite. Opposite infinities are never equal,
+ even if the difference is infinite, so this is easy. */
+ val0 = (inst[0].value == inst[1].value);
+ } else {
+ valf1 = decode_float(inst[1].value) - decode_float(inst[0].value);
+ valf2 = fabs(decode_float(inst[2].value));
+ val0 = (valf1 <= valf2 && valf1 >= -valf2);
+ }
+ if (!val0) {
+ value = inst[3].value;
+ goto PerformJump;
+ }
+ break;
+
+ case op_jflt:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 < valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfgt:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 > valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfle:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 <= valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfge:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 >= valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
#endif /* FLOAT_SUPPORT */
#ifdef GLULX_EXTEND_OPCODES
- GLULX_EXTEND_OPCODES
+ GLULX_EXTEND_OPCODES
#endif /* GLULX_EXTEND_OPCODES */
- default:
- fatal_error_i("Executed unknown opcode.", opcode);
- }
- }
- }
- /* done executing */
+ default:
+ fatal_error_i("Executed unknown opcode.", opcode);
+ }
+ }
+ }
+ /* done executing */
#if VM_DEBUGGER
- debugger_handle_quit();
+ debugger_handle_quit();
#endif /* VM_DEBUGGER */
}