#ifndef swShader_PS_2_0Assembler_hpp
#define swShader_PS_2_0Assembler_hpp

#include "PixelShader.hpp"
#include "Context.hpp"

#include "Instruction.hpp"
#include "Types.hpp"

namespace swShader
{
	struct Operand;

	class PS_2_0Assembler : protected Context, public PixelShader
	{
	public:
		PS_2_0Assembler();

		virtual ~PS_2_0Assembler();

		void execute();
		void (*executable())();

		void setConstant(int index, const float value[4]);

	protected:
		void setMnemonic(Instruction::Mnemonic mnemonic);
		void setModifier(Instruction::Modifier modifier);
		void setDestination(const Operand &operand);
		void setSource0(const Operand &operand);
		void setSource1(const Operand &operand);
		void setSource2(const Operand &operand);
		void setSource3(const Operand &operand);

		void newInstruction();

		void encode();

		// Shader registers
		static float4 r[12];
		static float4 t[8];
		static float4 s[16];
		static float4 v[2];
		static float4 c[32];
		static float4 oC[4];
		static float4 oDepth;

		// Register storage for internal use
		static float4 internal[8];

		bool vDcl[2];
		bool tDcl[8];
		bool sDcl[16];

		Instruction *intermediate;
		Instruction *instruction;

	private:
		void setupInterpolants();
		void depthTest();
		void shader();
		void alphaTest();
		void alphaBlend();
		void writeOC0();
		void interpolate();

		static Operand tmp0;
		static Operand tmp1;
		static Operand tmp2;
		static Operand tmp3;
		static Operand tmp4;
		static Operand tmp5;
		static Operand tmp6;
		static Operand tmp7;

		void (*code)();

		bool perspectiveCorrected;

		static int x;   // Pixel position

		// Interpolants not in shader registers
		static float4 RHW;
		static float4 Z;
		
		// Perspective correct w
		static float4 W;

		void *reference(const Operand &reg);
		void checkDcl(const Operand &op);
		void free(const Operand &tmp);
		void freeTemps();

		const SoftWire::OperandXMMREG r128(const Operand &reg, int next = 0);
		const SoftWire::OperandXMMREG x128(const Operand &reg, int next = 0);
		const SoftWire::OperandR_M128 m128(const Operand &r_m, int next = 0);
		const SoftWire::OperandXMM32 xmm32(const Operand &r_m, int next = 0);

		const SoftWire::OperandXMMREG r128(const SoftWire::OperandREF &ref);
		const SoftWire::OperandXMMREG x128(const SoftWire::OperandREF &ref);
		const SoftWire::OperandR_M128 m128(const SoftWire::OperandREF &ref);
		const SoftWire::OperandXMM32 xmm32(const SoftWire::OperandREF &ref);

		void free(const SoftWire::OperandREF &ref);

		typedef Operand &Dst;
		typedef Operand &Dest;
		typedef const Operand &Src;
		typedef const Operand &Src0;
		typedef const Operand &Src1;
		typedef const Operand &Src2;
		typedef const Operand &FValue1;
		typedef const Operand &FValue2;
		typedef const Operand &FValue3;
		typedef const Operand &FValue4;
		typedef Operand &Tmp;

		void PS_2_0();
		void DCL(Dest);
		void DCL_2D(Dest);
		void DCL_CUBE(Dest);
		void DCL_VOLUME(Dest);
		void DEF(Dest, FValue1, FValue2, FValue3, FValue4);
		void ABS(Dst, Src);
		void ADD(Dst, Src0, Src1);
		void CMP(Dst, Src0, Src1, Src2);
		void CRS(Dst, Src0, Src1);
		void DP2ADD(Dst, Src0, Src1, Src2);
		void DP3(Dst, Src0, Src1);
		void DP4(Dst, Src0, Src1);
		void EXP(Dst, Src);
		void FRC(Dst, Src);
		void LOG(Dst, Src);
		void LRP(Dst, Src0, Src1, Src2);
		void M3X2(Dst, Src0, Src1);
		void M3X3(Dst, Src0, Src1);
		void M3X4(Dst, Src0, Src1);
		void M4X3(Dst, Src0, Src1);
		void M4X4(Dst, Src0, Src1);
		void MAD(Dst, Src0, Src1, Src2);
		void MAX(Dst, Src0, Src1);
		void MIN(Dst, Src0, Src1);
		void MOV(Dst, Src);
		void MUL(Dst, Src0, Src1);
		void NOP();
		void NRM(Dst, Src);
		void POW(Dst, Src0, Src1);
		void RCP(Dst, Src);
		void RSQ(Dst, Src);
		void SINCOS(Dst, Src0, Src1, Src2);
		void SUB(Dst, Src0, Src1);
		void TEXKILL(Src);
		void TEXLD(Dst, Src0, Src1);
		void TEXLDB(Dst, Src0, Src1);
		void TEXLDP(Dst, Src0, Src1);

		// Helper macro instructions
		void NEG(Tmp, Src);
		void SWIZZLE(Tmp, Src);
		void MASK(Dst, Tmp);
		void SAT(Dst, Tmp);

		void NEG_SWIZZLE(Tmp, Src);
		void SAT_MASK(Dst, Tmp);
		void SAT_MOV_X(Dst, Tmp);
		void SAT_MOV_XY(Dst, Tmp);
		void SAT_MOV_XYZ(Dst, Tmp);
		void SAT_MOV_XYZW(Dst, Tmp);
	};
}

#endif   // swShader_PS_2_0Assembler_hpp
