#pragma once
#include "types.h"

enum Sh4RegType
{
	//GPRs
	reg_r0,
	reg_r1,
	reg_r2,
	reg_r3,
	reg_r4,
	reg_r5,
	reg_r6,
	reg_r7,
	reg_r8,
	reg_r9,
	reg_r10,
	reg_r11,
	reg_r12,
	reg_r13,
	reg_r14,
	reg_r15,

	//FPU, bank 0
	reg_fr_0,
	reg_fr_1,
	reg_fr_2,
	reg_fr_3,
	reg_fr_4,
	reg_fr_5,
	reg_fr_6,
	reg_fr_7,
	reg_fr_8,
	reg_fr_9,
	reg_fr_10,
	reg_fr_11,
	reg_fr_12,
	reg_fr_13,
	reg_fr_14,
	reg_fr_15,

	//FPU, bank 1
	reg_xf_0,
	reg_xf_1,
	reg_xf_2,
	reg_xf_3,
	reg_xf_4,
	reg_xf_5,
	reg_xf_6,
	reg_xf_7,
	reg_xf_8,
	reg_xf_9,
	reg_xf_10,
	reg_xf_11,
	reg_xf_12,
	reg_xf_13,
	reg_xf_14,
	reg_xf_15,

	//GPR Interrupt bank
	reg_r0_Bank,
	reg_r1_Bank,
	reg_r2_Bank,
	reg_r3_Bank,
	reg_r4_Bank,
	reg_r5_Bank,
	reg_r6_Bank,
	reg_r7_Bank,

	//Misc regs
	reg_gbr,
	reg_ssr,
	reg_spc,
	reg_sgr,
	reg_dbr,
	reg_vbr,
	reg_mach,
	reg_macl,
	reg_pr,
	reg_fpul,
    reg_nextpc,
	reg_sr,				//includes T (combined on read/separated on write)
	reg_old_sr_status,		//Only the status bits
	reg_sr_status,		//Only the status bits
	reg_sr_T,			//only T
	reg_old_fpscr,
	reg_fpscr,
	
	reg_pc_dyn,			//Write only, for dynarec only (dynamic block exit address)

	sh4_reg_count,

	/*
		These are virtual registers, used by the dynarec decoder
	*/
	regv_dr_0,
	regv_dr_2,
	regv_dr_4,
	regv_dr_6,
	regv_dr_8,
	regv_dr_10,
	regv_dr_12,
	regv_dr_14,

	regv_xd_0,
	regv_xd_2,
	regv_xd_4,
	regv_xd_6,
	regv_xd_8,
	regv_xd_10,
	regv_xd_12,
	regv_xd_14,

	regv_fv_0,
	regv_fv_4,
	regv_fv_8,
	regv_fv_12,

	regv_xmtrx,
	regv_fmtrx,

	NoReg=-1
};

//Varius sh4 registers

union sr_status_t
{
	struct
	{
		u32 T_h		:1;//<<0
		u32 S		:1;//<<1
		u32 rsvd0	:2;//<<2
		u32 IMASK	:4;//<<4
		u32 Q		:1;//<<8
		u32 M		:1;//<<9
		u32 rsvd1	:5;//<<10
		u32 FD		:1;//<<15
		u32 rsvd2	:12;//<<16
		u32 BL		:1;//<<28
		u32 RB		:1;//<<29
		u32 MD		:1;//<<20
		u32 rsvd3	:1;//<<31
	};
	u32 status;
};

//Status register bitfield
struct sr_t
{
	union
	{
		struct
		{
			u32 T_h		:1;//<<0
			u32 S		:1;//<<1
			u32 rsvd0	:2;//<<2
			u32 IMASK	:4;//<<4
			u32 Q		:1;//<<8
			u32 M		:1;//<<9
			u32 rsvd1	:5;//<<10
			u32 FD		:1;//<<15
			u32 rsvd2	:12;//<<16
			u32 BL		:1;//<<28
			u32 RB		:1;//<<29
			u32 MD		:1;//<<20
			u32 rsvd3	:1;//<<31
		};
		u32 status;
	};
	u32 T;
	INLINE u32 GetFull()
	{
		return (status & 0x700083F2) | T;
	}

	INLINE void SetFull(u32 value)
	{
		status=value & 0x700083F2;
		T=value&1;
	}

};

//FPSCR (fpu status and control register) bitfield
struct fpscr_t
{
	union
	{
		u32 full;
		struct
		{
			u32 RM:2;
			u32 finexact:1;
			u32 funderflow:1;
			u32 foverflow:1;
			u32 fdivbyzero:1;
			u32 finvalidop:1;
			u32 einexact:1;
			u32 eunderflow:1;
			u32 eoverflow:1;
			u32 edivbyzero:1;
			u32 einvalidop:1;
			u32 cinexact:1;
			u32 cunderflow:1;
			u32 coverflow:1;
			u32 cdivbyzero:1;
			u32 cinvalid:1;
			u32 cfpuerr:1;
			u32 DN:1;
			u32 PR:1;
			u32 SZ:1;
			u32 FR:1;
			u32 pad:10;
		};
		struct
		{

			u32 nil:2+1+1+1+1+4+8+1;
			u32 PR_SZ:2;
			u32 nilz:11;
		};
	};
};


typedef void RunFP();
typedef void StopFP();
typedef void StepFP();
typedef void SkipFP();
typedef void ResetFP(bool Manual);
typedef void InitFP();
typedef void TermFP();
typedef bool IsCpuRunningFP();

typedef void sh4_int_RaiseExeptionFP(u32 ExeptionCode,u32 VectorAddress);

/*
	The interface stuff should be replaced with something nicer
*/
//sh4 interface
struct sh4_if
{
	RunFP* Run;
	StopFP* Stop;
	StepFP* Step;
	SkipFP* Skip;
	ResetFP* Reset;
	InitFP* Init;
	TermFP* Term;

	TermFP* ResetCache;

	IsCpuRunningFP* IsCpuRunning;
};


struct Sh4Context
{
	union
	{
		struct
		{
			f32 xffr[32];
			u32 r[16];

			union
			{
				u64 full;
				struct
				{
					u32 l;
					u32 h;
				};
			} mac;

			u32 r_bank[8];

			u32 gbr,ssr,spc,sgr,dbr,vbr;
			u32 pr,fpul;
			u32 pc;

			u32 jdyn;

			sr_t sr;
			fpscr_t fpscr;
			sr_status_t old_sr;
			fpscr_t old_fpscr;

			volatile u32 CpuRunning;

			int sh4_sched_next;
			u32 interrupt_pend;
		};
		u64 raw[64-8];
	};

	u32 offset(u32 sh4_reg);
	u32 offset(Sh4RegType sh4_reg) { return offset(sh4_reg); }
};

void DYNACALL do_sqw_mmu(u32 dst);
extern "C" void DYNACALL do_sqw_nommu_area_3(u32 dst, u8* sqb);
void DYNACALL do_sqw_nommu_full(u32 dst, u8* sqb);

typedef void DYNACALL sqw_fp(u32 dst,u8* sqb);
typedef void DYNACALL TaListVoidFP(void* data);

struct Sh4RCB
{
	void* fpcb[8*1024*1024];
	u64 _pad[(0x40000-sizeof(Sh4Context)-64-8)/8];
	TaListVoidFP* tacmd_voud; //*TODO* remove (not used)
	sqw_fp* do_sqw_nommu;
	u64 sq_buffer[64/8];
	Sh4Context cntx;
};

extern Sh4RCB* p_sh4rcb;
extern u8* sh4_dyna_rcb;

#define do_sqw_nommu sh4rcb.do_sqw_nommu

template<typename T>
s32 rcb_noffs(T* ptr)  
{ 
	s32 rv= (u8*)ptr - (u8*)p_sh4rcb-sizeof(Sh4RCB); 
	verify(rv<0);

	return rv;
}

template<typename T>
s32 rcb_poffs(T* ptr)  
{ 
	s32 rv= (u8*)ptr - (u8*)p_sh4rcb-sizeof(Sh4RCB); 
	verify(rv>=0);

	return rv;
}

#define sh4rcb (*p_sh4rcb)
#define Sh4cntx (sh4rcb.cntx)

//Get an interface to sh4 interpreter
void Get_Sh4Interpreter(sh4_if* cpu);
void Get_Sh4Recompiler(sh4_if* cpu);