/* ================= VM_Compile ================= */ void VM_Compile( vm_t *vm, vmHeader_t *header ) { int op; int maxLength; int v; int i; // allocate a very large temp buffer, we will shrink it later maxLength = header->codeLength * 8; buf = Z_Malloc( maxLength ); jused = Z_Malloc(header->instructionCount + 2 ); Com_Memset(jused, 0, header->instructionCount+2); // ensure that the optimisation pass knows about all the jump // table targets for( i = 0; i < vm->numJumpTableTargets; i++ ) { jused[ *(int *)(vm->jumpTableTargets + ( i * sizeof( int ) ) ) ] = 1; } for(pass=0;pass<2;pass++) { oc0 = -23423; oc1 = -234354; pop0 = -43435; pop1 = -545455; // translate all instructions pc = 0; instruction = 0; code = (byte *)header + header->codeOffset; compiledOfs = 0; LastCommand = LAST_COMMAND_NONE; while ( instruction < header->instructionCount ) { if ( compiledOfs > maxLength - 16 ) { Com_Error( ERR_FATAL, "VM_CompileX86: maxLength exceeded" ); } vm->instructionPointers[ instruction ] = compiledOfs; instruction++; if ( pc > header->codeLength ) { Com_Error( ERR_FATAL, "VM_CompileX86: pc > header->codeLength" ); } op = code[ pc ]; 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: if (code[pc+4] == OP_LOAD4) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); EmitString( "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 (code[pc+4] == OP_LOAD2) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); EmitString( "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 (code[pc+4] == OP_LOAD1) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); EmitString( "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 (code[pc+4] == OP_STORE4) { EmitMovEBXEDI(vm, (vm->dataMask & ~3)); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); // if (!opt) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~3 ); // } EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_STORE4 instruction += 1; break; } if (code[pc+4] == OP_STORE2) { EmitMovEBXEDI(vm, (vm->dataMask & ~1)); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); // if (!opt) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~1 ); // } EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_STORE4 instruction += 1; break; } if (code[pc+4] == OP_STORE1) { EmitMovEBXEDI(vm, vm->dataMask); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); // if (!opt) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask ); // } EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_STORE4 instruction += 1; break; } if (code[pc+4] == OP_ADD) { EmitString( "81 07" ); // add dword ptr [edi], 0x1234567 Emit4( Constant4() ); pc++; // OP_ADD instruction += 1; break; } if (code[pc+4] == OP_SUB) { EmitString( "81 2F" ); // sub dword ptr [edi], 0x1234567 Emit4( Constant4() ); pc++; // OP_ADD instruction += 1; break; } EmitAddEDI4(vm); EmitString( "C7 07" ); // mov dword ptr [edi], 0x12345678 lastConst = Constant4(); Emit4( lastConst ); if (code[pc] == OP_JUMP) { jused[lastConst] = 1; } break; case OP_LOCAL: EmitAddEDI4(vm); EmitString( "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(vm); // mov eax,dword ptr [edi] EmitString( "89 86" ); // mov dword ptr [esi+database],eax // FIXME: range check Emit4( Constant1() + (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // 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: EmitAddEDI4(vm); break; case OP_POP: EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_LEAVE: v = Constant4(); EmitString( "81 C6" ); // add esi, 0x12345678 Emit4( v ); EmitString( "C3" ); // ret break; case OP_LOAD4: if (code[pc] == OP_CONST && code[pc+5] == OP_ADD && code[pc+6] == OP_STORE4) { if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { compiledOfs -= 11; vm->instructionPointers[ instruction-1 ] = compiledOfs; } pc++; // OP_CONST v = Constant4(); EmitMovEBXEDI(vm, vm->dataMask); if (v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "FF 83"); // inc dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); } else { EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitString( "05" ); // add eax, const Emit4( v ); if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } else { EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } } EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_ADD pc++; // OP_STORE instruction += 3; break; } if (code[pc] == OP_CONST && code[pc+5] == OP_SUB && code[pc+6] == OP_STORE4) { if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { compiledOfs -= 11; vm->instructionPointers[ instruction-1 ] = compiledOfs; } EmitMovEBXEDI(vm, vm->dataMask); EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); pc++; // OP_CONST v = Constant4(); if (v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "FF 8B"); // dec dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); } else { EmitString( "2D" ); // sub eax, const Emit4( v ); if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } else { EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } } 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; vm->instructionPointers[ instruction-1 ] = compiledOfs; EmitString( "8B 80"); // mov eax, dword ptr [eax + 0x1234567] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; } EmitMovEBXEDI(vm, vm->dataMask); EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_LOAD2: EmitMovEBXEDI(vm, vm->dataMask); EmitString( "0F B7 83" ); // movzx eax, word ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_LOAD1: EmitMovEBXEDI(vm, vm->dataMask); EmitString( "0F B6 83" ); // movzx eax, byte ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_STORE4: EmitMovEAXEDI(vm); EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] // if (pop1 != OP_CALL) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~3 ); // } EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_STORE2: EmitMovEAXEDI(vm); EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~1 ); EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_STORE1: EmitMovEAXEDI(vm); EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask ); EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_EQ: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_NE: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LTI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LEI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GTI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GEI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LTU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LEU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GTU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GEU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_EQF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_NEF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LTF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LEF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GTF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GEF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_NEGI: EmitString( "F7 1F" ); // neg dword ptr [edi] break; case OP_ADD: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "01 47 FC" ); // add dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_SUB: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "29 47 FC" ); // sub dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_BAND: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "21 47 FC" ); // and dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_BOR: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "09 47 FC" ); // or dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_BXOR: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "31 47 FC" ); // xor dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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( "D9 47 FC" ); // fld dword ptr [edi-4] EmitString( "D8 07" ); // fadd dword ptr [edi] EmitString( "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 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: EmitCommand(LAST_COMMAND_SUB_DI_4); // 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: EmitCommand(LAST_COMMAND_SUB_DI_4); // 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: #ifndef FTOL_PTR // WHENHELLISFROZENOVER // not IEEE complient, but simple and fast EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "DB 1F" ); // fistp dword ptr [edi] #else // FTOL_PTR // call the library conversion function EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "FF 15" ); // call ftolPtr Emit4( (int)&ftolPtr ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax #endif break; case OP_SEX8: EmitString( "0F BE 07" ); // movsx eax, byte ptr [edi] EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_SEX16: EmitString( "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 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 EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_JUMP: EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 ); break; } pop0 = pop1; pop1 = op; } } // copy to an exact size buffer on the hunk vm->codeLength = compiledOfs; #ifdef VM_X86_MMAP #if defined(__OpenBSD__) // OpenBSD mmap(2) requires PROT_READ with PROT_WRITE vm->codeBase = mmap(NULL, compiledOfs, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); #else vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); #endif if(vm->codeBase == (void*)-1) Com_Error(ERR_DROP, "VM_CompileX86: can't mmap memory"); #elif _WIN32 // allocate memory with EXECUTE permissions under windows. vm->codeBase = VirtualAlloc(NULL, compiledOfs, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!vm->codeBase) Com_Error(ERR_DROP, "VM_CompileX86: VirtualAlloc failed"); #else vm->codeBase = malloc(compiledOfs); #endif Com_Memcpy( vm->codeBase, buf, compiledOfs ); #ifdef VM_X86_MMAP if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC)) Com_Error(ERR_DROP, "VM_CompileX86: mprotect failed"); #elif _WIN32 { DWORD oldProtect = 0; // remove write permissions. if(!VirtualProtect(vm->codeBase, compiledOfs, PAGE_EXECUTE_READ, &oldProtect)) Com_Error(ERR_DROP, "VM_CompileX86: VirtualProtect failed"); } #endif Z_Free( buf ); Z_Free( jused ); Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs ); vm->destroy = VM_Destroy_Compiled; // offset all the instruction pointers for the new location for ( i = 0 ; i < header->instructionCount ; i++ ) { vm->instructionPointers[i] += (int)vm->codeBase; } }
/* ================= VM_Compile ================= */ void VM_Compile( vm_t *vm, vmHeader_t *header ) { int op; int maxLength; int v; int i; qboolean opt; // allocate a very large temp buffer, we will shrink it later maxLength = header->codeLength * 8; buf = (unsigned char *)Z_Malloc( maxLength, TAG_VM, qtrue ); jused = (unsigned char *)Z_Malloc(header->instructionCount + 2, TAG_VM, qtrue ); Com_Memset(jused, 0, header->instructionCount+2); for(pass=0;pass<2;pass++) { oc0 = -23423; oc1 = -234354; pop0 = -43435; pop1 = -545455; // translate all instructions pc = 0; instruction = 0; code = (byte *)header + header->codeOffset; compiledOfs = 0; LastCommand = LAST_COMMAND_NONE; while ( instruction < header->instructionCount ) { if ( compiledOfs > maxLength - 16 ) { Com_Error( ERR_FATAL, "VM_CompileX86: maxLength exceeded" ); } vm->instructionPointers[ instruction ] = compiledOfs; instruction++; if ( pc > header->codeLength ) { Com_Error( ERR_FATAL, "VM_CompileX86: pc > header->codeLength" ); } op = code[ pc ]; 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: if (code[pc+4] == OP_LOAD4) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); EmitString( "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 (code[pc+4] == OP_LOAD2) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); EmitString( "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 (code[pc+4] == OP_LOAD1) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); EmitString( "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 (code[pc+4] == OP_STORE4) { opt = EmitMovEBXEDI(vm, (vm->dataMask & ~3)); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); // if (!opt) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~3 ); // } EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_STORE4 instruction += 1; break; } if (code[pc+4] == OP_STORE2) { opt = EmitMovEBXEDI(vm, (vm->dataMask & ~1)); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); // if (!opt) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~1 ); // } EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_STORE4 instruction += 1; break; } if (code[pc+4] == OP_STORE1) { opt = EmitMovEBXEDI(vm, vm->dataMask); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); // if (!opt) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask ); // } EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_STORE4 instruction += 1; break; } if (code[pc+4] == OP_ADD) { EmitString( "81 07" ); // add dword ptr [edi], 0x1234567 Emit4( Constant4() ); pc++; // OP_ADD instruction += 1; break; } if (code[pc+4] == OP_SUB) { EmitString( "81 2F" ); // sub dword ptr [edi], 0x1234567 Emit4( Constant4() ); pc++; // OP_ADD instruction += 1; break; } EmitAddEDI4(vm); EmitString( "C7 07" ); // mov dword ptr [edi], 0x12345678 lastConst = Constant4(); Emit4( lastConst ); if (code[pc] == OP_JUMP) { jused[lastConst] = 1; } break; case OP_LOCAL: EmitAddEDI4(vm); EmitString( "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(vm); // mov eax,dword ptr [edi] EmitString( "89 86" ); // mov dword ptr [esi+database],eax // FIXME: range check Emit4( Constant1() + (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // 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: EmitAddEDI4(vm); break; case OP_POP: EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_LEAVE: v = Constant4(); EmitString( "81 C6" ); // add esi, 0x12345678 Emit4( v ); EmitString( "C3" ); // ret break; case OP_LOAD4: if (code[pc] == OP_CONST && code[pc+5] == OP_ADD && code[pc+6] == OP_STORE4) { if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { compiledOfs -= 11; vm->instructionPointers[ instruction-1 ] = compiledOfs; } pc++; // OP_CONST v = Constant4(); EmitMovEBXEDI(vm, vm->dataMask); if (v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "FF 83"); // inc dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); } else { EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitString( "05" ); // add eax, const Emit4( v ); if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } else { EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } } EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 pc++; // OP_ADD pc++; // OP_STORE instruction += 3; break; } if (code[pc] == OP_CONST && code[pc+5] == OP_SUB && code[pc+6] == OP_STORE4) { if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { compiledOfs -= 11; vm->instructionPointers[ instruction-1 ] = compiledOfs; } EmitMovEBXEDI(vm, vm->dataMask); EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); pc++; // OP_CONST v = Constant4(); if (v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "FF 8B"); // dec dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); } else { EmitString( "2D" ); // sub eax, const Emit4( v ); if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL) { EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } else { EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 EmitString( "8B 1F" ); // mov ebx, dword ptr [edi] EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); } } 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; vm->instructionPointers[ instruction-1 ] = compiledOfs; EmitString( "8B 80"); // mov eax, dword ptr [eax + 0x1234567] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; } EmitMovEBXEDI(vm, vm->dataMask); EmitString( "8B 83" ); // mov eax, dword ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_LOAD2: EmitMovEBXEDI(vm, vm->dataMask); EmitString( "0F B7 83" ); // movzx eax, word ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_LOAD1: EmitMovEBXEDI(vm, vm->dataMask); EmitString( "0F B6 83" ); // movzx eax, byte ptr [ebx + 0x12345678] Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_STORE4: EmitMovEAXEDI(vm); EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] // if (pop1 != OP_CALL) { // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~3 ); // } EmitString( "89 83" ); // mov dword ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_STORE2: EmitMovEAXEDI(vm); EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask & ~1 ); EmitString( "66 89 83" ); // mov word ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_STORE1: EmitMovEAXEDI(vm); EmitString( "8B 5F FC" ); // mov ebx, dword ptr [edi-4] // EmitString( "81 E3" ); // and ebx, 0x12345678 // Emit4( vm->dataMask ); EmitString( "88 83" ); // mov byte ptr [ebx+0x12345678], eax Emit4( (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_EQ: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_NE: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LTI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LEI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GTI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GEI: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LTU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LEU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GTU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GEU: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_EQF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_NEF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LTF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_LEF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GTF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_GEF: EmitCommand(LAST_COMMAND_SUB_DI_8); // 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] v = Constant4(); jused[v] = 1; Emit4( (int)vm->instructionPointers + v*4 ); break; case OP_NEGI: EmitString( "F7 1F" ); // neg dword ptr [edi] break; case OP_ADD: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "01 47 FC" ); // add dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_SUB: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "29 47 FC" ); // sub dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_BAND: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "21 47 FC" ); // and dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_BOR: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "09 47 FC" ); // or dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_BXOR: EmitMovEAXEDI(vm); // mov eax, dword ptr [edi] EmitString( "31 47 FC" ); // xor dword ptr [edi-4],eax EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 EmitCommand(LAST_COMMAND_SUB_DI_4); // 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( "D9 47 FC" ); // fld dword ptr [edi-4] EmitString( "D8 07" ); // fadd dword ptr [edi] EmitString( "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 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: EmitCommand(LAST_COMMAND_SUB_DI_4); // 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: EmitCommand(LAST_COMMAND_SUB_DI_4); // 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: #ifndef FTOL_PTR // WHENHELLISFROZENOVER // bk001213 - was used in 1.17 // not IEEE complient, but simple and fast EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "DB 1F" ); // fistp dword ptr [edi] #else // FTOL_PTR // call the library conversion function EmitString( "D9 07" ); // fld dword ptr [edi] EmitString( "FF 15" ); // call ftolPtr Emit4( (int)&ftolPtr ); EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax #endif break; case OP_SEX8: EmitString( "0F BE 07" ); // movsx eax, byte ptr [edi] EmitCommand(LAST_COMMAND_MOV_EDI_EAX); // mov dword ptr [edi], eax break; case OP_SEX16: EmitString( "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 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 EmitCommand(LAST_COMMAND_SUB_DI_8); // sub edi, 8 break; case OP_JUMP: EmitCommand(LAST_COMMAND_SUB_DI_4); // 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 ); } pop0 = pop1; pop1 = op; } } // copy to an exact size buffer on the hunk vm->codeLength = compiledOfs; vm->codeBase = (unsigned char *)Hunk_Alloc( compiledOfs, h_low ); Com_Memcpy( vm->codeBase, buf, compiledOfs ); Z_Free( buf ); Z_Free( jused ); 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_Compile ================= */ void VM_Compile( vm_t *vm, vmHeader_t *header ) { int op; int maxLength; int v; int i; // set up the into-to-float variables ((int *)itofConvert)[0] = 0x43300000; ((int *)itofConvert)[1] = 0x80000000; ((int *)itofConvert)[2] = 0x43300000; // allocate a very large temp buffer, we will shrink it later maxLength = header->codeLength * 8; buf = Z_Malloc( maxLength ); jused = Z_Malloc(header->instructionCount + 2); Com_Memset(jused, 0, header->instructionCount+2); // compile everything twice, so the second pass will have valid instruction // pointers for branches for ( pass = -1 ; pass < 2 ; pass++ ) { rtopped = qfalse; // translate all instructions pc = 0; pop0 = 343545; pop1 = 2443545; oc0 = -2343535; oc1 = 24353454; tvm = vm; code = (byte *)header + header->codeOffset; compiledOfs = 0; #ifndef __GNUC__ // metrowerks seems to require this header in front of functions Emit4( (int)(buf+2) ); Emit4( 0 ); #endif for ( instruction = 0 ; instruction < header->instructionCount ; instruction++ ) { if ( compiledOfs*4 > maxLength - 16 ) { Com_Error( ERR_DROP, "VM_Compile: maxLength exceeded" ); } op = code[ pc ]; if ( !pass ) { vm->instructionPointers[ instruction ] = compiledOfs * 4; } pc++; switch ( op ) { case 0: break; case OP_BREAK: InstImmU( PPC_ADDI, R_TOP, 0, 0 ); InstImm( PPC_LWZ, R_TOP, R_TOP, 0 ); // *(int *)0 to crash to debugger rtopped = qfalse; break; case OP_ENTER: InstImm( PPC_ADDI, R_STACK, R_STACK, -Constant4() ); // sub R_STACK, R_STACK, imm rtopped = qfalse; break; case OP_CONST: v = Constant4(); if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { v &= vm->dataMask; } if ( v < 32768 && v >= -32768 ) { InstImmU( PPC_ADDI, R_TOP, 0, v & 0xffff ); } else { InstImmU( PPC_ADDIS, R_TOP, 0, (v >> 16)&0xffff ); if ( v & 0xffff ) { InstImmU( PPC_ORI, R_TOP, R_TOP, v & 0xffff ); } } if (code[pc] == OP_LOAD4) { Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base pc++; instruction++; } else if (code[pc] == OP_LOAD2) { Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base pc++; instruction++; } else if (code[pc] == OP_LOAD1) { Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base pc++; instruction++; } if (code[pc] == OP_STORE4) { InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base pc++; instruction++; rtopped = qfalse; break; } else if (code[pc] == OP_STORE2) { InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base pc++; instruction++; rtopped = qfalse; break; } else if (code[pc] == OP_STORE1) { InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base pc++; instruction++; rtopped = qfalse; break; } if (code[pc] == OP_JUMP) { jused[v] = 1; } InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 ); rtopped = qtrue; break; case OP_LOCAL: oc0 = oc1; oc1 = Constant4(); if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { oc1 &= vm->dataMask; } InstImm( PPC_ADDI, R_TOP, R_STACK, oc1 ); if (code[pc] == OP_LOAD4) { Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base pc++; instruction++; } else if (code[pc] == OP_LOAD2) { Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base pc++; instruction++; } else if (code[pc] == OP_LOAD1) { Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base pc++; instruction++; } if (code[pc] == OP_STORE4) { InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base pc++; instruction++; rtopped = qfalse; break; } else if (code[pc] == OP_STORE2) { InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base pc++; instruction++; rtopped = qfalse; break; } else if (code[pc] == OP_STORE1) { InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base pc++; instruction++; rtopped = qfalse; break; } InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 ); rtopped = qtrue; break; case OP_ARG: ltop(); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); InstImm( PPC_ADDI, R_EA, R_STACK, Constant1() ); // location to put it Inst( PPC_STWX, R_TOP, R_EA, R_MEMBASE ); rtopped = qfalse; break; case OP_CALL: Inst( PPC_MFSPR, R_SECOND, 8, 0 ); // move from link register InstImm( PPC_STWU, R_SECOND, R_REAL_STACK, -16 ); // save off the old return address Inst( PPC_MTSPR, R_ASMCALL, 9, 0 ); // move to count register Inst( PPC_BCCTR | 1, 20, 0, 0 ); // jump and link to the count register InstImm( PPC_LWZ, R_SECOND, R_REAL_STACK, 0 ); // fetch the old return address InstImm( PPC_ADDI, R_REAL_STACK, R_REAL_STACK, 16 ); Inst( PPC_MTSPR, R_SECOND, 8, 0 ); // move to link register rtopped = qfalse; break; case OP_PUSH: InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, 4 ); rtopped = qfalse; break; case OP_POP: InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); rtopped = qfalse; break; case OP_LEAVE: InstImm( PPC_ADDI, R_STACK, R_STACK, Constant4() ); // add R_STACK, R_STACK, imm Inst( PPC_BCLR, 20, 0, 0 ); // branch unconditionally to link register rtopped = qfalse; break; case OP_LOAD4: ltop(); // get value from opstack //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); rtopped = qtrue; break; case OP_LOAD2: ltop(); // get value from opstack //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); rtopped = qtrue; break; case OP_LOAD1: ltop(); // get value from opstack //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); rtopped = qtrue; break; case OP_STORE4: ltopandsecond(); // get value from opstack //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base rtopped = qfalse; break; case OP_STORE2: ltopandsecond(); // get value from opstack //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base rtopped = qfalse; break; case OP_STORE1: ltopandsecond(); // get value from opstack //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base rtopped = qfalse; break; case OP_EQ: ltopandsecond(); // get value from opstack Inst( PPC_CMP, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; InstImm( PPC_BC, 4, 2, 8 ); if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } Emit4(PPC_B | (v&0x3ffffff) ); rtopped = qfalse; break; case OP_NE: ltopandsecond(); // get value from opstack Inst( PPC_CMP, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; InstImm( PPC_BC, 12, 2, 8 ); if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); // InstImm( PPC_BC, 4, 2, v ); rtopped = qfalse; break; case OP_LTI: ltopandsecond(); // get value from opstack Inst( PPC_CMP, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; InstImm( PPC_BC, 4, 0, 8 ); if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); // InstImm( PPC_BC, 12, 0, v ); rtopped = qfalse; break; case OP_LEI: ltopandsecond(); // get value from opstack Inst( PPC_CMP, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; InstImm( PPC_BC, 12, 1, 8 ); if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); // InstImm( PPC_BC, 4, 1, v ); rtopped = qfalse; break; case OP_GTI: ltopandsecond(); // get value from opstack Inst( PPC_CMP, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; InstImm( PPC_BC, 4, 1, 8 ); if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); // InstImm( PPC_BC, 12, 1, v ); rtopped = qfalse; break; case OP_GEI: ltopandsecond(); // get value from opstack Inst( PPC_CMP, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 4, 0, v ); rtopped = qfalse; break; case OP_LTU: ltopandsecond(); // get value from opstack Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 12, 0, v ); rtopped = qfalse; break; case OP_LEU: ltopandsecond(); // get value from opstack Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 4, 1, v ); rtopped = qfalse; break; case OP_GTU: ltopandsecond(); // get value from opstack Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 12, 1, v ); rtopped = qfalse; break; case OP_GEU: ltopandsecond(); // get value from opstack Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 4, 0, v ); rtopped = qfalse; break; case OP_EQF: fltopandsecond(); // get value from opstack Inst( PPC_FCMPU, 0, R_TOP, R_SECOND ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 12, 2, v ); rtopped = qfalse; break; case OP_NEF: fltopandsecond(); // get value from opstack Inst( PPC_FCMPU, 0, R_TOP, R_SECOND ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 4, 2, v ); rtopped = qfalse; break; case OP_LTF: fltopandsecond(); // get value from opstack Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 12, 0, v ); rtopped = qfalse; break; case OP_LEF: fltopandsecond(); // get value from opstack Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 4, 1, v ); rtopped = qfalse; break; case OP_GTF: fltopandsecond(); // get value from opstack Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 12, 1, v ); rtopped = qfalse; break; case OP_GEF: fltopandsecond(); // get value from opstack Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); i = Constant4(); jused[i] = 1; if ( pass==1 ) { v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; } else { v = 0; } InstImm( PPC_BC, 4, 0, v ); rtopped = qfalse; break; case OP_NEGI: ltop(); // get value from opstack InstImm( PPC_SUBFIC, R_TOP, R_TOP, 0 ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_ADD: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_ADD, R_TOP, R_TOP, R_SECOND ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_SUB: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_SUBF, R_TOP, R_TOP, R_SECOND ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_DIVI: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_DIVW, R_TOP, R_SECOND, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_DIVU: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_DIVWU, R_TOP, R_SECOND, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_MODI: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_DIVW, R_EA, R_SECOND, R_TOP ); Inst( PPC_MULLW, R_EA, R_TOP, R_EA ); Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_MODU: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_DIVWU, R_EA, R_SECOND, R_TOP ); Inst( PPC_MULLW, R_EA, R_TOP, R_EA ); Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_MULI: case OP_MULU: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_MULLW, R_TOP, R_SECOND, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_BAND: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_AND, R_SECOND, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_BOR: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_OR, R_SECOND, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_BXOR: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_XOR, R_SECOND, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_BCOM: ltop(); // get value from opstack Inst( PPC_NOR, R_TOP, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_LSH: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_SLW, R_SECOND, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_RSHI: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_SRAW, R_SECOND, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_RSHU: ltop(); // get value from opstack InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_SRW, R_SECOND, R_TOP, R_TOP ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qtrue; break; case OP_NEGF: InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack Inst( PPC_FNEG, R_TOP, 0, R_TOP ); InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qfalse; break; case OP_ADDF: InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_FADDS, R_TOP, R_SECOND, R_TOP ); InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qfalse; break; case OP_SUBF: InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_FSUBS, R_TOP, R_SECOND, R_TOP ); InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qfalse; break; case OP_DIVF: InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst( PPC_FDIVS, R_TOP, R_SECOND, R_TOP ); InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qfalse; break; case OP_MULF: InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack Inst4( PPC_FMULS, R_TOP, R_SECOND, 0, R_TOP ); InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qfalse; break; case OP_CVIF: v = (int)&itofConvert; InstImmU( PPC_ADDIS, R_EA, 0, (v >> 16)&0xffff ); InstImmU( PPC_ORI, R_EA, R_EA, v & 0xffff ); InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 ); // get value from opstack InstImmU( PPC_XORIS, R_TOP, R_TOP, 0x8000 ); InstImm( PPC_STW, R_TOP, R_EA, 12 ); InstImm( PPC_LFD, R_TOP, R_EA, 0 ); InstImm( PPC_LFD, R_SECOND, R_EA, 8 ); Inst( PPC_FSUB, R_TOP, R_SECOND, R_TOP ); // Inst( PPC_FRSP, R_TOP, 0, R_TOP ); InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack rtopped = qfalse; break; case OP_CVFI: InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack Inst( PPC_FCTIWZ, R_TOP, 0, R_TOP ); Inst( PPC_STFIWX, R_TOP, 0, R_OPSTACK ); // save value to opstack rtopped = qfalse; break; case OP_SEX8: ltop(); // get value from opstack Inst( PPC_EXTSB, R_TOP, R_TOP, 0 ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); rtopped = qtrue; break; case OP_SEX16: ltop(); // get value from opstack Inst( PPC_EXTSH, R_TOP, R_TOP, 0 ); InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); rtopped = qtrue; break; case OP_BLOCK_COPY: v = Constant4() >> 2; ltop(); // source InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // dest InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); InstImmU( PPC_ADDI, R_EA, 0, v ); // count // FIXME: range check Inst( PPC_MTSPR, R_EA, 9, 0 ); // move to count register Inst( PPC_ADD, R_TOP, R_TOP, R_MEMBASE ); InstImm( PPC_ADDI, R_TOP, R_TOP, -4 ); Inst( PPC_ADD, R_SECOND, R_SECOND, R_MEMBASE ); InstImm( PPC_ADDI, R_SECOND, R_SECOND, -4 ); InstImm( PPC_LWZU, R_EA, R_TOP, 4 ); // source InstImm( PPC_STWU, R_EA, R_SECOND, 4 ); // dest Inst( PPC_BC | 0xfff8 , 16, 0, 0 ); // loop rtopped = qfalse; break; case OP_JUMP: ltop(); // get value from opstack InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); Inst( PPC_RLWINM | ( 29 << 1 ), R_TOP, R_TOP, 2 ); // FIXME: range check Inst( PPC_LWZX, R_TOP, R_TOP, R_INSTRUCTIONS ); Inst( PPC_MTSPR, R_TOP, 9, 0 ); // move to count register Inst( PPC_BCCTR, 20, 0, 0 ); // jump to the count register rtopped = qfalse; break; default: Com_Error( ERR_DROP, "VM_CompilePPC: bad opcode %i at instruction %i, offset %i", op, instruction, pc ); } pop0 = pop1; pop1 = op; } Com_Printf( "VM file %s pass %d compiled to %i bytes of code\n", vm->name, (pass+1), compiledOfs*4 ); if ( pass == 0 ) { // copy to an exact size buffer on the hunk vm->codeLength = compiledOfs * 4; vm->codeBase = Hunk_Alloc( vm->codeLength, h_low ); Com_Memcpy( vm->codeBase, buf, vm->codeLength ); Z_Free( buf ); // offset all the instruction pointers for the new location for ( i = 0 ; i < header->instructionCount ; i++ ) { vm->instructionPointers[i] += (int)vm->codeBase; } // go back over it in place now to fixup reletive jump targets buf = (unsigned *)vm->codeBase; } } Z_Free( jused ); }