1043 lines · 31.1 KB · C
RawDownload
/* exec.c: Glulxe code for program execution. The main interpreter loop.    Designed by Andrew Plotkin <erkyrath@eblong.com>    http://eblong.com/zarf/glulx/index.html*/ #include "glk.h"#include "glulxe.h"#include "opcodes.h" #ifdef FLOAT_SUPPORT#include <math.h>#include <float.h>#endif /* FLOAT_SUPPORT */ /* execute_loop():   The main interpreter loop. This repeats until the program is done.*/void execute_loop(){  int done_executing = FALSE;  int ix;  glui32 opcode;  operandlist_t *oplist;  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) {     profile_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 = -(val0 / val1);          }        }        else {          val0 = vals0;          if (vals1 < 0) {            val1 = (-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)          fatal_error("Division by zero doing remainder.");        if (vals1 < 0) {            val1 = -vals1;        }        else {            val1 = vals1;        }        if (vals0 < 0) {          val0 = (-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 = (-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:        profile_fail("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)           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)

Lines 1–500 of 1043.