Пример #1
0
/*
=================
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 = 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) {
				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
			// 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;
#ifdef VM_X86_MMAP
	vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
	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;
	}
}
Пример #2
0
/*
=================
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 );
}