#ifndef RSP_PROC_H
#define RSP_PROC_H

#include <types.h>
#include <windows.h>
#include "r4300i.h"
#include "debugger.h"

#define C2VEC_B 0              // Load Byte into Vector
#define C2VEC_S 1              // Load Short into Vector
#define C2VEC_L 2              // Load Long/Word into Vector
#define C2VEC_D 3              // Load Double-Word into Vector
#define C2VEC_Q 4              // Load Quad-Word into Vector
#define C2VEC_R 5              // Load Rest...
#define C2VEC_P 6              // Load Packed...
#define C2VEC_U 7              // Load Unpacked...
#define C2VEC_H 8              // Load Half...
#define C2VEC_F 9              // Load Fourth...
#define C2VEC_W 10             // Load Wrap...
#define C2VEC_T 11             // Load Transpose...

extern int VectorShiftTable[12]; // {  0,   1,   2,   3,   4,   4,   3,   3,   4,   4,   4,   4 }
extern char VectorCodeTable[12]; // { 'B', 'S', 'L', 'D', 'Q', 'R', 'P', 'U', 'H', 'F', 'W', 'T' }

union Vector_Reg    // Declare union type vector register
{
   char    b[16];     //128 bits (16 bytes)
   short   s[8];      //128 bits (8 shorts)
   long    l[4];      //128 bits (4 ints?)
   __int64 d[2];      //128 bits (2 longs?)
};

extern HANDLE hRSPRunEvent, hRSPDebugEvent;
extern bool RSP_running, RSP_want_to_exit;

class RSP_Processor
{
private:
    HANDLE hRSPThread;
	ulong VFlags[4];
	Vector_Reg V[32];					// Vector registers
	ushort VDest[8];					// Destination vector holders to account for opcodes where source and dest are same
	ushort AccumH[8];					// Accumulator "high" to hold temporary results of some vector opcodes
	ushort AccumN[8];					// Accumulator "normal" to hold temporary results of some vector opcodes
	ushort high_source, recip_hi;		// temp values used in VRCPH/VRCPL pairing.

public:
	RSP_Processor();    /* Constructor */
    ~RSP_Processor();   // Destructor
	void RSP_Start(void);
	void RSP_Stop(void);

    void DoThread(void);        // Our thread...
	void Cold_Reset();     /* Cold Reset */
	
	ulong Instruction; /* This is the instruction to be executed */
    ulong RSP_GPR[32];
	ulong NextPC; /* Used to keep track of branch delay slots */

	// Since x86 memory is byteswapped (which messes up the union), these functions will use the appropriate
	// vector register.
	inline void Set_VectB(int reg, int sub, char val)    { V[reg].b[sub ^ 0x03] = val; }
	inline void Set_VectS(int reg, int sub, short val)   { V[reg].s[sub ^ 0x01] = val; }
	inline void Set_VectL(int reg, int sub, long val)    { V[reg].l[sub] = val; }
	inline void Set_VectD(int reg, int sub, __int64 val) { V[reg].d[sub] = val; }

	inline char    Get_VectB(int reg, int sub) { return V[reg].b[sub ^ 0x03]; }
	inline short   Get_VectS(int reg, int sub) { return V[reg].s[sub ^ 0x01]; }
	inline long    Get_VectL(int reg, int sub) { return V[reg].l[sub]; }
	inline __int64 Get_VectD(int reg, int sub) { return V[reg].d[sub]; }

	ushort RoundDown16(slong val);

	/*****************************************************************/
	/*                         Opcodes                               */
	/*****************************************************************/
	void Reserved_Instruction(void);
	void VECTOP(void);
	void REGIMM(void);
	void SPECIAL(void);

	void J(void);
	void JAL(void);
	void JR(void);
	void LBU(void);
	void LW(void);
	void LWC2(void);
	void LH(void);
	void SWC2(void);
	void LHU(void);
	void SH(void);
	void ADD(void);
	void ADDU(void);
	void SUB(void);
	void ADDI(void);
	void ADDIU(void);
	void ANDI(void);
	void BEQ(void);
	void BLTZ(void);
	void BGTZ(void);
	void BGEZALL(void);
	void BNE(void);
    void BREAK(void);
	void ORI(void);
	void XORI(void);
	void SLL(void);
	void SW(void);
	void MTC0(void);
	void MFC0(void);
	void MTC2(void);
	void MFC2(void);
	void CTC2(void);
	void CFC2(void);
	void LUI(void);
	void COP0(void);
	void COP2(void);
	void SRA(void);
	void SRL(void);
	void BLEZ(void);
	void BGEZ(void);
	void AND(void);
	void OR(void);
	void XOR(void);
	void NOR(void);
	void SLLV(void);
	void SRLV(void);
	void SRAV(void);
	void LB(void);
	void SB(void);

	void VMULF(void);
	void VMACF(void);
	void VNOOP(void);
	void VXOR(void);
	void VNXOR(void);
	void VAND(void);
	void VNAND(void);
	void VOR(void);
	void VNOR(void);
	void VADD(void);
	void VSUB(void);
	void VSUBC(void);
	void VSUT(void);
	void VMOV(void);
	void VRCPH(void);
	void VRCPL(void);
	void VMUDN(void);
	void VMADN(void);
	void VMUDH(void);
	void VMADH(void);
	void VMUDL(void);
	void VMADL(void);
	void VMUDM(void);
	void VMADM(void);

	void P_RSP_Reserved_Instruction(char *buf);
	void P_RSP_RegIMM(char *buf);
	void P_RSP_J(char *buf);
	void P_RSP_JAL(char *buf);
	void P_RSP_JR(char *buf);
	void P_RSP_LW(char *buf);
	void P_RSP_LWC2(char *buf);
	void P_RSP_LH(char *buf);
	void P_RSP_SWC2(char *buf);
	void P_RSP_ADDI(char *buf);
	void P_RSP_ADD(char *buf);
	void P_RSP_ANDI(char *buf);
	void P_RSP_BEQ(char *buf);
	void P_RSP_BGTZ(char *buf);
	void P_RSP_BNE(char *buf);
	void P_RSP_BREAK(char *buf);
	void P_RSP_ORI(char *buf);
	void P_RSP_SLL(char *buf);
	void P_RSP_SPECIAL(char *buf);
	void P_RSP_SW(char *buf);
	void P_RSP_MTC0(char *buf);
	void P_RSP_MFC0(char *buf);
	void P_RSP_MTC2(char *buf);
	void P_RSP_MFC2(char *buf);
	void P_RSP_CTC2(char *buf);
	void P_RSP_CFC2(char *buf);
	void P_RSP_COP0(char *buf);
	void P_RSP_COP2(char *buf);
	void P_RSP_VECTOP(char *buf);

	void P_RSP_BLEZ(char *buf);
	void P_RSP_ADDIU(char *buf);
	void P_RSP_SLTI(char *buf);
	void P_RSP_SLTIU(char *buf);
	void P_RSP_XORI(char *buf);
	void P_RSP_LUI(char *buf);
	void P_RSP_LB(char *buf);
	void P_RSP_LBU(char *buf);
	void P_RSP_LHU(char *buf);
	void P_RSP_SB(char *buf);
	void P_RSP_SH(char *buf);
	void P_RSP_SRL(char *buf);
	void P_RSP_SRA(char *buf);
	void P_RSP_SLLV(char *buf);
	void P_RSP_SRLV(char *buf);
	void P_RSP_SRAV(char *buf);
	void P_RSP_ADDU(char *buf);
	void P_RSP_SUB(char *buf);
	void P_RSP_SUBU(char *buf);
	void P_RSP_AND(char *buf);
	void P_RSP_OR(char *buf);
	void P_RSP_XOR(char *buf);
	void P_RSP_NOR(char *buf);
	void P_RSP_SLT(char *buf);
	void P_RSP_SLTU(char *buf);
	void P_RSP_BLTZ(char *buf);
	void P_RSP_BGEZ(char *buf);
	void P_RSP_BLTZAL(char *buf);
	void P_RSP_BGEZAL(char *buf);

	void P_RSP_VectorOp(char *buf, char *op);
	void P_RSP_VMULF(char *buf);
	void P_RSP_VMULU(char *buf);
	void P_RSP_VRNDP(char *buf);
	void P_RSP_VMULQ(char *buf);
	void P_RSP_VMUDL(char *buf);
	void P_RSP_VMUDM(char *buf);
	void P_RSP_VMUDN(char *buf);
	void P_RSP_VMUDH(char *buf);
	void P_RSP_VMACF(char *buf);
	void P_RSP_VMACU(char *buf);
	void P_RSP_VRNDN(char *buf);
	void P_RSP_VMACQ(char *buf);
	void P_RSP_VMADL(char *buf);
	void P_RSP_VMADM(char *buf);
	void P_RSP_VMADN(char *buf);
	void P_RSP_VMADH(char *buf);
	void P_RSP_VADD(char *buf);
	void P_RSP_VSUB(char *buf);
	void P_RSP_VSUT(char *buf);
	void P_RSP_VABS(char *buf);
	void P_RSP_VADDC(char *buf);
	void P_RSP_VSUBC(char *buf);
	void P_RSP_VADDB(char *buf);
	void P_RSP_VSUBB(char *buf);
	void P_RSP_VACCB(char *buf);
	void P_RSP_VSUCB(char *buf);
	void P_RSP_VSAD(char *buf);
	void P_RSP_VSAC(char *buf);
	void P_RSP_VSUM(char *buf);
	void P_RSP_VSAW(char *buf);
	void P_RSP_VLT(char *buf);
	void P_RSP_VEQ(char *buf);
	void P_RSP_VNE(char *buf);
	void P_RSP_VGE(char *buf);
	void P_RSP_VCL(char *buf);
	void P_RSP_VCH(char *buf);
	void P_RSP_VCR(char *buf);
	void P_RSP_VMRG(char *buf);
	void P_RSP_VAND(char *buf);
	void P_RSP_VNAND(char *buf);
	void P_RSP_VOR(char *buf);
	void P_RSP_VNOR(char *buf);
	void P_RSP_VXOR(char *buf);
	void P_RSP_VNXOR(char *buf);
	void P_RSP_VRCP(char *buf);
	void P_RSP_VRCPL(char *buf);
	void P_RSP_VRCPH(char *buf);
	void P_RSP_VMOV(char *buf);
	void P_RSP_VRSQ(char *buf);
	void P_RSP_VRSQL(char *buf);
	void P_RSP_VRSQH(char *buf);
	void P_RSP_VNOOP(char *buf);
	void P_RSP_VEXTT(char *buf);
	void P_RSP_VEXTQ(char *buf);
	void P_RSP_VEXTN(char *buf);
	void P_RSP_VINST(char *buf);
	void P_RSP_VINSQ(char *buf);
	void P_RSP_VINSN(char *buf);

	void (RSP_Processor::*Opcode[64])(void);
    void (RSP_Processor::*Special[64])(void);
    void (RSP_Processor::*RegIMM[32])(void);
	void (RSP_Processor::*Cop0[32])(void);
	void (RSP_Processor::*Cop2[32])(void);
	void (RSP_Processor::*VectOp[64])(void);
	void (*Cop0_Regs[16])(ulong val);	// addresses of SP and DPC regs
	ulong *Cop0_Addr[16];

	void (RSP_Processor::*RSPOpcodePrintTable[64])(char *buf);
    void (RSP_Processor::*P_Special[64])(char *buf);
    void (RSP_Processor::*P_RegIMM[32])(char *buf);
	void (RSP_Processor::*P_Cop0[32])(char *buf);
	void (RSP_Processor::*P_Cop2[32])(char *buf);
	void (RSP_Processor::*P_VectOp[64])(char *buf);

	void RSP_OpcodePrint(char *buf) { (this->*RSPOpcodePrintTable[(raw_opcode_ulong & 0xFC000000) >> 26])(buf); }
};

extern RSP_Processor *RSP;

#endif
