summaryrefslogtreecommitdiff
path: root/src/virtualmachine
diff options
context:
space:
mode:
authorSimon Robertshaw <simon@hardwired.org.uk>2012-11-17 19:44:09 (GMT)
committer Simon Robertshaw <simon@hardwired.org.uk>2012-11-17 19:44:09 (GMT)
commit058a2edd75debbd0297f92572316daa704bd379f (patch)
treead303f091f9a08b209b91eb34a9fcad996a3de69 /src/virtualmachine
parente3594aba9e05c6865d396418c028049cda92c2f3 (diff)
parent7a21ae192fe19868539956f3fe28e62b2c7c4429 (diff)
downloadpowder-058a2edd75debbd0297f92572316daa704bd379f.zip
powder-058a2edd75debbd0297f92572316daa704bd379f.tar.gz
Merge branch 'master' of github.com:FacialTurd/PowderToypp
Diffstat (limited to 'src/virtualmachine')
-rw-r--r--src/virtualmachine/Exceptions.h100
-rw-r--r--src/virtualmachine/JustInTime.cpp1144
-rw-r--r--src/virtualmachine/Operations.cpp356
-rw-r--r--src/virtualmachine/Operations.inl60
-rw-r--r--src/virtualmachine/Syscalls.cpp99
-rw-r--r--src/virtualmachine/Syscalls.inl14
-rw-r--r--src/virtualmachine/VirtualMachine.cpp405
-rw-r--r--src/virtualmachine/VirtualMachine.h282
8 files changed, 2460 insertions, 0 deletions
diff --git a/src/virtualmachine/Exceptions.h b/src/virtualmachine/Exceptions.h
new file mode 100644
index 0000000..9628d10
--- /dev/null
+++ b/src/virtualmachine/Exceptions.h
@@ -0,0 +1,100 @@
+#pragma once
+#include <stdexcept>
+#include <cstring>
+#include "Format.h"
+
+namespace vm
+{
+ class RuntimeException: public std::exception
+ {
+ char * error;
+ public:
+ RuntimeException() : error(NULL) {}
+ RuntimeException(char * message) : error(strdup(message)) {}
+ const char * what() const throw()
+ {
+ if(error)
+ return error;
+ else
+ return "VirtualMachine runtime exception";
+ }
+ ~RuntimeException() throw() {};
+ };
+
+ class StackOverflowException: public RuntimeException
+ {
+ public:
+ StackOverflowException() {}
+ const char * what() const throw()
+ {
+ return "VirtualMachine Stack overflow";
+ }
+ ~StackOverflowException() throw() {};
+ };
+
+ class StackUnderflowException: public RuntimeException
+ {
+ public:
+ StackUnderflowException() {}
+ const char * what() const throw()
+ {
+ return "VirtualMachine Stack underflow";
+ }
+ ~StackUnderflowException() throw() {};
+ };
+
+ class AccessViolationException: public RuntimeException
+ {
+ int address;
+ char * _what;
+ public:
+ AccessViolationException(int address = 0) : address(address)
+ {
+ _what = strdup(std::string("VirtualMachine Access violation at "+format::NumberToString<int>(address)).c_str());
+ }
+ const char * what() const throw()
+ {
+ if(address)
+ return _what;
+ return "VirtualMachine Access violation";
+ }
+ ~AccessViolationException() throw() {};
+ };
+
+ class JITException: public RuntimeException
+ {
+ char * _what;
+ public:
+ JITException(const char * what2)
+ {
+ _what = strdup(what2);
+ }
+ const char * what() const throw()
+ {
+ return _what;
+ }
+ ~JITException() throw() {};
+ };
+
+ class OutOfMemoryException: public RuntimeException
+ {
+ public:
+ OutOfMemoryException() {}
+ const char * what() const throw()
+ {
+ return "VirtualMachine Out of memory";
+ }
+ ~OutOfMemoryException() throw() {};
+ };
+
+ class InvalidProgramException: public RuntimeException
+ {
+ public:
+ InvalidProgramException() {}
+ const char * what() const throw()
+ {
+ return "Could not load program";
+ }
+ ~InvalidProgramException() throw() {};
+ };
+} \ No newline at end of file
diff --git a/src/virtualmachine/JustInTime.cpp b/src/virtualmachine/JustInTime.cpp
new file mode 100644
index 0000000..9929d81
--- /dev/null
+++ b/src/virtualmachine/JustInTime.cpp
@@ -0,0 +1,1144 @@
+#ifdef VMJIT
+
+#include <cstdio>
+#include "VirtualMachine.h"
+
+#ifdef WIN32
+#include "Windows.h"
+#endif
+
+namespace vm
+{
+ #define OP(n) OP##n
+ /*
+
+ eax scratch
+ ebx scratch
+ ecx scratch (required for shifts)
+ edx scratch (required for divisions)
+ esi RP
+ edi DP
+
+ */
+
+ // TTimo: initialised the statics, this fixes a crash when entering a compiled VM
+ static unsigned char *buf = NULL;
+ static unsigned char *jused = NULL;
+ static int compiledOfs = 0;
+ static int pc = 0;
+
+ //static int callMask = 0; // bk001213 - init
+ static int eDP;
+ static int eRP;
+ static int instruction, pass;
+ static int lastConst = 0;
+ static int oc0, oc1, pop0, pop1;
+
+ static int eRamMask = 0;
+ static int eRomMask = 0;
+ static int * eInstructionPointers = NULL;
+
+ static int eSyscallNum;
+ static void * eRam = NULL;
+ static VirtualMachine * eVM = NULL;
+
+ static int callFromCompiledPtr = (int)VirtualMachine::callFromCompiled;
+ static int callSyscallPtr = (int)VirtualMachine::callSyscall;
+
+ typedef enum
+ {
+ LAST_COMMAND_NONE = 0,
+ LAST_COMMAND_MOV_EDI_EAX,
+ LAST_COMMAND_SUB_DI_4,
+ LAST_COMMAND_SUB_DI_8,
+ } ELastCommand;
+
+ static ELastCommand LastCommand;
+
+ void VirtualMachine::callSyscall()
+ {
+ //throw RuntimeException("Turd");
+
+ /*VirtualMachine * savedVM;
+ int * callOpStack2;
+
+ savedVM = eVM;
+ callOpStack2 = (int*)eOpStack;
+
+ // save the stack to allow recursive VM entry
+ eVM->DP = eDP - 4;
+ *(int *)((byte *)eVM->ram + eDP + 4) = eSyscallNum;
+ //VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) );
+ *(callOpStack2+1) = eVM->syscall( *(int *)((unsigned char *)eVM->ram + eDP + 4) );
+
+ eVM = savedVM;*/
+ }
+
+ void VirtualMachine::callFromCompiled()
+ {
+ /*__asm__("doAsmCall: \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " subl $4,%%edi \n\t" \
+ " orl %%eax,%%eax \n\t" \
+ " jl systemCall \n\t" \
+ " shll $2,%%eax \n\t" \
+ " addl %3,%%eax \n\t" \
+ " call *(%%eax) \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " andl %5, %%eax \n\t" \
+ " jmp doret \n\t" \
+ "systemCall: \n\t" \
+ " negl %%eax \n\t" \
+ " decl %%eax \n\t" \
+ " movl %%eax,%0 \n\t" \
+ " movl %%esi,%1 \n\t" \
+ " movl %%edi,%2 \n\t" \
+ " pushl %%ecx \n\t" \
+ " pushl %%esi \n\t" \
+ " pushl %%edi \n\t" \
+ " call *%4 \n\t" \
+ " popl %%edi \n\t" \
+ " popl %%esi \n\t" \
+ " popl %%ecx \n\t" \
+ " addl $4,%%edi \n\t" \
+ "doret: \n\t" \
+ " ret \n\t" \
+ : "=rm" (eSyscallNum), "=rm" (eDP), "=rm" (eOpStack) \
+ : "rm" (eInstructionPointers), "r" (callSyscall), "m" (eRomMask) \
+ : "ax", "di", "si", "cx" \
+ );*/
+ //" call *%4 \n\t"
+
+ //" negl %%eax \n\t"
+ // " decl %%eax \n\t"
+ __asm__ volatile ("doAsmCall: \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " subl $4,%%edi \n\t" \
+ " orl %%eax,%%eax \n\t" \
+ " jl systemCall \n\t" \
+ " shll $2,%%eax \n\t" \
+ " addl %3,%%eax \n\t" \
+ " call *(%%eax) \n\t" \
+ " movl (%%edi),%%eax \n\t" \
+ " andl %5, %%eax \n\t" \
+ " ret \n\t" \
+ "systemCall: \n\t" \
+ " movl %%eax,%0 \n\t" \
+ " movl %%esi,%1 \n\t" \
+ " movl %%edi,%2 \n\t" \
+ " pushl %%ecx \n\t" \
+ " pushl %%esi \n\t" \
+ " pushl %%edi \n\t" \
+ : "=rm" (eSyscallNum), "=rm" (eRP), "=rm" (eDP) \
+ : "rm" (eInstructionPointers), "r" (callSyscall), "m" (eRomMask) \
+ : "ax", "di", "si", "cx" \
+ );
+ //printf("Syscall: %d\n", eSyscallNum);
+ //throw RuntimeException("Turd");
+
+ eVM->DP = eDP-((int)eRam);
+ eVM->syscall(eSyscallNum);
+ eDP = eVM->DP+((int)eRam)-4;
+
+ //" addl $4,%edi \n\t"
+ // " ret \n\t"
+ __asm__ volatile ("popl %edi \n\t" \
+ " popl %esi \n\t" \
+ " popl %ecx \n\t" \
+ "doret: \n\t" \
+ );
+ }
+
+ int VirtualMachine::CallCompiled(int address)
+ {
+ //int stack[1024];
+ //int stack[100];
+ int programStack;
+ int stackOnEntry;
+ unsigned char * image;
+ void * entryPoint;
+ int * oldInstructionPointers;
+ void * oldDataStack;
+
+ oldDataStack = eRam;
+ oldInstructionPointers = eInstructionPointers;
+
+ eVM = this;
+ eInstructionPointers = instructionPointers;
+
+ eRomMask = romMask;
+ eRamMask = ramMask;
+
+ // we might be called recursively, so this might not be the very top
+ eDP = ((int)ram)+DP;
+ stackOnEntry = DP;
+
+ // set up the stack frame
+ image = (unsigned char *)ram;//vm->dataBase;
+
+ eDP -= 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
+
+ // off we go into generated code...
+ entryPoint = compiledRom;//0;//vm->codeBase;
+ eRam = ram+dataStack;
+
+ #if defined(_MSC_VER)
+ __asm {
+ pushad
+ mov esi, DP;
+ mov edi, opStack
+ call entryPoint
+ mov DP, esi
+ mov opStack, edi
+ popad
+ }
+ #else
+ {
+ static int memDP;
+ static int memRP;
+ static void *memEntryPoint;
+
+ memDP = DP+((int)ram);
+ memRP = RP;
+ memEntryPoint = entryPoint;
+
+ __asm__(" pushal \r\n" \
+ " movl %0,%%esi \r\n" \
+ " movl %1,%%edi \r\n" \
+ " call *%2 \r\n" \
+ " movl %%esi,%0 \r\n" \
+ " movl %%edi,%1 \r\n" \
+ " popal \r\n" \
+ : "=m" (memRP), "=m" (memDP) \
+ : "m" (memEntryPoint), "0" (memRP), "1" (memDP) \
+ : "si", "di" \
+ );
+
+ DP = memDP-((int)ram);
+ RP = memRP;
+ }
+ #endif
+
+ if ( eRam != ram+dataStack ) {
+ throw RuntimeException("opStack corrupted in compiled code");
+ }
+ if ( DP != stackOnEntry+4 ) {
+ printf("DP: %d, stackOnEntry: %d\n", DP, stackOnEntry);
+ throw RuntimeException("programStack corrupted in compiled code");
+ }
+
+ DP = stackOnEntry;
+
+ // in case we were recursively called by another vm
+ eInstructionPointers = oldInstructionPointers;
+ eRam = oldDataStack;
+
+ return 0;//*(int *)eOpStack;
+ }
+
+ bool VirtualMachine::Compile()
+ {
+ Instruction op;
+ int maxLength;
+ int v;
+ int i;
+ bool opt;
+
+ // allocate a very large temp buffer, we will shrink it later
+ maxLength = romSize * 8;
+ buf = new unsigned char[maxLength];
+ jused = new unsigned char[romSize + 2];
+ instructionPointers = new int[romSize];
+ std::fill(jused, jused+romSize+2, 0);
+
+ for(pass=0; pass<2; pass++) {
+ oc0 = -23423;
+ oc1 = -234354;
+ pop0 = -43435;
+ pop1 = -545455;
+
+ // translate all instructions
+ pc = 0;
+ instruction = 0;
+ compiledOfs = 0;
+
+ LastCommand = LAST_COMMAND_NONE;
+
+ while (instruction < romSize)
+ {
+ if (compiledOfs > maxLength - 16)
+ {
+ throw JITException("Compile: maxLength exceeded");
+ }
+
+ instructionPointers[instruction] = compiledOfs;
+ instruction++;
+
+ if (pc > romSize)
+ {
+ throw JITException("Compile: program counter run off the edge");
+ }
+
+ op = rom[pc];
+ pc++;
+ switch ( op.Operation )
+ {
+ case 0:
+ break;
+ case OP(BREAK):
+ emitInstruction( "CC" ); // int 3
+ break;
+ case OP(ENTER):
+ //emitInstruction( "CC" ); // int 3
+ emitInstruction( "81 EE" ); // sub esi, 0x12345678
+ emit4( constant4() );
+ break;
+ case OP(CONST):
+ if (rom[pc].Operation == OP(LOAD4))
+ {
+ emitAddEDI4();
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ emit4( (constant4()&ramMask) + (int)ram);
+ emitInstruction( "8B 03" ); // mov eax, dword ptr [ebx]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ pc++; // OP(LOAD4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(LOAD2))
+ {
+ emitAddEDI4();
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ emit4( (constant4()&ramMask) + (int)ram);
+ emitInstruction( "0F B7 03" ); // movzx eax, word ptr [ebx]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ pc++; // OP(LOAD4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(LOAD1))
+ {
+ emitAddEDI4();
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ emit4( (constant4()&ramMask) + (int)ram);
+ emitInstruction( "0F B6 03" ); // movzx eax, byte ptr [ebx]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ pc++; // OP(LOAD4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(STORE4))
+ {
+ opt = emitMovEBXEDI((ramMask & ~3));
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( constant4() );
+ // if (!opt) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~3 );
+ // }
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(STORE4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(STORE2))
+ {
+ opt = emitMovEBXEDI((ramMask & ~1));
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( constant4() );
+ // if (!opt) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~1 );
+ // }
+ emitInstruction( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(STORE4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(STORE1))
+ {
+ opt = emitMovEBXEDI(ramMask);
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( constant4() );
+ // if (!opt) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask );
+ // }
+ emitInstruction( "88 83" ); // mov byte ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(STORE4)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(ADD))
+ {
+ emitInstruction( "81 07" ); // add dword ptr [edi], 0x1234567
+ emit4( constant4() );
+ pc++; // OP(ADD)
+ instruction += 1;
+ break;
+ }
+ if (rom[pc].Operation == OP(SUB))
+ {
+ emitInstruction( "81 2F" ); // sub dword ptr [edi], 0x1234567
+ emit4( constant4() );
+ pc++; // OP(ADD)
+ instruction += 1;
+ break;
+ }
+ emitAddEDI4();
+ emitInstruction( "C7 07" ); // mov dword ptr [edi], 0x12345678
+ lastConst = constant4();
+ emit4( lastConst );
+ if (rom[pc].Operation == OP(JUMP))
+ {
+ jused[lastConst] = 1;
+ }
+ break;
+ case OP(LOCAL):
+ emitAddEDI4();
+ emitInstruction( "8D 86" ); // lea eax, [0x12345678 + esi]
+ oc0 = oc1;
+ oc1 = constant4();
+ emit4( oc1 );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(ARG):
+ emitMovEAXEDI(); // mov eax,dword ptr [edi]
+ emitInstruction( "89 86" ); // mov dword ptr [esi+ram],eax
+ // FIXME: range check
+ emit4( constant1() + (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(CALL):
+ emitInstruction("C7 86"); // mov dword ptr [esi+ram],0x12345678
+ emit4((int)ram);
+ emit4(pc);
+ emitInstruction("FF 15"); // call callFromCompiled
+ emit4((int)&callFromCompiledPtr);
+ break;
+ case OP(PUSH):
+ emitAddEDI4();
+ break;
+ case OP(POP):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(LEAVE):
+ v = constant4();
+ emitInstruction( "81 C6" ); // add esi, 0x12345678
+ emit4( v );
+ emitInstruction( "C3" ); // ret
+ break;
+ case OP(LOAD4):
+ if (rom[pc].Operation == OP(CONST) && rom[pc+1].Operation == OP(ADD) && rom[pc+2].Operation == OP(STORE4))
+ {
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ compiledOfs -= 11;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ }
+ pc++; // OP(CONST)
+ v = constant4();
+ emitMovEBXEDI(ramMask);
+ if (v == 1 && oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "FF 83"); // inc dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitInstruction( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitInstruction( "05" ); // add eax, const
+ emit4( v );
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "8B 1F" ); // mov ebx, dword ptr [edi]
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ }
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(ADD)
+ pc++; // OP(STORE)
+ instruction += 3;
+ break;
+ }
+
+ if (rom[pc].Operation == OP(CONST) && rom[pc+1].Operation == OP(SUB) && rom[pc+2].Operation == OP(STORE4))
+ {
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL)) {
+ compiledOfs -= 11;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ }
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ pc++; // OP(CONST)
+ v = constant4();
+ if (v == 1 && oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "FF 8B"); // dec dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitInstruction( "2D" ); // sub eax, const
+ emit4( v );
+ if (oc0 == oc1 && pop0 == OP(LOCAL) && pop1 == OP(LOCAL))
+ {
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ else
+ {
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "8B 1F" ); // mov ebx, dword ptr [edi]
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ }
+ }
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ pc++; // OP(SUB)
+ pc++; // OP(STORE)
+ instruction += 3;
+ break;
+ }
+
+ if (buf[compiledOfs-2] == 0x89 && buf[compiledOfs-1] == 0x07)
+ {
+ compiledOfs -= 2;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "8B 80"); // mov eax, dword ptr [eax + 0x1234567]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ }
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(LOAD2):
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "0F B7 83" ); // movzx eax, word ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(LOAD1):
+ emitMovEBXEDI(ramMask);
+ emitInstruction( "0F B6 83" ); // movzx eax, byte ptr [ebx + 0x12345678]
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(STORE4):
+ emitMovEAXEDI();
+ emitInstruction( "8B 5F FC" ); // mov ebx, dword ptr [edi-4]
+ // if (pop1 != OP(CALL)) {
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~3 );
+ // }
+ emitInstruction( "89 83" ); // mov dword ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+ case OP(STORE2):
+ emitMovEAXEDI();
+ emitInstruction( "8B 5F FC" ); // mov ebx, dword ptr [edi-4]
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask & ~1 );
+ emitInstruction( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+ case OP(STORE1):
+ emitMovEAXEDI();
+ emitInstruction( "8B 5F FC" ); // mov ebx, dword ptr [edi-4]
+ // emitInstruction( "81 E3" ); // and ebx, 0x12345678
+ // emit4( ramMask );
+ emitInstruction( "88 83" ); // mov byte ptr [ebx+0x12345678], eax
+ emit4( (int)ram );
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+
+ case OP(EQ):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(NE):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LTI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7D 06" ); // jnl +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LEI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7F 06" ); // jnle +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GTI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7E 06" ); // jng +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GEI):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "7C 06" ); // jnge +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LTU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "73 06" ); // jnb +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LEU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "77 06" ); // jnbe +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GTU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "76 06" ); // jna +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GEU):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "8B 47 04" ); // mov eax, dword ptr [edi+4]
+ emitInstruction( "3B 47 08" ); // cmp eax, dword ptr [edi+8]
+ emitInstruction( "72 06" ); // jnae +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(EQF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 40" ); // test ah,0x40
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(NEF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 40" ); // test ah,0x40
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LTF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 01" ); // test ah,0x01
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(LEF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 41" ); // test ah,0x41
+ emitInstruction( "74 06" ); // je +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GTF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 41" ); // test ah,0x41
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(GEF):
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ emitInstruction( "D9 47 04" ); // fld dword ptr [edi+4]
+ emitInstruction( "D8 5F 08" ); // fcomp dword ptr [edi+8]
+ emitInstruction( "DF E0" ); // fnstsw ax
+ emitInstruction( "F6 C4 01" ); // test ah,0x01
+ emitInstruction( "75 06" ); // jne +6
+ emitInstruction( "FF 25" ); // jmp [0x12345678]
+ v = constant4();
+ jused[v] = 1;
+ emit4( (int)instructionPointers + v*4 );
+ break;
+ case OP(NEGI):
+ emitInstruction( "F7 1F" ); // neg dword ptr [edi]
+ break;
+ case OP(ADD):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "01 47 FC" ); // add dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(SUB):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "29 47 FC" ); // sub dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(DIVI):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "99" ); // cdq
+ emitInstruction( "F7 3F" ); // idiv dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(DIVU):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "33 D2" ); // xor edx, edx
+ emitInstruction( "F7 37" ); // div dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MODI):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "99" ); // cdq
+ emitInstruction( "F7 3F" ); // idiv dword ptr [edi]
+ emitInstruction( "89 57 FC" ); // mov dword ptr [edi-4],edx
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MODU):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "33 D2" ); // xor edx, edx
+ emitInstruction( "F7 37" ); // div dword ptr [edi]
+ emitInstruction( "89 57 FC" ); // mov dword ptr [edi-4],edx
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MULI):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "F7 2F" ); // imul dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(MULU):
+ emitInstruction( "8B 47 FC" ); // mov eax,dword ptr [edi-4]
+ emitInstruction( "F7 27" ); // mul dword ptr [edi]
+ emitInstruction( "89 47 FC" ); // mov dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BAND):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "21 47 FC" ); // and dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BOR):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "09 47 FC" ); // or dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BXOR):
+ emitMovEAXEDI(); // mov eax, dword ptr [edi]
+ emitInstruction( "31 47 FC" ); // xor dword ptr [edi-4],eax
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(BCOM):
+ emitInstruction( "F7 17" ); // not dword ptr [edi]
+ break;
+ case OP(LSH):
+ emitInstruction( "8B 0F" ); // mov ecx, dword ptr [edi]
+ emitInstruction( "D3 67 FC" ); // shl dword ptr [edi-4], cl
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(RSHI):
+ emitInstruction( "8B 0F" ); // mov ecx, dword ptr [edi]
+ emitInstruction( "D3 7F FC" ); // sar dword ptr [edi-4], cl
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(RSHU):
+ emitInstruction( "8B 0F" ); // mov ecx, dword ptr [edi]
+ emitInstruction( "D3 6F FC" ); // shr dword ptr [edi-4], cl
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(NEGF):
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D9 E0" ); // fchs
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(ADDF):
+ emitInstruction( "D9 47 FC" ); // fld dword ptr [edi-4]
+ emitInstruction( "D8 07" ); // fadd dword ptr [edi]
+ emitInstruction( "D9 5F FC" ); // fstp dword ptr [edi-4]
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ break;
+ case OP(SUBF):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D8 67 04" ); // fsub dword ptr [edi+4]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(DIVF):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D8 77 04" ); // fdiv dword ptr [edi+4]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(MULF):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "D8 4f 04" ); // fmul dword ptr [edi+4]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(CVIF):
+ emitInstruction( "DB 07" ); // fild dword ptr [edi]
+ emitInstruction( "D9 1F" ); // fstp dword ptr [edi]
+ break;
+ case OP(CVFI):
+ #ifndef FTOL_PTR // WHENHELLISFROZENOVER // bk001213 - was used in 1.17
+ // not IEEE complient, but simple and fast
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "DB 1F" ); // fistp dword ptr [edi]
+ #else // FTOL_PTR
+ // call the library conversion function
+ emitInstruction( "D9 07" ); // fld dword ptr [edi]
+ emitInstruction( "FF 15" ); // call ftolPtr
+ emit4( (int)&ftolPtr );
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ #endif
+ break;
+ case OP(SEX8):
+ emitInstruction( "0F BE 07" ); // movsx eax, byte ptr [edi]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+ case OP(SEX16):
+ emitInstruction( "0F BF 07" ); // movsx eax, word ptr [edi]
+ emitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax
+ break;
+
+ case OP(BLOCK_COPY):
+ // FIXME: range check
+ emitInstruction( "56" ); // push esi
+ emitInstruction( "57" ); // push edi
+ emitInstruction( "8B 37" ); // mov esi,[edi]
+ emitInstruction( "8B 7F FC" ); // mov edi,[edi-4]
+ emitInstruction( "B9" ); // mov ecx,0x12345678
+ emit4( constant4() >> 2 );
+ emitInstruction( "B8" ); // mov eax, ramMask
+ emit4( ramMask );
+ emitInstruction( "BB" ); // mov ebx, ram
+ emit4( (int)ram );
+ emitInstruction( "23 F0" ); // and esi, eax
+ emitInstruction( "03 F3" ); // add esi, ebx
+ emitInstruction( "23 F8" ); // and edi, eax
+ emitInstruction( "03 FB" ); // add edi, ebx
+ emitInstruction( "F3 A5" ); // rep movsd
+ emitInstruction( "5F" ); // pop edi
+ emitInstruction( "5E" ); // pop esi
+ emitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8
+ break;
+
+ case OP(JUMP):
+ emitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4
+ emitInstruction( "8B 47 04" ); // mov eax,dword ptr [edi+4]
+ // FIXME: range check
+ emitInstruction( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4]
+ emit4( (int)instructionPointers );
+ break;
+ default:
+ throw JITException("Compile: bad opcode");
+ }
+ pop0 = pop1;
+ pop1 = op.Operation;
+ }
+ }
+
+ // copy to an exact size buffer on the hunk
+ //codeLength = compiledOfs;
+ //codeBase = Hunk_Alloc( compiledOfs, h_low );
+
+ //Com_Memcpy( codeBase, buf, compiledOfs );
+
+ compiledRom = new char[compiledOfs];
+ std::copy(buf, buf+compiledOfs, compiledRom);
+ compiledRomSize = compiledOfs;
+ compiledRomMask = compiledOfs;
+
+ delete[] buf;
+ delete[] jused;
+
+ //printf( "VM file %s compiled to %i bytes of code\n", name, compiledOfs);
+
+ // offset all the instruction pointers for the new location
+ for ( i = 0 ; i < /*header->instructionCount*/ romSize ; i++ ) {
+ instructionPointers[i] += (int)rom;
+ }
+
+#ifdef WIN32
+ VirtualProtect(compiledRom, compiledRomSize, PAGE_EXECUTE, NULL);
+#endif
+ #if 0 // ndef _WIN32
+ // Must make the newly generated code executable
+ {
+ int r;
+ unsigned long addr;
+ int psize = getpagesize();
+
+ addr = ((int)codeBase & ~(psize-1)) - psize;
+
+ r = mprotect((char*)addr, codeLength + (int)codeBase - addr + psize,
+ PROT_READ | PROT_WRITE | PROT_EXEC );
+
+ if (r < 0)
+ Com_Error( ERR_FATAL, "mprotect failed to change PROT_EXEC" );
+ }
+ #endif
+ return true;
+
+ }
+
+ int VirtualMachine::constant4()
+ {
+ int v;
+
+ v = rom[pc-1].Parameter.int4;// | (rom[pc-1].Parameter<<8) | (rom[pc-1].Parameter<<16) | (rom[pc-1].Parameter<<24);
+ return v;
+ }
+
+ int VirtualMachine::constant1()
+ {
+ int v;
+
+ v = rom[pc-1].Parameter.uint1;
+ return v;
+ }
+
+ void VirtualMachine::emit1(int v)
+ {
+ buf[compiledOfs] = v;
+ compiledOfs++;
+
+ LastCommand = LAST_COMMAND_NONE;
+ }
+
+ void VirtualMachine::emit4(int v)
+ {
+ emit1(v & 255);
+ emit1((v >> 8) & 255);
+ emit1((v >> 16) & 255);
+ emit1((v >> 24) & 255);
+ }
+
+ void VirtualMachine::emitInstruction(const char *string)
+ {
+ int c1, c2;
+ int v;
+
+ while (true)
+ {
+ c1 = string[0];
+ c2 = string[1];
+
+ v = (hex( c1 ) << 4) | hex(c2);
+ emit1( v );
+
+ if (!string[2])
+ {
+ break;
+ }
+ string += 3;
+ }
+ }
+
+ void VirtualMachine::emitCommand(int command_)
+ {
+ ELastCommand command = (ELastCommand)command_;
+ switch(command)
+ {
+ case LAST_COMMAND_MOV_EDI_EAX:
+ emitInstruction( "89 07" ); // mov dword ptr [edi], eax
+ break;
+ case LAST_COMMAND_SUB_DI_4:
+ emitInstruction( "83 EF 04" ); // sub edi, 4
+ break;
+ case LAST_COMMAND_SUB_DI_8:
+ emitInstruction( "83 EF 08" ); // sub edi, 8
+ break;
+ default:
+ break;
+ }
+ LastCommand = command;
+ }
+
+ void VirtualMachine::emitAddEDI4()
+ {
+ if (LastCommand == LAST_COMMAND_SUB_DI_4 && jused[instruction-1] == 0)
+ { // sub di,4
+ compiledOfs -= 3;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ return;
+ }
+ if (LastCommand == LAST_COMMAND_SUB_DI_8 && jused[instruction-1] == 0)
+ { // sub di,8
+ compiledOfs -= 3;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "83 EF 04" ); // sub edi,4
+ return;
+ }
+ emitInstruction( "83 C7 04" ); // add edi,4
+ }
+
+ void VirtualMachine::emitMovEAXEDI()
+ {
+ if (LastCommand == LAST_COMMAND_MOV_EDI_EAX)
+ { // mov [edi], eax
+ compiledOfs -= 2;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ return;
+ }
+ if (pop1 == OP(DIVI) || pop1 == OP(DIVU) || pop1 == OP(MULI) || pop1 == OP(MULU) || pop1 == OP(STORE4) || pop1 == OP(STORE2) || pop1 == OP(STORE1) )
+ {
+ return;
+ }
+ if (pop1 == OP(CONST) && buf[compiledOfs-6] == 0xC7 && buf[compiledOfs-5] == 0x07)
+ { // mov edi, 0x123456
+ compiledOfs -= 6;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "B8" ); // mov eax, 0x12345678
+ emit4( lastConst );
+ return;
+ }
+ emitInstruction( "8B 07" ); // mov eax, dword ptr [edi]
+ }
+
+ bool VirtualMachine::emitMovEBXEDI(int andit)
+ {
+ if (LastCommand == LAST_COMMAND_MOV_EDI_EAX)
+ { // mov [edi], eax
+ compiledOfs -= 2;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "8B D8"); // mov bx, eax
+ return false;
+ }
+ if (pop1 == OP(DIVI) || pop1 == OP(DIVU) || pop1 == OP(MULI) || pop1 == OP(MULU) || pop1 == OP(STORE4) || pop1 == OP(STORE2) || pop1 == OP(STORE1) )
+ {
+ emitInstruction( "8B D8"); // mov bx, eax
+ return false;
+ }
+ if (pop1 == OP(CONST) && buf[compiledOfs-6] == 0xC7 && buf[compiledOfs-5] == 0x07 )
+ { // mov edi, 0x123456
+ compiledOfs -= 6;
+ instructionPointers[ instruction-1 ] = compiledOfs;
+ emitInstruction( "BB" ); // mov ebx, 0x12345678
+ if (andit) {
+ emit4( lastConst & andit );
+ } else {
+ emit4( lastConst );
+ }
+ return true;
+ }
+
+ emitInstruction( "8B 1F" ); // mov ebx, dword ptr [edi]
+ return false;
+ }
+
+ int VirtualMachine::hex(int c)
+ {
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ throw JITException("hex: bad character");
+
+ return 0;
+ }
+
+ #undef OP
+}
+
+#endif \ No newline at end of file
diff --git a/src/virtualmachine/Operations.cpp b/src/virtualmachine/Operations.cpp
new file mode 100644
index 0000000..0c998a8
--- /dev/null
+++ b/src/virtualmachine/Operations.cpp
@@ -0,0 +1,356 @@
+#include "VirtualMachine.h"
+
+namespace vm
+{
+ #define OPDEF(n) &VirtualMachine::Op##n,
+ OperationFunction VirtualMachine::operations[] =
+ {
+ #include "Operations.inl"
+ };
+ #undef OPDEF
+
+ #define OPDEF(n) int VirtualMachine::Op##n(word parameter)
+
+ #define R0 (r[0])
+ #define R1 (r[1])
+ #define R2 (r[2])
+
+
+ OPDEF(UNDEF)
+ {
+ /* Die horribly. */
+ throw RuntimeException();
+ return -1;
+ }
+
+ OPDEF(IGNORE)
+ {
+ /* NOP */
+ throw RuntimeException();
+ return 0;
+ }
+
+ OPDEF(BREAK)
+ {
+ /* Usage never spotted. */
+ /* Die horribly? */
+ throw RuntimeException();
+ return -1;
+ }
+
+ /*
+ Stack on entering...
+
+ no locals: ENTER 8
+ 1 words locals: ENTER 16
+ 2 words locals: ENTER 20
+ 3 words locals: ENTER 24
+ etc.
+
+ address of argument:
+ ADDRFP4 v => OP_LOCAL (16 + currentLocals + currentArgs + v)
+ address of local:
+ ADDRLP4 v => OP_LOCAL (8 + currentArgs + v)
+
+ RP [ ] ??? (oldPC?)
+ [ ] ???
+ [ ] \
+ ... > locals (args marshalling)
+ [ ] /
+ [ ] \
+ ... > locals
+ [ ] / (ADDRLP4 v => OP_LOCAL (8 + currentArgs + v))
+ (oldRP?) [ ] ???
+ [ ] ???
+ [ ] (my args?)
+ ...
+ [ ]
+ */
+
+ OPDEF(ENTER) /* ??? */
+ {
+ while (parameter.int4 > (2 * sizeof(word)))
+ {
+ RPush<int4_t>(0); /* init zero */
+ parameter.int4 -= sizeof(word);
+ }
+ RPush(Pop()); //Program Counter
+ RPush<int4_t>(0); //Unknown
+ return 0;
+ }
+
+ OPDEF(LEAVE) /* ??? */
+ {
+ RPop(); //Unknown
+ parameter.int4 -= sizeof(word);
+ PC = RPop<int4_t>(); //Program counter
+ parameter.int4 -= sizeof(word);
+ while (parameter.int4 > 0)
+ {
+ RPop();
+ parameter.int4 -= sizeof(word);
+ }
+ return 0;
+ }
+
+ OPDEF(CALL) /* Call subroutine. */
+ {
+ R0 = Pop();
+ Push<int4_t>(PC);
+ PC = R0.int4;
+ return 0;
+ }
+
+ OPDEF(PUSH) /* [DP] <- 0; DP++ */
+ {
+ Push(0);
+ return 0;
+ }
+
+ OPDEF(POP) /* DP-- */
+ {
+ Pop();
+ return 0;
+ }
+
+ OPDEF(CONST) /* [DP] <- parm; DP++ */
+ {
+ Push(parameter);
+ return 0;
+ }
+
+ OPDEF(LOCAL) /* [DP] <- [RP-n] */
+ {
+ Push<int4_t>(RP + parameter.int4);
+ return 0;
+ }
+
+ OPDEF(JUMP) /* PC <- [DP] */
+ {
+ PC = Pop<int4_t>();
+ return 0;
+ }
+
+ #define CMP(type, op) \
+ { \
+ R0 = Pop(); \
+ cm = (Pop<type##_t>() op R0.type); \
+ if (cm) \
+ PC = parameter.uint4; \
+ return 0; \
+ }
+
+ OPDEF(EQ) /* if [DP] == [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, ==)
+
+ OPDEF(NE) /* if [DP] == [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, !=)
+
+ OPDEF(LTI) /* if [DP] < [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, <)
+
+ OPDEF(LEI) /* if [DP] <= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, <=)
+
+ OPDEF(GTI) /* if [DP] > [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, >)
+
+ OPDEF(GEI) /* if [DP] >= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(int4, >=)
+
+ OPDEF(LTU) /* if [DP] < [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, <)
+
+ OPDEF(LEU) /* if [DP] <= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, <=)
+
+ OPDEF(GTU) /* if [DP] > [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, >)
+
+ OPDEF(GEU) /* if [DP] >= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(uint4, >=)
+
+ OPDEF(EQF) /* if [DP] == [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, ==)
+
+ OPDEF(NEF) /* if [DP] != [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, !=)
+
+ OPDEF(LTF) /* if [DP] < [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, <)
+
+ OPDEF(LEF) /* if [DP] <= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, <=)
+
+ OPDEF(GTF) /* if [DP] > [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, >)
+
+ OPDEF(GEF) /* if [DP] >= [DP-1] then PC <- parm; DP <- DP-2 */
+ CMP(float4, >=)
+
+
+ OPDEF(LOAD1) /* [DP] <- [[DP]] */
+ {
+ Push<uint1_t>(Get<uint1_t>(Pop<uint4_t>()));
+ return 0;
+ }
+
+ OPDEF(LOAD2) /* [DP] <- [[DP]] */
+ {
+ Push<uint2_t>(Get<uint2_t>(Pop<uint4_t>()));
+ return 0;
+ }
+
+ OPDEF(LOAD4) /* [DP] <- [[DP]] */
+ {
+ Push<uint4_t>(Get<uint4_t>(Pop<uint4_t>()));
+ return 0;
+ }
+
+ OPDEF(STORE1) /* [DP-1] <- [DP]; DP <- DP-2 */
+ {
+ Set<uint1_t>(Pop<uint4_t>(), Pop<uint1_t>());
+ return 0;
+ }
+
+ OPDEF(STORE2) /* [DP-1] <- [DP]; DP <- DP-2 */
+ {
+ Set<uint2_t>(Pop<uint4_t>(), Pop<uint2_t>());
+ return 0;
+ }
+
+ OPDEF(STORE4) /* [DP-1] <- [DP]; DP <- DP-2 */
+ {
+ Set<uint4_t>(Pop<uint4_t>(), Pop<uint4_t>());
+ return 0;
+ }
+
+ OPDEF(ARG) /* Marshal TOS to to-call argument list */
+ {
+ Marshal(parameter.uint1, Pop());
+ return 0;
+ }
+
+ OPDEF(BLOCK_COPY) /* XXX */
+ {
+ R1 = Pop();
+ R0 = Pop();
+ if(R0.int4 >= 0 && R0.int4 + parameter.int4 < ramSize && R1.int4 >= 0 && R1.int4 + parameter.int4 < ramSize)
+ memcpy(ram + R0.int4, ram + R1.int4, parameter.int4);
+ else
+ throw AccessViolationException();
+ return -1;
+ }
+
+ OPDEF(SEX8) /* Sign-extend 8-bit */
+ {
+ R0 = Pop();
+ if(R0.uint4 & 0x80)
+ R0.uint4 |= 0xFFFFFF80;
+ Push(R0);
+ return 0;
+ }
+
+ OPDEF(SEX16) /* Sign-extend 16-bit */
+ {
+ R0 = Pop();
+ if(R0.uint4 & 0x8000)
+ R0.uint4 |= 0xFFFF8000;
+ Push(R0);
+ return 0;
+ }
+
+ #define UNOP(type, op) \
+ { \
+ Push<type##_t>(op Pop<type##_t>()); \
+ return 0; \
+ }
+
+ #define BINOP(type, op) \
+ { \
+ R0 = Pop(); \
+ Push<type##_t>(Pop<type##_t>() op R0.type); \
+ return 0; \
+ }
+
+ OPDEF(NEGI) /* [DP] <- -[DP] */
+ UNOP(int4, -)
+
+ OPDEF(ADD) /* [DP-1] <- [DP-1] + [DP]; DP <- DP-1 */
+ BINOP(int4, +)
+
+ OPDEF(SUB) /* [DP-1] <- [DP-1] - [DP]; DP <- DP-1 */
+ BINOP(int4, -)
+
+ OPDEF(DIVI) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(int4, /)
+
+ OPDEF(DIVU) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(uint4, /)
+
+ OPDEF(MODI) /* [DP-1] <- [DP-1] % [DP]; DP <- DP-1 */
+ BINOP(int4, %)
+
+ OPDEF(MODU) /* [DP-1] <- [DP-1] % [DP]; DP <- DP-1 */
+ BINOP(uint4, %)
+
+ OPDEF(MULI) /* [DP-1] <- [DP-1] * [DP]; DP <- DP-1 */
+ BINOP(int4, *)
+
+ OPDEF(MULU) /* [DP-1] <- [DP-1] * [DP]; DP <- OP-1 */
+ BINOP(uint4, *)
+
+ OPDEF(BAND) /* [DP-1] <- [DP-1] & [DP]; DP <- DP-1 */
+ BINOP(uint4, &)
+
+ OPDEF(BOR) /* [DP-1] <- [DP-1] | [DP]; DP <- DP-1 */
+ BINOP(uint4, |)
+
+ OPDEF(BXOR) /* [DP-1] <- [DP-1] ^ [DP]; DP <- DP-1 */
+ BINOP(uint4, ^)
+
+ OPDEF(BCOM) /* [DP] <- ~[DP] */
+ UNOP(uint4, ~)
+
+ OPDEF(LSH) /* [DP-1] <- [DP-1] << [DP]; DP <- DP-1 */
+ BINOP(uint4, <<)
+
+ OPDEF(RSHI) /* [DP-1] <- [DP-1] >> [DP]; DP <- DP-1 */
+ {
+ R1.int4 = Pop<int4_t>();
+ R0.int4 = Pop<int4_t>();
+ R2.int4 = R0.int4 >> R1.int4;
+ Push(R2);
+ return 0;
+ }
+
+ OPDEF(RSHU) /* [DP-1] <- [DP-1] >> [DP]; DP <- DP-1 */
+ BINOP(uint4, >>)
+
+ OPDEF(NEGF) /* [DP] <- -[DP] */
+ UNOP(float4, -)
+
+ OPDEF(ADDF) /* [DP-1] <- [DP-1] + [DP]; DP <- DP-1 */
+ BINOP(float4, +)
+
+ OPDEF(SUBF) /* [DP-1] <- [DP-1] - [DP]; DP <- DP-1 */
+ BINOP(float4, -)
+
+ OPDEF(DIVF) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(float4, /)
+
+ OPDEF(MULF) /* [DP-1] <- [DP-1] / [DP]; DP <- DP-1 */
+ BINOP(float4, *)
+
+ OPDEF(CVIF) /* [DP] <- [DP] */
+ {
+ Push<float4_t>(Pop<int4_t>());
+ return 0;
+ }
+
+ OPDEF(CVFI) /* [DP] <- [DP] */
+ {
+ Push<int4_t>(Pop<float4_t>());
+ return 0;
+ }
+}
diff --git a/src/virtualmachine/Operations.inl b/src/virtualmachine/Operations.inl
new file mode 100644
index 0000000..a1d4b43
--- /dev/null
+++ b/src/virtualmachine/Operations.inl
@@ -0,0 +1,60 @@
+OPDEF(UNDEF)
+OPDEF(IGNORE) /* no-op */
+OPDEF(BREAK) /* ??? */
+OPDEF(ENTER) /* Begin subroutine. */
+OPDEF(LEAVE) /* End subroutine. */
+OPDEF(CALL) /* Call subroutine. */
+OPDEF(PUSH) /* push to stack. */
+OPDEF(POP) /* discard top-of-stack. */
+OPDEF(CONST) /* load constant to stack. */
+OPDEF(LOCAL) /* get local variable. */
+OPDEF(JUMP) /* unconditional jump. */
+OPDEF(EQ) /* compare integers, jump if equal. */
+OPDEF(NE) /* compare integers, jump if not equal. */
+OPDEF(LTI) /* compare integers, jump if less-than. */
+OPDEF(LEI) /* compare integers, jump if less-than-or-equal. */
+OPDEF(GTI) /* compare integers, jump if greater-than. */
+OPDEF(GEI) /* compare integers, jump if greater-than-or-equal. */
+OPDEF(LTU) /* compare unsigned integers, jump if less-than */
+OPDEF(LEU) /* compare unsigned integers, jump if less-than-or-equal */
+OPDEF(GTU) /* compare unsigned integers, jump if greater-than */
+OPDEF(GEU) /* compare unsigned integers, jump if greater-than-or-equal */
+OPDEF(EQF) /* compare floats, jump if equal */
+OPDEF(NEF) /* compare floats, jump if not-equal */
+OPDEF(LTF) /* compare floats, jump if less-than */
+OPDEF(LEF) /* compare floats, jump if less-than-or-equal */
+OPDEF(GTF) /* compare floats, jump if greater-than */
+OPDEF(GEF) /* compare floats, jump if greater-than-or-equal */
+OPDEF(LOAD1) /* load 1-byte from memory */
+OPDEF(LOAD2) /* load 2-byte from memory */
+OPDEF(LOAD4) /* load 4-byte from memory */
+OPDEF(STORE1) /* store 1-byte to memory */
+OPDEF(STORE2) /* store 2-byte to memory */
+OPDEF(STORE4) /* store 4-byte to memory */
+OPDEF(ARG) /* marshal argument */
+OPDEF(BLOCK_COPY) /* block copy... */
+OPDEF(SEX8) /* Pedophilia */
+OPDEF(SEX16) /* Sign-Extend 16-bit */
+OPDEF(NEGI) /* Negate integer. */
+OPDEF(ADD) /* Add integers (two's complement). */
+OPDEF(SUB) /* Subtract integers (two's complement). */
+OPDEF(DIVI) /* Divide signed integers. */
+OPDEF(DIVU) /* Divide unsigned integers. */
+OPDEF(MODI) /* Modulus (signed). */
+OPDEF(MODU) /* Modulus (unsigned). */
+OPDEF(MULI) /* Multiply signed integers. */
+OPDEF(MULU) /* Multiply unsigned integers. */
+OPDEF(BAND) /* Bitwise AND */
+OPDEF(BOR) /* Bitwise OR */
+OPDEF(BXOR) /* Bitwise eXclusive-OR */
+OPDEF(BCOM) /* Bitwise COMplement */
+OPDEF(LSH) /* Left-shift */
+OPDEF(RSHI) /* Right-shift (algebraic; preserve sign) */
+OPDEF(RSHU) /* Right-shift (bitwise; ignore sign) */
+OPDEF(NEGF) /* Negate float */
+OPDEF(ADDF) /* Add floats */
+OPDEF(SUBF) /* Subtract floats */
+OPDEF(DIVF) /* Divide floats */
+OPDEF(MULF) /* Multiply floats */
+OPDEF(CVIF) /* Convert to integer from float */
+OPDEF(CVFI) /* Convert to float from integer */ \ No newline at end of file
diff --git a/src/virtualmachine/Syscalls.cpp b/src/virtualmachine/Syscalls.cpp
new file mode 100644
index 0000000..31b7dd4
--- /dev/null
+++ b/src/virtualmachine/Syscalls.cpp
@@ -0,0 +1,99 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+#include "VirtualMachine.h"
+#include "simulation/Simulation.h"
+#include "graphics/Renderer.h"
+
+namespace vm
+{
+ #define ARG(n) (Get(RP + ((2 + n) * sizeof(word))))
+
+ #define TRAPDEF(f) int VirtualMachine::trap##f()
+
+ TRAPDEF(sin)
+ {
+ Push<float4_t>(sin(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(cos)
+ {
+ Push<float4_t>(cos(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(atan2)
+ {
+ Push<float4_t>(atan2(ARG(0).float4, ARG(1).float4));
+ return 0;
+ }
+
+ TRAPDEF(sqrt)
+ {
+ Push<float4_t>(sqrt(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(floor)
+ {
+ Push<float4_t>(floor(ARG(0).float4));
+ return 0;
+ }
+
+ TRAPDEF(ceil)
+ {
+ Push<float4_t>(ceil(ARG(0).float4));
+ return 0;
+ }
+
+
+ TRAPDEF(print)
+ {
+ char *text;
+ text = (char*)(ram) + ARG(0).int4;
+ printf("%s", text);
+ return 0;
+ }
+
+
+ TRAPDEF(error)
+ {
+ char *msg;
+ msg = (char*)(ram) + ARG(0).int4;
+ printf("%s", msg);
+ End();
+ return 0;
+ }
+
+
+ TRAPDEF(partCreate)
+ {
+ Push<int4_t>(sim->create_part(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4));
+ return 0;
+ }
+
+ TRAPDEF(partChangeType)
+ {
+ sim->part_change_type(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4);
+ return 0;
+ }
+
+ TRAPDEF(pmapData)
+ {
+ Push<int4_t>(sim->pmap[ARG(1).int4][ARG(0).int4]);
+ return 0;
+ }
+
+ TRAPDEF(deletePart)
+ {
+ sim->delete_part(ARG(0).int4, ARG(1).int4, ARG(2).int4);
+ return 0;
+ }
+
+ TRAPDEF(killPart)
+ {
+ sim->kill_part(ARG(0).int4);
+ return 0;
+ }
+}
diff --git a/src/virtualmachine/Syscalls.inl b/src/virtualmachine/Syscalls.inl
new file mode 100644
index 0000000..75455c1
--- /dev/null
+++ b/src/virtualmachine/Syscalls.inl
@@ -0,0 +1,14 @@
+TRAPDEF(-1, sin)
+TRAPDEF(-2, cos)
+TRAPDEF(-3, atan2)
+TRAPDEF(-4, sqrt)
+TRAPDEF(-5, floor)
+TRAPDEF(-6, ceil)
+
+TRAPDEF(-7, error)
+TRAPDEF(-8, print)
+TRAPDEF(-9, partCreate)
+TRAPDEF(-10, partChangeType)
+TRAPDEF(-11, pmapData)
+TRAPDEF(-12, deletePart)
+TRAPDEF(-13, killPart)
diff --git a/src/virtualmachine/VirtualMachine.cpp b/src/virtualmachine/VirtualMachine.cpp
new file mode 100644
index 0000000..929723a
--- /dev/null
+++ b/src/virtualmachine/VirtualMachine.cpp
@@ -0,0 +1,405 @@
+#include <string>
+#include <cstring>
+#include <stdio.h>
+#include <stdlib.h>
+#include "VirtualMachine.h"
+
+namespace vm
+{
+
+ VirtualMachine::VirtualMachine(int hunkMbytes):
+ bigEndian(false),
+ hunk(NULL),
+ hunkSize(1048576),
+ hunkFree(0),
+ rom(NULL),
+ romSize(0),
+ ram(NULL),
+ ramSize(0),
+ dataStack(0),
+ returnStack(0),
+ DP(0), /* Datastack pointer. */
+ RP(0), /* Return stack pointer. */
+ PC(0),
+ cm(0),
+ cycles(0),
+ sim(NULL),
+ ren(NULL)
+ {
+ hunk = new char[hunkSize];
+ std::fill(hunk, hunk+hunkSize, 0);
+ }
+
+ VirtualMachine::~VirtualMachine()
+ {
+ delete[] hunk;
+ }
+
+ #define DEBUGTRACE(args, ...) printf(args);
+
+ int VirtualMachine::opcodeParameterSize(int opcode)
+ {
+ #define OP(n) OP##n
+ switch (opcode)
+ {
+ case OP(ENTER):
+ case OP(LEAVE):
+ case OP(LOCAL):
+ 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(CONST):
+ case OP(BLOCK_COPY):
+ return sizeof(uint4_t);
+ break;
+ case OP(ARG):
+ return sizeof(uint1_t);
+ break;
+ }
+ return 0;
+ #undef OP
+ }
+
+ /* Read one octet from file. */
+ int VirtualMachine::readByte(std::istream & input)
+ {
+ int o;
+ o = input.get();
+ if (o < 0) o = 0; /* EOF (hack) */
+ return o;
+ }
+
+ /* Read little-endian 32-bit integer from file. */
+ int VirtualMachine::readInt(std::istream & input)
+ {
+ int a, b, c, d, n;
+
+ a = readByte(input);
+ b = readByte(input);
+ c = readByte(input);
+ d = readByte(input);
+ n = (a) | (b << 8) | (c << 16) | (d << 24);
+ return n;
+ }
+
+ int VirtualMachine::readProgram(std::istream & input)
+ {
+ qvm_header_t qvminfo;
+ int i, n;
+ uint1_t x[4];
+ word w;
+
+ DEBUGTRACE("Loading file...\n");
+ qvminfo.magic = readInt(input); /* magic. */
+ if (qvminfo.magic != QVM_MAGIC)
+ {
+ DEBUGTRACE("Invalid magic");
+ throw InvalidProgramException();
+ //q3vm_error("Does not appear to be a QVM file.");
+ /* XXX: option to force continue. */
+ return 0;
+ }
+ DEBUGTRACE("Magic OK\n");
+ /* variable-length instructions mean instruction count != code length */
+ qvminfo.inscount = readInt(input);
+ qvminfo.codeoff = readInt(input);
+ qvminfo.codelen = readInt(input);
+ qvminfo.dataoff = readInt(input);
+ qvminfo.datalen = readInt(input);
+ qvminfo.litlen = readInt(input);
+ qvminfo.bsslen = readInt(input);
+
+ /* Code segment should follow... */
+ /* XXX: use fseek with SEEK_CUR? */
+ DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, input.tellg());
+
+ // rom = (q3vm_rom_t*)(hunk); /* ROM-in-hunk */
+ rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0]));
+ while (input.tellg() < qvminfo.codeoff)
+ readByte(input);
+ while (romSize < qvminfo.inscount)
+ {
+ n = readByte(input);
+ w.int4 = 0;
+ if ((i = opcodeParameterSize(n)))
+ {
+ x[0] = x[1] = x[2] = x[3] = 0;
+ input.readsome((char*)x, i);
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ }
+ rom[romSize].Operation = n;
+ rom[romSize].Parameter = w;
+ romSize++;
+ }
+ DEBUGTRACE("After loading code: at %d, should be %d\n", input.tellg(), qvminfo.codeoff + qvminfo.codelen);
+
+ /* Then data segment. */
+ // ram = hunk + ((romlen + 3) & ~3); /* RAM-in-hunk */
+ ram = hunk;
+ DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, input.tellg());
+ while (input.tellg() < qvminfo.dataoff)
+ readByte(input);
+ for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++)
+ {
+ i = input.readsome((char*)x, sizeof(x));
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+
+ /* lit segment follows data segment. */
+ /* Assembler should have already padded properly. */
+ DEBUGTRACE("Loading .lit\n");
+ for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++)
+ {
+ i = input.readsome((char*)x, sizeof(x));
+ memcpy(&(w.uint1), &x, sizeof(x)); /* no byte-swapping. */
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+ /* bss segment. */
+ DEBUGTRACE("Allocating .bss %d (%X) bytes\n", qvminfo.bsslen, qvminfo.bsslen);
+ /* huge empty chunk. */
+ ramSize += qvminfo.bsslen;
+
+ hunkFree = hunkSize - ((ramSize * sizeof(uint1_t)) + 4);
+
+ DEBUGTRACE("VM hunk has %d of %d bytes free (RAM = %d B).\n", hunkFree, hunkSize, ramSize);
+ if (ramSize > hunkSize)
+ {
+ throw OutOfMemoryException();
+ return 0;
+ }
+
+ /* set up stack. */
+ {
+ int stacksize = 0x10000;
+ dataStack = ramSize - (stacksize / 2);
+ returnStack = ramSize;
+ //returnStack = dataStack+4;
+ RP = returnStack;
+ DP = dataStack;
+ }
+
+ /* set up PC for return-to-termination. */
+ PC = romSize + 1;
+
+ ramMask = ramSize;
+
+ return 1;
+ }
+
+ int VirtualMachine::LoadProgram(std::vector<char> data)
+ {
+ /*class vectorwrapbuf : public std::basic_streambuf<char, std::char_traits<char> >
+ {
+ public:
+ vectorwrapbuf(std::vector<char> &vec) {
+ setg(vec.data(), vec.data(), vec.data() + vec.size());
+ }
+ };
+ vectorwrapbuf databuf(data);
+ std::istream is(&databuf);
+ return readProgram(is);*/
+ std::stringstream ss(std::string(data.begin(), data.end()));
+ return readProgram((std::istream &)ss);
+ }
+
+ int VirtualMachine::LoadProgram(char * filename)
+ {
+ /*FILE * qvmfile = fopen(filename, "rb");
+ qvm_header_t qvminfo;
+ int i, n;
+ uint1_t x[4];
+ word w;
+
+ DEBUGTRACE("Loading file...\n");
+ qvminfo.magic = readInt(qvmfile);
+ if (qvminfo.magic != QVM_MAGIC)
+ {
+ DEBUGTRACE("Invalid magic");
+ return 0;
+ }
+ DEBUGTRACE("Magic OK\n");
+
+ qvminfo.inscount = readInt(qvmfile);
+ qvminfo.codeoff = readInt(qvmfile);
+ qvminfo.codelen = readInt(qvmfile);
+ qvminfo.dataoff = readInt(qvmfile);
+ qvminfo.datalen = readInt(qvmfile);
+ qvminfo.litlen = readInt(qvmfile);
+ qvminfo.bsslen = readInt(qvmfile);
+
+
+ DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, ftell(qvmfile));
+
+ rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0]));
+ while (ftell(qvmfile) < qvminfo.codeoff)
+ readByte(qvmfile);
+ while (romSize < qvminfo.inscount)
+ {
+ n = readByte(qvmfile);
+ w.int4 = 0;
+ if ((i = opcodeParameterSize(n)))
+ {
+ x[0] = x[1] = x[2] = x[3] = 0;
+ fread(&x, 1, i, qvmfile);
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ }
+ rom[romSize].Operation = n;
+ rom[romSize].Parameter = w;
+ romSize++;
+ }
+ DEBUGTRACE("After loading code: at %d, should be %d\n", ftell(qvmfile), qvminfo.codeoff + qvminfo.codelen);
+
+
+ ram = hunk;
+ DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, ftell(qvmfile));
+ while (ftell(qvmfile) < qvminfo.dataoff)
+ readByte(qvmfile);
+ for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++)
+ {
+ i = fread(&x, 1, sizeof(x), qvmfile);
+ w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24);
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+
+
+ DEBUGTRACE("Loading .lit\n");
+ for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++)
+ {
+ i = fread(&x, 1, sizeof(x), qvmfile);
+ memcpy(&(w.uint1), &x, sizeof(x));
+ *((word*)(ram + ramSize)) = w;
+ ramSize += sizeof(word);
+ }
+
+ DEBUGTRACE("Allocating .bss %d (%X) bytes\n", qvminfo.bsslen, qvminfo.bsslen);
+ ramSize += qvminfo.bsslen;
+
+ hunkFree = hunkSize - ((ramSize * sizeof(uint1_t)) + 4);
+
+ DEBUGTRACE("VM hunk has %d of %d bytes free (RAM = %d B).\n", hunkFree, hunkSize, ramSize);
+ if (ramSize > hunkSize)
+ {
+ throw OutOfMemoryException();
+ return 0;
+ }
+
+
+ {
+ int stacksize = 0x10000;
+ dataStack = ramSize - (stacksize / 2);
+ //returnStack = ramSize;
+ returnStack = dataStack+4;
+ RP = returnStack;
+ DP = dataStack;
+ }
+
+
+ PC = romSize + 1;
+
+ ramMask = ramSize;
+
+ return 1;*/
+ return 0; //temporary, something has to be returned for now
+ }
+
+ void VirtualMachine::End()
+ {
+ PC = romSize+1;
+ }
+
+ int VirtualMachine::CallInterpreted(int address)
+ {
+ word w;
+ int i, argCount = 0;
+
+ /* Set up call. */
+ OpPUSH(w);
+ DEBUGTRACE("Starting with PC=%d, DP=%d, RP=%d to %d\n", PC, DP, RP, address);
+ w.int4 = (argCount + 2) * sizeof(word);
+ OpENTER(w);
+ i = 8;
+ /**w.int4 = arg0; Marshal(i, w); i += 4;
+ w.int4 = arg1; Marshal(i, w); i += 4;
+ w.int4 = arg2; Marshal(i, w); i += 4;
+ w.int4 = arg3; Marshal(i, w); i += 4;
+ w.int4 = arg4; Marshal(i, w); i += 4;
+ w.int4 = arg5; Marshal(i, w); i += 4;
+ w.int4 = arg6; Marshal(i, w); i += 4;
+ w.int4 = arg7; Marshal(i, w); i += 4;
+ w.int4 = arg8; Marshal(i, w); i += 4;
+ w.int4 = arg9; Marshal(i, w); i += 4;
+ w.int4 = arg10; Marshal(i, w); i += 4;
+ w.int4 = arg11; Marshal(i, w); i += 4;
+ w.int4 = arg12; Marshal(i, w); i += 4;*/
+ w.int4 = address;
+ Push(w);
+ OpCALL(w);
+ DEBUGTRACE("Upon running PC=%d, DP=%d, RP=%d\n", PC, DP, RP);
+ Run();
+ DEBUGTRACE("At finish PC=%d, DP=%d, RP=%d\n", PC, DP, RP);
+ w.int4 = (argCount + 2) * sizeof(word);
+ OpLEAVE(w);
+ OpPOP(w);
+ PC = romSize + 1;
+ return 0;
+ }
+
+ int VirtualMachine::Run()
+ {
+ bool running = true;
+ int operation;
+ word parameter;
+ while(running)
+ {
+ cycles++;
+ if(PC > romSize)
+ {
+ running = false;
+ continue;
+ }
+ if (PC < 0)
+ {
+ syscall(PC);
+ continue;
+ }
+ operation = rom[PC].Operation;
+ parameter = rom[PC].Parameter;
+ PC++;
+ (this->*operations[operation])(parameter);
+ }
+ return 1;
+ }
+
+
+
+ int VirtualMachine::syscall(int trap)
+ {
+ PC = Pop<int4_t>();
+
+ switch (trap)
+ {
+ #define TRAPDEF(n, f) case n: trap##f(); break;
+ #include "Syscalls.inl"
+ #undef TRAPDEF
+ }
+
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/src/virtualmachine/VirtualMachine.h b/src/virtualmachine/VirtualMachine.h
new file mode 100644
index 0000000..b295d02
--- /dev/null
+++ b/src/virtualmachine/VirtualMachine.h
@@ -0,0 +1,282 @@
+#pragma once
+
+#include "Exceptions.h"
+
+class Simulation;
+class Renderer;
+
+namespace vm
+{
+
+ class VirtualMachine;
+
+ typedef char ram_t;
+
+
+
+ typedef unsigned int uint4_t;
+ typedef signed int int4_t;
+
+ typedef unsigned short uint2_t;
+ typedef signed short int2_t;
+
+ typedef unsigned char uint1_t;
+ typedef signed char int1_t;
+
+ typedef float float4_t;
+
+ union word
+ {
+ uint4_t uint4;
+ int4_t int4;
+ uint2_t uint2;
+ int2_t int2;
+ uint1_t uint1;
+ int1_t int1;
+ float4_t float4;
+ };
+
+ typedef int (VirtualMachine::*OperationFunction)(word parameter);
+
+ struct Instruction
+ {
+ int Operation;
+ word Parameter;
+ //opfunc opfunc;
+ };
+
+ enum
+ {
+ QVM_MAGIC = 0x12721444,
+ };
+
+ struct qvm_header_t
+ {
+ int magic;
+ /* not-entirely-RISC ISA, so instruction count != codelen */
+ int inscount; /* instruction count. */
+ int codeoff; /* file offset of code segment. */
+ int codelen; /* length of code segment, in octets. */
+ int dataoff; /* file offset of data segment. */
+ int datalen; /* length of data segment, in octets. */
+ int litlen; /* length of lit segment (which is embedded in data segment). */
+ int bsslen; /* length of bss segment. */
+ };
+
+ class VirtualMachine
+ {
+
+ int * instructionPointers;
+
+ bool bigEndian; /* host is big-endian (requires byte-swapping). */
+
+ /* Memory spaces. */
+ char * hunk; /* hunk space (malloc'd). */
+ int hunkSize; /* total hunk size. */
+ int hunkFree; /* free pointer. */
+
+ /* Read-Only Memory (code). */
+ Instruction * rom;
+ int romSize;
+ int romMask;
+
+ char * compiledRom;
+ int compiledRomSize;
+ int compiledRomMask;
+
+ /* Random-Access Memory (data). */
+ ram_t *ram;
+ int ramSize;
+ int ramMask;
+
+ int dataStack;
+ int returnStack;
+
+ word r[4]; /* registers. */
+ int DP; /* Datastack pointer. */
+ int RP; /* Return stack pointer. */
+ int PC; /* Program Counter. */
+ // int AP; /* Argument pointer. (hrm...) */
+
+ /* various flags. */
+ int cm:1;
+
+ /* Execution time */
+ int cycles;
+
+ #define TRAPDEF(n, f) int trap##f();
+ #include "Syscalls.inl"
+ #undef TRAPDEF
+
+ static OperationFunction operations[];
+
+
+ #define OPDEF(n) OP##n,
+ enum {
+ #include "Operations.inl"
+ };
+ #undef OPDEF
+
+ int readProgram(std::istream & input);
+ int readByte(std::istream & input);
+ int readInt(std::istream & input);
+ int opcodeParameterSize(int opcode);
+ int syscall(int programCounter);
+
+ //Used by the JIT
+ #ifdef VMJIT
+ int constant4();
+ int constant1();
+ void emit1(int v);
+ void emit4(int v);
+ void emitInstruction(const char *string);
+ void emitCommand(int command);
+ void emitAddEDI4();
+ void emitMovEAXEDI();
+ bool emitMovEBXEDI(int andit);
+ static int hex(int c);
+ #endif
+public:
+ #ifdef VMJIT
+ static void callFromCompiled();
+ static void callSyscall();
+ bool Compile();
+ int CallCompiled(int address);
+ #endif
+ Simulation * sim;
+ Renderer * ren;
+
+ #define OPDEF(n) int Op##n(word parameter);
+ #include "Operations.inl"
+ #undef OPDEF
+
+ VirtualMachine(int hunkMbytes);
+ virtual ~VirtualMachine();
+
+ int LoadProgram(char * filename);
+ int LoadProgram(std::vector<char> fileData);
+ int Run();
+ int CallInterpreted(int address);
+ void End();
+ void Marshal(int address, word element)
+ {
+ ram_t * ptr = ram+RP+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(RP+address);
+ *((word*)ptr) = element;
+ }
+
+ template <typename T> T Get(int address)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ return *((T*)ptr);
+ }
+
+ template <typename T> void Set(int address, T value)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ *((T*)ptr) = value;
+ }
+
+ template <typename T> T Pop ()
+ {
+ ram_t * ptr = ram+DP;
+ if(DP + sizeof(word) < hunkSize)
+ DP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((T*)ptr);
+ };
+
+ template <typename T> T RPop ()
+ {
+ ram_t * ptr = ram+RP;
+ if(RP + sizeof(word) < hunkSize)
+ RP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((T*)ptr);
+ };
+
+ template <typename T> void Push(T value)
+ {
+ if(DP - sizeof(word) >= 0)
+ DP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+DP;
+ *((T*)ptr) = value;
+ };
+
+ template <typename T> void RPush(T value)
+ {
+ if(RP - sizeof(word) >= 0)
+ RP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+RP;
+ *((T*)ptr) = value;
+ };
+
+ word Get(int address)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ return *((word*)ptr);
+ }
+
+ void Set(int address, word value)
+ {
+ ram_t * ptr = ram+address;
+ if(ptr < ram || ptr > ram+ramSize - sizeof(word))
+ throw AccessViolationException(address);
+ *((word*)ptr) = value;
+ }
+
+ word Pop()
+ {
+ ram_t * ptr = ram+DP;
+ if(DP + sizeof(word) < hunkSize)
+ DP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((word*)ptr);
+ };
+
+ void Push(word value)
+ {
+ if(DP - sizeof(word) >= 0)
+ DP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+DP;
+ *((word*)ptr) = value;
+ };
+
+ word RPop()
+ {
+ ram_t * ptr = ram+RP;
+ if(RP + sizeof(word) < hunkSize)
+ RP += sizeof(word);
+ else
+ throw StackUnderflowException();
+ return *((word*)ptr);
+ };
+
+ void RPush(word value)
+ {
+ if(RP - sizeof(word) >= 0)
+ RP -= sizeof(word);
+ else
+ throw StackOverflowException();
+ ram_t * ptr = ram+RP;
+ *((word*)ptr) = value;
+ };
+ };
+
+}