#include "CodeGenerator.hpp"

#include "Error.hpp"

#include <stdio.h>

namespace SoftWire
{
	bool CodeGenerator::emulateSSE = false;
	bool CodeGenerator::copyPropagation = true;
	bool CodeGenerator::dropUnmodified = true;
	bool CodeGenerator::spillUnrelocated = false;

	CodeGenerator::CodeGenerator()
	{
	}

	CodeGenerator::~CodeGenerator()
	{
	}

	const OperandREG8 CodeGenerator::r8(const OperandREF &ref, bool copy)
	{
		OperandREG32 reg = r32(ref, copy);

		// Make sure we only have al, cl, dl or bl
		if(reg.reg == Encoding::ESI ||
		   reg.reg == Encoding::EDI)
		{
			spill(reg);

			// Need to spill one of al, cl, dl or bl
			Encoding::Reg candidate = Encoding::REG_UNKNOWN;
			unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

			if(EAX.priority < priority && real(EAX.reference)) {priority = EAX.priority; candidate = Encoding::EAX;}
			if(ECX.priority < priority && real(ECX.reference)) {priority = ECX.priority; candidate = Encoding::ECX;}
			if(EDX.priority < priority && real(EDX.reference)) {priority = EDX.priority; candidate = Encoding::EDX;}
			if(EBX.priority < priority && real(EBX.reference)) {priority = EBX.priority; candidate = Encoding::EBX;}

			switch(candidate)
			{
			case Encoding::REG_UNKNOWN: throw Error("Out of physical general purpose registers. Use free().");
			case Encoding::EAX: spill(eax); return (OperandREG8)assign(eax, ref, copy);
			case Encoding::ECX: spill(ecx); return (OperandREG8)assign(ecx, ref, copy);
			case Encoding::EDX: spill(edx); return (OperandREG8)assign(edx, ref, copy);
			case Encoding::EBX: spill(ebx); return (OperandREG8)assign(ebx, ref, copy);
			default: throw INTERNAL_ERROR;
			}
		}

		return (OperandREG8)reg;
	}

	const OperandREG8 CodeGenerator::x8(const OperandREF &ref, bool copy)
	{
		free(ref);
		return r8(ref, copy);
	}

	const OperandREG8 CodeGenerator::t8(int i)
	{
		return x8((OperandREF)i);
	}

	const OperandR_M8 CodeGenerator::m8(const OperandREF &ref)
	{
		return (OperandR_M8)m32(ref);
	}

	const OperandREG16 CodeGenerator::r16(const OperandREF &ref, bool copy)
	{
		return (OperandREG16)r32(ref, copy);
	}

	const OperandREG16 CodeGenerator::x16(const OperandREF &ref, bool copy)
	{
		return (OperandREG16)x32(ref, copy);
	}

	const OperandREG16 CodeGenerator::t16(int i)
	{
		return (OperandREG16)t32(i);
	}

	const OperandR_M16 CodeGenerator::m16(const OperandREF &ref)
	{
		return (OperandR_M16)m32(ref);
	}

	const OperandREG32 &CodeGenerator::r32(const OperandREF &ref, bool copy)
	{
		if(ref == 0 && copy) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(EAX.reference == ref) {if(EAX.copy) EAX.copy->retain(); EAX.copy = 0; return access(eax);}
		else if(ECX.reference == ref) {if(ECX.copy) ECX.copy->retain(); ECX.copy = 0; return access(ecx);}
		else if(EDX.reference == ref) {if(EDX.copy) EDX.copy->retain(); EDX.copy = 0; return access(edx);}
		else if(EBX.reference == ref) {if(EBX.copy) EBX.copy->retain(); EBX.copy = 0; return access(ebx);}
		else if(ESI.reference == ref) {if(ESI.copy) ESI.copy->retain(); ESI.copy = 0; return access(esi);}
		else if(EDI.reference == ref) {if(EDI.copy) EDI.copy->retain(); EDI.copy = 0; return access(edi);}

		// Search for free registers
		     if(EAX.reference == 0 && EAX.priority == 0) return assign(eax, ref, copy);
		else if(ECX.reference == 0 && ECX.priority == 0) return assign(ecx, ref, copy);
		else if(EDX.reference == 0 && EDX.priority == 0) return assign(edx, ref, copy);
		else if(EBX.reference == 0 && EBX.priority == 0) return assign(ebx, ref, copy);
		else if(ESI.reference == 0 && ESI.priority == 0) return assign(esi, ref, copy);
		else if(EDI.reference == 0 && EDI.priority == 0) return assign(edi, ref, copy);

		// Need to spill one
		Encoding::Reg candidate = Encoding::REG_UNKNOWN;
		unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

		if(EAX.priority < priority && real(EAX.reference)) {priority = EAX.priority; candidate = Encoding::EAX;}
		if(ECX.priority < priority && real(ECX.reference)) {priority = ECX.priority; candidate = Encoding::ECX;}
		if(EDX.priority < priority && real(EDX.reference)) {priority = EDX.priority; candidate = Encoding::EDX;}
		if(EBX.priority < priority && real(EBX.reference)) {priority = EBX.priority; candidate = Encoding::EBX;}
		if(ESI.priority < priority && real(ESI.reference)) {priority = ESI.priority; candidate = Encoding::ESI;}
		if(EDI.priority < priority && real(EDI.reference)) {priority = EDI.priority; candidate = Encoding::EDI;}

		switch(candidate)
		{
		case Encoding::REG_UNKNOWN: throw Error("Out of physical general purpose registers. Use free().");
		case Encoding::EAX: spill(eax); return assign(eax, ref, copy);
		case Encoding::ECX: spill(ecx); return assign(ecx, ref, copy);
		case Encoding::EDX: spill(edx); return assign(edx, ref, copy);
		case Encoding::EBX: spill(ebx); return assign(ebx, ref, copy);
		case Encoding::ESI: spill(esi); return assign(esi, ref, copy);
		case Encoding::EDI: spill(edi); return assign(edi, ref, copy);
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandREG32 &CodeGenerator::x32(const OperandREF &ref, bool copy)
	{
		free(ref);
		return r32(ref, copy);
	}

	const OperandREG32 &CodeGenerator::t32(int i)
	{
		if(i < 0 || i >= 6) throw Error("Register allocator t32 index out of range");

		return x32((OperandREF)i);
	}

	const OperandR_M32 CodeGenerator::m32(const OperandREF &ref)
	{
		if(ref == 0) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(EAX.reference == ref) return access(eax);
		else if(ECX.reference == ref) return access(ecx);
		else if(EDX.reference == ref) return access(edx);
		else if(EBX.reference == ref) return access(ebx);
		else if(ESI.reference == ref) return access(esi);
		else if(EDI.reference == ref) return access(edi);

		return (OperandR_M32)dword_ptr [ref];
	}

	const OperandREG32 &CodeGenerator::allocate(const OperandREG32 &reg, const OperandREF &ref, bool copy)
	{
		return assign(reg, ref, copy);
	}

	const OperandREG32 &CodeGenerator::assign(const OperandREG32 &reg, const OperandREF &ref, bool copy)
	{
		switch(reg.reg)
		{
		case Encoding::EAX:
			if(EAX.reference != 0) throw Error("eax not available for register allocation");
			EAX.reference = ref;
			if(copy && real(ref)) mov(eax, dword_ptr [ref]);
			EAX.modified = false;
			return access(eax);
		case Encoding::ECX:
			if(ECX.reference != 0) throw Error("ecx not available for register allocation");
			ECX.reference = ref;
			if(copy && real(ref)) mov(ecx, dword_ptr [ref]);
			ECX.modified = false;
			return access(ecx);
		case Encoding::EDX:
			if(EDX.reference != 0) throw Error("edx not available for register allocation");
			EDX.reference = ref;
			if(copy && real(ref)) mov(edx, dword_ptr [ref]);
			EDX.modified = false;
			return access(edx);
		case Encoding::EBX:
			if(EBX.reference != 0) throw Error("ebx not available for register allocation");
			EBX.reference = ref;
			if(copy && real(ref)) mov(ebx, dword_ptr [ref]);
			EBX.modified = false;
			return access(ebx);
		case Encoding::ESI:
			if(ESI.reference != 0) throw Error("esi not available for register allocation");
			ESI.reference = ref;
			if(copy && real(ref)) mov(esi, dword_ptr [ref]);
			ESI.modified = false;
			return access(esi);
		case Encoding::EDI:
			if(EDI.reference != 0) throw Error("edi not available for register allocation");
			EDI.reference = ref;
			if(copy && real(ref)) mov(edi, dword_ptr [ref]);
			EDI.modified = false;
			return access(edi);
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandREG32 &CodeGenerator::access(const OperandREG32 &reg)
	{
		// Decrease priority of other registers
		if(reg.reg != Encoding::EAX && EAX.priority) EAX.priority--;
		if(reg.reg != Encoding::ECX && ECX.priority) ECX.priority--;
		if(reg.reg != Encoding::EDX && EDX.priority) EDX.priority--;
		if(reg.reg != Encoding::EBX && EBX.priority) EBX.priority--;
		if(reg.reg != Encoding::ESI && ESI.priority) ESI.priority--;
		if(reg.reg != Encoding::EDI && EDI.priority) EDI.priority--;

		// Give highest priority
		     if(reg.reg == Encoding::EAX) EAX.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::ECX) ECX.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::EDX) EDX.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::EBX) EBX.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::ESI) ESI.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::EDI) EDI.priority = 0xFFFFFFFF;

		return reg;
	}

	void CodeGenerator::free(const OperandREG32 &reg)
	{
		switch(reg.reg)
		{
		case Encoding::EAX: EAX.free(); break;
		case Encoding::ECX: ECX.free(); break;
		case Encoding::EDX: EDX.free(); break;
		case Encoding::EBX: EBX.free(); break;
		case Encoding::ESI: ESI.free(); break;
		case Encoding::EDI: EDI.free(); break;
		default: throw INTERNAL_ERROR;
		}
	}

	void CodeGenerator::spill(const OperandREG32 &reg)
	{
		switch(reg.reg)
		{
		case Encoding::EAX:
			if(!EAX.modified && dropUnmodified) break;
			if(EAX.copy) EAX.copy->retain();
			if(real(EAX.reference)) mov(dword_ptr [EAX.reference], eax);
			break;
		case Encoding::ECX:
			if(!ECX.modified && dropUnmodified) break;
			if(ECX.copy) ECX.copy->retain();
			if(real(ECX.reference)) mov(dword_ptr [ECX.reference], ecx);
			break;
		case Encoding::EDX:
			if(!EDX.modified && dropUnmodified) break;
			if(EDX.copy) EDX.copy->retain();
			if(real(EDX.reference)) mov(dword_ptr [EDX.reference], edx);
			break;
		case Encoding::EBX:
			if(!EBX.modified && dropUnmodified) break;
			if(EBX.copy) EBX.copy->retain();
			if(real(EBX.reference)) mov(dword_ptr [EBX.reference], ebx);
			break;
		case Encoding::ESI:
			if(!ESI.modified && dropUnmodified) break;
			if(ESI.copy) ESI.copy->retain();
			if(real(ESI.reference)) mov(dword_ptr [ESI.reference], esi);
			break;
		case Encoding::EDI:
			if(!EDI.modified && dropUnmodified) break;
			if(EDI.copy) EDI.copy->retain();
			if(real(EDI.reference)) mov(dword_ptr [EDI.reference], edi);
			break;
		default: throw INTERNAL_ERROR;
		}

		free(reg);
	}

	const OperandMMREG &CodeGenerator::r64(const OperandREF &ref, bool copy)
	{
		if(ref == 0 && copy) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(MM0.reference == ref) {if(MM0.copy) MM0.copy->retain(); MM0.copy = 0; return access(mm0);}
		else if(MM1.reference == ref) {if(MM1.copy) MM1.copy->retain(); MM1.copy = 0; return access(mm1);}
		else if(MM2.reference == ref) {if(MM2.copy) MM2.copy->retain(); MM2.copy = 0; return access(mm2);}
		else if(MM3.reference == ref) {if(MM3.copy) MM3.copy->retain(); MM3.copy = 0; return access(mm3);}
		else if(MM4.reference == ref) {if(MM4.copy) MM4.copy->retain(); MM4.copy = 0; return access(mm4);}
		else if(MM5.reference == ref) {if(MM5.copy) MM5.copy->retain(); MM5.copy = 0; return access(mm5);}
		else if(MM6.reference == ref) {if(MM6.copy) MM6.copy->retain(); MM6.copy = 0; return access(mm6);}
		else if(MM7.reference == ref) {if(MM7.copy) MM7.copy->retain(); MM7.copy = 0; return access(mm7);}

		// Search for free registers
		     if(MM0.reference == 0 && MM0.priority == 0) return assign(mm0, ref, copy);
		else if(MM1.reference == 0 && MM1.priority == 0) return assign(mm1, ref, copy);
		else if(MM2.reference == 0 && MM2.priority == 0) return assign(mm2, ref, copy);
		else if(MM3.reference == 0 && MM3.priority == 0) return assign(mm3, ref, copy);
		else if(MM4.reference == 0 && MM4.priority == 0) return assign(mm4, ref, copy);
		else if(MM5.reference == 0 && MM5.priority == 0) return assign(mm5, ref, copy);
		else if(MM6.reference == 0 && MM6.priority == 0) return assign(mm6, ref, copy);
		else if(MM7.reference == 0 && MM7.priority == 0) return assign(mm7, ref, copy);

		// Need to spill one
		Encoding::Reg candidate = Encoding::REG_UNKNOWN;
		unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

		if(MM0.priority < priority && real(MM0.reference)) {priority = MM0.priority; candidate = Encoding::MM0;}
		if(MM1.priority < priority && real(MM1.reference)) {priority = MM1.priority; candidate = Encoding::MM1;}
		if(MM2.priority < priority && real(MM2.reference)) {priority = MM2.priority; candidate = Encoding::MM2;}
		if(MM3.priority < priority && real(MM3.reference)) {priority = MM3.priority; candidate = Encoding::MM3;}
		if(MM4.priority < priority && real(MM4.reference)) {priority = MM4.priority; candidate = Encoding::MM4;}
		if(MM5.priority < priority && real(MM5.reference)) {priority = MM5.priority; candidate = Encoding::MM5;}
		if(MM6.priority < priority && real(MM6.reference)) {priority = MM6.priority; candidate = Encoding::MM6;}
		if(MM7.priority < priority && real(MM7.reference)) {priority = MM7.priority; candidate = Encoding::MM7;}

		switch(candidate)
		{
		case Encoding::REG_UNKNOWN: throw Error("Out of physical MMX registers. Use free().");
		case Encoding::MM0: spill(mm0); return assign(mm0, ref, copy);
		case Encoding::MM1: spill(mm1); return assign(mm1, ref, copy);
		case Encoding::MM2: spill(mm2); return assign(mm2, ref, copy);
		case Encoding::MM3: spill(mm3); return assign(mm3, ref, copy);
		case Encoding::MM4: spill(mm4); return assign(mm4, ref, copy);
		case Encoding::MM5: spill(mm5); return assign(mm5, ref, copy);
		case Encoding::MM6: spill(mm6); return assign(mm6, ref, copy);
		case Encoding::MM7: spill(mm7); return assign(mm7, ref, copy);
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandMMREG &CodeGenerator::x64(const OperandREF &ref, bool copy)
	{
		free(ref);
		return r64(ref, copy);
	}

	const OperandMMREG &CodeGenerator::t64(int i)
	{
		if(i < 0 || i >= 8) throw Error("Register allocator t64 index out of range");

		return x64((OperandREF)i);
	}

	const OperandR_M64 CodeGenerator::m64(const OperandREF &ref)
	{
		if(ref == 0) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(MM0.reference == ref) return access(mm0);
		else if(MM1.reference == ref) return access(mm1);
		else if(MM2.reference == ref) return access(mm2);
		else if(MM3.reference == ref) return access(mm3);
		else if(MM4.reference == ref) return access(mm4);
		else if(MM5.reference == ref) return access(mm5);
		else if(MM6.reference == ref) return access(mm6);
		else if(MM7.reference == ref) return access(mm7);

		return (OperandR_M64)qword_ptr [ref];
	}

	const OperandMMREG &CodeGenerator::allocate(const OperandMMREG &reg, const OperandREF &ref, bool copy)
	{
		return assign(reg, ref, copy);
	}

	const OperandMMREG &CodeGenerator::assign(const OperandMMREG &reg, const OperandREF &ref, bool copy)
	{
		switch(reg.reg)
		{
		case Encoding::MM0:
			if(MM0.reference != 0) throw Error("mm0 not available for register allocation");
			MM0.reference = ref;
			if(copy && real(ref)) movq(mm0, qword_ptr [ref]);
			MM0.modified = false;
			return access(mm0);
		case Encoding::MM1:
			if(MM1.reference != 0) throw Error("mm1 not available for register allocation");
			MM1.reference = ref;
			if(copy && real(ref)) movq(mm1, qword_ptr [ref]);
			MM1.modified = false;
			return access(mm1);
		case Encoding::MM2:
			if(MM2.reference != 0) throw Error("mm2 not available for register allocation");
			MM2.reference = ref;
			if(copy && real(ref)) movq(mm2, qword_ptr [ref]);
			MM2.modified = false;
			return access(mm2);
		case Encoding::MM3:
			if(MM3.reference != 0) throw Error("mm3 not available for register allocation");
			MM3.reference = ref;
			if(copy && real(ref)) movq(mm3, qword_ptr [ref]);
			MM3.modified = false;
			return access(mm3);
		case Encoding::MM4:
			if(MM4.reference != 0) throw Error("mm4 not available for register allocation");
			MM4.reference = ref;
			if(copy && real(ref)) movq(mm4, qword_ptr [ref]);
			MM4.modified = false;
			return access(mm4);
		case Encoding::MM5:
			if(MM5.reference != 0) throw Error("mm5 not available for register allocation");
			MM5.reference = ref;
			if(copy && real(ref)) movq(mm5, qword_ptr [ref]);
			MM5.modified = false;
			return access(mm5);
		case Encoding::MM6:
			if(MM6.reference != 0) throw Error("mm6 not available for register allocation");
			MM6.reference = ref;
			if(copy && real(ref)) movq(mm6, qword_ptr [ref]);
			MM6.modified = false;
			return access(mm6);
		case Encoding::MM7:
			if(MM7.reference != 0) throw Error("mm7 not available for register allocation");
			MM7.reference = ref;
			if(copy && real(ref)) movq(mm7, qword_ptr [ref]);
			MM7.modified = false;
			return access(mm7);
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandMMREG &CodeGenerator::access(const OperandMMREG &reg)
	{
		// Decrease priority of other registers
		if(reg.reg != Encoding::MM0 && MM0.priority) MM0.priority--;
		if(reg.reg != Encoding::MM1 && MM1.priority) MM1.priority--;
		if(reg.reg != Encoding::MM2 && MM2.priority) MM2.priority--;
		if(reg.reg != Encoding::MM3 && MM3.priority) MM3.priority--;
		if(reg.reg != Encoding::MM4 && MM4.priority) MM4.priority--;
		if(reg.reg != Encoding::MM5 && MM5.priority) MM5.priority--;
		if(reg.reg != Encoding::MM6 && MM6.priority) MM6.priority--;
		if(reg.reg != Encoding::MM7 && MM7.priority) MM7.priority--;

		// Give highest priority
		     if(reg.reg == Encoding::MM0) MM0.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM1) MM1.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM2) MM2.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM3) MM3.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM4) MM4.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM5) MM5.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM6) MM6.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::MM7) MM7.priority = 0xFFFFFFFF;

		return reg;
	}

	void CodeGenerator::free(const OperandMMREG &reg)
	{
		bool free = false;

		switch(reg.reg)
		{
		case Encoding::MM0: free = (MM0.priority != 0); MM0.free(); break;
		case Encoding::MM1: free = (MM1.priority != 0); MM1.free(); break;
		case Encoding::MM2: free = (MM2.priority != 0); MM2.free(); break;
		case Encoding::MM3: free = (MM3.priority != 0); MM3.free(); break;
		case Encoding::MM4: free = (MM4.priority != 0); MM4.free(); break;
		case Encoding::MM5: free = (MM5.priority != 0); MM5.free(); break;
		case Encoding::MM6: free = (MM6.priority != 0); MM6.free(); break;
		case Encoding::MM7: free = (MM7.priority != 0); MM7.free(); break;
		default: throw INTERNAL_ERROR;
		}

		if(emulateSSE && free)
		{
			if(!MM0.priority &&
			   !MM1.priority &&
			   !MM2.priority &&
			   !MM3.priority &&
			   !MM4.priority &&
			   !MM5.priority &&
			   !MM6.priority &&
			   !MM7.priority)
			{
				emms();
			}
		}
	}

	void CodeGenerator::spill(const OperandMMREG &reg)
	{
		switch(reg.reg)
		{
		case Encoding::MM0:
			if(!MM0.modified && dropUnmodified) break;
			if(MM0.copy) MM0.copy->retain(); 
			if(real(MM0.reference)) movq(qword_ptr [MM0.reference], mm0);
			break;
		case Encoding::MM1:
			if(!MM1.modified && dropUnmodified) break;
			if(MM1.copy) MM1.copy->retain();
			if(real(MM1.reference)) movq(qword_ptr [MM1.reference], mm1);
			break;
		case Encoding::MM2:
			if(!MM2.modified && dropUnmodified) break;
			if(MM2.copy) MM2.copy->retain();
			if(real(MM2.reference)) movq(qword_ptr [MM2.reference], mm2);
			break;
		case Encoding::MM3:
			if(!MM3.modified && dropUnmodified) break;
			if(MM3.copy) MM3.copy->retain();
			if(real(MM3.reference)) movq(qword_ptr [MM3.reference], mm3);
			break;
		case Encoding::MM4:
			if(!MM4.modified && dropUnmodified) break;
			if(MM4.copy) MM4.copy->retain();
			if(real(MM4.reference)) movq(qword_ptr [MM4.reference], mm4);
			break;
		case Encoding::MM5:
			if(!MM5.modified && dropUnmodified) break;
			if(MM5.copy) MM5.copy->retain();
			if(real(MM5.reference)) movq(qword_ptr [MM5.reference], mm5);
			break;
		case Encoding::MM6:
			if(!MM6.modified && dropUnmodified) break;
			if(MM6.copy) MM6.copy->retain();
			if(real(MM6.reference)) movq(qword_ptr [MM6.reference], mm6);
			break;
		case Encoding::MM7:
			if(!MM7.modified && dropUnmodified) break;
			if(MM7.copy) MM7.copy->retain();
			if(real(MM7.reference)) movq(qword_ptr [MM7.reference], mm7);
			break;
		default: throw INTERNAL_ERROR;
		}

		free(reg);
	}

	const OperandXMMREG &CodeGenerator::r128(const OperandREF &ref, bool copy, bool ss)
	{
		if(ref == 0 && copy) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(XMM0.reference == ref) {if(XMM0.copy) XMM0.copy->retain(); XMM0.copy = 0; return access(xmm0);}
		else if(XMM1.reference == ref) {if(XMM1.copy) XMM1.copy->retain(); XMM1.copy = 0; return access(xmm1);}
		else if(XMM2.reference == ref) {if(XMM2.copy) XMM2.copy->retain(); XMM2.copy = 0; return access(xmm2);}
		else if(XMM3.reference == ref) {if(XMM3.copy) XMM3.copy->retain(); XMM3.copy = 0; return access(xmm3);}
		else if(XMM4.reference == ref) {if(XMM4.copy) XMM4.copy->retain(); XMM4.copy = 0; return access(xmm4);}
		else if(XMM5.reference == ref) {if(XMM5.copy) XMM5.copy->retain(); XMM5.copy = 0; return access(xmm5);}
		else if(XMM6.reference == ref) {if(XMM6.copy) XMM6.copy->retain(); XMM6.copy = 0; return access(xmm6);}
		else if(XMM7.reference == ref) {if(XMM7.copy) XMM7.copy->retain(); XMM7.copy = 0; return access(xmm7);}

		// Search for free registers
		     if(XMM0.reference == 0 && XMM0.priority == 0) return assign(xmm0, ref, copy, ss);
		else if(XMM1.reference == 0 && XMM1.priority == 0) return assign(xmm1, ref, copy, ss);
		else if(XMM2.reference == 0 && XMM2.priority == 0) return assign(xmm2, ref, copy, ss);
		else if(XMM3.reference == 0 && XMM3.priority == 0) return assign(xmm3, ref, copy, ss);
		else if(XMM4.reference == 0 && XMM4.priority == 0) return assign(xmm4, ref, copy, ss);
		else if(XMM5.reference == 0 && XMM5.priority == 0) return assign(xmm5, ref, copy, ss);
		else if(XMM6.reference == 0 && XMM6.priority == 0) return assign(xmm6, ref, copy, ss);
		else if(XMM7.reference == 0 && XMM7.priority == 0) return assign(xmm7, ref, copy, ss);

		// Need to spill one
		Encoding::Reg candidate = Encoding::REG_UNKNOWN;
		unsigned int priority = 0xFFFFFFFF - 2;   // Don't spill most recently used

		if(XMM0.priority < priority && real(XMM0.reference)) {priority = XMM0.priority; candidate = Encoding::XMM0;}
		if(XMM1.priority < priority && real(XMM1.reference)) {priority = XMM1.priority; candidate = Encoding::XMM1;}
		if(XMM2.priority < priority && real(XMM2.reference)) {priority = XMM2.priority; candidate = Encoding::XMM2;}
		if(XMM3.priority < priority && real(XMM3.reference)) {priority = XMM3.priority; candidate = Encoding::XMM3;}
		if(XMM4.priority < priority && real(XMM4.reference)) {priority = XMM4.priority; candidate = Encoding::XMM4;}
		if(XMM5.priority < priority && real(XMM5.reference)) {priority = XMM5.priority; candidate = Encoding::XMM5;}
		if(XMM6.priority < priority && real(XMM6.reference)) {priority = XMM6.priority; candidate = Encoding::XMM6;}
		if(XMM7.priority < priority && real(XMM7.reference)) {priority = XMM7.priority; candidate = Encoding::XMM7;}

		switch(candidate)
		{
		case Encoding::REG_UNKNOWN: throw Error("Out of physical SSE registers. Use free().");
		case Encoding::XMM0: spill(xmm0); return assign(xmm0, ref, copy, ss);
		case Encoding::XMM1: spill(xmm1); return assign(xmm1, ref, copy, ss);
		case Encoding::XMM2: spill(xmm2); return assign(xmm2, ref, copy, ss);
		case Encoding::XMM3: spill(xmm3); return assign(xmm3, ref, copy, ss);
		case Encoding::XMM4: spill(xmm4); return assign(xmm4, ref, copy, ss);
		case Encoding::XMM5: spill(xmm5); return assign(xmm5, ref, copy, ss);
		case Encoding::XMM6: spill(xmm6); return assign(xmm6, ref, copy, ss);
		case Encoding::XMM7: spill(xmm7); return assign(xmm7, ref, copy, ss);
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandXMMREG &CodeGenerator::x128(const OperandREF &ref, bool copy, bool ss)
	{
		free(ref);
		return r128(ref, copy, ss);
	}

	const OperandXMMREG &CodeGenerator::t128(int i, bool ss)
	{
		if(i < 0 || i >= 8) throw Error("Register allocator t128 index out of range");

		return x128((OperandREF)i, ss);
	}

	const OperandR_M128 CodeGenerator::m128(const OperandREF &ref, bool ss)
	{
		if(ref == 0) throw Error("Cannot dereference 0");

		// Check if already allocated
		     if(XMM0.reference == ref) return access(xmm0);
		else if(XMM1.reference == ref) return access(xmm1);
		else if(XMM2.reference == ref) return access(xmm2);
		else if(XMM3.reference == ref) return access(xmm3);
		else if(XMM4.reference == ref) return access(xmm4);
		else if(XMM5.reference == ref) return access(xmm5);
		else if(XMM6.reference == ref) return access(xmm6);
		else if(XMM7.reference == ref) return access(xmm7);

		return (OperandR_M128)xword_ptr [ref];
	}

	const OperandXMMREG &CodeGenerator::allocate(const OperandXMMREG &reg, const OperandREF &ref, bool copy, bool ss)
	{
		return assign(reg, ref, copy, ss);
	}

	const OperandXMMREG &CodeGenerator::assign(const OperandXMMREG &reg, const OperandREF &ref, bool copy, bool ss)
	{
		switch(reg.reg)
		{
		case Encoding::XMM0:
			if(XMM0.reference != 0) throw Error("xmm0 not available for register allocation");
			XMM0.reference = ref;
			XMM0.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm0, dword_ptr [ref]);
				else   movaps(xmm0, xword_ptr [ref]);  
			}
			XMM0.modified = false;
			return access(xmm0);
		case Encoding::XMM1:
			if(XMM1.reference != 0) throw Error("xmm1 not available for register allocation");
			XMM1.reference = ref;
			XMM1.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm1, dword_ptr [ref]);
				else   movaps(xmm1, xword_ptr [ref]);  
			}
			XMM1.modified = false;
			return access(xmm1);
		case Encoding::XMM2:
			if(XMM2.reference != 0) throw Error("xmm2 not available for register allocation");
			XMM2.reference = ref;
			XMM2.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm2, dword_ptr [ref]);
				else   movaps(xmm2, xword_ptr [ref]);  
			}
			XMM2.modified = false;
			return access(xmm2);
		case Encoding::XMM3:
			if(XMM3.reference != 0) throw Error("xmm3 not available for register allocation");
			XMM3.reference = ref;
			XMM3.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm3, dword_ptr [ref]);
				else   movaps(xmm3, xword_ptr [ref]);  
			}
			XMM3.modified = false;
			return access(xmm3);
		case Encoding::XMM4:
			if(XMM4.reference != 0) throw Error("xmm4 not available for register allocation");
			XMM4.reference = ref;
			XMM4.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm4, dword_ptr [ref]);
				else   movaps(xmm4, xword_ptr [ref]);  
			}
			XMM4.modified = false;
			return access(xmm4);
		case Encoding::XMM5:
			if(XMM5.reference != 0) throw Error("xmm5 not available for register allocation");
			XMM5.reference = ref;
			XMM5.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm5, dword_ptr [ref]);
				else   movaps(xmm5, xword_ptr [ref]);  
			}
			XMM5.modified = false;
			return access(xmm5);
		case Encoding::XMM6:
			if(XMM6.reference != 0) throw Error("xmm6 not available for register allocation");
			XMM6.reference = ref;
			XMM6.partial = ss;
			if(copy && real(ref))
			{
				if(ss) movss(xmm6, dword_ptr [ref]);
				else   movaps(xmm6, xword_ptr [ref]);  
			}
			XMM6.modified = false;
			return access(xmm6);
		case Encoding::XMM7:
			if(XMM7.reference != 0) throw Error("xmm7 not available for register allocation");
			XMM7.reference = ref;
			XMM7.partial = ss;
		if(copy && real(ref))
		{
				if(ss) movss(xmm7, dword_ptr [ref]);
				else   movaps(xmm7, xword_ptr [ref]);  
			}
			XMM7.modified = false;
			return access(xmm7);
		default: throw INTERNAL_ERROR;
		}
	}

	const OperandXMMREG &CodeGenerator::access(const OperandXMMREG &reg)
	{
		// Decrease priority of other registers
		if(reg.reg != Encoding::XMM0 && XMM0.priority) XMM0.priority--;
		if(reg.reg != Encoding::XMM1 && XMM1.priority) XMM1.priority--;
		if(reg.reg != Encoding::XMM2 && XMM2.priority) XMM2.priority--;
		if(reg.reg != Encoding::XMM3 && XMM3.priority) XMM3.priority--;
		if(reg.reg != Encoding::XMM4 && XMM4.priority) XMM4.priority--;
		if(reg.reg != Encoding::XMM5 && XMM5.priority) XMM5.priority--;
		if(reg.reg != Encoding::XMM6 && XMM6.priority) XMM6.priority--;
		if(reg.reg != Encoding::XMM7 && XMM7.priority) XMM7.priority--;

		// Give highest priority
		     if(reg.reg == Encoding::XMM0) XMM0.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM1) XMM1.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM2) XMM2.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM3) XMM3.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM4) XMM4.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM5) XMM5.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM6) XMM6.priority = 0xFFFFFFFF;
		else if(reg.reg == Encoding::XMM7) XMM7.priority = 0xFFFFFFFF;

		return reg;
	}

	void CodeGenerator::free(const OperandXMMREG &reg)
	{
		switch(reg.reg)
		{
		case Encoding::XMM0: XMM0.free(); break;
		case Encoding::XMM1: XMM1.free(); break;
		case Encoding::XMM2: XMM2.free(); break;
		case Encoding::XMM3: XMM3.free(); break;
		case Encoding::XMM4: XMM4.free(); break;
		case Encoding::XMM5: XMM5.free(); break;
		case Encoding::XMM6: XMM6.free(); break;
		case Encoding::XMM7: XMM7.free(); break;
		default: throw INTERNAL_ERROR;
		}
	}

	void CodeGenerator::spill(const OperandXMMREG &reg)
	{
		switch(reg.reg)
		{
		case Encoding::XMM0:
			if(!XMM0.modified && dropUnmodified) break;
			if(XMM0.copy) XMM0.copy->retain();
			if(real(XMM0.reference))
			{
				if(XMM0.partial) movss(dword_ptr [XMM0.reference], xmm0);
				else             movaps(xword_ptr [XMM0.reference], xmm0);
			}
			break;
		case Encoding::XMM1:
			if(!XMM1.modified && dropUnmodified) break;
			if(XMM1.copy) XMM1.copy->retain();
			if(real(XMM1.reference))
		{
				if(XMM1.partial) movss(dword_ptr [XMM1.reference], xmm1);
				else             movaps(xword_ptr [XMM1.reference], xmm1);
			}
			break;
		case Encoding::XMM2:
			if(!XMM2.modified && dropUnmodified) break;
			if(XMM2.copy) XMM2.copy->retain();
			if(real(XMM2.reference))
			{
				if(XMM2.partial) movss(dword_ptr [XMM2.reference], xmm2);
				else             movaps(xword_ptr [XMM2.reference], xmm2);
			}
			break;
		case Encoding::XMM3:
			if(!XMM3.modified && dropUnmodified) break;
			if(XMM3.copy) XMM3.copy->retain();
			if(real(XMM3.reference))
			{
				if(XMM3.partial) movss(dword_ptr [XMM3.reference], xmm3);
				else             movaps(xword_ptr [XMM3.reference], xmm3);
			}
			break;
		case Encoding::XMM4:
			if(!XMM4.modified && dropUnmodified) break;
			if(XMM4.copy) XMM4.copy->retain();
			if(real(XMM4.reference))
			{
				if(XMM4.partial) movss(dword_ptr [XMM4.reference], xmm4);
				else             movaps(xword_ptr [XMM4.reference], xmm4);
			}
			break;
		case Encoding::XMM5:
			if(!XMM5.modified && dropUnmodified) break;
			if(XMM5.copy) XMM5.copy->retain();
			if(real(XMM5.reference))
			{
				if(XMM5.partial) movss(dword_ptr [XMM5.reference], xmm5);
				else             movaps(xword_ptr [XMM5.reference], xmm5);
			}
			break;
		case Encoding::XMM6:
			if(!XMM6.modified && dropUnmodified) break;
			if(XMM6.copy) XMM6.copy->retain();
			if(real(XMM6.reference))
			{
				if(XMM6.partial) movss(dword_ptr [XMM6.reference], xmm6);
				else             movaps(xword_ptr [XMM6.reference], xmm6);
			}
			break;
		case Encoding::XMM7:
			if(!XMM7.modified && dropUnmodified) break;
			if(XMM7.copy) XMM7.copy->retain();
			if(real(XMM7.reference))
			{
				if(XMM7.partial) movss(dword_ptr [XMM7.reference], xmm7);
				else             movaps(xword_ptr [XMM7.reference], xmm7);
			}
			break;
		default: throw INTERNAL_ERROR;
		}

		free(reg);
	}

	const OperandXMMREG &CodeGenerator::rSS(const OperandREF &ref, bool copy, bool ss)
	{
		return r128(ref, copy, ss);
	}

	const OperandXMMREG &CodeGenerator::xSS(const OperandREF &ref, bool copy, bool ss)
	{
		return x128(ref, copy, ss);
	}

	const OperandXMMREG &CodeGenerator::tSS(int i, bool ss)
	{
		return t128(i, ss);
	}

	const OperandXMM32 CodeGenerator::mSS(const OperandREF &ref, bool ss)
	{
		return (OperandXMM32)m128(ref, ss);
	}

	bool CodeGenerator::real(const OperandREF &ref)
	{
		return ref.baseReg != Encoding::REG_UNKNOWN ||
		       ref.indexReg != Encoding::REG_UNKNOWN ||
		       ref.scale != 0 ||
		       ref.displacement >= 8;
	}

	const CodeGenerator::AllocationTable CodeGenerator::getAllocationState()
	{
		AllocationTable state;

		if(spillUnrelocated)
		{
			spillAll();
			return state;
		}

		state.eax = EAX;
		state.ecx = ECX;
		state.edx = EDX;
		state.ebx = EBX;
		state.esi = ESI;
		state.edi = EDI;

		state.mm0 = MM0;
		state.mm1 = MM1;
		state.mm2 = MM2;
		state.mm3 = MM3;
		state.mm4 = MM4;
		state.mm5 = MM5;
		state.mm6 = MM6;
		state.mm7 = MM7;

		state.xmm0 = XMM0;
		state.xmm1 = XMM1;
		state.xmm2 = XMM2;
		state.xmm3 = XMM3;
		state.xmm4 = XMM4;
		state.xmm5 = XMM5;
		state.xmm6 = XMM6;
		state.xmm7 = XMM7;

		return state;
	}

	void CodeGenerator::free(const OperandREF &ref)
	{
		     if(EAX.reference == ref) free(eax);
		else if(ECX.reference == ref) free(ecx);
		else if(EDX.reference == ref) free(edx);
		else if(EBX.reference == ref) free(ebx);
		else if(ESI.reference == ref) free(esi);
		else if(EDI.reference == ref) free(edi);

		     if(MM0.reference == ref) free(mm0);
		else if(MM1.reference == ref) free(mm1);
		else if(MM2.reference == ref) free(mm2);
		else if(MM3.reference == ref) free(mm3);
		else if(MM4.reference == ref) free(mm4);
		else if(MM5.reference == ref) free(mm5);
		else if(MM6.reference == ref) free(mm6);
		else if(MM7.reference == ref) free(mm7);

		     if(XMM0.reference == ref) free(xmm0);
		else if(XMM1.reference == ref) free(xmm1);
		else if(XMM2.reference == ref) free(xmm2);
		else if(XMM3.reference == ref) free(xmm3);
		else if(XMM4.reference == ref) free(xmm4);
		else if(XMM5.reference == ref) free(xmm5);
		else if(XMM6.reference == ref) free(xmm6);
		else if(XMM7.reference == ref) free(xmm7);
	}

	void CodeGenerator::spill(const OperandREF &ref)
	{
		     if(EAX.reference == ref) spill(eax);
		else if(ECX.reference == ref) spill(ecx);
		else if(EDX.reference == ref) spill(edx);
		else if(EBX.reference == ref) spill(ebx);
		else if(ESI.reference == ref) spill(esi);
		else if(EDI.reference == ref) spill(edi);

		     if(MM0.reference == ref) spill(mm0);
		else if(MM1.reference == ref) spill(mm1);
		else if(MM2.reference == ref) spill(mm2);
		else if(MM3.reference == ref) spill(mm3);
		else if(MM4.reference == ref) spill(mm4);
		else if(MM5.reference == ref) spill(mm5);
		else if(MM6.reference == ref) spill(mm6);
		else if(MM7.reference == ref) spill(mm7);

		     if(XMM0.reference == ref) spill(xmm0);
		else if(XMM1.reference == ref) spill(xmm1);
		else if(XMM2.reference == ref) spill(xmm2);
		else if(XMM3.reference == ref) spill(xmm3);
		else if(XMM4.reference == ref) spill(xmm4);
		else if(XMM5.reference == ref) spill(xmm5);
		else if(XMM6.reference == ref) spill(xmm6);
		else if(XMM7.reference == ref) spill(xmm7);

		free(ref);
	}

	void CodeGenerator::freeAll()
	{
		free(eax);
		free(ecx);
		free(edx);
		free(ebx);
		free(esi);
		free(edi);

		free(mm0);
		free(mm1);
		free(mm2);
		free(mm3);
		free(mm4);
		free(mm5);
		free(mm6);
		free(mm7);

		free(xmm0);
		free(xmm1);
		free(xmm2);
		free(xmm3);
		free(xmm4);
		free(xmm5);
		free(xmm6);
		free(xmm7);
	}

	void CodeGenerator::spillAll(const AllocationTable &allocationState)
	{
		if(spillUnrelocated)
		{
			spillAll();
			return;
		}

		if(EAX.reference != allocationState.eax.reference) spill(eax);
		if(ECX.reference != allocationState.ecx.reference) spill(ecx);
		if(EDX.reference != allocationState.edx.reference) spill(edx);
		if(EBX.reference != allocationState.ebx.reference) spill(ebx);
		if(ESI.reference != allocationState.esi.reference) spill(esi);
		if(EDI.reference != allocationState.edi.reference) spill(edi);

		if(MM0.reference != allocationState.mm0.reference) spill(mm0);
		if(MM1.reference != allocationState.mm1.reference) spill(mm1);
		if(MM2.reference != allocationState.mm2.reference) spill(mm2);
		if(MM3.reference != allocationState.mm3.reference) spill(mm3);
		if(MM4.reference != allocationState.mm4.reference) spill(mm4);
		if(MM5.reference != allocationState.mm5.reference) spill(mm5);
		if(MM6.reference != allocationState.mm6.reference) spill(mm6);
		if(MM7.reference != allocationState.mm7.reference) spill(mm7);

		if(XMM0.reference != allocationState.xmm0.reference) spill(xmm0);
		if(XMM1.reference != allocationState.xmm1.reference) spill(xmm1);
		if(XMM2.reference != allocationState.xmm2.reference) spill(xmm2);
		if(XMM3.reference != allocationState.xmm3.reference) spill(xmm3);
		if(XMM4.reference != allocationState.xmm4.reference) spill(xmm4);
		if(XMM5.reference != allocationState.xmm5.reference) spill(xmm5);
		if(XMM6.reference != allocationState.xmm6.reference) spill(xmm6);
		if(XMM7.reference != allocationState.xmm7.reference) spill(xmm7);
	}

	void CodeGenerator::spillAll()
	{
		spill(eax);
		spill(ecx);
		spill(edx);
		spill(ebx);
		spill(esi);
		spill(edi);

		spill(mm0);
		spill(mm1);
		spill(mm2);
		spill(mm3);
		spill(mm4);
		spill(mm5);
		spill(mm6);
		spill(mm7);

		spill(xmm0);
		spill(xmm1);
		spill(xmm2);
		spill(xmm3);
		spill(xmm4);
		spill(xmm5);
		spill(xmm6);
		spill(xmm7);
	}

	void CodeGenerator::spillMMX()
	{
		spill(mm0);
		spill(mm1);
		spill(mm2);
		spill(mm3);
		spill(mm4);
		spill(mm5);
		spill(mm6);
		spill(mm7);
	}

	void CodeGenerator::spillMMXcept(const OperandMMREG &reg)
	{
		if(reg.reg != Encoding::MM0) spill(mm0);
		if(reg.reg != Encoding::MM1) spill(mm1);
		if(reg.reg != Encoding::MM2) spill(mm2);
		if(reg.reg != Encoding::MM3) spill(mm3);
		if(reg.reg != Encoding::MM4) spill(mm4);
		if(reg.reg != Encoding::MM5) spill(mm5);
		if(reg.reg != Encoding::MM6) spill(mm6);
		if(reg.reg != Encoding::MM7) spill(mm7);

		emms();
	}

	void CodeGenerator::enableEmulateSSE()
	{
		emulateSSE = true;
		copyPropagation = false;
		dropUnmodified = false;
		spillUnrelocated = true;
	}

	void CodeGenerator::disableEmulateSSE()
	{
		emulateSSE = false;
	}

	void CodeGenerator::enableCopyPropagation()
	{
		if(!emulateSSE)
		{
			copyPropagation = true;
		}
		else
		{
			copyPropagation = false;
		}
	}

	void CodeGenerator::disableCopyPropagation()
	{
		copyPropagation = false;
	}

	void CodeGenerator::enableDropUnmodified()
	{
		if(!emulateSSE)
		{
			dropUnmodified = true;
		}
		else
		{
			dropUnmodified = false;
		}
	}

	void CodeGenerator::disableDropUnmodified()
	{
		dropUnmodified = false;
	}

	void CodeGenerator::enableSpillUnrelocated()
	{
		spillUnrelocated = true;
	}

	void CodeGenerator::disableSpillUnrelocated()
	{
		if(!emulateSSE)
		{
			spillUnrelocated = false;
		}
		else
		{
			spillUnrelocated = true;
		}
	}

	Encoding *CodeGenerator::addps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[i][0]]);
			fadd(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);

			fld(dword_ptr [&sse[i][1]]);
			fadd(dword_ptr [&sse[j][1]]);
			fstp(dword_ptr [&sse[i][1]]);

			fld(dword_ptr [&sse[i][2]]);
			fadd(dword_ptr [&sse[j][2]]);
			fstp(dword_ptr [&sse[i][2]]);

			fld(dword_ptr [&sse[i][3]]);
			fadd(dword_ptr [&sse[j][3]]);
			fstp(dword_ptr [&sse[i][3]]);

			return 0;
		}
		else
		{
			return Assembler::addps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::addps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fld(dword_ptr [&sse[i][0]]);
			fadd((OperandMEM32)(mem128+0));
			fstp(dword_ptr [&sse[i][0]]);

			fld(dword_ptr [&sse[i][1]]);
			fadd((OperandMEM32)(mem128+4));
			fstp(dword_ptr [&sse[i][1]]);

			fld(dword_ptr [&sse[i][2]]);
			fadd((OperandMEM32)(mem128+8));
			fstp(dword_ptr [&sse[i][2]]);

			fld(dword_ptr [&sse[i][3]]);
			fadd((OperandMEM32)(mem128+12));
			fstp(dword_ptr [&sse[i][3]]);
			
			return 0;
		}
		else
		{
			return Assembler::addps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::addps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return addps(xmm, (OperandXMMREG)r_m128);
			else                               return addps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::addps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::addss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fadd(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::addss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::addss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fadd((OperandMEM32)mem32);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::addss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::addss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return addss(xmm, (OperandXMMREG)xmm32);
			else                              return addss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::addss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::andnps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			not(dword_ptr [&sse[i][0]]);
			and(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			not(dword_ptr [&sse[i][1]]);
			and(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			not(dword_ptr [&sse[i][2]]);
			and(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			not(dword_ptr [&sse[i][3]]);
			and(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::andnps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::andnps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(mem128+0));
			not(dword_ptr [&sse[i][0]]);
			and(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+4));
			not(dword_ptr [&sse[i][1]]);
			and(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+8));
			not(dword_ptr [&sse[i][2]]);
			and(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+12));
			not(dword_ptr [&sse[i][3]]);
			and(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::andnps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::andnps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return andnps(xmm, (OperandXMMREG)r_m128);
			else                               return andnps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::andnps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::andps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			and(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			and(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			and(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			and(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::andps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::andps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(mem128+0));
			and(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+4));
			and(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+8));
			and(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+12));
			and(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::andps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::andps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return andps(xmm, (OperandXMMREG)r_m128);
			else                               return andps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::andps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::cmpps(OperandXMMREG xmmi, OperandXMMREG xmmj, char c)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static float zero = 0;
			static float one = 1;
			fld(dword_ptr [&zero]);		// st2
			fld(dword_ptr [&one]);		// st1

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][0]]);

			fld(dword_ptr [&sse[j][1]]);
			fld(dword_ptr [&sse[i][1]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][1]]);

			fld(dword_ptr [&sse[j][2]]);
			fld(dword_ptr [&sse[i][2]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][2]]);

			fld(dword_ptr [&sse[j][3]]);
			fld(dword_ptr [&sse[i][3]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][3]]);

			ffree(st0);
			ffree(st1);

			return 0;
		}
		else
		{
			return Assembler::cmpps(xmmi, xmmj, c);
		}
	}

	Encoding *CodeGenerator::cmpps(OperandXMMREG xmm, OperandMEM128 mem128, char c)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float zero = 0;
			static float one = 1;
			fld(dword_ptr [&zero]);		// st2
			fld(dword_ptr [&one]);		// st1

			fld((OperandMEM32)(mem128+0));
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][0]]);

			fld((OperandMEM32)(mem128+4));
			fld(dword_ptr [&sse[i][1]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][1]]);

			fld((OperandMEM32)(mem128+8));
			fld(dword_ptr [&sse[i][2]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][2]]);

			fld((OperandMEM32)(mem128+12));
			fld(dword_ptr [&sse[i][3]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][3]]);

			ffree(st0);
			ffree(st1);

			return 0;
		}
		else
		{
			return Assembler::cmpps(xmm, mem128, c);
		}
	}

	Encoding *CodeGenerator::cmpps(OperandXMMREG xmm, OperandR_M128 r_m128, char c)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return cmpps(xmm, (OperandXMMREG)r_m128, c);
			else                               return cmpps(xmm, (OperandMEM128)r_m128, c);
		}
		else
		{
			return Assembler::cmpps(xmm, r_m128, c);
		}
	}

	Encoding *CodeGenerator::cmpeqps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 0);
	}

	Encoding *CodeGenerator::cmpeqps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 0);
	}

	Encoding *CodeGenerator::cmpeqps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 0);
	}

	Encoding *CodeGenerator::cmpleps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 2);
	}

	Encoding *CodeGenerator::cmpleps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 2);
	}

	Encoding *CodeGenerator::cmpleps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 2);
	}

	Encoding *CodeGenerator::cmpltps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 1);
	}

	Encoding *CodeGenerator::cmpltps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 1);
	}

	Encoding *CodeGenerator::cmpltps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 1);
	}

	Encoding *CodeGenerator::cmpneqps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 4);
	}

	Encoding *CodeGenerator::cmpneqps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 4);
	}

	Encoding *CodeGenerator::cmpneqps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 4);
	}

	Encoding *CodeGenerator::cmpnleps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 6);
	}

	Encoding *CodeGenerator::cmpnleps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 6);
	}

	Encoding *CodeGenerator::cmpnleps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 6);
	}

	Encoding *CodeGenerator::cmpnltps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 5);
	}

	Encoding *CodeGenerator::cmpnltps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 5);
	}

	Encoding *CodeGenerator::cmpnltps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 5);
	}

	Encoding *CodeGenerator::cmpordps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 7);
	}

	Encoding *CodeGenerator::cmpordps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 7);
	}

	Encoding *CodeGenerator::cmpordps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 7);
	}

	Encoding *CodeGenerator::cmpunordps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpps(xmmi, xmmj, 3);
	}

	Encoding *CodeGenerator::cmpunordps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		return cmpps(xmm, mem128, 3);
	}

	Encoding *CodeGenerator::cmpunordps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		return cmpps(xmm, r_m128, 3);
	}

	Encoding *CodeGenerator::cmpss(OperandXMMREG xmmi, OperandXMMREG xmmj, char c)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static float zero = 0;
			static float one = 1;
			fld(dword_ptr [&zero]);		// st2
			fld(dword_ptr [&one]);		// st1

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][0]]);

			ffree(st0);
			ffree(st1);

			return 0;
		}
		else
		{
			return Assembler::cmpss(xmmi, xmmj, c);
		}
	}

	Encoding *CodeGenerator::cmpss(OperandXMMREG xmm, OperandMEM32 mem32, char c)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float zero = 0;
			static float one = 1;
			fld(dword_ptr [&zero]);		// st2
			fld(dword_ptr [&one]);		// st1

			fld((OperandMEM32)(mem32+0));
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			switch(c)
			{
			case 0:   // CMPEQPS
				fcmove(st1);
				fcmovne(st2);
				break;
			case 1:   // CMPLTPS
				fcmovb(st1);
				fcmovnb(st2);
				break;
			case 2:   // CMPLEPS
				fcmovbe(st1);
				fcmovnbe(st2);
				break;
			case 3:   // CMPUNORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			case 4:   // CMPNEQPS
				fcmovne(st1);
				fcmove(st2);
				break;
			case 5:   // CMPNLTPS
				fcmovnb(st1);
				fcmovb(st2);
				break;
			case 6:   // CMPNLEPS
				fcmovnbe(st1);
				fcmovbe(st2);
				break;
			case 7:   // CMPORDPS
				fcmovnu(st1);
				fcmovu(st2);
				break;
			default:
				throw INTERNAL_ERROR;
			}
			fstp(dword_ptr [&sse[i][0]]);

			ffree(st0);
			ffree(st1);

			return 0;
		}
		else
		{
			return Assembler::cmpss(xmm, mem32, c);
		}
	}

	Encoding *CodeGenerator::cmpss(OperandXMMREG xmm, OperandXMM32 xmm32, char c)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return cmpss(xmm, (OperandXMMREG)xmm32, c);
			else                              return cmpss(xmm, (OperandMEM32)xmm32, c);
		}
		else
		{
			return Assembler::cmpss(xmm, xmm32, c);
		}
	}

	Encoding *CodeGenerator::cmpeqss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 0);
	}

	Encoding *CodeGenerator::cmpeqss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 0);
	}

	Encoding *CodeGenerator::cmpeqss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 0);
	}

	Encoding *CodeGenerator::cmpless(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 2);
	}

	Encoding *CodeGenerator::cmpless(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 2);
	}

	Encoding *CodeGenerator::cmpless(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 2);
	}

	Encoding *CodeGenerator::cmpltss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 1);
	}

	Encoding *CodeGenerator::cmpltss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 1);
	}

	Encoding *CodeGenerator::cmpltss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 1);
	}

	Encoding *CodeGenerator::cmpneqss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 4);
	}

	Encoding *CodeGenerator::cmpneqss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 4);
	}

	Encoding *CodeGenerator::cmpneqss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 4);
	}

	Encoding *CodeGenerator::cmpnless(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 6);
	}

	Encoding *CodeGenerator::cmpnless(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 6);
	}

	Encoding *CodeGenerator::cmpnless(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 6);
	}

	Encoding *CodeGenerator::cmpnltss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 5);
	}

	Encoding *CodeGenerator::cmpnltss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 5);
	}

	Encoding *CodeGenerator::cmpnltss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 5);
	}

	Encoding *CodeGenerator::cmpordss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 7);
	}

	Encoding *CodeGenerator::cmpordss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 7);
	}

	Encoding *CodeGenerator::cmpordss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 7);
	}

	Encoding *CodeGenerator::cmpunordss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		return cmpss(xmmi, xmmj, 3);
	}

	Encoding *CodeGenerator::cmpunordss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		return cmpss(xmm, mem32, 3);
	}

	Encoding *CodeGenerator::cmpunordss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		return cmpss(xmm, xmm32, 3);
	}

	Encoding *CodeGenerator::cmpxchg(OperandR_M8 r_m8, OperandREG8 r8)
	{
		markModified(r_m8);
		markModified(r8);

		return Assembler::cmpxchg(r_m8, r8);
	}

	Encoding *CodeGenerator::cmpxchg(OperandR_M16 r_m16, OperandREG16 r16)
	{
		markModified(r_m16);
		markModified(r16);

		return Assembler::cmpxchg(r_m16, r16);
	}

	Encoding *CodeGenerator::cmpxchg(OperandR_M32 r_m32, OperandREG32 r32)
	{
		markModified(r_m32);
		markModified(r32);

		return Assembler::cmpxchg(r_m32, r32);
	}

	Encoding *CodeGenerator::lock_cmpxchg(OperandMEM8 m8, OperandREG8 r8)
	{
		markModified(m8);
		markModified(r8);

		return Assembler::lock_cmpxchg(m8, r8);
	}

	Encoding *CodeGenerator::lock_cmpxchg(OperandMEM16 m16, OperandREG16 r16)
	{
		markModified(m16);
		markModified(r16);

		return Assembler::lock_cmpxchg(m16, r16);
	}

	Encoding *CodeGenerator::lock_cmpxchg(OperandMEM32 m32, OperandREG32 r32)
	{
		markModified(m32);
		markModified(r32);

		return Assembler::lock_cmpxchg(m32, r32);
	}

	Encoding *CodeGenerator::comiss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::comiss(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::comiss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			
			fld(mem32);
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::comiss(xmm, mem32);
		}
	}

	Encoding *CodeGenerator::comiss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return comiss(xmm, (OperandXMMREG)xmm32);
			else                              return comiss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::comiss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::cvtpi2ps(OperandXMMREG xmm, OperandMMREG mm)
	{
		if(emulateSSE)
		{
			static int dword[2];
			movq(qword_ptr [dword], mm);
			const int i = xmm.reg;
			spillMMX();

			fild(dword_ptr [&dword[0]]);
			fstp(dword_ptr [&sse[i][0]]);
			fild(dword_ptr [&dword[1]]);
			fstp(dword_ptr [&sse[i][1]]);

			return 0;
		}
		else
		{
			return Assembler::cvtpi2ps(xmm, mm);
		}
	}

	Encoding *CodeGenerator::cvtpi2ps(OperandXMMREG xmm, OperandMEM64 mem64)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fild((OperandMEM32)(mem64+0));
			fstp(dword_ptr [&sse[i][0]]);
			fild((OperandMEM32)(mem64+4));
			fstp(dword_ptr [&sse[i][1]]);

			return 0;
		}
		else
		{
			return Assembler::cvtpi2ps(xmm, mem64);
		}
	}

	Encoding *CodeGenerator::cvtpi2ps(OperandXMMREG xmm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			if(r_m64.type == Operand::MMREG) return cvtpi2ps(xmm, (OperandMMREG)r_m64);
			else                             return cvtpi2ps(xmm, (OperandMEM64)r_m64);
		}
		else
		{
			return Assembler::cvtpi2ps(xmm, r_m64);
		}
	}

	Encoding *CodeGenerator::cvtps2pi(OperandMMREG mm, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int dword[2];

			spillMMXcept(mm);
			const int i = xmm.reg;
		//	short fpuCW1;
		//	short fpuCW2;

		//	fldcw(word_ptr [&fpuCW1]);
		//	fldcw(word_ptr [&fpuCW2]);
		//	and(word_ptr [&fpuCW2], (short)0xF3FF);
		//	fstcw(word_ptr [&fpuCW2]);

			fld(dword_ptr [&sse[i][0]]);
			fistp(dword_ptr [&dword[0]]);
			fld(dword_ptr [&sse[i][1]]);
			fistp(dword_ptr [&dword[1]]);

		//	fstcw(word_ptr [&fpuCW1]);
			movq(mm, qword_ptr [dword]);

			return 0;
		}
		else
		{
			return Assembler::cvtps2pi(mm, xmm);
		}
	}

	Encoding *CodeGenerator::cvtps2pi(OperandMMREG mm, OperandMEM64 mem64)
	{
		if(emulateSSE)
		{
			static int dword[2];

			spillMMXcept(mm);
		//	short fpuCW1;
		//	short fpuCW2;

		//	fldcw(word_ptr [&fpuCW1]);
		//	fldcw(word_ptr [&fpuCW2]);
		//	and(word_ptr [&fpuCW2], (short)0xF3FF);
		//	fstcw(word_ptr [&fpuCW2]);

			fld((OperandMEM32)(mem64+0));
			fistp(dword_ptr [&dword[0]]);
			fld((OperandMEM32)(mem64+4));
			fistp(dword_ptr [&dword[1]]);

		//	fstcw(word_ptr [&fpuCW1]);
			movq(mm, qword_ptr [dword]);

			return 0;
		}
		else
		{
			return Assembler::cvtps2pi(mm, mem64);
		}
	}

	Encoding *CodeGenerator::cvtps2pi(OperandMMREG mm, OperandXMM64 xmm64)
	{
		if(emulateSSE)
		{
			if(xmm64.type == Operand::XMMREG) return cvtps2pi(mm, (OperandXMMREG)xmm64);
			else                              return cvtps2pi(mm, (OperandMEM64)xmm64);
		}
		else
		{
			return Assembler::cvtps2pi(mm, xmm64);
		}
	}

	Encoding *CodeGenerator::cvttps2pi(OperandMMREG mm, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int dword[2];
			spillMMXcept(mm);
			const int i = xmm.reg;
			short fpuCW1;
			short fpuCW2;

			fstcw(word_ptr [&fpuCW1]);
			fstcw(word_ptr [&fpuCW2]);
			or(word_ptr [&fpuCW2], (short)0x0C00);
			fldcw(word_ptr [&fpuCW2]);

			fld(dword_ptr [&sse[i][0]]);
			fistp(dword_ptr [&dword[0]]);
			fld(dword_ptr [&sse[i][1]]);
			fistp(dword_ptr [&dword[1]]);

			fldcw(word_ptr [&fpuCW1]);
			movq(mm, qword_ptr [dword]);

			return 0;
		}
		else
		{
			return Assembler::cvttps2pi(mm, xmm);
		}
	}

	Encoding *CodeGenerator::cvttps2pi(OperandMMREG mm, OperandMEM64 mem64)
	{
		if(emulateSSE)
		{
			static int dword[2];

			spillMMXcept(mm);
			static short fpuCW1;
			static short fpuCW2;

			fstcw(word_ptr [&fpuCW1]);
			fstcw(word_ptr [&fpuCW2]);
			or(word_ptr [&fpuCW2], (short)0x0C00);
			fldcw(word_ptr [&fpuCW2]);

			fld((OperandMEM32)(mem64+0));
			fistp(dword_ptr [&dword[0]]);
			fld((OperandMEM32)(mem64+4));
			fistp(dword_ptr [&dword[1]]);

			fldcw(word_ptr [&fpuCW1]);
			movq(mm, qword_ptr [dword]);

			return 0;
		}
		else
		{
			return Assembler::cvttps2pi(mm, mem64);
		}
	}

	Encoding *CodeGenerator::cvttps2pi(OperandMMREG mm, OperandXMM64 xmm64)
	{
		if(emulateSSE)
		{
			if(xmm64.type == Operand::XMMREG) return cvttps2pi(mm, (OperandXMMREG)xmm64);
			else                              return cvttps2pi(mm, (OperandMEM64)xmm64);
		}
		else
		{
			return Assembler::cvttps2pi(mm, xmm64);
		}
	}

	Encoding *CodeGenerator::cvtsi2ss(OperandXMMREG xmm, OperandREG32 reg32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static int dword;

			mov(dword_ptr [&dword], reg32);
			fild(dword_ptr [&dword]);
			fstp(dword_ptr [&sse[i][0]]);

			return 0;
		}
		else
		{
			return Assembler::cvtsi2ss(xmm, reg32);
		}
	}

	Encoding *CodeGenerator::cvtsi2ss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fild(mem32);
			fstp(dword_ptr [&sse[i][0]]);

			return 0;
		}
		else
		{
			return Assembler::cvtsi2ss(xmm, mem32);
		}
	}

	Encoding *CodeGenerator::cvtsi2ss(OperandXMMREG xmm, OperandR_M32 r_m32)
	{
		if(emulateSSE)
		{
			if(r_m32.type == Operand::REG32) return cvtsi2ss(xmm, (OperandREG32)r_m32);
			else                             return cvtsi2ss(xmm, (OperandMEM32)r_m32);
		}
		else
		{
			return Assembler::cvtsi2ss(xmm, r_m32);
		}
	}

	Encoding *CodeGenerator::cvtss2si(OperandREG32 reg32, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
		//	short fpuCW1;
		//	short fpuCW2;
			static int dword;

		//	fldcw(word_ptr [&fpuCW1]);
		//	fldcw(word_ptr [&fpuCW2]);
		//	and(word_ptr [&fpuCW2], (short)0xF3FF);
		//	fstcw(word_ptr [&fpuCW2]);

			fld(dword_ptr [&sse[i][0]]);
			fistp(dword_ptr [&dword]);
			mov(reg32, dword_ptr [&dword]);

		//	fstcw(word_ptr [&fpuCW1]);

			return 0;
		}
		else
		{
			return Assembler::cvtss2si(reg32, xmm);
		}
	}

	Encoding *CodeGenerator::cvtss2si(OperandREG32 reg32, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
		//	short fpuCW1;
		//	short fpuCW2;
			static int dword;

		//	fldcw(word_ptr [&fpuCW1]);
		//	fldcw(word_ptr [&fpuCW2]);
		//	and(word_ptr [&fpuCW2], (short)0xF3FF);
		//	fstcw(word_ptr [&fpuCW2]);

			fld(mem32);
			fistp(dword_ptr [&dword]);
			mov(reg32, dword_ptr [&dword]);

		//	fstcw(word_ptr [&fpuCW1]);

			return 0;
		}
		else
		{
			return Assembler::cvtss2si(reg32, mem32);
		}
	}

	Encoding *CodeGenerator::cvtss2si(OperandREG32 reg32, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return cvtss2si(reg32, (OperandXMMREG)xmm32);
			else                              return cvtss2si(reg32, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::cvtss2si(reg32, xmm32);
		}
	}

	Encoding *CodeGenerator::cvttss2si(OperandREG32 reg32, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static short fpuCW1;
			static short fpuCW2;
			static int dword;

			fstcw(word_ptr [&fpuCW1]);
			fstcw(word_ptr [&fpuCW2]);
			or(word_ptr [&fpuCW2], (short)0x0C00);
			fldcw(word_ptr [&fpuCW2]);

			fld(dword_ptr [&sse[i][0]]);
			fistp(dword_ptr [&dword]);
			mov(reg32, dword_ptr [&dword]);

			fldcw(word_ptr [&fpuCW1]);

			return 0;
		}
		else
		{
			return Assembler::cvttss2si(reg32, xmm);
		}
	}

	Encoding *CodeGenerator::cvttss2si(OperandREG32 reg32, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			static short fpuCW1;
			static short fpuCW2;
			static int dword;

			fstcw(word_ptr [&fpuCW1]);
			fstcw(word_ptr [&fpuCW2]);
			or(word_ptr [&fpuCW2], (short)0x0C00);
			fldcw(word_ptr [&fpuCW2]);

			fld(mem32);
			fistp(dword_ptr [&dword]);
			mov(reg32, dword_ptr [&dword]);

			fldcw(word_ptr [&fpuCW1]);

			return 0;
		}
		else
		{
			return Assembler::cvttss2si(reg32, mem32);
		}
	}

	Encoding *CodeGenerator::cvttss2si(OperandREG32 reg32, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return cvttss2si(reg32, (OperandXMMREG)xmm32);
			else                              return cvttss2si(reg32, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::cvttss2si(reg32, xmm32);
		}
	}

	Encoding *CodeGenerator::divps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fdiv(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[i][1]]);
			fdiv(dword_ptr [&sse[j][1]]);
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[i][2]]);
			fdiv(dword_ptr [&sse[j][2]]);
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[i][3]]);
			fdiv(dword_ptr [&sse[j][3]]);
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::divps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::divps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fdiv((OperandMEM32)(mem128+0));
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[i][1]]);
			fdiv((OperandMEM32)(mem128+4));
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[i][2]]);
			fdiv((OperandMEM32)(mem128+8));
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[i][3]]);
			fdiv((OperandMEM32)(mem128+12));
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::divps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::divps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return divps(xmm, (OperandXMMREG)r_m128);
			else                               return divps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::divps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::divss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fdiv(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::divss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::divss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fdiv((OperandMEM32)mem32);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::divss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::divss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return divss(xmm, (OperandXMMREG)xmm32);
			else                              return divss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::divss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::ldmxcsr(OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return Assembler::ldmxcsr(mem32);
		}
	}

	Encoding *CodeGenerator::maskmovq(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			static short qword1[4];
			static short qword2[4];

			static int t0;

			movq(qword_ptr [&qword1], mmi);
			movq(qword_ptr [&qword2], mmj);

			test(byte_ptr [&qword2+0], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+0]);
			cmovnz(r32(&t0), dword_ptr [&qword1+0]);
			mov(byte_ptr [edi+0], r8(&t0));

			test(byte_ptr [&qword2+1], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+1]);
			cmovnz(r32(&t0), dword_ptr [&qword1+1]);
			mov(byte_ptr [edi+1], r8(&t0));

			test(byte_ptr [&qword2+2], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+2]);
			cmovnz(r32(&t0), dword_ptr [&qword1+2]);
			mov(byte_ptr [edi+2], r8(&t0));

			test(byte_ptr [&qword2+3], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+3]);
			cmovnz(r32(&t0), dword_ptr [&qword1+3]);
			mov(byte_ptr [edi+3], r8(&t0));

			test(byte_ptr [&qword2+4], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+4]);
			cmovnz(r32(&t0), dword_ptr [&qword1+4]);
			mov(byte_ptr [edi+4], r8(&t0));

			test(byte_ptr [&qword2+5], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+5]);
			cmovnz(r32(&t0), dword_ptr [&qword1+5]);
			mov(byte_ptr [edi+5], r8(&t0));

			test(byte_ptr [&qword2+6], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+6]);
			cmovnz(r32(&t0), dword_ptr [&qword1+6]);
			mov(byte_ptr [edi+6], r8(&t0));

			test(byte_ptr [&qword2+7], (char)0x80);
			mov(x8(&t0), byte_ptr [edi+7]);
			cmovnz(r32(&t0), dword_ptr [&qword1+7]);
			mov(byte_ptr [edi+7], r8(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::maskmovq(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::maxps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			fld(dword_ptr [&sse[j][1]]);
			fld(dword_ptr [&sse[i][1]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][1]]);
			ffree(st0);

			fld(dword_ptr [&sse[j][2]]);
			fld(dword_ptr [&sse[i][2]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][2]]);
			ffree(st0);

			fld(dword_ptr [&sse[j][3]]);
			fld(dword_ptr [&sse[i][3]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][3]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::maxps(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::maxps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fld((OperandMEM32)(mem128+0));
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			fld((OperandMEM32)(mem128+4));
			fld(dword_ptr [&sse[i][1]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][1]]);
			ffree(st0);

			fld((OperandMEM32)(mem128+8));
			fld(dword_ptr [&sse[i][2]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][2]]);
			ffree(st0);

			fld((OperandMEM32)(mem128+0));
			fld(dword_ptr [&sse[i][3]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][3]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::maxps(xmm, mem128);
		}
	}

	Encoding *CodeGenerator::maxps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return maxps(xmm, (OperandXMMREG)r_m128);
			else                               return maxps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::maxps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::maxss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::maxss(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::maxss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fld(mem32);
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::maxss(xmm, mem32);
		}
	}

	Encoding *CodeGenerator::maxss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return maxss(xmm, (OperandXMMREG)xmm32);
			else                              return maxss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::maxss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::minps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			fld(dword_ptr [&sse[j][1]]);
			fld(dword_ptr [&sse[i][1]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][1]]);
			ffree(st0);

			fld(dword_ptr [&sse[j][2]]);
			fld(dword_ptr [&sse[i][2]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][2]]);
			ffree(st0);

			fld(dword_ptr [&sse[j][3]]);
			fld(dword_ptr [&sse[i][3]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][3]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::minps(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::minps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fld((OperandMEM32)(mem128+0));
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			fld((OperandMEM32)(mem128+4));
			fld(dword_ptr [&sse[i][1]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][1]]);
			ffree(st0);

			fld((OperandMEM32)(mem128+8));
			fld(dword_ptr [&sse[i][2]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][2]]);
			ffree(st0);

			fld((OperandMEM32)(mem128+0));
			fld(dword_ptr [&sse[i][3]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][3]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::minps(xmm, mem128);
		}
	}

	Encoding *CodeGenerator::minps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return minps(xmm, (OperandXMMREG)r_m128);
			else                               return minps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::minps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::minss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::minss(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::minss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;

			fld(mem32);
			fld(dword_ptr [&sse[i][0]]);
			fucomi(st0, st1);
			fcmovnb(st1);
			fstp(dword_ptr [&sse[i][0]]);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::minss(xmm, mem32);
		}
	}

	Encoding *CodeGenerator::minss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return minss(xmm, (OperandXMMREG)xmm32);
			else                              return minss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::minss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::mov(OperandREG32 r32i, OperandREG32 r32j)
	{
		if(r32i == r32j) return 0;

		Encoding *mov = Assembler::mov(r32i, r32j);

		if(r32i.reg == Encoding::ESP || r32i.reg == Encoding::EBP ||
		   r32j.reg == Encoding::ESP || r32j.reg == Encoding::EBP)
		{
			return mov;
		}
		else if(copyPropagation)
		{
			// Return if not in allocation table
			switch(r32i.reg)
			{
			case Encoding::EAX: if(EAX.reference == 0) return mov; break;
			case Encoding::ECX: if(ECX.reference == 0) return mov; break;
			case Encoding::EDX: if(EDX.reference == 0) return mov; break;
			case Encoding::EBX: if(EBX.reference == 0) return mov; break;
			case Encoding::ESI: if(ESI.reference == 0) return mov; break;
			case Encoding::EDI: if(EDI.reference == 0) return mov; break;
			}

			// Attempt copy propagation
			mov->reserve();
				
			Allocation *destination;

			switch(r32i.reg)
			{
			case Encoding::EAX: EAX.copy = mov; destination = &EAX; break;
			case Encoding::ECX: ECX.copy = mov; destination = &ECX; break;
			case Encoding::EDX: EDX.copy = mov; destination = &EDX; break;
			case Encoding::EBX: EBX.copy = mov; destination = &EBX; break;
			case Encoding::ESI: ESI.copy = mov; destination = &ESI; break;
			case Encoding::EDI: EDI.copy = mov; destination = &EDI; break;
			default:
				mov->retain();
				return mov;
			}

			Allocation *source;

			switch(r32j.reg)
			{
			case Encoding::EAX: source = &EAX; break;
			case Encoding::ECX: source = &ECX; break;
			case Encoding::EDX: source = &EDX; break;
			case Encoding::EBX: source = &EBX; break;
			case Encoding::ESI: source = &ESI; break;
			case Encoding::EDI: source = &EDI; break;
			}

			swapAllocation(source, destination);
		}

		return mov;
	}

	Encoding *CodeGenerator::movaps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		switch(xmmi.reg)
		{
		case Encoding::XMM0: XMM0.partial = false; break;
		case Encoding::XMM1: XMM1.partial = false; break;
		case Encoding::XMM2: XMM2.partial = false; break;
		case Encoding::XMM3: XMM3.partial = false; break;
		case Encoding::XMM4: XMM4.partial = false; break;
		case Encoding::XMM5: XMM5.partial = false; break;
		case Encoding::XMM6: XMM6.partial = false; break;
		case Encoding::XMM7: XMM7.partial = false; break;
		}

		switch(xmmj.reg)
		{
		case Encoding::XMM0: XMM0.partial = false; break;
		case Encoding::XMM1: XMM1.partial = false; break;
		case Encoding::XMM2: XMM2.partial = false; break;
		case Encoding::XMM3: XMM3.partial = false; break;
		case Encoding::XMM4: XMM4.partial = false; break;
		case Encoding::XMM5: XMM5.partial = false; break;
		case Encoding::XMM6: XMM6.partial = false; break;
		case Encoding::XMM7: XMM7.partial = false; break;
		}

		if(xmmi == xmmj) return 0;
		
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}

		Encoding *movaps = Assembler::movaps(xmmi, xmmj);
		
		if(copyPropagation)
		{
			// Return if not in allocation table
			switch(xmmi.reg)
			{
			case Encoding::XMM0: if(XMM0.reference == 0) return movaps; break;
			case Encoding::XMM1: if(XMM1.reference == 0) return movaps; break;
			case Encoding::XMM2: if(XMM2.reference == 0) return movaps; break;
			case Encoding::XMM3: if(XMM3.reference == 0) return movaps; break;
			case Encoding::XMM4: if(XMM4.reference == 0) return movaps; break;
			case Encoding::XMM5: if(XMM5.reference == 0) return movaps; break;
			case Encoding::XMM6: if(XMM6.reference == 0) return movaps; break;
			case Encoding::XMM7: if(XMM7.reference == 0) return movaps; break;
			}

			// Attempt copy propagation
			movaps->reserve();
				
			Allocation *destination;

			switch(xmmi.reg)
			{
			case Encoding::XMM0: XMM0.copy = movaps; destination = &XMM0; break;
			case Encoding::XMM1: XMM1.copy = movaps; destination = &XMM1; break;
			case Encoding::XMM2: XMM2.copy = movaps; destination = &XMM2; break;
			case Encoding::XMM3: XMM3.copy = movaps; destination = &XMM3; break;
			case Encoding::XMM4: XMM4.copy = movaps; destination = &XMM4; break;
			case Encoding::XMM5: XMM5.copy = movaps; destination = &XMM5; break;
			case Encoding::XMM6: XMM6.copy = movaps; destination = &XMM6; break;
			case Encoding::XMM7: XMM7.copy = movaps; destination = &XMM7; break;
			}

			Allocation *source;

			switch(xmmj.reg)
			{
			case Encoding::XMM0: source = &XMM0; break;
			case Encoding::XMM1: source = &XMM1; break;
			case Encoding::XMM2: source = &XMM2; break;
			case Encoding::XMM3: source = &XMM3; break;
			case Encoding::XMM4: source = &XMM4; break;
			case Encoding::XMM5: source = &XMM5; break;
			case Encoding::XMM6: source = &XMM6; break;
			case Encoding::XMM7: source = &XMM7; break;
			}

			swapAllocation(source, destination);
		}

		return movaps;
	}

	Encoding *CodeGenerator::movaps(OperandXMMREG xmm, OperandMEM128 m128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(m128+0));
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m128+4));
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m128+8));
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m128+12));
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movaps(xmm, m128);
		}
	}

	Encoding *CodeGenerator::movaps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(xmm == r_m128) return 0;

		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return movaps(xmm, (OperandXMMREG)r_m128);
			else                               return movaps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::movaps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::movaps(OperandMEM128 m128, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov((OperandMEM32)(m128+0), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			mov((OperandMEM32)(m128+4), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][2]]);
			mov((OperandMEM32)(m128+8), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][3]]);
			mov((OperandMEM32)(m128+12), r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movaps(m128, xmm);
		}
	}

	Encoding *CodeGenerator::movaps(OperandR_M128 r_m128, OperandXMMREG xmm)
	{
		if(r_m128 == xmm) return 0;

		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return movaps((OperandXMMREG)r_m128, xmm);
			else                               return movaps((OperandMEM128)r_m128, xmm);
		}
		else
		{
			return Assembler::movaps(r_m128, xmm);
		}
	}

	Encoding *CodeGenerator::movhlps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movhlps(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::movhps(OperandXMMREG xmm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(m64+0));
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m64+4));
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movhps(xmm, m64);
		}
	}

	Encoding *CodeGenerator::movhps(OperandMEM64 m64, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][2]]);
			mov((OperandMEM32)(m64+0), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][3]]);
			mov((OperandMEM32)(m64+4), r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movhps(m64, xmm);
		}
	}

	Encoding *CodeGenerator::movhps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(xmmi == xmmj) return 0;

		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movhps(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::movlhps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movlhps(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::movlps(OperandXMMREG xmm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(m64+0));
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m64+4));
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movlps(xmm, m64);
		}
	}

	Encoding *CodeGenerator::movlps(OperandMEM64 m64, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov((OperandMEM32)(m64+0), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			mov((OperandMEM32)(m64+4), r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movlps(m64, xmm);
		}
	}

	Encoding *CodeGenerator::movmskps(OperandREG32 reg32, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			const int i = xmm.reg;

			static int t0;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			shr(r32(&t0), 31);
			mov(reg32, r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			shr(r32(&t0), 31);
			shl(r32(&t0), 1);
			or(reg32, r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][2]]);
			shr(r32(&t0), 31);
			shl(r32(&t0), 2);
			or(reg32, r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][3]]);
			shr(r32(&t0), 31);
			shl(r32(&t0), 3);
			or(reg32, r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movmskps(reg32, xmm);
		}
	}

	Encoding *CodeGenerator::movntps(OperandMEM128 m128, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov((OperandMEM32)(m128+0), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			mov((OperandMEM32)(m128+4), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][2]]);
			mov((OperandMEM32)(m128+8), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][3]]);
			mov((OperandMEM32)(m128+12), r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movntps(m128, xmm);
		}
	}

	Encoding *CodeGenerator::movntq(OperandMEM64 m64, OperandMMREG xmm)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov((OperandMEM32)(m64+0), r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			mov((OperandMEM32)(m64+4), r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movntq(m64, xmm);
		}
	}

	Encoding *CodeGenerator::movq(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(mmi == mmj) return 0;
		
		Encoding *movq = Assembler::movq(mmi, mmj);

		if(copyPropagation)
		{
			// Return if not in allocation table
			switch(mmi.reg)
			{
			case Encoding::MM0: if(MM0.reference == 0) return movq; break;
			case Encoding::MM1: if(MM1.reference == 0) return movq; break;
			case Encoding::MM2: if(MM2.reference == 0) return movq; break;
			case Encoding::MM3: if(MM3.reference == 0) return movq; break;
			case Encoding::MM4: if(MM4.reference == 0) return movq; break;
			case Encoding::MM5: if(MM5.reference == 0) return movq; break;
			case Encoding::MM6: if(MM6.reference == 0) return movq; break;
			case Encoding::MM7: if(MM7.reference == 0) return movq; break;
			}

			// Attempt copy propagation
			movq->reserve();
					
			Allocation *destination;

			switch(mmi.reg)
			{
			case Encoding::MM0: MM0.copy = movq; destination = &MM0; break;
			case Encoding::MM1: MM1.copy = movq; destination = &MM1; break;
			case Encoding::MM2: MM2.copy = movq; destination = &MM2; break;
			case Encoding::MM3: MM3.copy = movq; destination = &MM3; break;
			case Encoding::MM4: MM4.copy = movq; destination = &MM4; break;
			case Encoding::MM5: MM5.copy = movq; destination = &MM5; break;
			case Encoding::MM6: MM6.copy = movq; destination = &MM6; break;
			case Encoding::MM7: MM7.copy = movq; destination = &MM7; break;
			}

			Allocation *source;

			switch(mmj.reg)
			{
			case Encoding::MM0: source = &MM0; break;
			case Encoding::MM1: source = &MM1; break;
			case Encoding::MM2: source = &MM2; break;
			case Encoding::MM3: source = &MM3; break;
			case Encoding::MM4: source = &MM4; break;
			case Encoding::MM5: source = &MM5; break;
			case Encoding::MM6: source = &MM6; break;
			case Encoding::MM7: source = &MM7; break;
			}

			swapAllocation(source, destination);
		}

		return movq;
	}

	Encoding *CodeGenerator::movq(OperandMMREG mm, OperandMEM64 mem64)
	{
		return Assembler::movq(mm, mem64);
	}

	Encoding *CodeGenerator::movq(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(mm == r_m64) return 0;

		return Assembler::movq(mm, r_m64);
	}

	Encoding *CodeGenerator::movq(OperandMEM64 mem64, OperandMMREG mm)
	{
		return Assembler::movq(mem64, mm);
	}

	Encoding *CodeGenerator::movq(OperandR_M64 r_m64, OperandMMREG mm)
	{
		if(r_m64 == mm) return 0;

		return Assembler::movq(r_m64, mm);
	}

	Encoding *CodeGenerator::movss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(xmmi == xmmj) return 0;

		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movss(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::movss(OperandXMMREG xmm, OperandMEM32 m32)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), m32);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(dword_ptr [&sse[i][1]], 0);
			mov(dword_ptr [&sse[i][2]], 0);
			mov(dword_ptr [&sse[i][3]], 0);

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movss(xmm, m32);
		}
	}

	Encoding *CodeGenerator::movss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return movss(xmm, (OperandXMMREG)xmm32);
			else                              return movss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::movss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::movss(OperandMEM32 m32, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov(m32, r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::movss(m32, xmm);
		}
	}

	Encoding *CodeGenerator::movss(OperandXMM32 xmm32, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return movss((OperandXMMREG)xmm32, xmm);
			else                              return movss((OperandMEM32)xmm32, xmm);
		}
		else
		{
			return Assembler::movss(xmm32, xmm);
		}
	}

	Encoding *CodeGenerator::movups(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			return movaps(xmmi, xmmj);
		}
		else
		{
			return Assembler::movups(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::movups(OperandXMMREG xmm, OperandMEM128 m128)
	{
		if(emulateSSE)
		{
			return movaps(xmm, m128);
		}
		else
		{
			return Assembler::movups(xmm, m128);
		}
	}

	Encoding *CodeGenerator::movups(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			return movaps(xmm, r_m128);
		}
		else
		{
			return Assembler::movups(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::movups(OperandMEM128 m128, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			return movaps(m128, xmm);
		}
		else
		{
			return Assembler::movups(m128, xmm);
		}
	}

	Encoding *CodeGenerator::movups(OperandR_M128 r_m128, OperandXMMREG xmm)
	{
		if(emulateSSE)
		{
			return movaps(r_m128, xmm);
		}
		else
		{
			return Assembler::movups(r_m128, xmm);
		}
	}

	Encoding *CodeGenerator::mulps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fmul(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[i][1]]);
			fmul(dword_ptr [&sse[j][1]]);
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[i][2]]);
			fmul(dword_ptr [&sse[j][2]]);
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[i][3]]);
			fmul(dword_ptr [&sse[j][3]]);
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::mulps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::mulps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fmul((OperandMEM32)(mem128+0));
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[i][1]]);
			fmul((OperandMEM32)(mem128+4));
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[i][2]]);
			fmul((OperandMEM32)(mem128+8));
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[i][3]]);
			fmul((OperandMEM32)(mem128+12));
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::mulps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::mulps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return mulps(xmm, (OperandXMMREG)r_m128);
			else                               return mulps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::mulps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::mulss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fmul(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::mulss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::mulss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fmul((OperandMEM32)mem32);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::mulss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::mulss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return mulss(xmm, (OperandXMMREG)xmm32);
			else                              return mulss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::mulss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::orps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			or(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			or(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			or(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			or(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::orps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::orps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(mem128+0));
			or(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+4));
			or(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+8));
			or(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+12));
			or(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::orps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::orps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return orps(xmm, (OperandXMMREG)r_m128);
			else                               return orps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::orps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::pavgb(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			static unsigned char byte1[8];
			static unsigned char byte2[8];

			movq(qword_ptr [byte1], mmi);
			movq(qword_ptr [byte2], mmj);

			static int t0;
			static int t1;

			movzx(x32(&t0), byte_ptr [&byte1[0]]);
			movzx(x32(&t1), byte_ptr [&byte2[0]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[0]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[1]]);
			movzx(x32(&t1), byte_ptr [&byte2[1]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[1]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[2]]);
			movzx(x32(&t1), byte_ptr [&byte2[2]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[2]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[3]]);
			movzx(x32(&t1), byte_ptr [&byte2[3]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[3]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[4]]);
			movzx(x32(&t1), byte_ptr [&byte2[4]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[4]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[5]]);
			movzx(x32(&t1), byte_ptr [&byte2[5]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[5]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[6]]);
			movzx(x32(&t1), byte_ptr [&byte2[6]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[6]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[7]]);
			movzx(x32(&t1), byte_ptr [&byte2[7]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[7]], r8(&t0));

			movq(mmi, qword_ptr [byte1]);

			free(&t0);
			free(&t1);
			return 0;
		}
		else
		{
			return Assembler::pavgb(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pavgb(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			static unsigned char byte1[8];

			movq(qword_ptr [byte1], mm);

			static int t0;
			static int t1;

			movzx(x32(&t0), byte_ptr [&byte1[0]]);
			movzx(x32(&t1), (OperandMEM8)(m64+0));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[0]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[1]]);
			movzx(x32(&t1), (OperandMEM8)(m64+1));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[1]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[2]]);
			movzx(x32(&t1), (OperandMEM8)(m64+2));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[2]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[3]]);
			movzx(x32(&t1), (OperandMEM8)(m64+3));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[3]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[4]]);
			movzx(x32(&t1), (OperandMEM8)(m64+4));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[4]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[5]]);
			movzx(x32(&t1), (OperandMEM8)(m64+5));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[5]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[6]]);
			movzx(x32(&t1), (OperandMEM8)(m64+6));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[6]], r8(&t0));

			movzx(x32(&t0), byte_ptr [&byte1[7]]);
			movzx(x32(&t1), (OperandMEM8)(m64+7));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(byte_ptr [&byte1[7]], r8(&t0));

			movq(mm, qword_ptr [byte1]);

			free(&t0);
			free(&t1);
			return 0;
		}
		else
		{
			return Assembler::pavgb(mm, m64);
		}
	}

	Encoding *CodeGenerator::pavgb(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			if(r_m64.type == Operand::MMREG) return pavgb(mm, (OperandMMREG)r_m64);
			else                             return pavgb(mm, (OperandMEM64)r_m64);
		}
		else
		{
			return Assembler::pavgb(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::pavgw(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			static unsigned short word1[4];
			static unsigned short word2[4];

			movq(qword_ptr [word1], mmi);
			movq(qword_ptr [word2], mmj);

			static int t0;
			static int t1;

			movzx(x32(&t0), word_ptr [&word1[0]]);
			movzx(x32(&t1), word_ptr [&word2[0]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[0]], r16(&t0));

			movzx(x32(&t0), word_ptr [&word1[1]]);
			movzx(x32(&t1), word_ptr [&word2[1]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[1]], r16(&t0));

			movzx(x32(&t0), word_ptr [&word1[2]]);
			movzx(x32(&t1), word_ptr [&word2[2]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[2]], r16(&t0));

			movzx(x32(&t0), word_ptr [&word1[3]]);
			movzx(x32(&t1), word_ptr [&word2[3]]);
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[3]], r16(&t0));

			movq(mmi, qword_ptr [word1]);

			free(&t0);
			free(&t1);
			return 0;
		}
		else
		{
			return Assembler::pavgw(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pavgw(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			static unsigned char word1[8];

			movq(qword_ptr [word1], mm);

			static int t0;
			static int t1;

			movzx(x32(&t0), word_ptr [&word1[0]]);
			movzx(x32(&t1), (OperandMEM16)(m64+0));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[0]], r16(&t0));

			movzx(x32(&t0), word_ptr [&word1[1]]);
			movzx(x32(&t1), (OperandMEM16)(m64+2));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[1]], r16(&t0));

			movzx(x32(&t0), word_ptr [&word1[2]]);
			movzx(x32(&t1), (OperandMEM16)(m64+4));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[2]], r16(&t0));

			movzx(x32(&t0), word_ptr [&word1[3]]);
			movzx(x32(&t1), (OperandMEM16)(m64+6));
			add(r32(&t0), r32(&t1));
			shr(r32(&t0), 1);
			mov(word_ptr [&word1[3]], r16(&t0));

			movq(mm, qword_ptr [word1]);

			free(&t0);
			free(&t1);
			return 0;
		}
		else
		{
			return Assembler::pavgw(mm, m64);
		}
	}

	Encoding *CodeGenerator::pavgw(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			if(r_m64.type == Operand::MMREG) return pavgw(mm, (OperandMMREG)r_m64);
			else                             return pavgw(mm, (OperandMEM64)r_m64);
		}
		else
		{
			return Assembler::pavgw(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::pextrw(OperandREG32 r32, OperandMMREG mm, unsigned char c)
	{
		if(emulateSSE)
		{
			static short word[4];

			movq(qword_ptr [word], mm);
			xor(r32, r32);
			mov((OperandREG16)r32, word_ptr [&word[c & 0x03]]);

			return 0;
		}
		else
		{
			return Assembler::pextrw(r32, mm, c);
		}
	}

	Encoding *CodeGenerator::pinsrw(OperandMMREG mm, OperandREG16 r16, unsigned char c)
	{
		if(emulateSSE)
		{
			static short word[4];

			movq(qword_ptr [word], mm);
			mov(word_ptr [&word[c & 0x03]], r16);
			movq(mm, qword_ptr [word]);

			return 0;
		}
		else
		{
			return Assembler::pinsrw(mm, r16, c);
		}
	}

	Encoding *CodeGenerator::pinsrw(OperandMMREG mm, OperandMEM16 m16, unsigned char c)
	{
		if(emulateSSE)
		{
			static short word[4];
			static int t0;

			movq(qword_ptr [word], mm);
			mov(x16(&t0), m16);
			mov(word_ptr [&word[c & 0x03]], r16(&t0));
			movq(mm, qword_ptr [word]);

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::pinsrw(mm, m16, c);
		}
	}

	Encoding *CodeGenerator::pinsrw(OperandMMREG mm, OperandR_M16 r_m16, unsigned char c)
	{
		if(emulateSSE)
		{
			if(r_m16.type == Operand::REG16) return pinsrw(mm, (OperandREG16)r_m16, c);
			else                             return pinsrw(mm, (OperandMEM16)r_m16, c);
		}
		else
		{
			return Assembler::pinsrw(mm, r_m16, c);
		}
	}

	Encoding *CodeGenerator::pmaxsw(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pmaxsw(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pmaxsw(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pmaxsw(mm, m64);
		}
	}

	Encoding *CodeGenerator::pmaxsw(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pmaxsw(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::pmaxub(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pmaxub(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pmaxub(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pmaxub(mm, m64);
		}
	}

	Encoding *CodeGenerator::pmaxub(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pmaxub(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::pminsw(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pminsw(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pminsw(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pminsw(mm, m64);
		}
	}

	Encoding *CodeGenerator::pminsw(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pminsw(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::pminub(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pminub(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pminub(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pminub(mm, m64);
		}
	}

	Encoding *CodeGenerator::pminub(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			throw Error("Unimplemented SSE instruction emulation");
		}
		else
		{
			return Assembler::pminub(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::pmulhuw(OperandMMREG mmi, OperandMMREG mmj)
	{
		if(emulateSSE)
		{
			static short word1[4];
			static short word2[4];

			movq(qword_ptr [word1], mmi);
			movq(qword_ptr [word2], mmj);
			push(eax);
			push(edx);

			mov(ax, word_ptr [&word1[0]]);
			mul(word_ptr [&word2[0]]);
			mov(word_ptr [&word1[0]], dx);

			mov(ax, word_ptr [&word1[1]]);
			mul(word_ptr [&word2[1]]);
			mov(word_ptr [&word1[1]], dx);

			mov(ax, word_ptr [&word1[2]]);
			mul(word_ptr [&word2[2]]);
			mov(word_ptr [&word1[2]], dx);

			mov(ax, word_ptr [&word1[3]]);
			mul(word_ptr [&word2[3]]);
			mov(word_ptr [&word1[3]], dx);

			pop(edx);
			pop(eax);
			movq(mmi, qword_ptr [word1]);

			return 0;
		}
		else
		{
			return Assembler::pmulhuw(mmi, mmj);
		}
	}

	Encoding *CodeGenerator::pmulhuw(OperandMMREG mm, OperandMEM64 m64)
	{
		if(emulateSSE)
		{
			static short word1[4];
			static short word2[4];

			movq(qword_ptr [word1], mm);
			movq(mm, m64);
			movq(qword_ptr [word2], mm);
			push(eax);
			push(edx);

			mov(ax, word_ptr [&word1[0]]);
			mul(word_ptr [&word2[0]]);
			mov(word_ptr [&word1[0]], dx);

			mov(ax, word_ptr [&word1[1]]);
			mul(word_ptr [&word2[1]]);
			mov(word_ptr [&word1[1]], dx);

			mov(ax, word_ptr [&word1[2]]);
			mul(word_ptr [&word2[2]]);
			mov(word_ptr [&word1[2]], dx);

			mov(ax, word_ptr [&word1[3]]);
			mul(word_ptr [&word2[3]]);
			mov(word_ptr [&word1[3]], dx);

			pop(edx);
			pop(eax);
			movq(mm, qword_ptr [word1]);

			return 0;
		}
		else
		{
			return Assembler::pmulhuw(mm, m64);
		}
	}

	Encoding *CodeGenerator::pmulhuw(OperandMMREG mm, OperandR_M64 r_m64)
	{
		if(emulateSSE)
		{
			if(r_m64.type == Operand::MMREG) return pmulhuw(mm, (OperandMMREG)r_m64);
			else                             return pmulhuw(mm, (OperandMEM64)r_m64);
		}
		else
		{
			return Assembler::pmulhuw(mm, r_m64);
		}
	}

	Encoding *CodeGenerator::prefetchnta(OperandMEM mem)
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return prefetchnta(mem);
		}
	}

	Encoding *CodeGenerator::prefetcht0(OperandMEM mem)
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return prefetcht0(mem);
		}
	}

	Encoding *CodeGenerator::prefetcht1(OperandMEM mem)
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return prefetcht1(mem);
		}
	}

	Encoding *CodeGenerator::prefetcht2(OperandMEM mem)
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return prefetcht2(mem);
		}
	}

	Encoding *CodeGenerator::pshufw(OperandMMREG mmi, OperandMMREG mmj, unsigned char c)
	{
		if(c == 0xE4)
		{
			if(mmi == mmj) return 0;
			else return movq(mmi, mmj);
		}

		if(emulateSSE)
		{
			static short word1[4];
			static short word2[4];
			static int t0;

			movq(qword_ptr [word1], mmj);

			mov(x16(&t0), word_ptr [&word1[(c >> 0) & 0x03]]);
			mov(word_ptr [&word2[0]], r16(&t0));

			mov(x16(&t0), word_ptr [&word1[(c >> 2) & 0x03]]);
			mov(word_ptr [&word2[1]], r16(&t0));

			mov(x16(&t0), word_ptr [&word1[(c >> 4) & 0x03]]);
			mov(word_ptr [&word2[2]], r16(&t0));

			mov(x16(&t0), word_ptr [&word1[(c >> 6) & 0x03]]);
			mov(word_ptr [&word2[3]], r16(&t0));

			movq(mmi, qword_ptr [word2]);

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::pshufw(mmi, mmj, c);
		}
	}

	Encoding *CodeGenerator::pshufw(OperandMMREG mm, OperandMEM64 m64, unsigned char c)
	{
		if(emulateSSE)
		{
			static short word[4];
			static int t0;

			mov(x16(&t0), (OperandMEM16)(m64+((c>>0)&0x03)*2));
			mov(word_ptr [&word[0]], r16(&t0));

			mov(x16(&t0), (OperandMEM16)(m64+((c>>2)&0x03)*2));
			mov(word_ptr [&word[1]], r16(&t0));

			mov(x16(&t0), (OperandMEM16)(m64+((c>>4)&0x03)*2));
			mov(word_ptr [&word[2]], r16(&t0));

			mov(x16(&t0), (OperandMEM16)(m64+((c>>6)&0x03)*2));
			mov(word_ptr [&word[3]], r16(&t0));

			movq(mm, qword_ptr [word]);

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::pshufw(mm, m64, c);
		}
	}

	Encoding *CodeGenerator::pshufw(OperandMMREG mm, OperandR_M64 r_m64, unsigned char c)
	{
		if(emulateSSE)
		{
			if(r_m64.type == Operand::MMREG) return pshufw(mm, (OperandMMREG)r_m64, c);
			else                             return pshufw(mm, (OperandMEM64)r_m64, c);
		}
		else
		{
			return Assembler::pshufw(mm, r_m64, c);
		}
	}

	Encoding *CodeGenerator::rcpps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][1]]);
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][2]]);
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][3]]);
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::rcpps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::rcpps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+0));
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+4));
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+8));
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+12));
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::rcpps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::rcpps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return rcpps(xmm, (OperandXMMREG)r_m128);
			else                               return rcpps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::rcpps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::rcpss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::rcpss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::rcpss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)mem32);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::rcpss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::rcpss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return rcpss(xmm, (OperandXMMREG)xmm32);
			else                              return rcpss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::rcpss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::rsqrtps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][0]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][1]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][2]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][3]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::rsqrtps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::rsqrtps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+0));
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+4));
			fsqrt();
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+8));
			fsqrt();
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)(mem128+12));
			fsqrt();
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::rsqrtps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::rsqrtps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return rsqrtps(xmm, (OperandXMMREG)r_m128);
			else                               return rsqrtps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::rsqrtps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::rsqrtss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv(dword_ptr [&sse[j][0]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::rsqrtss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::rsqrtss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float one = 1.0f;
			fld(dword_ptr [&one]);
			fdiv((OperandMEM32)mem32);
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::rsqrtss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::rsqrtss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return rsqrtss(xmm, (OperandXMMREG)xmm32);
			else                              return rsqrtss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::rsqrtss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::sfence()
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return Assembler::sfence();
		}
	}

	Encoding *CodeGenerator::shufps(OperandXMMREG xmmi, OperandXMMREG xmmj, unsigned char c)
	{
		if(c == 0xE4)
		{
			if(xmmi == xmmj) return 0;
			else return movaps(xmmi, xmmj);
		}

		if(emulateSSE)
		{
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			static int t0;

			mov(x32(&t0), dword_ptr [&sse[i][(c >> 0) & 0x03]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][(c >> 2) & 0x03]]);
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][(c >> 4) & 0x03]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][(c >> 6) & 0x03]]);
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::shufps(xmmi, xmmj, c);
		}
	}

	Encoding *CodeGenerator::shufps(OperandXMMREG xmm, OperandMEM128 m128, unsigned char c)
	{
		if(emulateSSE)
		{
			const int i = xmm.reg;
			static int t0;

			mov(x32(&t0), dword_ptr [&sse[i][(c >> 0) & 0x03]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][(c >> 2) & 0x03]]);
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m128+((c>>4)&0x03)*4));
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(m128+((c>>6)&0x03)*4));
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::shufps(xmm, m128, c);
		}
	}

	Encoding *CodeGenerator::shufps(OperandXMMREG xmm, OperandR_M128 r_m128, unsigned char c)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return shufps(xmm, (OperandXMMREG)r_m128, c);
			else                               return shufps(xmm, (OperandMEM128)r_m128, c);
		}
		else
		{
			return Assembler::shufps(xmm, r_m128, c);
		}
	}

	Encoding *CodeGenerator::sqrtps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[j][0]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[j][1]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[j][2]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[j][3]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::sqrtps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::sqrtps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld((OperandMEM32)(mem128+0));
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			fld((OperandMEM32)(mem128+4));
			fsqrt();
			fstp(dword_ptr [&sse[i][1]]);
			fld((OperandMEM32)(mem128+8));
			fsqrt();
			fstp(dword_ptr [&sse[i][2]]);
			fld((OperandMEM32)(mem128+12));
			fsqrt();
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::sqrtps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::sqrtps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return sqrtps(xmm, (OperandXMMREG)r_m128);
			else                               return sqrtps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::sqrtps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::sqrtss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[j][0]]);
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::sqrtss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::sqrtss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			static float one = 1.0f;
			fld(mem32);
			fsqrt();
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::sqrtss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::sqrtss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return sqrtss(xmm, (OperandXMMREG)xmm32);
			else                              return sqrtss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::sqrtss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::stmxcsr(OperandMEM32 m32)
	{
		if(emulateSSE)
		{
			return 0;
		}
		else
		{
			return Assembler::stmxcsr(m32);
		}
	}

	Encoding *CodeGenerator::subps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fsub(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[i][1]]);
			fsub(dword_ptr [&sse[j][1]]);
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[i][2]]);
			fsub(dword_ptr [&sse[j][2]]);
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[i][3]]);
			fsub(dword_ptr [&sse[j][3]]);
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::subps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::subps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fsub((OperandMEM32)(mem128+0));
			fstp(dword_ptr [&sse[i][0]]);
			fld(dword_ptr [&sse[i][1]]);
			fsub((OperandMEM32)(mem128+4));
			fstp(dword_ptr [&sse[i][1]]);
			fld(dword_ptr [&sse[i][2]]);
			fsub((OperandMEM32)(mem128+8));
			fstp(dword_ptr [&sse[i][2]]);
			fld(dword_ptr [&sse[i][3]]);
			fsub((OperandMEM32)(mem128+12));
			fstp(dword_ptr [&sse[i][3]]);
			return 0;
		}
		else
		{
			return Assembler::subps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::subps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return subps(xmm, (OperandXMMREG)r_m128);
			else                               return subps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::subps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::subss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;
			fld(dword_ptr [&sse[i][0]]);
			fsub(dword_ptr [&sse[j][0]]);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::subss(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::subss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			fld(dword_ptr [&sse[i][0]]);
			fsub((OperandMEM32)mem32);
			fstp(dword_ptr [&sse[i][0]]);
			return 0;
		}
		else
		{
			return Assembler::subss(xmm, mem32);
		}
	}
	
	Encoding *CodeGenerator::subss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return subss(xmm, (OperandXMMREG)xmm32);
			else                              return subss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::subss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::ucomiss(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			fld(dword_ptr [&sse[j][0]]);
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::ucomiss(xmmi, xmmj);
		}
	}

	Encoding *CodeGenerator::ucomiss(OperandXMMREG xmm, OperandMEM32 mem32)
	{
		if(emulateSSE)
		{
			spillMMX();
			const int i = xmm.reg;
			
			fld(mem32);
			fld(dword_ptr [&sse[i][0]]);
			fcomip(st0, st1);
			ffree(st0);

			return 0;
		}
		else
		{
			return Assembler::ucomiss(xmm, mem32);
		}
	}

	Encoding *CodeGenerator::ucomiss(OperandXMMREG xmm, OperandXMM32 xmm32)
	{
		if(emulateSSE)
		{
			if(xmm32.type == Operand::XMMREG) return ucomiss(xmm, (OperandXMMREG)xmm32);
			else                              return ucomiss(xmm, (OperandMEM32)xmm32);
		}
		else
		{
			return Assembler::ucomiss(xmm, xmm32);
		}
	}

	Encoding *CodeGenerator::unpckhps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[i][2]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][3]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::unpckhps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::unpckhps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][2]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][3]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+8));
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+12));
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::unpckhps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::unpckhps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return unpckhps(xmm, (OperandXMMREG)r_m128);
			else                               return unpckhps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::unpckhps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::unpcklps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::unpcklps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::unpcklps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), dword_ptr [&sse[i][0]]);
			mov(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[i][1]]);
			mov(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+0));
			mov(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+4));
			mov(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::unpcklps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::unpcklps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return unpcklps(xmm, (OperandXMMREG)r_m128);
			else                               return unpcklps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::unpcklps(xmm, r_m128);
		}
	}

	Encoding *CodeGenerator::xadd(OperandR_M8 r_m8, OperandREG8 r8)
	{
		markModified(r_m8);
		markModified(r8);

		return Assembler::xadd(r_m8, r8);
	}

	Encoding *CodeGenerator::xadd(OperandR_M16 r_m16, OperandREG16 r16)
	{
		markModified(r_m16);
		markModified(r16);

		return Assembler::xadd(r_m16, r16);
	}

	Encoding *CodeGenerator::xadd(OperandR_M32 r_m32, OperandREG32 r32)
	{
		markModified(r_m32);
		markModified(r32);

		return Assembler::xadd(r_m32, r32);
	}

	Encoding *CodeGenerator::xchg(OperandR_M8 r_m8, OperandREG8 r8)
	{
		markModified(r_m8);
		markModified(r8);

		return Assembler::xchg(r_m8, r8);
	}

	Encoding *CodeGenerator::xchg(OperandR_M16 r_m16, OperandREG16 r16)
	{
		markModified(r_m16);
		markModified(r16);

		return Assembler::xchg(r_m16, r16);
	}

	Encoding *CodeGenerator::xchg(OperandR_M32 r_m32, OperandREG32 r32)
	{
		markModified(r_m32);
		markModified(r32);

		return Assembler::xchg(r_m32, r32);
	}

	Encoding *CodeGenerator::xchg(OperandREG8 r8, OperandR_M8 r_m8)
	{
		markModified(r8);
		markModified(r_m8);

		return Assembler::xchg(r8, r_m8);
	}

	Encoding *CodeGenerator::xchg(OperandREG16 r16, OperandR_M16 r_m16)
	{
		markModified(r16);
		markModified(r_m16);

		return Assembler::xchg(r16, r_m16);
	}

	Encoding *CodeGenerator::xchg(OperandREG32 r32, OperandR_M32 r_m32)
	{
		markModified(r32);
		markModified(r_m32);

		return Assembler::xchg(r32, r_m32);
	}

	Encoding *CodeGenerator::lock_xadd(OperandMEM8 m8, OperandREG8 r8)
	{
		markModified(m8);
		markModified(r8);

		return Assembler::lock_xadd(m8, r8);
	}

	Encoding *CodeGenerator::lock_xadd(OperandMEM16 m16, OperandREG16 r16)
	{
		markModified(m16);
		markModified(r16);

		return Assembler::lock_xadd(m16, r16);
	}

	Encoding *CodeGenerator::lock_xadd(OperandMEM32 m32, OperandREG32 r32)
	{
		markModified(m32);
		markModified(r32);

		return Assembler::lock_xadd(m32, r32);
	}

	Encoding *CodeGenerator::lock_xchg(OperandMEM8 m8, OperandREG8 r8)
	{
		markModified(m8);
		markModified(r8);

		return Assembler::lock_xadd(m8, r8);
	}

	Encoding *CodeGenerator::lock_xchg(OperandMEM16 m16, OperandREG16 r16)
	{
		markModified(m16);
		markModified(r16);

		return Assembler::lock_xadd(m16, r16);
	}

	Encoding *CodeGenerator::lock_xchg(OperandMEM32 m32, OperandREG32 r32)
	{
		markModified(m32);
		markModified(r32);

		return Assembler::lock_xadd(m32, r32);
	}

	Encoding *CodeGenerator::xorps(OperandXMMREG xmmi, OperandXMMREG xmmj)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmmi.reg;
			const int j = xmmj.reg;

			mov(x32(&t0), dword_ptr [&sse[j][0]]);
			xor(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][1]]);
			xor(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][2]]);
			xor(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), dword_ptr [&sse[j][3]]);
			xor(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::xorps(xmmi, xmmj);
		}
	}
	
	Encoding *CodeGenerator::xorps(OperandXMMREG xmm, OperandMEM128 mem128)
	{
		if(emulateSSE)
		{
			static int t0;
			const int i = xmm.reg;

			mov(x32(&t0), (OperandMEM32)(mem128+0));
			xor(dword_ptr [&sse[i][0]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+4));
			xor(dword_ptr [&sse[i][1]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+8));
			xor(dword_ptr [&sse[i][2]], r32(&t0));

			mov(x32(&t0), (OperandMEM32)(mem128+12));
			xor(dword_ptr [&sse[i][3]], r32(&t0));

			free(&t0);
			return 0;
		}
		else
		{
			return Assembler::xorps(xmm, mem128);
		}
	}
	
	Encoding *CodeGenerator::xorps(OperandXMMREG xmm, OperandR_M128 r_m128)
	{
		if(emulateSSE)
		{
			if(r_m128.type == Operand::XMMREG) return xorps(xmm, (OperandXMMREG)r_m128);
			else                               return xorps(xmm, (OperandMEM128)r_m128);
		}
		else
		{
			return Assembler::xorps(xmm, r_m128);
		}
	}

	void CodeGenerator::dumpSSE()
	{
		pushad();
		emms();

		static float sse[8][4];

		movups(xword_ptr [sse[0]], xmm0);
		movups(xword_ptr [sse[1]], xmm1);
		movups(xword_ptr [sse[2]], xmm2);
		movups(xword_ptr [sse[3]], xmm3);
		movups(xword_ptr [sse[4]], xmm4);
		movups(xword_ptr [sse[5]], xmm5);
		movups(xword_ptr [sse[6]], xmm6);
		movups(xword_ptr [sse[7]], xmm7);

		static FILE *file;
		static char *perm = "a";
		static char *name;

		if(emulateSSE)
		{
			name = "dumpEmulate.txt";
		}
		else
		{
			name = "dumpNative.txt";
		}

		mov(eax, dword_ptr [&perm]); 
		push(eax);
		mov(ecx, dword_ptr [&name]); 
		push(ecx);
		call((int)fopen);
		add(esp, 8);
		mov(dword_ptr [&file], eax);

		static char *string0 = "xmm0: %f, %f, %f, %f\n";
		static char *string1 = "xmm1: %f, %f, %f, %f\n";
		static char *string2 = "xmm2: %f, %f, %f, %f\n";
		static char *string3 = "xmm3: %f, %f, %f, %f\n";
		static char *string4 = "xmm4: %f, %f, %f, %f\n";
		static char *string5 = "xmm5: %f, %f, %f, %f\n";
		static char *string6 = "xmm6: %f, %f, %f, %f\n";
		static char *string7 = "xmm7: %f, %f, %f, %f\n";
		static char *newline = "\n";

		// fprintf(file, string0, sse[0][0], sse[0][1], sse[0][2], sse[0][3]);
		fld(dword_ptr [&sse[0][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[0][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[0][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[0][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string0]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, string1, sse[1][0], sse[1][1], sse[1][2], sse[1][3]);
		fld(dword_ptr [&sse[1][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[1][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[1][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[1][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string1]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, string2, sse[2][0], sse[2][1], sse[2][2], sse[2][3]);
		fld(dword_ptr [&sse[2][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[2][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[2][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[2][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string2]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, string3, sse[3][0], sse[3][1], sse[3][2], sse[3][3]);
		fld(dword_ptr [&sse[3][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[3][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[3][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[3][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string3]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, string4, sse[4][0], sse[4][1], sse[4][2], sse[4][3]);
		fld(dword_ptr [&sse[4][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[4][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[4][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[4][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string4]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, string5, sse[5][0], sse[5][1], sse[5][2], sse[5][3]);
		fld(dword_ptr [&sse[5][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[5][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[5][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[5][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string5]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28);

		// fprintf(file, string6, sse[6][0], sse[6][1], sse[6][2], sse[6][3]);
		fld(dword_ptr [&sse[6][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[6][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[6][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[6][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string6]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, string7, sse[7][0], sse[7][1], sse[7][2], sse[7][3]);
		fld(dword_ptr [&sse[7][3]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[7][2]]);
		sub(esp, 8); 
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[7][1]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		fld(dword_ptr [&sse[7][0]]);
		sub(esp, 8);
		fstp(qword_ptr [esp]);
		mov(eax, dword_ptr [&string7]); 
		push(eax);
		mov(ecx, dword_ptr [&file]); 
		push(ecx);
		call((int)fprintf); 
		add(esp, 0x28); 

		// fprintf(file, newline);
		mov(eax, dword_ptr [&newline]);
		push(eax);
		mov(ecx, dword_ptr [&file]);
		push(ecx);
		call((int)fprintf); 
		add(esp, 8);

		// fclose(file);
		mov(eax, dword_ptr [&file]); 
		push(eax);
		call((int)fclose);
		add(esp, 4);

		popad();

	//	int3();
	}

	void CodeGenerator::markModified(const Operand &op)
	{
		if(emulateSSE) return;

		if(Operand::isReg(op))
		{
			if(op.type == Operand::REG32 ||
			   op.type == Operand::REG16 ||
			   op.type == Operand::REG8 ||
			   op.type == Operand::EAX ||
			   op.type == Operand::ECX ||
			   op.type == Operand::AX ||
			   op.type == Operand::DX ||
			   op.type == Operand::CX ||
			   op.type == Operand::AL ||
			   op.type == Operand::CL)
			{
				switch(op.reg)
				{
				case Encoding::EAX: EAX.modified = true; return;
				case Encoding::ECX: ECX.modified = true; return;
				case Encoding::EDX: EDX.modified = true; return;
				case Encoding::EBX: EBX.modified = true; return;
				case Encoding::ESI: ESI.modified = true; return;
				case Encoding::EDI: EDI.modified = true; return;
				default: throw INTERNAL_ERROR;
				}
			}

			if(op.type == Operand::MMREG)
			{
				switch(op.reg)
				{
				case Encoding::MM0: MM0.modified = true; return;
				case Encoding::MM1: MM1.modified = true; return;
				case Encoding::MM2: MM2.modified = true; return;
				case Encoding::MM3: MM3.modified = true; return;
				case Encoding::MM4: MM4.modified = true; return;
				case Encoding::MM5: MM5.modified = true; return;
				case Encoding::MM6: MM6.modified = true; return;
				case Encoding::MM7: MM7.modified = true; return;
				default: throw INTERNAL_ERROR;
				}
			}

			if(op.type == Operand::XMMREG)
			{
				switch(op.reg)
				{
				case Encoding::XMM0: XMM0.modified = true; return;
				case Encoding::XMM1: XMM1.modified = true; return;
				case Encoding::XMM2: XMM2.modified = true; return;
				case Encoding::XMM3: XMM3.modified = true; return;
				case Encoding::XMM4: XMM4.modified = true; return;
				case Encoding::XMM5: XMM5.modified = true; return;
				case Encoding::XMM6: XMM6.modified = true; return;
				case Encoding::XMM7: XMM7.modified = true; return;
				default: throw INTERNAL_ERROR;
				}
			}

			throw INTERNAL_ERROR;
		}
	}

	Encoding *CodeGenerator::x86(int instructionID, const Operand &firstOperand, const Operand &secondOperand, const Operand &thirdOperand)
	{
		markModified(firstOperand);

		return Assembler::x86(instructionID, firstOperand, secondOperand, thirdOperand);
	}

	void CodeGenerator::swapAllocation(Allocation *source, Allocation *destination)
	{
		OperandREF swapRef = source->reference;
		source->reference = destination->reference;
		destination->reference = swapRef;

		int swapPriority = source->priority;
		source->priority = destination->priority;
		destination->priority = swapPriority;

		bool swapModified = source->modified;
		source->modified = destination->modified;
		destination->modified = swapModified;

		bool swapPartial = source->partial;
		source->partial = destination->partial;
		destination->partial = swapPartial;
	}
}
