summaryrefslogtreecommitdiff
path: root/src/qcommon/vm_interpreted.c
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2017-03-22 17:56:34 +0100
committerPaweł Redman <pawel.redman@gmail.com>2017-03-22 17:56:34 +0100
commit6a777afc079c2a8d3af3ecd2145fe8dd50567a39 (patch)
tree520f4489cebf8564ef6cb27064ceea45cbc005b3 /src/qcommon/vm_interpreted.c
Funko sources as released by Rotacak.HEADmaster
Diffstat (limited to 'src/qcommon/vm_interpreted.c')
-rw-r--r--src/qcommon/vm_interpreted.c922
1 files changed, 922 insertions, 0 deletions
diff --git a/src/qcommon/vm_interpreted.c b/src/qcommon/vm_interpreted.c
new file mode 100644
index 0000000..35096e5
--- /dev/null
+++ b/src/qcommon/vm_interpreted.c
@@ -0,0 +1,922 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2006 Tim Angus
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include "vm_local.h"
+
+//#define DEBUG_VM
+#ifdef DEBUG_VM // bk001204
+static char *opnames[256] = {
+ "OP_UNDEF",
+
+ "OP_IGNORE",
+
+ "OP_BREAK",
+
+ "OP_ENTER",
+ "OP_LEAVE",
+ "OP_CALL",
+ "OP_PUSH",
+ "OP_POP",
+
+ "OP_CONST",
+
+ "OP_LOCAL",
+
+ "OP_JUMP",
+
+ //-------------------
+
+ "OP_EQ",
+ "OP_NE",
+
+ "OP_LTI",
+ "OP_LEI",
+ "OP_GTI",
+ "OP_GEI",
+
+ "OP_LTU",
+ "OP_LEU",
+ "OP_GTU",
+ "OP_GEU",
+
+ "OP_EQF",
+ "OP_NEF",
+
+ "OP_LTF",
+ "OP_LEF",
+ "OP_GTF",
+ "OP_GEF",
+
+ //-------------------
+
+ "OP_LOAD1",
+ "OP_LOAD2",
+ "OP_LOAD4",
+ "OP_STORE1",
+ "OP_STORE2",
+ "OP_STORE4",
+ "OP_ARG",
+
+ "OP_BLOCK_COPY",
+
+ //-------------------
+
+ "OP_SEX8",
+ "OP_SEX16",
+
+ "OP_NEGI",
+ "OP_ADD",
+ "OP_SUB",
+ "OP_DIVI",
+ "OP_DIVU",
+ "OP_MODI",
+ "OP_MODU",
+ "OP_MULI",
+ "OP_MULU",
+
+ "OP_BAND",
+ "OP_BOR",
+ "OP_BXOR",
+ "OP_BCOM",
+
+ "OP_LSH",
+ "OP_RSHI",
+ "OP_RSHU",
+
+ "OP_NEGF",
+ "OP_ADDF",
+ "OP_SUBF",
+ "OP_DIVF",
+ "OP_MULF",
+
+ "OP_CVIF",
+ "OP_CVFI"
+};
+#endif
+
+#if idppc
+
+//FIXME: these, um... look the same to me
+#if defined(__GNUC__)
+static ID_INLINE unsigned int loadWord(void *addr) {
+ unsigned int word;
+
+ asm("lwbrx %0,0,%1" : "=r" (word) : "r" (addr));
+ return word;
+}
+#else
+static ID_INLINE unsigned int __lwbrx(register void *addr,
+ register int offset) {
+ register unsigned int word;
+
+ asm("lwbrx %0,%2,%1" : "=r" (word) : "r" (addr), "b" (offset));
+ return word;
+}
+#define loadWord(addr) __lwbrx(addr,0)
+#endif
+
+#else
+ static ID_INLINE int loadWord(void *addr) {
+ int word;
+ memcpy(&word, addr, 4);
+ return LittleLong(word);
+ }
+#endif
+
+char *VM_Indent( vm_t *vm ) {
+ static char *string = " ";
+ if ( vm->callLevel > 20 ) {
+ return string;
+ }
+ return string + 2 * ( 20 - vm->callLevel );
+}
+
+void VM_StackTrace( vm_t *vm, int programCounter, int programStack ) {
+ int count;
+
+ count = 0;
+ do {
+ Com_Printf( "%s\n", VM_ValueToSymbol( vm, programCounter ) );
+ programStack = *(int *)&vm->dataBase[programStack+4];
+ programCounter = *(int *)&vm->dataBase[programStack];
+ } while ( programCounter != -1 && ++count < 32 );
+
+}
+
+
+/*
+====================
+VM_PrepareInterpreter
+====================
+*/
+void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) {
+ int op;
+ int pc;
+ byte *code;
+ int instruction;
+ int *codeBase;
+
+ vm->codeBase = Hunk_Alloc( vm->codeLength*4, h_high ); // we're now int aligned
+// memcpy( vm->codeBase, (byte *)header + header->codeOffset, vm->codeLength );
+
+ // we don't need to translate the instructions, but we still need
+ // to find each instructions starting point for jumps
+ pc = 0;
+ instruction = 0;
+ code = (byte *)header + header->codeOffset;
+ codeBase = (int *)vm->codeBase;
+
+ while ( instruction < header->instructionCount ) {
+ vm->instructionPointers[ instruction ] = pc;
+ instruction++;
+
+ op = code[ pc ];
+ codeBase[pc] = op;
+ if ( pc > header->codeLength ) {
+ Com_Error( ERR_FATAL, "VM_PrepareInterpreter: pc > header->codeLength" );
+ }
+
+ pc++;
+
+ // these are the only opcodes that aren't a single byte
+ switch ( op ) {
+ case OP_ENTER:
+ case OP_CONST:
+ case OP_LOCAL:
+ case OP_LEAVE:
+ case OP_EQ:
+ case OP_NE:
+ case OP_LTI:
+ case OP_LEI:
+ case OP_GTI:
+ case OP_GEI:
+ case OP_LTU:
+ case OP_LEU:
+ case OP_GTU:
+ case OP_GEU:
+ case OP_EQF:
+ case OP_NEF:
+ case OP_LTF:
+ case OP_LEF:
+ case OP_GTF:
+ case OP_GEF:
+ case OP_BLOCK_COPY:
+ codeBase[pc+0] = loadWord(&code[pc]);
+ pc += 4;
+ break;
+ case OP_ARG:
+ codeBase[pc+0] = code[pc];
+ pc += 1;
+ break;
+ default:
+ break;
+ }
+
+ }
+ pc = 0;
+ instruction = 0;
+ code = (byte *)header + header->codeOffset;
+ codeBase = (int *)vm->codeBase;
+
+ while ( instruction < header->instructionCount ) {
+ op = code[ pc ];
+ instruction++;
+ pc++;
+ switch ( op ) {
+ case OP_ENTER:
+ case OP_CONST:
+ case OP_LOCAL:
+ case OP_LEAVE:
+ case OP_EQ:
+ case OP_NE:
+ case OP_LTI:
+ case OP_LEI:
+ case OP_GTI:
+ case OP_GEI:
+ case OP_LTU:
+ case OP_LEU:
+ case OP_GTU:
+ case OP_GEU:
+ case OP_EQF:
+ case OP_NEF:
+ case OP_LTF:
+ case OP_LEF:
+ case OP_GTF:
+ case OP_GEF:
+ case OP_BLOCK_COPY:
+ switch(op) {
+ case OP_EQ:
+ case OP_NE:
+ case OP_LTI:
+ case OP_LEI:
+ case OP_GTI:
+ case OP_GEI:
+ case OP_LTU:
+ case OP_LEU:
+ case OP_GTU:
+ case OP_GEU:
+ case OP_EQF:
+ case OP_NEF:
+ case OP_LTF:
+ case OP_LEF:
+ case OP_GTF:
+ case OP_GEF:
+ codeBase[pc] = vm->instructionPointers[codeBase[pc]];
+ break;
+ default:
+ break;
+ }
+ pc += 4;
+ break;
+ case OP_ARG:
+ pc += 1;
+ break;
+ default:
+ break;
+ }
+
+ }
+}
+
+/*
+==============
+VM_Call
+
+
+Upon a system call, the stack will look like:
+
+sp+32 parm1
+sp+28 parm0
+sp+24 return stack
+sp+20 return address
+sp+16 local1
+sp+14 local0
+sp+12 arg1
+sp+8 arg0
+sp+4 return stack
+sp return address
+
+An interpreted function will immediately execute
+an OP_ENTER instruction, which will subtract space for
+locals from sp
+==============
+*/
+#define MAX_STACK 256
+#define STACK_MASK (MAX_STACK-1)
+
+#define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack )
+
+int VM_CallInterpreted( vm_t *vm, int *args ) {
+ int stack[MAX_STACK];
+ int *opStack;
+ int programCounter;
+ int programStack;
+ int stackOnEntry;
+ byte *image;
+ int *codeImage;
+ int v1;
+ int dataMask;
+#ifdef DEBUG_VM
+ vmSymbol_t *profileSymbol;
+#endif
+
+ // interpret the code
+ vm->currentlyInterpreting = qtrue;
+
+ // we might be called recursively, so this might not be the very top
+ programStack = stackOnEntry = vm->programStack;
+
+#ifdef DEBUG_VM
+ profileSymbol = VM_ValueToFunctionSymbol( vm, 0 );
+ // uncomment this for debugging breakpoints
+ vm->breakFunction = 0;
+#endif
+ // set up the stack frame
+
+ image = vm->dataBase;
+ codeImage = (int *)vm->codeBase;
+ dataMask = vm->dataMask;
+
+ // leave a free spot at start of stack so
+ // that as long as opStack is valid, opStack-1 will
+ // not corrupt anything
+ opStack = stack;
+ programCounter = 0;
+
+ programStack -= 48;
+
+ *(int *)&image[ programStack + 44] = args[9];
+ *(int *)&image[ programStack + 40] = args[8];
+ *(int *)&image[ programStack + 36] = args[7];
+ *(int *)&image[ programStack + 32] = args[6];
+ *(int *)&image[ programStack + 28] = args[5];
+ *(int *)&image[ programStack + 24] = args[4];
+ *(int *)&image[ programStack + 20] = args[3];
+ *(int *)&image[ programStack + 16] = args[2];
+ *(int *)&image[ programStack + 12] = args[1];
+ *(int *)&image[ programStack + 8 ] = args[0];
+ *(int *)&image[ programStack + 4 ] = 0; // return stack
+ *(int *)&image[ programStack ] = -1; // will terminate the loop on return
+
+ vm->callLevel = 0;
+
+ VM_Debug(0);
+
+// vm_debugLevel=2;
+ // main interpreter loop, will exit when a LEAVE instruction
+ // grabs the -1 program counter
+
+#define r2 codeImage[programCounter]
+
+ while ( 1 ) {
+ int opcode, r0, r1;
+// unsigned int r2;
+
+nextInstruction:
+ r0 = ((int *)opStack)[0];
+ r1 = ((int *)opStack)[-1];
+nextInstruction2:
+#ifdef DEBUG_VM
+ if ( (unsigned)programCounter >= vm->codeLength ) {
+ Com_Error( ERR_DROP, "VM pc out of range" );
+ }
+
+ if ( opStack < stack ) {
+ Com_Error( ERR_DROP, "VM opStack underflow" );
+ }
+ if ( opStack >= stack+MAX_STACK ) {
+ Com_Error( ERR_DROP, "VM opStack overflow" );
+ }
+
+ if ( programStack <= vm->stackBottom ) {
+ Com_Error( ERR_DROP, "VM stack overflow" );
+ }
+
+ if ( programStack & 3 ) {
+ Com_Error( ERR_DROP, "VM program stack misaligned" );
+ }
+
+ if ( vm_debugLevel > 1 ) {
+ Com_Printf( "%s %s\n", DEBUGSTR, opnames[opcode] );
+ }
+ profileSymbol->profileCount++;
+#endif
+ opcode = codeImage[ programCounter++ ];
+
+ switch ( opcode ) {
+#ifdef DEBUG_VM
+ default:
+ Com_Error( ERR_DROP, "Bad VM instruction" ); // this should be scanned on load!
+#endif
+ case OP_BREAK:
+ vm->breakCount++;
+ goto nextInstruction2;
+ case OP_CONST:
+ opStack++;
+ r1 = r0;
+ r0 = *opStack = r2;
+
+ programCounter += 4;
+ goto nextInstruction2;
+ case OP_LOCAL:
+ opStack++;
+ r1 = r0;
+ r0 = *opStack = r2+programStack;
+
+ programCounter += 4;
+ goto nextInstruction2;
+
+ case OP_LOAD4:
+#ifdef DEBUG_VM
+ if ( *opStack & 3 ) {
+ Com_Error( ERR_DROP, "OP_LOAD4 misaligned" );
+ }
+#endif
+ r0 = *opStack = *(int *)&image[ r0&dataMask ];
+ goto nextInstruction2;
+ case OP_LOAD2:
+ r0 = *opStack = *(unsigned short *)&image[ r0&dataMask ];
+ goto nextInstruction2;
+ case OP_LOAD1:
+ r0 = *opStack = image[ r0&dataMask ];
+ goto nextInstruction2;
+
+ case OP_STORE4:
+ *(int *)&image[ r1&(dataMask & ~3) ] = r0;
+ opStack -= 2;
+ goto nextInstruction;
+ case OP_STORE2:
+ *(short *)&image[ r1&(dataMask & ~1) ] = r0;
+ opStack -= 2;
+ goto nextInstruction;
+ case OP_STORE1:
+ image[ r1&dataMask ] = r0;
+ opStack -= 2;
+ goto nextInstruction;
+
+ case OP_ARG:
+ // single byte offset from programStack
+ *(int *)&image[ codeImage[programCounter] + programStack ] = r0;
+ opStack--;
+ programCounter += 1;
+ goto nextInstruction;
+
+ case OP_BLOCK_COPY:
+ {
+ int *src, *dest;
+ int i, count, srci, desti;
+
+ count = r2;
+ // MrE: copy range check
+ srci = r0 & dataMask;
+ desti = r1 & dataMask;
+ count = ((srci + count) & dataMask) - srci;
+ count = ((desti + count) & dataMask) - desti;
+
+ src = (int *)&image[ r0&dataMask ];
+ dest = (int *)&image[ r1&dataMask ];
+ if ( ( (intptr_t)src | (intptr_t)dest | count ) & 3 ) {
+ // happens in westernq3
+ Com_Printf( S_COLOR_YELLOW "Warning: OP_BLOCK_COPY not dword aligned\n");
+ }
+ count >>= 2;
+ for ( i = count-1 ; i>= 0 ; i-- ) {
+ dest[i] = src[i];
+ }
+ programCounter += 4;
+ opStack -= 2;
+ }
+ goto nextInstruction;
+
+ case OP_CALL:
+ // save current program counter
+ *(int *)&image[ programStack ] = programCounter;
+
+ // jump to the location on the stack
+ programCounter = r0;
+ opStack--;
+ if ( programCounter < 0 ) {
+ // system call
+ int r;
+ int temp;
+#ifdef DEBUG_VM
+ int stomped;
+
+ if ( vm_debugLevel ) {
+ Com_Printf( "%s---> systemcall(%i)\n", DEBUGSTR, -1 - programCounter );
+ }
+#endif
+ // save the stack to allow recursive VM entry
+ temp = vm->callLevel;
+ vm->programStack = programStack - 4;
+#ifdef DEBUG_VM
+ stomped = *(int *)&image[ programStack + 4 ];
+#endif
+ *(int *)&image[ programStack + 4 ] = -1 - programCounter;
+
+//VM_LogSyscalls( (int *)&image[ programStack + 4 ] );
+ {
+ intptr_t* argptr = (intptr_t *)&image[ programStack + 4 ];
+ #if __WORDSIZE == 64
+ // the vm has ints on the stack, we expect
+ // longs so we have to convert it
+ intptr_t argarr[16];
+ int i;
+ for (i = 0; i < 16; ++i) {
+ argarr[i] = *(int*)&image[ programStack + 4 + 4*i ];
+ }
+ argptr = argarr;
+ #endif
+ r = vm->systemCall( argptr );
+ }
+
+#ifdef DEBUG_VM
+ // this is just our stack frame pointer, only needed
+ // for debugging
+ *(int *)&image[ programStack + 4 ] = stomped;
+#endif
+
+ // save return value
+ opStack++;
+ *opStack = r;
+ programCounter = *(int *)&image[ programStack ];
+ vm->callLevel = temp;
+#ifdef DEBUG_VM
+ if ( vm_debugLevel ) {
+ Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) );
+ }
+#endif
+ } else if ( (unsigned)programCounter >= vm->codeLength ) {
+ Com_Error( ERR_DROP, "VM program counter out of range in OP_CALL" );
+ } else {
+ programCounter = vm->instructionPointers[ programCounter ];
+ }
+ goto nextInstruction;
+
+ // push and pop are only needed for discarded or bad function return values
+ case OP_PUSH:
+ opStack++;
+ goto nextInstruction;
+ case OP_POP:
+ opStack--;
+ goto nextInstruction;
+
+ case OP_ENTER:
+#ifdef DEBUG_VM
+ profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter );
+#endif
+ // get size of stack frame
+ v1 = r2;
+
+ programCounter += 4;
+ programStack -= v1;
+#ifdef DEBUG_VM
+ // save old stack frame for debugging traces
+ *(int *)&image[programStack+4] = programStack + v1;
+ if ( vm_debugLevel ) {
+ Com_Printf( "%s---> %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter - 5 ) );
+ if ( vm->breakFunction && programCounter - 5 == vm->breakFunction ) {
+ // this is to allow setting breakpoints here in the debugger
+ vm->breakCount++;
+// vm_debugLevel = 2;
+// VM_StackTrace( vm, programCounter, programStack );
+ }
+ vm->callLevel++;
+ }
+#endif
+ goto nextInstruction;
+ case OP_LEAVE:
+ // remove our stack frame
+ v1 = r2;
+
+ programStack += v1;
+
+ // grab the saved program counter
+ programCounter = *(int *)&image[ programStack ];
+#ifdef DEBUG_VM
+ profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter );
+ if ( vm_debugLevel ) {
+ vm->callLevel--;
+ Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) );
+ }
+#endif
+ // check for leaving the VM
+ if ( programCounter == -1 ) {
+ goto done;
+ } else if ( (unsigned)programCounter >= vm->codeLength ) {
+ Com_Error( ERR_DROP, "VM program counter out of range in OP_LEAVE" );
+ }
+ goto nextInstruction;
+
+ /*
+ ===================================================================
+ BRANCHES
+ ===================================================================
+ */
+
+ case OP_JUMP:
+ programCounter = r0;
+ programCounter = vm->instructionPointers[ programCounter ];
+ opStack--;
+ goto nextInstruction;
+
+ case OP_EQ:
+ opStack -= 2;
+ if ( r1 == r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_NE:
+ opStack -= 2;
+ if ( r1 != r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_LTI:
+ opStack -= 2;
+ if ( r1 < r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_LEI:
+ opStack -= 2;
+ if ( r1 <= r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_GTI:
+ opStack -= 2;
+ if ( r1 > r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_GEI:
+ opStack -= 2;
+ if ( r1 >= r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_LTU:
+ opStack -= 2;
+ if ( ((unsigned)r1) < ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_LEU:
+ opStack -= 2;
+ if ( ((unsigned)r1) <= ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_GTU:
+ opStack -= 2;
+ if ( ((unsigned)r1) > ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_GEU:
+ opStack -= 2;
+ if ( ((unsigned)r1) >= ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ goto nextInstruction;
+ }
+
+ case OP_EQF:
+ if ( ((float *)opStack)[-1] == *(float *)opStack ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ opStack -= 2;
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ opStack -= 2;
+ goto nextInstruction;
+ }
+
+ case OP_NEF:
+ if ( ((float *)opStack)[-1] != *(float *)opStack ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ opStack -= 2;
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ opStack -= 2;
+ goto nextInstruction;
+ }
+
+ case OP_LTF:
+ if ( ((float *)opStack)[-1] < *(float *)opStack ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ opStack -= 2;
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ opStack -= 2;
+ goto nextInstruction;
+ }
+
+ case OP_LEF:
+ if ( ((float *)opStack)[-1] <= *(float *)opStack ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ opStack -= 2;
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ opStack -= 2;
+ goto nextInstruction;
+ }
+
+ case OP_GTF:
+ if ( ((float *)opStack)[-1] > *(float *)opStack ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ opStack -= 2;
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ opStack -= 2;
+ goto nextInstruction;
+ }
+
+ case OP_GEF:
+ if ( ((float *)opStack)[-1] >= *(float *)opStack ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ opStack -= 2;
+ goto nextInstruction;
+ } else {
+ programCounter += 4;
+ opStack -= 2;
+ goto nextInstruction;
+ }
+
+
+ //===================================================================
+
+ case OP_NEGI:
+ *opStack = -r0;
+ goto nextInstruction;
+ case OP_ADD:
+ opStack[-1] = r1 + r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_SUB:
+ opStack[-1] = r1 - r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_DIVI:
+ opStack[-1] = r1 / r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_DIVU:
+ opStack[-1] = ((unsigned)r1) / ((unsigned)r0);
+ opStack--;
+ goto nextInstruction;
+ case OP_MODI:
+ opStack[-1] = r1 % r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_MODU:
+ opStack[-1] = ((unsigned)r1) % (unsigned)r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_MULI:
+ opStack[-1] = r1 * r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_MULU:
+ opStack[-1] = ((unsigned)r1) * ((unsigned)r0);
+ opStack--;
+ goto nextInstruction;
+
+ case OP_BAND:
+ opStack[-1] = ((unsigned)r1) & ((unsigned)r0);
+ opStack--;
+ goto nextInstruction;
+ case OP_BOR:
+ opStack[-1] = ((unsigned)r1) | ((unsigned)r0);
+ opStack--;
+ goto nextInstruction;
+ case OP_BXOR:
+ opStack[-1] = ((unsigned)r1) ^ ((unsigned)r0);
+ opStack--;
+ goto nextInstruction;
+ case OP_BCOM:
+ *opStack = ~ ((unsigned)r0);
+ goto nextInstruction;
+
+ case OP_LSH:
+ opStack[-1] = r1 << r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_RSHI:
+ opStack[-1] = r1 >> r0;
+ opStack--;
+ goto nextInstruction;
+ case OP_RSHU:
+ opStack[-1] = ((unsigned)r1) >> r0;
+ opStack--;
+ goto nextInstruction;
+
+ case OP_NEGF:
+ *(float *)opStack = -*(float *)opStack;
+ goto nextInstruction;
+ case OP_ADDF:
+ *(float *)(opStack-1) = *(float *)(opStack-1) + *(float *)opStack;
+ opStack--;
+ goto nextInstruction;
+ case OP_SUBF:
+ *(float *)(opStack-1) = *(float *)(opStack-1) - *(float *)opStack;
+ opStack--;
+ goto nextInstruction;
+ case OP_DIVF:
+ *(float *)(opStack-1) = *(float *)(opStack-1) / *(float *)opStack;
+ opStack--;
+ goto nextInstruction;
+ case OP_MULF:
+ *(float *)(opStack-1) = *(float *)(opStack-1) * *(float *)opStack;
+ opStack--;
+ goto nextInstruction;
+
+ case OP_CVIF:
+ *(float *)opStack = (float)*opStack;
+ goto nextInstruction;
+ case OP_CVFI:
+ *opStack = (int) *(float *)opStack;
+ goto nextInstruction;
+ case OP_SEX8:
+ *opStack = (signed char)*opStack;
+ goto nextInstruction;
+ case OP_SEX16:
+ *opStack = (short)*opStack;
+ goto nextInstruction;
+ }
+ }
+
+done:
+ vm->currentlyInterpreting = qfalse;
+
+ if ( opStack != &stack[1] ) {
+ Com_Error( ERR_DROP, "Interpreter error: opStack = %ld", (long int) (opStack - stack) );
+ }
+
+ vm->programStack = stackOnEntry;
+
+ // return the result
+ return *opStack;
+}