/*  Pcsx2 - Pc Ps2 Emulator
 *  Copyright (C) 2002-2005  Pcsx2 Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IR5900_H__
#define __IR5900_H__

#include "VU.h"
#include "iCore.h"

// these might not work anymore
#define ARITHMETICIMM_RECOMPILE
#define ARITHMETIC_RECOMPILE
#define MULTDIV_RECOMPILE
#define SHIFT_RECOMPILE
#define BRANCH_RECOMPILE
#define JUMP_RECOMPILE
#define LOADSTORE_RECOMPILE
#define MOVE_RECOMPILE
#define MMI_RECOMPILE
#define MMI0_RECOMPILE
#define MMI1_RECOMPILE
#define MMI2_RECOMPILE
#define MMI3_RECOMPILE
#define FPU_RECOMPILE
#define CP0_RECOMPILE
#define CP2_RECOMPILE

#define EE_CONST_PROP // rec2 - enables constant propagation (faster)
#define EE_FPU_REGCACHING 1

#define PC_GETBLOCK(x) PC_GETBLOCK_(x, recLUT)

void recClearMem(BASEBLOCK* p);
#define REC_CLEARM(mem) { \
	if ((mem) < maxrecmem && recLUT[(mem) >> 16]) { \
		BASEBLOCK* p = PC_GETBLOCK(mem); \
		if( *(u32*)p ) recClearMem(p); \
	} \
} \

extern u32 pc;
extern int branch;
extern uptr* recLUT;

extern u32 pc;			         // recompiler pc
extern int branch;		         // set for branch
extern u32 target;		         // branch target
extern u16 x86FpuState;
extern u16 iCWstate;
extern u32 s_nBlockCycles;		// cycles of current block recompiling

#define REC_FUNC_INLINE( f, delreg ) \
	MOV32ItoM( (u32)&cpuRegs.code, cpuRegs.code ); \
	MOV32ItoM( (u32)&cpuRegs.pc, pc ); \
	iFlushCall(FLUSH_EVERYTHING); \
	if( (delreg) > 0 ) _deleteEEreg(delreg, 0); \
	CALLFunc( (u32)f ); 

#define REC_FUNC( f, delreg ) \
   void f( void ); \
   void rec##f( void ) \
   { \
	   MOV32ItoM( (u32)&cpuRegs.code, cpuRegs.code ); \
	   MOV32ItoM( (u32)&cpuRegs.pc, pc ); \
	   iFlushCall(FLUSH_EVERYTHING); \
	   if( (delreg) > 0 ) _deleteEEreg(delreg, 0); \
	   CALLFunc( (u32)f ); \
   }

#define REC_SYS( f ) \
   void f( void ); \
   void rec##f( void ) \
   { \
	   MOV32ItoM( (u32)&cpuRegs.code, cpuRegs.code ); \
	   MOV32ItoM( (u32)&cpuRegs.pc, pc ); \
	   iFlushCall(FLUSH_EVERYTHING); \
	   CALLFunc( (u32)f ); \
	   branch = 2; \
   }

// used when processing branches
void SaveBranchState();
void LoadBranchState();

void recompileNextInstruction(int delayslot);
void SetBranchReg( u32 reg );
void SetBranchImm( u32 imm );

void iFlushCall(int flushtype);
void SaveCW();
void LoadCW();

extern void (*recBSC[64])();
extern void (*recSPC[64])();
extern void (*recREG[32])();
extern void (*recCP0[32])();
extern void (*recCP0BC0[32])();
extern void (*recCP0C0[64])();
extern void (*recCP1[32])();
extern void (*recCP1BC1[32])();
extern void (*recCP1S[64])();
extern void (*recCP1W[64])();
extern void (*recMMIt[64])();
extern void (*recMMI0t[32])();
extern void (*recMMI1t[32])();
extern void (*recMMI2t[32])();
extern void (*recMMI3t[32])();

u32* _eeGetConstReg(int reg); // gets a memory pointer to the constant reg

void _eeFlushAllUnused();
void _eeOnWriteReg(int reg, int signext);

// totally deletes from const, xmm, and mmx entries
// if flush is 1, also flushes to memory
// if 0, only flushes if not an xmm reg (used when overwriting lower 64bits of reg)
void _deleteEEreg(int reg, int flush);

// allocates memory on the instruction size and returns the pointer
void* recAllocStackMem(int size, int align);

//////////////////////////////////////
// Templates for code recompilation //
//////////////////////////////////////
typedef void (*R5900FNPTR)();
typedef void (*R5900FNPTR_INFO)(int info);

#define EERECOMPILE_CODE0(fn, xmminfo) \
void rec##fn(void) \
{ \
	eeRecompileCode0(rec##fn##_const, rec##fn##_consts, rec##fn##_constt, rec##fn##_, xmminfo); \
} \

#define EERECOMPILE_CODEX(codename, fn) \
void rec##fn(void) \
{ \
	codename(rec##fn##_const, rec##fn##_); \
} \

//
// MMX/XMM caching helpers
//

// rd = rs op rt
void eeRecompileCode0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode, int xmminfo);
// rt = rs op imm16
void eeRecompileCode1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode);
// rd = rt op sa
void eeRecompileCode2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode);
// rt op rs  (SPECIAL)
void eeRecompileCode3(R5900FNPTR constcode, R5900FNPTR_INFO multicode);

//
// non mmx/xmm version, slower
//
// rd = rs op rt
#define EERECOMPILE_CONSTCODE0(fn) \
void rec##fn(void) \
{ \
	eeRecompileCodeConst0(rec##fn##_const, rec##fn##_consts, rec##fn##_constt, rec##fn##_); \
} \

// rt = rs op imm16
#define EERECOMPILE_CONSTCODE1(fn) \
void rec##fn(void) \
{ \
	eeRecompileCodeConst1(rec##fn##_const, rec##fn##_); \
} \

// rd = rt op sa
#define EERECOMPILE_CONSTCODE2(fn) \
void rec##fn(void) \
{ \
	eeRecompileCodeConst2(rec##fn##_const, rec##fn##_); \
} \

// rd = rt op rs
#define EERECOMPILE_CONSTCODESPECIAL(fn, mult) \
void rec##fn(void) \
{ \
	eeRecompileCodeConstSPECIAL(rec##fn##_const, rec##fn##_, mult); \
} \

// rd = rs op rt
void eeRecompileCodeConst0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode);
// rt = rs op imm16
void eeRecompileCodeConst1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode);
// rd = rt op sa
void eeRecompileCodeConst2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode);
// rd = rt MULT rs  (SPECIAL)
void eeRecompileCodeConstSPECIAL(R5900FNPTR constcode, R5900FNPTR_INFO multicode, int MULT);

// XMM caching helpers
#define XMMINFO_READLO	0x01
#define XMMINFO_READHI	0x02
#define XMMINFO_WRITELO	0x04
#define XMMINFO_WRITEHI	0x08
#define XMMINFO_WRITED	0x10
#define XMMINFO_READD	0x20
#define XMMINFO_READS	0x40
#define XMMINFO_READT	0x80
#define XMMINFO_READD_LO	0x100 // if set and XMMINFO_READD is set, reads only low 64 bits of D
#define XMMINFO_READACC		0x200
#define XMMINFO_WRITEACC	0x400

#define CPU_SSE_XMMCACHE_START(xmminfo) \
	if (cpucaps.hasStreamingSIMDExtensions) \
    { \
		int info = eeRecompileCodeXMM(xmminfo); \

#define CPU_SSE2_XMMCACHE_START(xmminfo) \
	if (cpucaps.hasStreamingSIMD2Extensions) \
    { \
		int info = eeRecompileCodeXMM(xmminfo); \

#define CPU_SSE_XMMCACHE_END \
		_clearNeededXMMregs(); \
		return; \
	}  \

#ifdef __x86_64__
#define FPURECOMPILE_CONSTCODE(fn, xmminfo) \
void rec##fn(void) \
{ \
	eeFPURecompileCode(rec##fn##_xmm, NULL, xmminfo); \
}
#else
#define FPURECOMPILE_CONSTCODE(fn, xmminfo) \
void rec##fn(void) \
{ \
	eeFPURecompileCode(rec##fn##_xmm, rec##fn##_, xmminfo); \
}
#endif

// rd = rs op rt (all regs need to be in xmm)
int eeRecompileCodeXMM(int xmminfo);
void eeFPURecompileCode(R5900FNPTR_INFO xmmcode, R5900FNPTR_INFO fpucode, int xmminfo);

#endif // __IR5900_H__
