Updated interpreters
[projects/chimara/chimara.git] / interpreters / glulxe / exec.c
index 03ac456aff88d11a36ee522c3cd299c03dfc522e..4dd3fcdc757d7c36f9e5159b11088bbe766f13c6 100644 (file)
@@ -7,6 +7,10 @@
 #include "glulxe.h"
 #include "opcodes.h"
 
+#ifdef FLOAT_SUPPORT
+#include <math.h>
+#endif /* FLOAT_SUPPORT */
+
 /* execute_loop():
    The main interpreter loop. This repeats until the program is done.
 */
@@ -16,11 +20,14 @@ void execute_loop()
   int ix;
   glui32 opcode;
   operandlist_t *oplist;
-  instruction_t inst;
+  oparg_t inst[MAX_OPERANDS];
   glui32 value, addr, val0, val1;
   glsi32 vals0, vals1;
   glui32 *arglist;
   glui32 arglistfix[3];
+#ifdef FLOAT_SUPPORT
+  gfloat32 valf, valf1, valf2;
+#endif /* FLOAT_SUPPORT */
 
   while (!done_executing) {
 
@@ -66,7 +73,7 @@ void execute_loop()
 
     /* 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);
+    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
@@ -80,96 +87,114 @@ void execute_loop()
         break;
 
       case op_add:
-        value = inst.value[0] + inst.value[1];
-        store_operand(inst.desttype, inst.value[2], value);
+        value = inst[0].value + inst[1].value;
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_sub:
-        value = inst.value[0] - inst.value[1];
-        store_operand(inst.desttype, inst.value[2], value);
+        value = inst[0].value - inst[1].value;
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_mul:
-        value = inst.value[0] * inst.value[1];
-        store_operand(inst.desttype, inst.value[2], value);
+        value = inst[0].value * inst[1].value;
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_div:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        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. */
-        if (vals1 < 0) {
-          vals0 = (-vals0);
-          vals1 = (-vals1);
-        }
-        if (vals0 >= 0) {
-          value = vals0 / vals1;
+           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 = -(val0 / val1);
+          }
         }
         else {
-          value = -((-vals0) / vals1);
+          val0 = vals0;
+          if (vals1 < 0) {
+            val1 = (-vals1);
+            value = -(val0 / val1);
+          }
+          else {
+            val1 = vals1;
+            value = val0 / val1;
+          }
         }
-        store_operand(inst.desttype, inst.value[2], value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_mod:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
         if (vals1 == 0)
           fatal_error("Division by zero doing remainder.");
         if (vals1 < 0) {
-          vals1 = (-vals1);
+            val1 = -vals1;
         }
-        if (vals0 >= 0) {
-          value = vals0 % vals1;
+        else {
+            val1 = vals1;
+        }
+        if (vals0 < 0) {
+          val0 = (-vals0);
+          value = -(val0 % val1);
         }
         else {
-          value = -((-vals0) % vals1);
+          val0 = vals0;
+          value = val0 % val1;
         }
-        store_operand(inst.desttype, inst.value[2], value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_neg:
-        vals0 = inst.value[0];
+        vals0 = inst[0].value;
         value = (-vals0);
-        store_operand(inst.desttype, inst.value[1], value);
+        store_operand(inst[1].desttype, inst[1].value, value);
         break;
 
       case op_bitand:
-        value = (inst.value[0] & inst.value[1]);
-        store_operand(inst.desttype, inst.value[2], value);
+        value = (inst[0].value & inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_bitor:
-        value = (inst.value[0] | inst.value[1]);
-        store_operand(inst.desttype, inst.value[2], value);
+        value = (inst[0].value | inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_bitxor:
-        value = (inst.value[0] ^ inst.value[1]);
-        store_operand(inst.desttype, inst.value[2], value);
+        value = (inst[0].value ^ inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_bitnot:
-        value = ~(inst.value[0]);
-        store_operand(inst.desttype, inst.value[1], value);
+        value = ~(inst[0].value);
+        store_operand(inst[1].desttype, inst[1].value, value);
         break;
 
       case op_shiftl:
-        vals0 = inst.value[1];
+        vals0 = inst[1].value;
         if (vals0 < 0 || vals0 >= 32)
           value = 0;
         else
-          value = ((glui32)(inst.value[0]) << (glui32)vals0);
-        store_operand(inst.desttype, inst.value[2], value);
+          value = ((glui32)(inst[0].value) << (glui32)vals0);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_ushiftr:
-        vals0 = inst.value[1];
+        vals0 = inst[1].value;
         if (vals0 < 0 || vals0 >= 32)
           value = 0;
         else
-          value = ((glui32)(inst.value[0]) >> (glui32)vals0);
-        store_operand(inst.desttype, inst.value[2], value);
+          value = ((glui32)(inst[0].value) >> (glui32)vals0);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
       case op_sshiftr:
-        vals0 = inst.value[1];
+        vals0 = inst[1].value;
         if (vals0 < 0 || vals0 >= 32) {
-          if (inst.value[0] & 0x80000000)
+          if (inst[0].value & 0x80000000)
             value = 0xFFFFFFFF;
           else
             value = 0;
@@ -178,13 +203,13 @@ void execute_loop()
           /* 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.value[0]) >> (glsi32)vals0);
+          value = ((glsi32)(inst[0].value) >> (glsi32)vals0);
         }
-        store_operand(inst.desttype, inst.value[2], value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
 
       case op_jump:
-        value = inst.value[0];
+        value = inst[0].value;
         /* fall through to PerformJump label. */
 
       PerformJump: /* goto label for successful jumping... ironic, no? */
@@ -206,99 +231,99 @@ void execute_loop()
         break;
 
       case op_jz:
-        if (inst.value[0] == 0) {
-          value = inst.value[1];
+        if (inst[0].value == 0) {
+          value = inst[1].value;
           goto PerformJump;
         }
         break;
       case op_jnz:
-        if (inst.value[0] != 0) {
-          value = inst.value[1];
+        if (inst[0].value != 0) {
+          value = inst[1].value;
           goto PerformJump;
         }
         break;
       case op_jeq:
-        if (inst.value[0] == inst.value[1]) {
-          value = inst.value[2];
+        if (inst[0].value == inst[1].value) {
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jne:
-        if (inst.value[0] != inst.value[1]) {
-          value = inst.value[2];
+        if (inst[0].value != inst[1].value) {
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jlt:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
         if (vals0 < vals1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jgt:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
         if (vals0 > vals1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jle:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
         if (vals0 <= vals1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jge:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
         if (vals0 >= vals1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jltu:
-        val0 = inst.value[0];
-        val1 = inst.value[1];
+        val0 = inst[0].value;
+        val1 = inst[1].value;
         if (val0 < val1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jgtu:
-        val0 = inst.value[0];
-        val1 = inst.value[1];
+        val0 = inst[0].value;
+        val1 = inst[1].value;
         if (val0 > val1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jleu:
-        val0 = inst.value[0];
-        val1 = inst.value[1];
+        val0 = inst[0].value;
+        val1 = inst[1].value;
         if (val0 <= val1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
       case op_jgeu:
-        val0 = inst.value[0];
-        val1 = inst.value[1];
+        val0 = inst[0].value;
+        val1 = inst[1].value;
         if (val0 >= val1) {
-          value = inst.value[2];
+          value = inst[2].value;
           goto PerformJump;
         }
         break;
 
       case op_call:
-        value = inst.value[1];
+        value = inst[1].value;
         arglist = pop_arguments(value, 0);
-        push_callstub(inst.desttype, inst.value[2]);
-        enter_function(inst.value[0], value, arglist);
+        push_callstub(inst[2].desttype, inst[2].value);
+        enter_function(inst[0].value, value, arglist);
         break;
       case op_return:
         leave_function();
@@ -306,120 +331,120 @@ void execute_loop()
           done_executing = TRUE;
           break;
         }
-        pop_callstub(inst.value[0]);
+        pop_callstub(inst[0].value);
         break;
       case op_tailcall:
-        value = inst.value[1];
+        value = inst[1].value;
         arglist = pop_arguments(value, 0);
         leave_function();
-        enter_function(inst.value[0], value, arglist);
+        enter_function(inst[0].value, value, arglist);
         break;
 
       case op_catch:
-        push_callstub(inst.desttype, inst.value[0]);
-        value = inst.value[1];
+        push_callstub(inst[0].desttype, inst[0].value);
+        value = inst[1].value;
         val0 = stackptr;
-        store_operand(inst.desttype, inst.value[0], val0);
+        store_operand(inst[0].desttype, inst[0].value, val0);
         goto PerformJump;
         break;
       case op_throw:
         profile_fail("throw");
-        value = inst.value[0];
-        stackptr = inst.value[1];
+        value = inst[0].value;
+        stackptr = inst[1].value;
         pop_callstub(value);
         break;
 
       case op_copy:
-        value = inst.value[0];
-        store_operand(inst.desttype, inst.value[1], value);
+        value = inst[0].value;
+        store_operand(inst[1].desttype, inst[1].value, value);
         break;
       case op_copys:
-        value = inst.value[0];
-        store_operand_s(inst.desttype, inst.value[1], value);
+        value = inst[0].value;
+        store_operand_s(inst[1].desttype, inst[1].value, value);
         break;
       case op_copyb:
-        value = inst.value[0];
-        store_operand_b(inst.desttype, inst.value[1], value);
+        value = inst[0].value;
+        store_operand_b(inst[1].desttype, inst[1].value, value);
         break;
 
       case op_sexs:
-        val0 = inst.value[0];
+        val0 = inst[0].value;
         if (val0 & 0x8000)
           val0 |= 0xFFFF0000;
         else
           val0 &= 0x0000FFFF;
-        store_operand(inst.desttype, inst.value[1], val0);
+        store_operand(inst[1].desttype, inst[1].value, val0);
         break;
       case op_sexb:
-        val0 = inst.value[0];
+        val0 = inst[0].value;
         if (val0 & 0x80)
           val0 |= 0xFFFFFF00;
         else
           val0 &= 0x000000FF;
-        store_operand(inst.desttype, inst.value[1], val0);
+        store_operand(inst[1].desttype, inst[1].value, val0);
         break;
 
       case op_aload:
-        value = inst.value[0];
-        value += 4 * inst.value[1];
+        value = inst[0].value;
+        value += 4 * inst[1].value;
         val0 = Mem4(value);
-        store_operand(inst.desttype, inst.value[2], val0);
+        store_operand(inst[2].desttype, inst[2].value, val0);
         break;
       case op_aloads:
-        value = inst.value[0];
-        value += 2 * inst.value[1];
+        value = inst[0].value;
+        value += 2 * inst[1].value;
         val0 = Mem2(value);
-        store_operand(inst.desttype, inst.value[2], val0);
+        store_operand(inst[2].desttype, inst[2].value, val0);
         break;
       case op_aloadb:
-        value = inst.value[0];
-        value += inst.value[1];
+        value = inst[0].value;
+        value += inst[1].value;
         val0 = Mem1(value);
-        store_operand(inst.desttype, inst.value[2], val0);
+        store_operand(inst[2].desttype, inst[2].value, val0);
         break;
       case op_aloadbit:
-        value = inst.value[0];
-        vals0 = inst.value[1];
+        value = inst[0].value;
+        vals0 = inst[1].value;
         val1 = (vals0 & 7);
         if (vals0 >= 0)
           value += (vals0 >> 3);
         else
-          value -= ((-1 - vals0) >> 3);
+          value -= (1 + ((-1 - vals0) >> 3));
         if (Mem1(value) & (1 << val1))
           val0 = 1;
         else
           val0 = 0;
-        store_operand(inst.desttype, inst.value[2], val0);
+        store_operand(inst[2].desttype, inst[2].value, val0);
         break;
 
       case op_astore:
-        value = inst.value[0];
-        value += 4 * inst.value[1];
-        val0 = inst.value[2];
+        value = inst[0].value;
+        value += 4 * inst[1].value;
+        val0 = inst[2].value;
         MemW4(value, val0);
         break;
       case op_astores:
-        value = inst.value[0];
-        value += 2 * inst.value[1];
-        val0 = inst.value[2];
+        value = inst[0].value;
+        value += 2 * inst[1].value;
+        val0 = inst[2].value;
         MemW2(value, val0);
         break;
       case op_astoreb:
-        value = inst.value[0];
-        value += inst.value[1];
-        val0 = inst.value[2];
+        value = inst[0].value;
+        value += inst[1].value;
+        val0 = inst[2].value;
         MemW1(value, val0);
         break;
       case op_astorebit:
-        value = inst.value[0];
-        vals0 = inst.value[1];
+        value = inst[0].value;
+        vals0 = inst[1].value;
         val1 = (vals0 & 7);
         if (vals0 >= 0)
           value += (vals0 >> 3);
         else
-          value -= ((-1 - vals0) >> 3);
+          value -= (1 + ((-1 - vals0) >> 3));
         val0 = Mem1(value);
-        if (inst.value[2])
+        if (inst[2].value)
           val0 |= (1 << val1);
         else
           val0 &= ~((glui32)(1 << val1));
@@ -428,14 +453,14 @@ void execute_loop()
 
       case op_stkcount:
         value = (stackptr - valstackbase) / 4;
-        store_operand(inst.desttype, inst.value[0], value);
+        store_operand(inst[0].desttype, inst[0].value, value);
         break;
       case op_stkpeek:
-        vals0 = inst.value[0] * 4;
+        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.desttype, inst.value[1], value);
+        store_operand(inst[1].desttype, inst[1].value, value);
         break;
       case op_stkswap:
         if (stackptr < valstackbase+8) {
@@ -447,7 +472,7 @@ void execute_loop()
         StkW4(stackptr-8, val0);
         break;
       case op_stkcopy:
-        vals0 = inst.value[0];
+        vals0 = inst[0].value;
         if (vals0 < 0)
           fatal_error("Negative operand in stkcopy.");
         if (vals0 == 0)
@@ -464,8 +489,8 @@ void execute_loop()
         stackptr += vals0*4;
         break;
       case op_stkroll:
-        vals0 = inst.value[0];
-        vals1 = inst.value[1];
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
         if (vals0 < 0)
           fatal_error("Negative operand in stkroll.");
         if (stackptr < valstackbase+vals0*4)
@@ -499,25 +524,25 @@ void execute_loop()
 
       case op_streamchar:
         profile_in(2, FALSE);
-        value = inst.value[0] & 0xFF;
+        value = inst[0].value & 0xFF;
         (*stream_char_handler)(value);
         profile_out();
         break;
       case op_streamunichar:
         profile_in(2, FALSE);
-        value = inst.value[0];
+        value = inst[0].value;
         (*stream_unichar_handler)(value);
         profile_out();
         break;
       case op_streamnum:
         profile_in(2, FALSE);
-        vals0 = inst.value[0];
+        vals0 = inst[0].value;
         stream_num(vals0, FALSE, 0);
         profile_out();
         break;
       case op_streamstr:
         profile_in(2, FALSE);
-        stream_string(inst.value[0], 0, 0);
+        stream_string(inst[0].value, 0, 0);
         profile_out();
         break;
 
@@ -530,91 +555,91 @@ void execute_loop()
       switch (opcode) {
 
       case op_gestalt:
-        value = do_gestalt(inst.value[0], inst.value[1]);
-        store_operand(inst.desttype, inst.value[2], value);
+        value = do_gestalt(inst[0].value, inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
         break;
 
       case op_debugtrap:
-        fatal_error_i("user debugtrap encountered.", inst.value[0]);
+        fatal_error_i("user debugtrap encountered.", inst[0].value);
 
       case op_jumpabs:
-        pc = inst.value[0];
+        pc = inst[0].value;
         break;
 
       case op_callf:
-        push_callstub(inst.desttype, inst.value[1]);
-        enter_function(inst.value[0], 0, arglistfix);
+        push_callstub(inst[1].desttype, inst[1].value);
+        enter_function(inst[0].value, 0, arglistfix);
         break;
       case op_callfi:
-        arglistfix[0] = inst.value[1];
-        push_callstub(inst.desttype, inst.value[2]);
-        enter_function(inst.value[0], 1, arglistfix);
+        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.value[1];
-        arglistfix[1] = inst.value[2];
-        push_callstub(inst.desttype, inst.value[3]);
-        enter_function(inst.value[0], 2, arglistfix);
+        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.value[1];
-        arglistfix[1] = inst.value[2];
-        arglistfix[2] = inst.value[3];
-        push_callstub(inst.desttype, inst.value[4]);
-        enter_function(inst.value[0], 3, arglistfix);
+        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.desttype, inst.value[0], endmem);
+        store_operand(inst[0].desttype, inst[0].value, endmem);
         break;
       case op_setmemsize:
-        value = change_memsize(inst.value[0], FALSE);
-        store_operand(inst.desttype, inst.value[1], value);
+        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.desttype, inst.value[0], value);
+        store_operand(inst[0].desttype, inst[0].value, value);
         break;
       case op_setstringtbl:
-        stream_set_table(inst.value[0]);
+        stream_set_table(inst[0].value);
         break;
 
       case op_getiosys:
         stream_get_iosys(&val0, &val1);
-        store_operand(inst.desttype, inst.value[0], val0);
-        store_operand(inst.desttype, inst.value[1], 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.value[0], inst.value[1]);
+        stream_set_iosys(inst[0].value, inst[1].value);
         break;
 
       case op_glk:
         profile_in(1, FALSE);
-        value = inst.value[1];
+        value = inst[1].value;
         arglist = pop_arguments(value, 0);
-        val0 = perform_glk(inst.value[0], value, arglist);
-        store_operand(inst.desttype, inst.value[2], val0);
+        val0 = perform_glk(inst[0].value, value, arglist);
+        store_operand(inst[2].desttype, inst[2].value, val0);
         profile_out();
         break;
 
       case op_random:
-        vals0 = inst.value[0];
+        vals0 = inst[0].value;
         if (vals0 == 0)
-          value = glulx_random() ^ (glulx_random() << 16);
+          value = glulx_random();
         else if (vals0 >= 1)
           value = glulx_random() % (glui32)(vals0);
         else 
           value = -(glulx_random() % (glui32)(-vals0));
-        store_operand(inst.desttype, inst.value[1], value);
+        store_operand(inst[1].desttype, inst[1].value, value);
         break;
       case op_setrandom:
-        glulx_setrandom(inst.value[0]);
+        glulx_setrandom(inst[0].value);
         break;
 
       case op_verify:
         value = perform_verify();
-        store_operand(inst.desttype, inst.value[0], value);
+        store_operand(inst[0].desttype, inst[0].value, value);
         break;
 
       case op_restart:
@@ -623,8 +648,8 @@ void execute_loop()
         break;
 
       case op_protect:
-        val0 = inst.value[0];
-        val1 = val0 + inst.value[1];
+        val0 = inst[0].value;
+        val1 = val0 + inst[1].value;
         if (val0 == val1) {
           val0 = 0;
           val1 = 0;
@@ -634,14 +659,14 @@ void execute_loop()
         break;
 
       case op_save:
-        push_callstub(inst.desttype, inst.value[1]);
-        value = perform_save(find_stream_by_id(inst.value[0]));
+        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:
         profile_fail("restore");
-        value = perform_restore(find_stream_by_id(inst.value[0]));
+        value = perform_restore(find_stream_by_id(inst[0].value));
         if (value == 0) {
           /* We've succeeded, and the stack now contains the callstub
              saved during saveundo. Ignore this opcode's operand. */
@@ -651,12 +676,12 @@ void execute_loop()
         else {
           /* We've failed, so we must store the failure in this opcode's
              operand. */
-          store_operand(inst.desttype, inst.value[1], value);
+          store_operand(inst[1].desttype, inst[1].value, value);
         }
         break;
 
       case op_saveundo:
-        push_callstub(inst.desttype, inst.value[0]);
+        push_callstub(inst[0].desttype, inst[0].value);
         value = perform_saveundo();
         pop_callstub(value);
         break;
@@ -673,7 +698,7 @@ void execute_loop()
         else {
           /* We've failed, so we must store the failure in this opcode's
              operand. */
-          store_operand(inst.desttype, inst.value[0], value);
+          store_operand(inst[0].desttype, inst[0].value, value);
         }
         break;
 
@@ -682,25 +707,25 @@ void execute_loop()
         break;
 
       case op_linearsearch:
-        value = linear_search(inst.value[0], inst.value[1], inst.value[2]
-          inst.value[3], inst.value[4], inst.value[5], inst.value[6]);
-        store_operand(inst.desttype, inst.value[7], value);
+        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.value[0], inst.value[1], inst.value[2]
-          inst.value[3], inst.value[4], inst.value[5], inst.value[6]);
-        store_operand(inst.desttype, inst.value[7], value);
+        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.value[0], inst.value[1], inst.value[2]
-          inst.value[3], inst.value[4], inst.value[5]);
-        store_operand(inst.desttype, inst.value[6], value);
+        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: {
         glui32 lx;
-        glui32 count = inst.value[0];
-        addr = inst.value[1];
+        glui32 count = inst[0].value;
+        addr = inst[1].value;
         for (lx=0; lx<count; lx++, addr++) {
           MemW1(addr, 0);
         }
@@ -708,9 +733,9 @@ void execute_loop()
         break;
       case op_mcopy: {
         glui32 lx;
-        glui32 count = inst.value[0];
-        glui32 addrsrc = inst.value[1];
-        glui32 addrdest = inst.value[2];
+        glui32 count = inst[0].value;
+        glui32 addrsrc = inst[1].value;
+        glui32 addrdest = inst[2].value;
         if (addrdest < addrsrc) {
           for (lx=0; lx<count; lx++, addrsrc++, addrdest++) {
             value = Mem1(addrsrc);
@@ -728,20 +753,274 @@ void execute_loop()
         }
         break;
       case op_malloc:
-        value = heap_alloc(inst.value[0]);
-        store_operand(inst.desttype, inst.value[1], value);
+        value = heap_alloc(inst[0].value);
+        store_operand(inst[1].desttype, inst[1].value, value);
         break;
       case op_mfree:
-        heap_free(inst.value[0]);
+        heap_free(inst[0].value);
         break;
 
       case op_accelfunc:
-        accel_set_func(inst.value[0], inst.value[1]);
+        accel_set_func(inst[0].value, inst[1].value);
         break;
       case op_accelparam:
-        accel_set_param(inst.value[0], inst.value[1]);
+        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 = (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 = 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 */
+
       default:
         fatal_error_i("Executed unknown opcode.", opcode);
       }