/* exec.c: Glulxe code for program execution. The main interpreter loop. Designed by Andrew Plotkin http://eblong.com/zarf/glulx/index.html */ #include "glulxe.h" #include "opcodes.h" #include /* A couple of macros which test a pair of glui32 words as a double */ #define DOUBLE_PAIR_ISINF(vhi, vlo) (((vhi) == 0x7FF00000 || (vhi) == 0xFFF00000) && (vlo) == 0) #define DOUBLE_PAIR_ISNAN(vhi, vlo) (((vhi) & 0x7FF00000) == 0x7FF00000 && (((vhi) & 0xFFFFF) != 0 || (vlo) != 0)) /* execute_loop(): The main interpreter loop. This repeats until the program is done. */ void execute_loop() { int done_executing = FALSE; int ix; glui32 opcode; const operandlist_t *oplist; oparg_t inst[MAX_OPERANDS]; glui32 value, addr, val0, val1; glsi32 vals0, vals1; glui32 *arglist; glui32 arglistfix[3]; gfloat32 valf, valf1, valf2; glui32 val0hi, val0lo; gfloat64 vald, vald1, vald2; while (!done_executing) { /* 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) trap(TRAP_INTEGER_DIVIDE_BY_ZERO); if (vals0 == -0x80000000 && vals1 == -1) { trap(TRAP_INTEGER_OVERFLOW); } /* 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 = (-(glui32)vals0); if (vals1 < 0) { val1 = (-(glui32)vals1); value = val0 / val1; } else { val1 = vals1; value = -(val0 / val1); } } else { val0 = vals0; if (vals1 < 0) { val1 = (-(glui32)vals1); value = -(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) trap(TRAP_INTEGER_DIVIDE_BY_ZERO); if (vals1 < 0) { val1 = -(glui32)vals1; } else { val1 = vals1; } if (vals0 < 0) { val0 = (-(glui32)vals0); value = -(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 = (-(glui32)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 = ((glui32)(inst[0].value) << (glui32)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 = ((glui32)(inst[0].value) >> (glui32)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 = ((glsi32)(inst[0].value) >> (glsi32)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: value = inst[0].value; stackptr = inst[1].value; pop_callstub(value); break; case op_copy: value = inst[0].value; 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 &= ~((glui32)(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 >= (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) trap(TRAP_STACK_EXHAUSTED); addr = stackptr - vals0*4; for (ix=0; ix 0) { vals1 = vals1 % vals0; vals1 = (vals0) - vals1; } else { vals1 = (-(glui32)vals1) % vals0; } if (vals1 == 0) break; addr = stackptr - vals0*4; for (ix=0; ix 2147483647.0)) vals0 = 0x7FFFFFFF; else vals0 = (glsi32)(truncf(valf)); } else { if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) vals0 = 0x80000000; else vals0 = (glsi32)(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 = (glsi32)(roundf(valf)); } else { if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) vals0 = 0x80000000; else vals0 = (glsi32)(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 = fabsf(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 = fabsf(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_numtod: vals0 = inst[0].value; encode_double((gfloat64)vals0, &val0hi, &val0lo); store_operand(inst[1].desttype, inst[1].value, val0lo); store_operand(inst[2].desttype, inst[2].value, val0hi); break; case op_dtonumz: vald = decode_double(inst[0].value, inst[1].value); if (!signbit(vald)) { if (isnan(vald) || isinf(vald) || (vald > 2147483647.0)) vals0 = 0x7FFFFFFF; else vals0 = (glsi32)(trunc(vald)); } else { if (isnan(vald) || isinf(vald) || (vald < -2147483647.0)) vals0 = 0x80000000; else vals0 = (glsi32)(trunc(vald)); } store_operand(inst[2].desttype, inst[2].value, vals0); break; case op_dtonumn: vald = decode_double(inst[0].value, inst[1].value); if (!signbit(vald)) { if (isnan(vald) || isinf(vald) || (vald > 2147483647.0)) vals0 = 0x7FFFFFFF; else vals0 = (glsi32)(round(vald)); } else { if (isnan(vald) || isinf(vald) || (vald < -2147483647.0)) vals0 = 0x80000000; else vals0 = (glsi32)(round(vald)); } store_operand(inst[2].desttype, inst[2].value, vals0); break; case op_ftod: valf = decode_float(inst[0].value); encode_double((gfloat64)valf, &val0hi, &val0lo); store_operand(inst[1].desttype, inst[1].value, val0lo); store_operand(inst[2].desttype, inst[2].value, val0hi); break; case op_dtof: vald = decode_double(inst[0].value, inst[1].value); value = encode_float((gfloat32)vald); store_operand(inst[2].desttype, inst[2].value, value); break; case op_dadd: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); encode_double(vald1 + vald2, &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_dsub: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); encode_double(vald1 - vald2, &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_dmul: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); encode_double(vald1 * vald2, &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_ddiv: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); encode_double(vald1 / vald2, &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_dmodr: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); vald = fmod(vald1, vald2); encode_double(vald, &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_dmodq: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); vald = fmod(vald1, vald2); vald = (vald1-vald) / vald2; encode_double(vald, &val0hi, &val0lo); if ((val0hi == 0x0 || val0hi == 0x80000000) && val0lo == 0x0) { /* When the quotient is zero, the sign has been lost in the shuffle. We'll set that by hand, based on the original arguments. */ val0hi = (inst[0].value ^ inst[2].value) & 0x80000000; } store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_dfloor: vald = decode_double(inst[0].value, inst[1].value); encode_double(floor(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dceil: vald = decode_double(inst[0].value, inst[1].value); encode_double(ceil(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dsqrt: vald = decode_double(inst[0].value, inst[1].value); encode_double(sqrt(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dlog: vald = decode_double(inst[0].value, inst[1].value); encode_double(log(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dexp: vald = decode_double(inst[0].value, inst[1].value); encode_double(exp(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dpow: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); encode_double(glulx_pow(vald1, vald2), &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_dsin: vald = decode_double(inst[0].value, inst[1].value); encode_double(sin(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dcos: vald = decode_double(inst[0].value, inst[1].value); encode_double(cos(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dtan: vald = decode_double(inst[0].value, inst[1].value); encode_double(tan(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dasin: vald = decode_double(inst[0].value, inst[1].value); encode_double(asin(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_dacos: vald = decode_double(inst[0].value, inst[1].value); encode_double(acos(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_datan: vald = decode_double(inst[0].value, inst[1].value); encode_double(atan(vald), &val0hi, &val0lo); store_operand(inst[2].desttype, inst[2].value, val0lo); store_operand(inst[3].desttype, inst[3].value, val0hi); break; case op_datan2: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); vald = atan2(vald1, vald2); encode_double(vald, &val0hi, &val0lo); store_operand(inst[4].desttype, inst[4].value, val0lo); store_operand(inst[5].desttype, inst[5].value, val0hi); break; case op_jdisinf: /* Infinity is well-defined, so we don't bother to convert to float. */ val0 = inst[0].value; val1 = inst[1].value; if (DOUBLE_PAIR_ISINF(val0, val1)) { value = inst[2].value; goto PerformJump; } break; case op_jdisnan: /* NaN is well-defined, so we don't bother to convert to float. */ val0 = inst[0].value; val1 = inst[1].value; if (DOUBLE_PAIR_ISNAN(val0, val1)) { value = inst[2].value; goto PerformJump; } break; case op_jdeq: if (DOUBLE_PAIR_ISNAN(inst[4].value, inst[5].value)) { /* The delta is NaN, which can never match. */ val0 = 0; } else if (DOUBLE_PAIR_ISINF(inst[0].value, inst[1].value) && DOUBLE_PAIR_ISINF(inst[2].value, inst[3].value)) { /* Both are infinite. Opposite infinities are never equal, even if the difference is infinite, so this is easy. (We only need to compare the high words, because the low word of both INF and -INF is zero.) */ val0 = (inst[0].value == inst[2].value); } else { vald1 = decode_double(inst[2].value, inst[3].value) - decode_double(inst[0].value, inst[1].value); vald2 = fabs(decode_double(inst[4].value, inst[5].value)); val0 = (vald1 <= vald2 && vald1 >= -vald2); } if (val0) { value = inst[6].value; goto PerformJump; } break; case op_jdne: if (DOUBLE_PAIR_ISNAN(inst[4].value, inst[5].value)) { /* The delta is NaN, which can never match. */ val0 = 0; } else if (DOUBLE_PAIR_ISINF(inst[0].value, inst[1].value) && DOUBLE_PAIR_ISINF(inst[2].value, inst[3].value)) { /* Both are infinite. Opposite infinities are never equal, even if the difference is infinite, so this is easy. (We only need to compare the high words, because the low word of both INF and -INF is zero.) */ val0 = (inst[0].value == inst[2].value); } else { vald1 = decode_double(inst[2].value, inst[3].value) - decode_double(inst[0].value, inst[1].value); vald2 = fabs(decode_double(inst[4].value, inst[5].value)); val0 = (vald1 <= vald2 && vald1 >= -vald2); } if (!val0) { value = inst[6].value; goto PerformJump; } break; case op_jdlt: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); if (vald1 < vald2) { value = inst[4].value; goto PerformJump; } break; case op_jdgt: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); if (vald1 > vald2) { value = inst[4].value; goto PerformJump; } break; case op_jdle: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); if (vald1 <= vald2) { value = inst[4].value; goto PerformJump; } break; case op_jdge: vald1 = decode_double(inst[0].value, inst[1].value); vald2 = decode_double(inst[2].value, inst[3].value); if (vald1 >= vald2) { value = inst[4].value; goto PerformJump; } break; default: fatal_error_i("Executed unknown opcode.", opcode); } } } /* done executing */ }