/* =========================================================================== Return to Castle Wolfenstein multiplayer GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”). RTCW MP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW MP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW MP Source Code. If not, see . In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ // vm_x86.c -- load time compiler and execution environment for x86 #include "vm_local.h" #ifndef _WIN32 #include // for PROT_ stuff #endif /* eax scratch ebx scratch ecx scratch (required for shifts) edx scratch (required for divisions) esi program stack edi opstack */ static byte *buf; static int compiledOfs; static byte *code; static int pc; static int *instructionPointers; #ifdef _WIN32 void AsmCall( void ); int _ftol( float ); //static int ftolPtr = (int)_ftol; static int asmCallPtr = (int)AsmCall; #else void doAsmCall( void ); static int asmCallPtr = (int)doAsmCall; #endif /* ================= AsmCall ================= */ #ifdef _WIN32 __declspec( naked ) void AsmCall( void ) { static int programStack; static int *opStack; static int syscallNum; __asm { mov eax, dword ptr [edi] sub edi, 4 or eax,eax jl systemCall // calling another vm function shl eax,2 add eax, dword ptr [instructionPointers] call dword ptr [eax] ret systemCall: // convert negative num to system call number // and store right before the first arg neg eax dec eax mov dword ptr syscallNum, eax // so C code can get at it mov dword ptr programStack, esi // so C code can get at it mov dword ptr opStack, edi push ecx push esi // we may call recursively, so the push edi // statics aren't guaranteed to be around } // save the stack to allow recursive VM entry currentVM->programStack = programStack - 4; *( int * )( (byte *)currentVM->dataBase + programStack + 4 ) = syscallNum; //VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) ); *( opStack + 1 ) = currentVM->systemCall( ( int * )( (byte *)currentVM->dataBase + programStack + 4 ) ); _asm { pop edi pop esi pop ecx add edi, 4 // we added the return value ret } } #else static int callProgramStack; static int *callOpStack; static int callSyscallNum; void callAsmCall( void ) { // save the stack to allow recursive VM entry currentVM->programStack = callProgramStack - 4; *( int * )( (byte *)currentVM->dataBase + callProgramStack + 4 ) = callSyscallNum; //VM_LogSyscalls( (int *)((byte *)currentVM->dataBase + programStack + 4) ); *( callOpStack + 1 ) = currentVM->systemCall( ( int * )( (byte *)currentVM->dataBase + callProgramStack + 4 ) ); } void AsmCall( void ) { __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"\ " 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 callAsmCall \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" ( callSyscallNum ), "=rm" ( callProgramStack ), "=rm" ( callOpStack ) \ : "rm" ( instructionPointers ) \ : "ax", "di", "si", "cx" \ ); } #endif static int Constant4( void ) { int v; v = code[pc] | ( code[pc + 1] << 8 ) | ( code[pc + 2] << 16 ) | ( code[pc + 3] << 24 ); pc += 4; return v; } static int Constant1( void ) { int v; v = code[pc]; pc += 1; return v; } static void Emit1( int v ) { buf[ compiledOfs ] = v; compiledOfs++; } #if 0 static void Emit2( int v ) { Emit1( v & 255 ); Emit1( ( v >> 8 ) & 255 ); } #endif static void Emit4( int v ) { Emit1( v & 255 ); Emit1( ( v >> 8 ) & 255 ); Emit1( ( v >> 16 ) & 255 ); Emit1( ( v >> 24 ) & 255 ); } static int 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'; } Com_Error( ERR_DROP, "Hex: bad char '%c'", c ); return 0; } static void EmitString( const char *string ) { int c1, c2; int v; while ( 1 ) { c1 = string[0]; c2 = string[1]; v = ( Hex( c1 ) << 4 ) | Hex( c2 ); Emit1( v ); if ( !string[2] ) { break; } string += 3; } } /* ================= VM_Compile ================= */ void VM_Compile( vm_t *vm, vmHeader_t *header ) { int op; int maxLength; int v; int i; int instruction; int lastConst; // allocate a very large temp buffer, we will shrink it later maxLength = header->codeLength * 8; buf = Z_Malloc( maxLength ); // translate all instructions pc = 0; instruction = 0; code = (byte *)header + header->codeOffset; compiledOfs = 0; while ( instruction < header->instructionCount ) { if ( compiledOfs > maxLength - 16 ) { Com_Error( ERR_FATAL, "VM_CompileX86: maxLength exceeded" ); } vm->instructionPointers[ instruction ] = compiledOfs; instruction++; op = code[ pc ]; if ( pc > header->codeLength ) { Com_Error( ERR_FATAL, "VM_CompileX86: pc > header->codeLength" ); } pc++; switch ( op ) { case 0: break; case OP_BREAK: EmitString( "CC" ); // int 3 break; case OP_ENTER: EmitString( "81 EE" ); // sub esi, 0x12345678 Emit4( Constant4() ); break; case OP_CONST: EmitString( "83 C7 04" ); // add edi,4 EmitString( "C7 07" ); // mov dword ptr [edi], 0x12345678 lastConst = Constant4(); Emit4( lastConst ); break; case OP_LOCAL: EmitString( "83 C7 04" ); // add edi,4 EmitString( "8D 86" ); // lea eax, [0x12345678 + esi] Emit4( Constant4() ); EmitString( "89 07" ); // mov dword ptr [edi], eax break; case OP_ARG: EmitString( "8B 07" ); // mov eax,dword ptr [edi] EmitString( "89 86" ); // mov dword ptr [esi+database],eax // FIXME: range check Emit4( Constant1() + (int)vm->dataBase ); EmitString( "83 EF 04" ); // sub edi,4 break; case OP_CALL: EmitString( "C7 86" ); // mov dword ptr [esi+database],0x12345678 Emit4( (int)vm->dataBase ); Emit4( pc ); EmitString( "FF 15" ); // call asmCallPtr Emit4( (int)&asmCallPtr ); break; case OP_PUSH: EmitString( "83 C7 04" ); // add edi,4 break; case OP_POP: EmitString( "83 EF 04" ); // sub edi, 4 break; case OP_LEAVE: v = Constant4(); EmitString( "81 C6" ); // add esi, 0x12345678 Emit4( v ); EmitString( "C3" ); // ret break; case OP_LOAD4: EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "81 E3" ); // and ebx, 0x12345678 Emit4( vm->dataMask ); EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitString( "89 07" ); // mov dword ptr [edi], eax break; case OP_LOAD2: EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "81 E3" ); // and ebx, 0x12345678 Emit4( vm->dataMask ); EmitString( "0F B7 83" ); // movzx eax, word ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitString( "89 07" ); // mov dword ptr [edi], eax break; case OP_LOAD1: EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "81 E3" ); // and ebx, 0x12345678 Emit4( vm->dataMask ); EmitString( "0F B6 83" ); // movzx eax, byte ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitString( "89 07" ); // mov dword ptr [edi], eax break; case OP_STORE4: EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] EmitString( "81 E3" ); // and ebx, 0x12345678 Emit4( vm->dataMask & ~3 ); EmitString( "8B 07" ); // mov eax, dword ptr [edi]; EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitString( "83 EF 08" ); // sub edi, 8 break; case OP_STORE2: EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] EmitString( "81 E3" ); // and ebx, 0x12345678 Emit4( vm->dataMask & ~1 ); EmitString( "8B 07" ); // mov eax, dword ptr [edi]; EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitString( "83 EF 08" ); // sub edi, 8 break; case OP_STORE1: EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] EmitString( "81 E3" ); // and ebx, 0x12345678 Emit4( vm->dataMask ); EmitString( "8B 07" ); // mov eax, dword ptr [edi]; EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitString( "83 EF 08" ); // sub edi, 8 break; case OP_EQ: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "75 06" ); // jne +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_NE: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "74 06" ); // je +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_LTI: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "7D 06" ); // jnl +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_LEI: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "7F 06" ); // jnle +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_GTI: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "7E 06" ); // jng +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_GEI: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "7C 06" ); // jnge +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_LTU: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "73 06" ); // jnb +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_LEU: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "77 06" ); // jnbe +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_GTU: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "76 06" ); // jna +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_GEU: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "8B 47 04" ); // mov eax, dword ptr [edi+4] EmitString( "3B 47 08" ); // cmp eax, dword ptr [edi+8] EmitString( "72 06" ); // jnae +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_EQF: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "D9 47 04" ); // fld dword ptr [edi+4] EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] EmitString( "DF E0" ); // fnstsw ax EmitString( "F6 C4 40" ); // test ah,0x40 EmitString( "74 06" ); // je +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_NEF: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "D9 47 04" ); // fld dword ptr [edi+4] EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] EmitString( "DF E0" ); // fnstsw ax EmitString( "F6 C4 40" ); // test ah,0x40 EmitString( "75 06" ); // jne +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_LTF: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "D9 47 04" ); // fld dword ptr [edi+4] EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] EmitString( "DF E0" ); // fnstsw ax EmitString( "F6 C4 01" ); // test ah,0x01 EmitString( "74 06" ); // je +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_LEF: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "D9 47 04" ); // fld dword ptr [edi+4] EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] EmitString( "DF E0" ); // fnstsw ax EmitString( "F6 C4 41" ); // test ah,0x41 EmitString( "74 06" ); // je +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_GTF: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "D9 47 04" ); // fld dword ptr [edi+4] EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] EmitString( "DF E0" ); // fnstsw ax EmitString( "F6 C4 41" ); // test ah,0x41 EmitString( "75 06" ); // jne +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_GEF: EmitString( "83 EF 08" ); // sub edi,8 EmitString( "D9 47 04" ); // fld dword ptr [edi+4] EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] EmitString( "DF E0" ); // fnstsw ax EmitString( "F6 C4 01" ); // test ah,0x01 EmitString( "75 06" ); // jne +6 EmitString( "FF 25" ); // jmp [0x12345678] Emit4( (int)vm->instructionPointers + Constant4() * 4 ); break; case OP_NEGI: EmitString( "F7 1F" ); // neg dword ptr [edi] break; case OP_ADD: EmitString( "8B 07" ); // mov eax, dword ptr [edi] EmitString( "01 47 FC" ); // add dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_SUB: EmitString( "8B 07" ); // mov eax, dword ptr [edi] EmitString( "29 47 FC" ); // sub dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_DIVI: EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] EmitString( "99" ); // cdq EmitString( "F7 3F" ); // idiv dword ptr [edi] EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_DIVU: EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] EmitString( "33 D2" ); // xor edx, edx EmitString( "F7 37" ); // div dword ptr [edi] EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_MODI: EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] EmitString( "99" ); // cdq EmitString( "F7 3F" ); // idiv dword ptr [edi] EmitString( "89 57 FC" ); // mov dword ptr [edi-4],edx EmitString( "83 EF 04" ); // sub edi,4 break; case OP_MODU: EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] EmitString( "33 D2" ); // xor edx, edx EmitString( "F7 37" ); // div dword ptr [edi] EmitString( "89 57 FC" ); // mov dword ptr [edi-4],edx EmitString( "83 EF 04" ); // sub edi,4 break; case OP_MULI: EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] EmitString( "F7 2F" ); // imul dword ptr [edi] EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_MULU: EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] EmitString( "F7 27" ); // mul dword ptr [edi] EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_BAND: EmitString( "8B 07" ); // mov eax, dword ptr [edi] EmitString( "21 47 FC" ); // and dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_BOR: EmitString( "8B 07" ); // mov eax, dword ptr [edi] EmitString( "09 47 FC" ); // or dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_BXOR: EmitString( "8B 07" ); // mov eax, dword ptr [edi] EmitString( "31 47 FC" ); // xor dword ptr [edi-4],eax EmitString( "83 EF 04" ); // sub edi,4 break; case OP_BCOM: EmitString( "F7 17" ); // not dword ptr [edi] break; case OP_LSH: EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] EmitString( "D3 67 FC" ); // shl dword ptr [edi-4], cl EmitString( "83 EF 04" ); // sub edi,4 break; case OP_RSHI: EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] EmitString( "D3 7F FC" ); // sar dword ptr [edi-4], cl EmitString( "83 EF 04" ); // sub edi,4 break; case OP_RSHU: EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] EmitString( "D3 6F FC" ); // shr dword ptr [edi-4], cl EmitString( "83 EF 04" ); // sub edi,4 break; case OP_NEGF: EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "D9 E0" ); // fchs EmitString( "D9 1F" ); // fstp dword ptr [edi] break; case OP_ADDF: EmitString( "83 EF 04" ); // sub edi,4 EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "D8 47 04" ); // fadd dword ptr [edi+4] EmitString( "D9 1F" ); // fstp dword ptr [edi] break; case OP_SUBF: EmitString( "83 EF 04" ); // sub edi,4 EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "D8 67 04" ); // fsub dword ptr [edi+4] EmitString( "D9 1F" ); // fstp dword ptr [edi] break; case OP_DIVF: EmitString( "83 EF 04" ); // sub edi,4 EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "D8 77 04" ); // fdiv dword ptr [edi+4] EmitString( "D9 1F" ); // fstp dword ptr [edi] break; case OP_MULF: EmitString( "83 EF 04" ); // sub edi,4 EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "D8 4f 04" ); // fmul dword ptr [edi+4] EmitString( "D9 1F" ); // fstp dword ptr [edi] break; case OP_CVIF: EmitString( "DB 07" ); // fild dword ptr [edi] EmitString( "D9 1F" ); // fstp dword ptr [edi] break; case OP_CVFI: EmitString( "D9 07" ); // fld dword ptr [edi] #if 1 // not IEEE complient, but simple and fast EmitString( "DB 1F" ); // fistp dword ptr [edi] #else // call the library conversion function EmitString( "FF 15" ); // call ftolPtr Emit4( (int)&ftolPtr ); EmitString( "89 07" ); // mov dword ptr [edi], eax #endif break; case OP_SEX8: EmitString( "0F BE 07" ); // movsx eax, byte ptr [edi] EmitString( "89 07" ); // mov dword ptr [edi], eax break; case OP_SEX16: EmitString( "0F BF 07" ); // movsx eax, word ptr [edi] EmitString( "89 07" ); // mov dword ptr [edi], eax break; case OP_BLOCK_COPY: // FIXME: range check EmitString( "56" ); // push esi EmitString( "57" ); // push edi EmitString( "8B 37" ); // mov esi,[edi] EmitString( "8B 7F FC" ); // mov edi,[edi-4] EmitString( "B9" ); // mov ecx,0x12345678 Emit4( Constant4() >> 2 ); EmitString( "B8" ); // mov eax, datamask Emit4( vm->dataMask ); EmitString( "BB" ); // mov ebx, database Emit4( (int)vm->dataBase ); EmitString( "23 F0" ); // and esi, eax EmitString( "03 F3" ); // add esi, ebx EmitString( "23 F8" ); // and edi, eax EmitString( "03 FB" ); // add edi, ebx EmitString( "F3 A5" ); // rep movsd EmitString( "5F" ); // pop edi EmitString( "5E" ); // pop esi EmitString( "83 EF 08" ); // sub edi,8 break; case OP_JUMP: EmitString( "83 EF 04" ); // sub edi,4 EmitString( "8B 47 04" ); // mov eax,dword ptr [edi+4] // FIXME: range check EmitString( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4] Emit4( (int)vm->instructionPointers ); break; default: Com_Error( ERR_DROP, "VM_CompileX86: bad opcode %i at offset %i", op, pc ); } } // copy to an exact size buffer on the hunk vm->codeLength = compiledOfs; vm->codeBase = Hunk_Alloc( compiledOfs, h_low ); memcpy( vm->codeBase, buf, compiledOfs ); Z_Free( buf ); Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs ); // offset all the instruction pointers for the new location for ( i = 0 ; i < header->instructionCount ; i++ ) { vm->instructionPointers[i] += (int)vm->codeBase; } #if 0 // ndef _WIN32 // Must make the newly generated code executable { int r; unsigned long addr; int psize = getpagesize(); addr = ( (int)vm->codeBase & ~( psize - 1 ) ) - psize; r = mprotect( (char*)addr, vm->codeLength + (int)vm->codeBase - addr + psize, PROT_READ | PROT_WRITE | PROT_EXEC ); if ( r < 0 ) { Com_Error( ERR_FATAL, "mprotect failed to change PROT_EXEC" ); } } #endif } /* ============== VM_CallCompiled This function is called directly by the generated code ============== */ int VM_CallCompiled( vm_t *vm, int *args ) { int stack[1024]; int programCounter; int programStack; int stackOnEntry; byte *image; void *entryPoint; void *opStack; int *oldInstructionPointers; oldInstructionPointers = instructionPointers; currentVM = vm; instructionPointers = vm->instructionPointers; // interpret the code vm->currentlyInterpreting = qtrue; // we might be called recursively, so this might not be the very top programStack = vm->programStack; stackOnEntry = programStack; // set up the stack frame image = vm->dataBase; programCounter = 0; programStack -= 48; *(int *)&image[ programStack + 44] = args[9]; *(int *)&image[ programStack + 40] = args[8]; *(int *)&image[ programStack + 36] = args[7]; *(int *)&image[ programStack + 32] = args[6]; *(int *)&image[ programStack + 28] = args[5]; *(int *)&image[ programStack + 24] = args[4]; *(int *)&image[ programStack + 20] = args[3]; *(int *)&image[ programStack + 16] = args[2]; *(int *)&image[ programStack + 12] = args[1]; *(int *)&image[ programStack + 8 ] = args[0]; *(int *)&image[ programStack + 4 ] = 0; // return stack *(int *)&image[ programStack ] = -1; // will terminate the loop on return // off we go into generated code... entryPoint = vm->codeBase; opStack = &stack; #ifdef _WIN32 __asm { pushad mov esi, programStack; mov edi, opStack call entryPoint mov programStack, esi mov opStack, edi popad } #else { static int memProgramStack; static void *memOpStack; static void *memEntryPoint; memProgramStack = programStack; memOpStack = opStack; 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" ( memProgramStack ), "=m" ( memOpStack ) \ : "m" ( memEntryPoint ), "0" ( memProgramStack ), "1" ( memOpStack ) \ : "si", "di" \ ); programStack = memProgramStack; opStack = memOpStack; } #endif if ( opStack != &stack[1] ) { Com_Error( ERR_DROP, "opStack corrupted in compiled code" ); } if ( programStack != stackOnEntry - 48 ) { Com_Error( ERR_DROP, "programStack corrupted in compiled code" ); } vm->programStack = stackOnEntry; // in case we were recursively called by another vm instructionPointers = oldInstructionPointers; return *(int *)opStack; }