diff options
| author | Simon 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) |
| commit | 058a2edd75debbd0297f92572316daa704bd379f (patch) | |
| tree | ad303f091f9a08b209b91eb34a9fcad996a3de69 /src/virtualmachine | |
| parent | e3594aba9e05c6865d396418c028049cda92c2f3 (diff) | |
| parent | 7a21ae192fe19868539956f3fe28e62b2c7c4429 (diff) | |
| download | powder-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.h | 100 | ||||
| -rw-r--r-- | src/virtualmachine/JustInTime.cpp | 1144 | ||||
| -rw-r--r-- | src/virtualmachine/Operations.cpp | 356 | ||||
| -rw-r--r-- | src/virtualmachine/Operations.inl | 60 | ||||
| -rw-r--r-- | src/virtualmachine/Syscalls.cpp | 99 | ||||
| -rw-r--r-- | src/virtualmachine/Syscalls.inl | 14 | ||||
| -rw-r--r-- | src/virtualmachine/VirtualMachine.cpp | 405 | ||||
| -rw-r--r-- | src/virtualmachine/VirtualMachine.h | 282 |
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; + }; + }; + +} |
