#ifndef SoftWire_CodeGenerator_hpp
#define SoftWire_CodeGenerator_hpp

#include "Assembler.hpp"

namespace SoftWire
{
	class CodeGenerator : public Assembler
	{
	public:
		CodeGenerator();

		const OperandREG8 &r8(const OperandREF &ref, bool copy = true);
		const OperandREG8 &x8(const OperandREF &ref, bool copy = false);
		const OperandREG8 &t8(int i);
		const OperandR_M8 m8(const OperandREF &ref);

		const OperandREG16 &r16(const OperandREF &ref, bool copy = true);
		const OperandREG16 &x16(const OperandREF &ref, bool copy = false);
		const OperandREG16 &t16(int i);
		const OperandR_M16 m16(const OperandREF &ref);

		const OperandREG32 &r32(const OperandREF &ref, bool copy = true);
		const OperandREG32 &x32(const OperandREF &ref, bool copy = false);
		const OperandREG32 &t32(int i);
		const OperandR_M32 m32(const OperandREF &ref);
		const OperandREG32 &allocate(const OperandREG32 &reg, const OperandREF &ref, bool copy = false);
		const OperandREG32 &assign(const OperandREG32 &reg, const OperandREF &ref, bool copy = true);
		const OperandREG32 &access(const OperandREG32 &reg);
		void free(const OperandREG32 &reg);
		void spill(const OperandREG32 &reg);

		const OperandMMREG &r64(const OperandREF &ref, bool copy = true);
		const OperandMMREG &x64(const OperandREF &ref, bool copy = false);
		const OperandMMREG &t64(int i);
		const OperandR_M64 m64(const OperandREF &ref);
		const OperandMMREG &allocate(const OperandMMREG &reg, const OperandREF &ref, bool copy = false);
		const OperandMMREG &assign(const OperandMMREG &reg, const OperandREF &ref, bool copy = true);
		const OperandMMREG &access(const OperandMMREG &reg);
		void free(const OperandMMREG &reg);
		void spill(const OperandMMREG &reg);

		const OperandXMMREG &r128(const OperandREF &ref, bool copy = true);
		const OperandXMMREG &x128(const OperandREF &ref, bool copy = false);
		const OperandXMMREG &t128(int i);
		const OperandR_M128 m128(const OperandREF &ref);
		const OperandXMMREG &allocate(const OperandXMMREG &reg, const OperandREF &ref, bool copy = false);
		const OperandXMMREG &assign(const OperandXMMREG &reg, const OperandREF &ref, bool copy = true);
		const OperandXMMREG &access(const OperandXMMREG &reg);
		void free(const OperandXMMREG &reg);
		void spill(const OperandXMMREG &reg);

		bool real(const OperandREF &ref);

		void free(const OperandREF &ref);
		void spill(const OperandREF &ref);

		void freeAll();
		void spillAll();
		void spillMMX();   // Specifically for using FPU after MMX
		void spillMMXcept(const OperandMMREG &reg);   // Empty MMX state but leave one dangling

		static void enableEmulateSSE();
		static void disableEmulateSSE();

		// Overloaded to optimize or emulate
		int addps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int addps(OperandXMMREG xmm, OperandMEM128 mem128);
		int addps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int addss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int addss(OperandXMMREG xmm, OperandMEM32 mem32);
		int addss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int andnps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int andnps(OperandXMMREG xmm, OperandMEM128 mem128);
		int andnps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int andps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int andps(OperandXMMREG xmm, OperandMEM128 mem128);
		int andps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int cmpps(OperandXMMREG xmmi, OperandXMMREG xmmj, char c);
		int cmpps(OperandXMMREG xmm, OperandMEM128 mem128, char c);
		int cmpps(OperandXMMREG xmm, OperandR_M128 r_m128, char c);

		int cmpeqps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpeqps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpeqps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpleps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpleps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpleps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpltps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpltps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpltps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpneqps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpneqps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpneqps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpnleps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpnleps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpnleps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpnltps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpnltps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpnltps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpordps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpordps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpordps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int cmpunordps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpunordps(OperandXMMREG xmm, OperandMEM128 mem128);
		int cmpunordps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int cmpss(OperandXMMREG xmmi, OperandXMMREG xmmj, char c);
		int cmpss(OperandXMMREG xmm, OperandMEM32 mem32, char c);
		int cmpss(OperandXMMREG xmm, OperandXMM32 xmm32, char c);

		int cmpeqss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpeqss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpeqss(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpless(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpless(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpless(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpltss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpltss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpltss(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpneqss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpneqss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpneqss(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpnless(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpnless(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpnless(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpnltss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpnltss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpnltss(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpordss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpordss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpordss(OperandXMMREG xmm, OperandXMM32 xmm32);
		int cmpunordss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int cmpunordss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cmpunordss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int comiss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int comiss(OperandXMMREG xmm, OperandMEM32 mem32);
		int comiss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int cvtpi2ps(OperandXMMREG xmm, OperandMMREG mm);
		int cvtpi2ps(OperandXMMREG xmm, OperandMEM64 mem64);
		int cvtpi2ps(OperandXMMREG xmm, OperandR_M64 r_m64);

		int cvtps2pi(OperandMMREG mm, OperandXMMREG xmm);
		int cvtps2pi(OperandMMREG mm, OperandMEM64 mem64);
		int cvtps2pi(OperandMMREG mm, OperandXMM64 xmm64);

		int cvttps2pi(OperandMMREG mm, OperandXMMREG xmm);
		int cvttps2pi(OperandMMREG mm, OperandMEM64 mem64);
		int cvttps2pi(OperandMMREG mm, OperandXMM64 xmm64);

		int cvtsi2ss(OperandXMMREG xmm, OperandREG32 reg32);
		int cvtsi2ss(OperandXMMREG xmm, OperandMEM32 mem32);
		int cvtsi2ss(OperandXMMREG xmm, OperandR_M32 r_m32);

		int cvtss2si(OperandREG32 reg32, OperandXMMREG xmm);
		int cvtss2si(OperandREG32 reg32, OperandMEM32 mem32);
		int cvtss2si(OperandREG32 reg32, OperandXMM32 xmm32);

		int cvttss2si(OperandREG32 reg32, OperandXMMREG xmm);
		int cvttss2si(OperandREG32 reg32, OperandMEM32 mem32);
		int cvttss2si(OperandREG32 reg32, OperandXMM32 xmm32);

		int divps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int divps(OperandXMMREG xmm, OperandMEM128 mem128);
		int divps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int divss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int divss(OperandXMMREG xmm, OperandMEM32 mem32);
		int divss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int ldmxcsr(OperandMEM32 mem32);

		int maskmovq(OperandMMREG mmi, OperandMMREG mmj);

		int maxps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int maxps(OperandXMMREG xmm, OperandMEM128 mem128);
		int maxps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int maxss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int maxss(OperandXMMREG xmm, OperandMEM32 mem32);
		int maxss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int minps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int minps(OperandXMMREG xmm, OperandMEM128 mem128);
		int minps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int minss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int minss(OperandXMMREG xmm, OperandMEM32 mem32);
		int minss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int movaps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int movaps(OperandXMMREG xmm, OperandMEM128 m128);
		int movaps(OperandXMMREG xmm, OperandR_M128 r_m128);
		int movaps(OperandMEM128 m128, OperandXMMREG xmm);
		int movaps(OperandR_M128 r_m128, OperandXMMREG xmm);

		int movhlps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int movhps(OperandXMMREG xmm, OperandMEM64 m64);
		int movhps(OperandMEM64 m64, OperandXMMREG xmm);
		int movhps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int movlhps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int movlps(OperandXMMREG xmm, OperandMEM64 m64);
		int movlps(OperandMEM64 m64, OperandXMMREG xmm);
		int movlps(OperandXMMREG xmmi, OperandXMMREG xmmj);

		int movmskps(OperandREG32 r32, OperandXMMREG xmm);

		int movntps(OperandMEM128 m128, OperandXMMREG xmm);
		int movntq(OperandMEM64 m64, OperandMMREG mm);

		int movq(OperandMMREG mmi, OperandMMREG mmj);
		int movq(OperandMMREG mm, OperandMEM64 mem64);
		int movq(OperandMMREG mm, OperandR_M64 r_m64);
		int movq(OperandMEM64 mem64, OperandMMREG mm);
		int movq(OperandR_M64 r_m64, OperandMMREG mm);

		int movss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int movss(OperandXMMREG xmm, OperandMEM32 m32);
		int movss(OperandXMMREG xmm, OperandXMM32 r_m32);
		int movss(OperandMEM32 m32, OperandXMMREG xmm);
		int movss(OperandXMM32 r_m32, OperandXMMREG xmm);

		int movups(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int movups(OperandXMMREG xmm, OperandMEM128 m128);
		int movups(OperandXMMREG xmm, OperandR_M128 r_m128);
		int movups(OperandMEM128 m128, OperandXMMREG xmm);
		int movups(OperandR_M128 r_m128, OperandXMMREG xmm);

		int mulps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int mulps(OperandXMMREG xmm, OperandMEM128 mem128);
		int mulps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int mulss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int mulss(OperandXMMREG xmm, OperandMEM32 mem32);
		int mulss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int orps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int orps(OperandXMMREG xmm, OperandMEM128 mem128);
		int orps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int pavgb(OperandMMREG mmi, OperandMMREG mmj);
		int pavgb(OperandMMREG mm, OperandMEM64 m64);
		int pavgb(OperandMMREG mm, OperandR_M64 r_m64);

		int pavgw(OperandMMREG mmi, OperandMMREG mmj);
		int pavgw(OperandMMREG mm, OperandMEM64 m64);
		int pavgw(OperandMMREG mm, OperandR_M64 r_m64);

		int pextrw(OperandREG32 r32, OperandMMREG mm, unsigned char c);
		int pinsrw(OperandMMREG mm, OperandREG16 r16, unsigned char c);
		int pinsrw(OperandMMREG mm, OperandMEM16 m16, unsigned char c);
		int pinsrw(OperandMMREG mm, OperandR_M16 r_m16, unsigned char c);

		int pmaxsw(OperandMMREG mmi, OperandMMREG mmj);
		int pmaxsw(OperandMMREG mm, OperandMEM64 m64);
		int pmaxsw(OperandMMREG mm, OperandR_M64 r_m64);

		int pmaxub(OperandMMREG mmi, OperandMMREG mmj);
		int pmaxub(OperandMMREG mm, OperandMEM64 m64);
		int pmaxub(OperandMMREG mm, OperandR_M64 r_m64);

		int pminsw(OperandMMREG mmi, OperandMMREG mmj);
		int pminsw(OperandMMREG mm, OperandMEM64 m64);
		int pminsw(OperandMMREG mm, OperandR_M64 r_m64);

		int pminub(OperandMMREG mmi, OperandMMREG mmj);
		int pminub(OperandMMREG mm, OperandMEM64 m64);
		int pminub(OperandMMREG mm, OperandR_M64 r_m64);

		int pmulhuw(OperandMMREG mmi, OperandMMREG mmj);
		int pmulhuw(OperandMMREG mm, OperandMEM64 m64);
		int pmulhuw(OperandMMREG mm, OperandR_M64 r_m64);

		int prefetchnta(OperandMEM mem);
		int prefetcht0(OperandMEM mem);
		int prefetcht1(OperandMEM mem);
		int prefetcht2(OperandMEM mem);

		int pshufw(OperandMMREG mmi, OperandMMREG mmj, unsigned char c);
		int pshufw(OperandMMREG mm, OperandMEM64 m64, unsigned char c);
		int pshufw(OperandMMREG mm, OperandR_M64 r_m64, unsigned char c);

		int rcpps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int rcpps(OperandXMMREG xmm, OperandMEM128 mem128);
		int rcpps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int rcpss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int rcpss(OperandXMMREG xmm, OperandMEM32 mem32);
		int rcpss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int rsqrtps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int rsqrtps(OperandXMMREG xmm, OperandMEM128 mem128);
		int rsqrtps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int rsqrtss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int rsqrtss(OperandXMMREG xmm, OperandMEM32 mem32);
		int rsqrtss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int sfence();

		int shufps(OperandXMMREG xmmi, OperandXMMREG xmmj, unsigned char c);
		int shufps(OperandXMMREG xmm, OperandMEM128 m128, unsigned char c);
		int shufps(OperandXMMREG xmm, OperandR_M128 r_m128, unsigned char c);

		int sqrtps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int sqrtps(OperandXMMREG xmm, OperandMEM128 mem128);
		int sqrtps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int sqrtss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int sqrtss(OperandXMMREG xmm, OperandMEM32 mem32);
		int sqrtss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int stmxcsr(OperandMEM32 m32);

		int subps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int subps(OperandXMMREG xmm, OperandMEM128 mem128);
		int subps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int subss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int subss(OperandXMMREG xmm, OperandMEM32 mem32);
		int subss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int ucomiss(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int ucomiss(OperandXMMREG xmm, OperandMEM32 mem32);
		int ucomiss(OperandXMMREG xmm, OperandXMM32 xmm32);

		int unpckhps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int unpckhps(OperandXMMREG xmm, OperandMEM128 mem128);
		int unpckhps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int unpcklps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int unpcklps(OperandXMMREG xmm, OperandMEM128 mem128);
		int unpcklps(OperandXMMREG xmm, OperandR_M128 r_m128);

		int xorps(OperandXMMREG xmmi, OperandXMMREG xmmj);
		int xorps(OperandXMMREG xmm, OperandMEM128 mem128);
		int xorps(OperandXMMREG xmm, OperandR_M128 r_m128);

		// Debugging tools
		void dumpSSE();

	private:
		OperandREF physicalEAX;
		OperandREF physicalECX;
		OperandREF physicalEDX;
		OperandREF physicalEBX;
		OperandREF physicalESI;
		OperandREF physicalEDI;

		OperandREF physicalMM0;
		OperandREF physicalMM1;
		OperandREF physicalMM2;
		OperandREF physicalMM3;
		OperandREF physicalMM4;
		OperandREF physicalMM5;
		OperandREF physicalMM6;
		OperandREF physicalMM7;

		OperandREF physicalXMM0;
		OperandREF physicalXMM1;
		OperandREF physicalXMM2;
		OperandREF physicalXMM3;
		OperandREF physicalXMM4;
		OperandREF physicalXMM5;
		OperandREF physicalXMM6;
		OperandREF physicalXMM7;

		unsigned int priorityEAX;
		unsigned int priorityECX;
		unsigned int priorityEDX;
		unsigned int priorityEBX;
		unsigned int priorityESI;
		unsigned int priorityEDI;

		unsigned int priorityMM0;
		unsigned int priorityMM1;
		unsigned int priorityMM2;
		unsigned int priorityMM3;
		unsigned int priorityMM4;
		unsigned int priorityMM5;
		unsigned int priorityMM6;
		unsigned int priorityMM7;

		unsigned int priorityXMM0;
		unsigned int priorityXMM1;
		unsigned int priorityXMM2;
		unsigned int priorityXMM3;
		unsigned int priorityXMM4;
		unsigned int priorityXMM5;
		unsigned int priorityXMM6;
		unsigned int priorityXMM7;

		float sse[8][4];   // Storage for SSE emulation registers
		static bool emulateSSE;
	};
}

#endif   // SoftWire_CodeGenerator_hpp
