/* Mednafen - Multi-system Emulator
 *
 * 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
 */

#include "psx.h"
#include "cpu.h"

#ifdef _MSC_VER
#include <windows.h>
#endif

//#define DPRINT(...)
#define NOPRINT(...)
#define DPRINT(...) fprintf(__VA_ARGS__)
#define DEBUG(...) NOPRINT(stderr,__VA_ARGS__)
#define DEBUG_PRINT(...) NOPRINT(stderr,__VA_ARGS__)
#define DEBUG_LOAD(...) NOPRINT(stderr,__VA_ARGS__)
#define DEBUG_STORE(...) NOPRINT(stderr,__VA_ARGS__)
#define DEBUG_TRACE(...) DPRINT(stderr,__VA_ARGS__)

bool dotrace = false;
uint64 counter = 0;

/* TODO
	Make sure load delays are correct.

	Consider preventing IRQs being taken while in a branch delay slot, to prevent potential problems with games that like to be too clever and perform
	un-restartable sequences of instructions.
*/

namespace MDFN_IEN_PSX
{


PS_CPU::PS_CPU()
{
 Halted = false;

 memset(FastMap, 0, sizeof(FastMap));
 memset(DummyPage, 0xFF, sizeof(DummyPage));	// 0xFF to trigger an illegal instruction exception, so we'll know what's up when debugging.

 for(uint64 a = 0x00000000; a < (1ULL << 32); a += FAST_MAP_PSIZE)
  SetFastMap(DummyPage, a, FAST_MAP_PSIZE);

 CPUHook = NULL;
 ADDBT = NULL;
}

PS_CPU::~PS_CPU()
{


}

void PS_CPU::SetFastMap(void *region_mem, uint32 region_address, uint32 region_size)
{
 // FAST_MAP_SHIFT
 // FAST_MAP_PSIZE

 for(uint64 A = region_address; A < (uint64)region_address + region_size; A += FAST_MAP_PSIZE)
 {
  FastMap[A >> FAST_MAP_SHIFT] = ((uint8 *)region_mem - region_address);
 }
}

INLINE void PS_CPU::RecalcIPCache(void)
{
 IPCache = 0;

 if((CP0.SR & CP0.CAUSE & 0xFF00) && (CP0.SR & 1))
  IPCache = 0x80;

 if(Halted)
  IPCache = 0x80;
}

void PS_CPU::SetHalt(bool status)
{
 Halted = status;
 RecalcIPCache();
}

void PS_CPU::Power(void)
{
 memset(GPR, 0, sizeof(GPR));
 memset(&CP0, 0, sizeof(CP0));
 LO = 0;
 HI = 0;

 gte_ts_done = 0;

 BACKED_PC = 0xBFC00000;
 BACKED_new_PC = 4;
 BACKED_new_PC_mask = ~0U;

 BACKED_LDWhich = 0x20;
 BACKED_LDValue = 0;
 
 CP0.SR |= (1 << 22);	// BEV
 CP0.SR |= (1 << 21);	// TS

 CP0.PRID = 0x300;	// PRId: FIXME(test on real thing)

 RecalcIPCache();

 GTE_Power();
}

int PS_CPU::StateAction(StateMem *sm, int load, int data_only)
{
 SFORMAT StateRegs[] =
 {
  SFARRAY32(GPR, 32),
  SFVAR(LO),
  SFVAR(HI),
  SFVAR(BACKED_PC),
  SFVAR(BACKED_new_PC),
  SFVAR(BACKED_new_PC_mask),

  SFVAR(IPCache),
  SFVAR(Halted),

  SFVAR(BACKED_LDWhich),
  SFVAR(BACKED_LDValue),

  SFVAR(next_event_ts),
  SFVAR(gte_ts_done),

  SFARRAY32(CP0.Regs, 32),

  SFEND
 };
 int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "CPU");

 ret &= GTE_StateAction(sm, load, data_only);

 if(load)
 {

 }

 return(ret);
}

void PS_CPU::AssertIRQ(int which, bool asserted)
{
 assert(which >= 0 && which <= 5);

 CP0.CAUSE &= ~(1 << (10 + which));

 if(asserted)
  CP0.CAUSE |= 1 << (10 + which);

 RecalcIPCache();
}

template<typename T>
INLINE T PS_CPU::ReadMemory(pscpu_timestamp_t &timestamp, uint32 address, bool DS24)
{
 T ret;

 timestamp += 2;
 //timestamp++;
 //assert(!(CP0.SR & 0x10000));

 if(sizeof(T) == 1)
  ret = PSX_MemRead8(timestamp, address);
 else if(sizeof(T) == 2)
  ret = PSX_MemRead16(timestamp, address);
 else
 {
  if(DS24)
   ret = PSX_MemRead24(timestamp, address) & 0xFFFFFF;
  else
   ret = PSX_MemRead32(timestamp, address);
 }

 return(ret);
}

template<typename T>
INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t &timestamp, uint32 address, uint32 value, bool DS24)
{
 DEBUG_STORE("[0x%08X] = 0x%08X\n",address,value);
 if(!(CP0.SR & 0x10000))
 {
  if(sizeof(T) == 1)
   PSX_MemWrite8(timestamp, address, value);
  else if(sizeof(T) == 2)
   PSX_MemWrite16(timestamp, address, value);
  else
  {
   if(DS24)
    PSX_MemWrite24(timestamp, address, value);
   else
    PSX_MemWrite32(timestamp, address, value);
  }
 }
 //else
 // printf("ISC WRITE%d %08x %08x\n", (int)sizeof(T), address, value);
}

uint32 PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NPM)
{
 const bool InBDSlot = !(NPM & 0x3);
 uint32 handler = 0x80000080;

 assert(code < 16);

 if(code==EXCEPTION_INT)
 {
	 int zzz=9;
 }
 if(code != EXCEPTION_INT && code != EXCEPTION_BP && code != EXCEPTION_SYSCALL)
 {
  printf("Exception: %08x @ PC=0x%08x(IBDS=%d) -- IPCache=0x%02x -- IPEND=0x%02x -- SR=0x%08x ; IRQC_Status=0x%04x -- IRQC_Mask=0x%04x\n", code, PC, InBDSlot, IPCache, (CP0.CAUSE >> 8) & 0xFF, CP0.SR,
	IRQ_GetRegister(IRQ_GSREG_STATUS, NULL, 0), IRQ_GetRegister(IRQ_GSREG_MASK, NULL, 0));
  //assert(0);
 }

 if(CP0.SR & (1 << 22))	// BEV
  handler = 0xBFC00180;

 CP0.EPC = PC;
 if(InBDSlot)
  CP0.EPC -= 4;

 if(ADDBT)
  ADDBT(PC, handler, true);

 // "Push" IEc and KUc(so that the new IEc and KUc are 0)
 CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F);

 // Setup cause register
 CP0.CAUSE &= 0x0000FF00;
 CP0.CAUSE |= code << 2;

 // If EPC was adjusted -= 4 because we were in a branch delay slot, set the bit.
 if(InBDSlot)
  CP0.CAUSE |= 0x80000000;

 RecalcIPCache();

 return(handler);
}

//zero 07-feb-2012 - alternate computed goto approach 
static __int32 op_goto_table[256] = {0};
static __int32* op_goto_table_ptr = &op_goto_table[0];
#  define STORE_ADDRESS(index,label) { int _idx = index; \
     __asm lea eax, label\
     __asm mov edx,op_goto_table_ptr\
     __asm mov ebx, _idx\
     __asm mov [edx + ebx*4],eax\
}
#  define JUMP_TO_IP(opf) { int addr = op_goto_table[opf]; __asm jmp addr }


#define BACKING_TO_ACTIVE			\
	PC = BACKED_PC;				\
	new_PC = BACKED_new_PC;			\
	new_PC_mask = BACKED_new_PC_mask;	\
	LDWhich = BACKED_LDWhich;		\
	LDValue = BACKED_LDValue;

#define ACTIVE_TO_BACKING			\
	BACKED_PC = PC;				\
	BACKED_new_PC = new_PC;			\
	BACKED_new_PC_mask = new_PC_mask;	\
	BACKED_LDWhich = LDWhich;		\
	BACKED_LDValue = LDValue;


template<bool DebugMode, bool ILHMode>
pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
{
 register pscpu_timestamp_t timestamp = timestamp_in;

 register uint32 PC;
 register uint32 new_PC;
 register uint32 new_PC_mask;
 register uint32 LDWhich;
 register uint32 LDValue;
 
 BACKING_TO_ACTIVE;

 do
 {
  //printf("Running: %d %d\n", timestamp, next_event_ts);
  while(timestamp < next_event_ts)
  {
   uint32 instr;
   uint32 opf;

   // Zero must be zero...until the Master Plan is enacted.
   GPR[0] = 0;

   if(DebugMode && CPUHook)
   {
    ACTIVE_TO_BACKING;

    CPUHook(PC);

    BACKING_TO_ACTIVE;
   }

   if(!ILHMode)
   {
    if(PC == 0xB0)
    {
     if(GPR[9] == 0x3D)
     {
      //if(GPR[4] == 'L')
      // DBG_Break();
      fputc(GPR[4], stderr);
      //if(GPR[4] == '\n')
      //{
      // fputc('%', stderr);
      // fputc(' ', stderr);
      //}
     }
    }
		//if(GPR[9] == 0x35)
		//{
		//	const int len = GPR[6];
		//	for(int i=0;i<len;i++)
		//		fputc(*(MainRAM.ptr() + (GPR[5]&(2*1024*1024-1)) + i),stderr); //zero 01-apr-2012 - add .ptr
		//}
   }

/*
   if(PC == 0xB0)
   {
    if(GPR[9] != 0xB)
    PSX_WARNING("[BIOS] 0xB0 t1=0x%02x", GPR[9]);
   }
*/
   instr = LoadU32_LE((uint32 *)&FastMap[PC >> FAST_MAP_SHIFT][PC]);

   //printf("PC=%08x, SP=%08x - op=0x%02x - funct=0x%02x - instr=0x%08x\n", PC, GPR[29], instr >> 26, instr & 0x3F, instr);
   //for(int i = 0; i < 32; i++)
   // printf("%02x : %08x\n", i, GPR[i]);
   //printf("\n");

   opf = instr & 0x3F;

   if(instr & (0x3F << 26))
    opf = 0x40 | (instr >> 26);

   opf |= IPCache;

   timestamp++;

   #define DO_LDS() { GPR[LDWhich] = LDValue; LDWhich = 0x20; }
   #define BEGIN_OPF(name, arg_op, arg_funct) { op_##name: /*assert( ((arg_op) ? (0x40 | (arg_op)) : (arg_funct)) == opf); */
   #define END_OPF goto OpDone; }

   #define DO_BRANCH(offset, mask)			\
	{						\
	 if(ILHMode)					\
	 {								\
	  uint32 old_PC = PC;						\
	  PC = (PC & new_PC_mask) + new_PC;				\
	  if(old_PC == ((PC & (mask)) + (offset)))			\
	  {								\
	   if(*(uint32 *)&FastMap[PC >> FAST_MAP_SHIFT][PC] == 0)	\
	   {								\
	    if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */		\
	    {								\
	     timestamp = next_event_ts;					\
	    }								\
	   }								\
	  }								\
	 }						\
	 else						\
	  PC = (PC & new_PC_mask) + new_PC;		\
	 new_PC = (offset);				\
	 new_PC_mask = (mask) & ~3;			\
	 /* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */	\
							\
         if(DebugMode && ADDBT)                 	\
	 {						\
          ADDBT(PC, (PC & new_PC_mask) + new_PC, false);	\
	 }						\
	 goto SkipNPCStuff;				\
	}

   //zero 07-feb-2012 - removed __attribute__((unused))
   //#define ITYPE uint32 rs __attribute__((unused)) = (instr >> 21) & 0x1F; uint32 rt __attribute__((unused)) = (instr >> 16) & 0x1F; int32 immediate = (int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
   //#define ITYPE_ZE uint32 rs __attribute__((unused)) = (instr >> 21) & 0x1F; uint32 rt __attribute__((unused)) = (instr >> 16) & 0x1F; uint32 immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
   //#define JTYPE uint32 target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/
   //#define RTYPE uint32 rs __attribute__((unused)) = (instr >> 21) & 0x1F; uint32 rt __attribute__((unused)) = (instr >> 16) & 0x1F; uint32 rd __attribute__((unused)) = (instr >> 11) & 0x1F; uint32 shamt __attribute__((unused)) = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/
   #define ITYPE uint32 rs  = (instr >> 21) & 0x1F; uint32 rt  = (instr >> 16) & 0x1F; int32 immediate = (int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
   #define ITYPE_ZE uint32 rs  = (instr >> 21) & 0x1F; uint32 rt  = (instr >> 16) & 0x1F; uint32 immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
   #define JTYPE uint32 target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/
   #define RTYPE uint32 rs  = (instr >> 21) & 0x1F; uint32 rt  = (instr >> 16) & 0x1F; uint32 rd  = (instr >> 11) & 0x1F; uint32 shamt  = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/

   //zero 07-feb-2012 - alternate computed goto approach (courtesy of http://abepralle.wordpress.com/2009/01/25/how-not-to-make-a-virtual-machine-label-based-threading/ )
   static bool table_generated = false;
   if(!table_generated)
     {
       table_generated = true;
      int __ctr=0;
 STORE_ADDRESS(__ctr++,op_SLL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_SRL); STORE_ADDRESS(__ctr++,op_SRA); STORE_ADDRESS(__ctr++,op_SLLV); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_SRLV); STORE_ADDRESS(__ctr++,op_SRAV);
    STORE_ADDRESS(__ctr++,op_JR); STORE_ADDRESS(__ctr++,op_JALR); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_SYSCALL); STORE_ADDRESS(__ctr++,op_BREAK); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_MFHI); STORE_ADDRESS(__ctr++,op_MTHI); STORE_ADDRESS(__ctr++,op_MFLO); STORE_ADDRESS(__ctr++,op_MTLO); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_MULT); STORE_ADDRESS(__ctr++,op_MULTU); STORE_ADDRESS(__ctr++,op_DIV); STORE_ADDRESS(__ctr++,op_DIVU); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_ADD); STORE_ADDRESS(__ctr++,op_ADDU); STORE_ADDRESS(__ctr++,op_SUB); STORE_ADDRESS(__ctr++,op_SUBU); STORE_ADDRESS(__ctr++,op_AND); STORE_ADDRESS(__ctr++,op_OR); STORE_ADDRESS(__ctr++,op_XOR); STORE_ADDRESS(__ctr++,op_NOR);
    STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_SLT); STORE_ADDRESS(__ctr++,op_SLTU); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);

    __ctr++; STORE_ADDRESS(__ctr++,op_BCOND); STORE_ADDRESS(__ctr++,op_J); STORE_ADDRESS(__ctr++,op_JAL); STORE_ADDRESS(__ctr++,op_BEQ); STORE_ADDRESS(__ctr++,op_BNE); STORE_ADDRESS(__ctr++,op_BLEZ); STORE_ADDRESS(__ctr++,op_BGTZ);
    STORE_ADDRESS(__ctr++,op_ADDI); STORE_ADDRESS(__ctr++,op_ADDIU); STORE_ADDRESS(__ctr++,op_SLTI); STORE_ADDRESS(__ctr++,op_SLTIU); STORE_ADDRESS(__ctr++,op_ANDI); STORE_ADDRESS(__ctr++,op_ORI); STORE_ADDRESS(__ctr++,op_XORI); STORE_ADDRESS(__ctr++,op_LUI);
    STORE_ADDRESS(__ctr++,op_COP0); STORE_ADDRESS(__ctr++,op_COP1); STORE_ADDRESS(__ctr++,op_COP2); STORE_ADDRESS(__ctr++,op_COP3); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_LB); STORE_ADDRESS(__ctr++,op_LH); STORE_ADDRESS(__ctr++,op_LWL); STORE_ADDRESS(__ctr++,op_LW); STORE_ADDRESS(__ctr++,op_LBU); STORE_ADDRESS(__ctr++,op_LHU); STORE_ADDRESS(__ctr++,op_LWR); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_SB); STORE_ADDRESS(__ctr++,op_SH); STORE_ADDRESS(__ctr++,op_SWL); STORE_ADDRESS(__ctr++,op_SW); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_SWR); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_LWC0); STORE_ADDRESS(__ctr++,op_LWC1); STORE_ADDRESS(__ctr++,op_LWC2); STORE_ADDRESS(__ctr++,op_LWC3); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);
    STORE_ADDRESS(__ctr++,op_SWC0); STORE_ADDRESS(__ctr++,op_SWC1); STORE_ADDRESS(__ctr++,op_SWC2); STORE_ADDRESS(__ctr++,op_SWC3); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL); STORE_ADDRESS(__ctr++,op_ILL);

    // Interrupt portion of this table is constructed so that an interrupt won't be taken when the PC is pointing to a GTE instruction,
    // to avoid problems caused by pipeline vs coprocessor nuances that aren't emulated.
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);

    __ctr++; STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_COP2); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
    STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT); STORE_ADDRESS(__ctr++,op_INTERRUPT);
   }
	 
	 counter++;
const uint32 opc = instr>>26;
	const uint32 func = instr&0x3F;
	 if(dotrace) DEBUG_TRACE("{%12lld} %08X: [%08X] (%d,%d) %s\n",counter,PC, instr,opc,func,MDFN_IEN_PSX::DisassembleMIPS(PC,instr).c_str());
   
	 JUMP_TO_IP(opf);
   //goto *op_goto_table[opf];

   {
    BEGIN_OPF(ILL, 0, 0);
	     PSX_WARNING("[CPU] Unknown instruction @%08x = %08x, op=%02x, funct=%02x", PC, instr, instr >> 26, (instr & 0x3F));
	     DO_LDS();
	     new_PC = Exception(EXCEPTION_RI, PC, new_PC_mask);
	     new_PC_mask = 0;
    END_OPF;

    //
    // ADD - Add Word
    //
    BEGIN_OPF(ADD, 0, 0x20);
	RTYPE;
	uint32 result = GPR[rs] + GPR[rt];
	bool ep = ((~(GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000;

	DO_LDS();

	if(ep)
	{
	 new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else
	 GPR[rd] = result;

    END_OPF;

    //
    // ADDI - Add Immediate Word
    //
    BEGIN_OPF(ADDI, 0x08, 0);
	ITYPE;
        uint32 result = GPR[rs] + immediate;
	bool ep = ((~(GPR[rs] ^ immediate)) & (GPR[rs] ^ result)) & 0x80000000;

	DO_LDS();

        if(ep)
	{
	 new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask);
         new_PC_mask = 0;
	}
        else
         GPR[rt] = result;

    END_OPF;

    //
    // ADDIU - Add Immediate Unsigned Word
    //
    BEGIN_OPF(ADDIU, 0x09, 0);
	ITYPE;
	uint32 result = GPR[rs] + immediate;

	DO_LDS();

	GPR[rt] = result;

    END_OPF;

    //
    // ADDU - Add Unsigned Word
    //
    BEGIN_OPF(ADDU, 0, 0x21);
	RTYPE;
	uint32 result = GPR[rs] + GPR[rt];

	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // AND - And
    //
    BEGIN_OPF(AND, 0, 0x24);
	RTYPE;
	uint32 result = GPR[rs] & GPR[rt];

	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // ANDI - And Immediate
    //
    BEGIN_OPF(ANDI, 0x0C, 0);
	ITYPE_ZE;
	uint32 result = GPR[rs] & immediate;

	DO_LDS();

	GPR[rt] = result;

    END_OPF;

    //
    // BEQ - Branch on Equal
    //
    BEGIN_OPF(BEQ, 0x04, 0);
	ITYPE;
	bool result = (GPR[rs] == GPR[rt]);

	DO_LDS();

	if(result)
	{
	 DO_BRANCH((immediate << 2), ~0U);
	}
    END_OPF;

    // Bah, why does MIPS encoding have to be funky like this. :(
    // Handles BGEZ, BGEZAL, BLTZ, BLTZAL
    BEGIN_OPF(BCOND, 0x01, 0);
	const uint32 tv = GPR[(instr >> 21) & 0x1F];
	uint32 riv = (instr >> 16) & 0x1F;
	int32 immediate = (int16)(instr & 0xFFFF);
	bool result = (int32)(tv ^ (riv << 31)) < 0;
	//if(riv & ~(0x11))
	 //PSX_WARNING("[CPU] Unknown instruction %08x, op=%02x, funct=%02x", instr, instr >> 26, (instr & 0x3F))

	DO_LDS();

	if(riv & 0x10)	// Unconditional link reg setting.
	 GPR[31] = PC + 8;

        if(result)
	{
	 DO_BRANCH((immediate << 2), ~0U);
	}

    END_OPF;


    //
    // BGTZ - Branch on Greater than Zero
    //
    BEGIN_OPF(BGTZ, 0x07, 0);
	ITYPE;
	bool result = (int32)GPR[rs] > 0;

	DO_LDS();

	if(result)
	{
	 DO_BRANCH((immediate << 2), ~0U);
	}
    END_OPF;

    //
    // BLEZ - Branch on Less Than or Equal to Zero
    //
    BEGIN_OPF(BLEZ, 0x06, 0);
	ITYPE;
	bool result = (int32)GPR[rs] <= 0;

	DO_LDS();

	if(result)
	{
	 DO_BRANCH((immediate << 2), ~0U);
	}

    END_OPF;

    //
    // BNE - Branch on Not Equal
    //
    BEGIN_OPF(BNE, 0x05, 0);
	ITYPE;
	bool result = GPR[rs] != GPR[rt];

	DO_LDS();

	if(result)
	{
	 DO_BRANCH((immediate << 2), ~0U);
	}

    END_OPF;

    //
    // BREAK - Breakpoint
    //
    BEGIN_OPF(BREAK, 0, 0x0D);
	PSX_WARNING("[CPU] BREAK BREAK BREAK BREAK DAAANCE -- PC=0x%08x", PC);

	DO_LDS();
	new_PC = Exception(EXCEPTION_BP, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    // Cop "instructions":	CFCz(no CP0), COPz, CTCz(no CP0), LWCz(no CP0), MFCz, MTCz, SWCz(no CP0)
    //
    // COP0 instructions
    BEGIN_OPF(COP0, 0x10, 0);
	uint32 sub_op = (instr >> 21) & 0x1F;

	if(sub_op & 0x10)
	 sub_op = 0x10 + (instr & 0x3F);

	//printf("COP0 thing: %02x\n", sub_op);
	switch(sub_op)
	{
	 default:
		DO_LDS();
		break;

	 case 0x00:		// MFC0	- Move from Coprocessor
		{
		 uint32 rt = (instr >> 16) & 0x1F;
		 uint32 rd = (instr >> 11) & 0x1F;

		 //printf("MFC0: rt=%d <- rd=%d(%08x)\n", rt, rd, CP0.Regs[rd]);
		 DO_LDS();

		 LDWhich = rt;
		 LDValue = CP0.Regs[rd];
		}
		break;

	 case 0x04:		// MTC0	- Move to Coprocessor
		{
		 uint32 rt = (instr >> 16) & 0x1F;
		 uint32 rd = (instr >> 11) & 0x1F;
		 uint32 val = GPR[rt];

		 if(rd != CP0REG_CAUSE && rd != CP0REG_SR && val)
		 {
	 	  PSX_WARNING("[CPU] Unimplemented MTC0: rt=%d(%08x) -> rd=%d", rt, GPR[rt], rd);
		 }

		 switch(rd)
		 {
		  case CP0REG_BPC:
			CP0.BPC = val;
			break;

		  case CP0REG_BDA:
			CP0.BDA = val;
			break;

		  case CP0REG_TAR:
			CP0.TAR = val;
			break;

		  case CP0REG_DCIC:
			CP0.DCIC = val & 0xFF80003F;
			break;

  		  case CP0REG_BDAM:
			CP0.BDAM = val;
			break;

  		  case CP0REG_BPCM:
			CP0.BPCM = val;
			break;

		  case CP0REG_CAUSE:
			CP0.CAUSE &= ~(0x3 << 8);
			CP0.CAUSE |= val & (0x3 << 8);
			RecalcIPCache();
			break;

		  case CP0REG_SR:
			CP0.SR = val & ~( (0x3 << 26) | (0x3 << 23) | (0x3 << 6));
			RecalcIPCache();

			if(CP0.SR & 0x10000)
			 PSX_WARNING("[CPU] IsC set");
			break;
		 }
		}
		DO_LDS();
		break;

	 case (0x10 + 0x10):	// RFE
		// "Pop"
		DO_LDS();
		CP0.SR = (CP0.SR & ~0x0F) | ((CP0.SR >> 2) & 0x0F);
		RecalcIPCache();
		break;
	}
    END_OPF;

    //
    // COP1
    //
    BEGIN_OPF(COP1, 0x11, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    //
    // COP2
    //
    BEGIN_OPF(COP2, 0x12, 0);
	uint32 sub_op = (instr >> 21) & 0x1F;

	switch(sub_op)
	{
	 default:
		DO_LDS();
		break;

	 case 0x00:		// MFC2	- Move from Coprocessor
		{
		 uint32 rt = (instr >> 16) & 0x1F;
		 uint32 rd = (instr >> 11) & 0x1F;

	         if(timestamp < gte_ts_done)
	          timestamp = gte_ts_done;

 	 	 DO_LDS();

		 LDWhich = rt;
		 LDValue = GTE_ReadDR(rd);
		}
		break;

	 case 0x04:		// MTC2	- Move to Coprocessor
		{
		 uint32 rt = (instr >> 16) & 0x1F;
		 uint32 rd = (instr >> 11) & 0x1F;
		 uint32 val = GPR[rt];

	         if(timestamp < gte_ts_done)
	          timestamp = gte_ts_done;

		 //printf("GTE WriteDR: %d %d\n", rd, val);
		 GTE_WriteDR(rd, val);
	         DO_LDS();
		}
		break;

	 case 0x02:		// CFC2
		{
		 uint32 rt = (instr >> 16) & 0x1F;
		 uint32 rd = (instr >> 11) & 0x1F;

	         if(timestamp < gte_ts_done)
	          timestamp = gte_ts_done;

	 	 DO_LDS();

		 LDWhich = rt;
		 LDValue = GTE_ReadCR(rd);
		//printf("GTE ReadCR: %d %d\n", rd, GPR[rt]);
		}		
		break;

	 case 0x06:		// CTC2
		{
		 uint32 rt = (instr >> 16) & 0x1F;
		 uint32 rd = (instr >> 11) & 0x1F;
		 uint32 val = GPR[rt];

		//printf("GTE WriteCR: %d %d\n", rd, val);

 	         if(timestamp < gte_ts_done)
	          timestamp = gte_ts_done;

		 GTE_WriteCR(rd, val);		 
	 	 DO_LDS();
		}
		break;

    //zero 07-feb-2012 - remove case range
   case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F:
		//printf("%08x\n", PC);
	        if(timestamp < gte_ts_done)
	         timestamp = gte_ts_done;
		gte_ts_done = timestamp + GTE_Instruction(instr);
		DO_LDS();
		break;
	}
    END_OPF;

    //
    // COP3
    //
    BEGIN_OPF(COP3, 0x13, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    //
    // LWC0
    //
    BEGIN_OPF(LWC0, 0x30, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    //
    // LWC1
    //
    BEGIN_OPF(LWC1, 0x31, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    //
    // LWC2
    //
    BEGIN_OPF(LWC2, 0x32, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        if(address & 3)
	{
         new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask);
         new_PC_mask = 0;
	}
        else
	{
         if(timestamp < gte_ts_done)
          timestamp = gte_ts_done;

         GTE_WriteDR(rt, PSX_MemRead32(timestamp, address));
	}
	DO_LDS();
	// GTE stuff here
    END_OPF;

    //
    // LWC3
    //
    BEGIN_OPF(LWC3, 0x33, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;


    //
    // SWC0
    //
    BEGIN_OPF(SWC0, 0x38, 0);
	DO_LDS();
	new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    //
    // SWC1
    //
    BEGIN_OPF(SWC1, 0x39, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;

    //
    // SWC2
    //
    BEGIN_OPF(SWC2, 0x3A, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	if(address & 0x3)
	{
	 new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else if(!(CP0.SR & 0x10000))
	{
         if(timestamp < gte_ts_done)
          timestamp = gte_ts_done;

	 PSX_MemWrite32(timestamp, address, GTE_ReadDR(rt));
	}
	DO_LDS();
    END_OPF;

    //
    // SWC3
    ///
    BEGIN_OPF(SWC3, 0x3B, 0);
	DO_LDS();
        new_PC = Exception(EXCEPTION_RI, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;


    //
    // DIV - Divide Word
    //
    BEGIN_OPF(DIV, 0, 0x1A);
	RTYPE;

        if(!GPR[rt])
        {
         //PSX_WARNING("[CPU] Division(signed) by zero at PC=%08x", PC);

	 if(GPR[rs] & 0x80000000)
	  LO = 1;
	 else
	  LO = 0xFFFFFFFF;

	 HI = GPR[rs];
        }
	else if(GPR[rs] == 0x80000000 && GPR[rt] == 0xFFFFFFFF)
	{
	 LO = 0x80000000;
	 HI = 0;
	}
        else
        {
         LO = (int32)GPR[rs] / (int32)GPR[rt];
         HI = (int32)GPR[rs] % (int32)GPR[rt];
        }

	DO_LDS();

    END_OPF;


    //
    // DIVU - Divide Unsigned Word
    //
    BEGIN_OPF(DIVU, 0, 0x1B);
	RTYPE;

	if(!GPR[rt])
	{
	 //PSX_WARNING("[CPU] Division(unsigned) by zero at PC=%08x", PC);

	 LO = 0xFFFFFFFF;
	 HI = GPR[rs];
	}
	else
	{
	 LO = GPR[rs] / GPR[rt];
	 HI = GPR[rs] % GPR[rt];
	}

	DO_LDS();
    END_OPF;

    //
    // J - Jump
    //
    BEGIN_OPF(J, 0x02, 0);
	JTYPE;

	DO_LDS();

	DO_BRANCH(target << 2, 0xF0000000);
    END_OPF;

    //
    // JAL - Jump and Link
    //
    BEGIN_OPF(JAL, 0x03, 0);
	JTYPE;

	DO_LDS();

	GPR[31] = PC + 8;

	DO_BRANCH(target << 2, 0xF0000000);
    END_OPF;

    //
    // JALR - Jump and Link Register
    //
    BEGIN_OPF(JALR, 0, 0x09);
	RTYPE;
	uint32 tmp = GPR[rs];

	DO_LDS();

	GPR[rd] = PC + 8;

	DO_BRANCH(tmp, 0);

    END_OPF;

    //
    // JR - Jump Register
    //
    BEGIN_OPF(JR, 0, 0x08);
	RTYPE;
	uint32 bt = GPR[rs];

	DO_LDS();

	DO_BRANCH(bt, 0);

    END_OPF;

    //
    // LUI - Load Upper Immediate
    //
    BEGIN_OPF(LUI, 0x0F, 0);
	ITYPE_ZE;		// Actually, probably would be sign-extending...if we were emulating a 64-bit MIPS chip :b

	DO_LDS();

	GPR[rt] = immediate << 16;

    END_OPF;

    //
    // MFHI - Move from HI
    //
    BEGIN_OPF(MFHI, 0, 0x10);
	RTYPE;

	DO_LDS();

	GPR[rd] = HI;

    END_OPF;


    //
    // MFLO - Move from LO
    //
    BEGIN_OPF(MFLO, 0, 0x12);
	RTYPE;

	DO_LDS();

	GPR[rd] = LO;

    END_OPF;


    //
    // MTHI - Move to HI
    //
    BEGIN_OPF(MTHI, 0, 0x11);
	RTYPE;

	HI = GPR[rs];

	DO_LDS();

    END_OPF;

    //
    // MTLO - Move to LO
    //
    BEGIN_OPF(MTLO, 0, 0x13);
	RTYPE;

	LO = GPR[rs];

	DO_LDS();

    END_OPF;


    //
    // MULT - Multiply Word
    //
    BEGIN_OPF(MULT, 0, 0x18);
	RTYPE;
	uint64 result;

	result = (int64)(int32)GPR[rs] * (int32)GPR[rt];

	DO_LDS();

	LO = result;
	HI = result >> 32;

    END_OPF;

    //
    // MULTU - Multiply Unsigned Word
    //
    BEGIN_OPF(MULTU, 0, 0x19);
	RTYPE;
	uint64 result;

	result = (uint64)GPR[rs] * GPR[rt];

	DO_LDS();

	LO = result;
	HI = result >> 32;

    END_OPF;


    //
    // NOR - NOR
    //
    BEGIN_OPF(NOR, 0, 0x27);
	RTYPE;
	uint32 result = ~(GPR[rs] | GPR[rt]);

	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // OR - OR
    //
    BEGIN_OPF(OR, 0, 0x25);
	RTYPE;
	uint32 result = GPR[rs] | GPR[rt];

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // ORI - OR Immediate
    //
    BEGIN_OPF(ORI, 0x0D, 0);
	ITYPE_ZE;
	uint32 result = GPR[rs] | immediate;

	DO_LDS();

	GPR[rt] = result;

    END_OPF;


    //
    // SLL - Shift Word Left Logical
    //
    BEGIN_OPF(SLL, 0, 0x00);	// SLL
	RTYPE;
	uint32 result = GPR[rt] << shamt;

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SLLV - Shift Word Left Logical Variable
    //
    BEGIN_OPF(SLLV, 0, 0x04);
	RTYPE;
	uint32 result = GPR[rt] << (GPR[rs] & 0x1F);

	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // SLT - Set on Less Than
    //
    BEGIN_OPF(SLT, 0, 0x2A);
	RTYPE;
	uint32 result = (bool)((int32)GPR[rs] < (int32)GPR[rt]);

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SLTI - Set on Less Than Immediate
    //
    BEGIN_OPF(SLTI, 0x0A, 0);
	ITYPE;
	uint32 result = (bool)((int32)GPR[rs] < immediate);

	DO_LDS();

	GPR[rt] = result;

    END_OPF;


    //
    // SLTIU - Set on Less Than Immediate, Unsigned
    //
    BEGIN_OPF(SLTIU, 0x0B, 0);
	ITYPE;
	uint32 result = (bool)(GPR[rs] < (uint32)immediate);

	DO_LDS();

	GPR[rt] = result;

    END_OPF;


    //
    // SLTU - Set on Less Than, Unsigned
    //
    BEGIN_OPF(SLTU, 0, 0x2B);
	RTYPE;
	uint32 result = (bool)(GPR[rs] < GPR[rt]);

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SRA - Shift Word Right Arithmetic
    //
    BEGIN_OPF(SRA, 0, 0x03);
	RTYPE;
	uint32 result = ((int32)GPR[rt]) >> shamt;

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SRAV - Shift Word Right Arithmetic Variable
    //
    BEGIN_OPF(SRAV, 0, 0x07);
	RTYPE;
	uint32 result = ((int32)GPR[rt]) >> (GPR[rs] & 0x1F);

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SRL - Shift Word Right Logical
    //
    BEGIN_OPF(SRL, 0, 0x02);
	RTYPE;
	uint32 result = GPR[rt] >> shamt;

	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // SRLV - Shift Word Right Logical Variable
    //
    BEGIN_OPF(SRLV, 0, 0x06);
	RTYPE;
	uint32 result = GPR[rt] >> (GPR[rs] & 0x1F);

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SUB - Subtract Word
    //
    BEGIN_OPF(SUB, 0, 0x22);
	RTYPE;
	uint32 result = GPR[rs] - GPR[rt];
	bool ep = (((GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000;

	DO_LDS();

	if(ep)
	{
	 new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else
	 GPR[rd] = result;

    END_OPF;


    //
    // SUBU - Subtract Unsigned Word
    //
    BEGIN_OPF(SUBU, 0, 0x23); // SUBU
	RTYPE;
	uint32 result = GPR[rs] - GPR[rt];

	DO_LDS();

	GPR[rd] = result;

    END_OPF;


    //
    // SYSCALL
    //
    BEGIN_OPF(SYSCALL, 0, 0x0C);
	DO_LDS();

	new_PC = Exception(EXCEPTION_SYSCALL, PC, new_PC_mask);
        new_PC_mask = 0;
    END_OPF;


    //
    // XOR
    //
    BEGIN_OPF(XOR, 0, 0x26);
	RTYPE;
	uint32 result = GPR[rs] ^ GPR[rt];

	DO_LDS();

	GPR[rd] = result;

    END_OPF;

    //
    // XORI - Exclusive OR Immediate
    //
    BEGIN_OPF(XORI, 0x0E, 0);
	ITYPE_ZE;
	uint32 result = GPR[rs] ^ immediate;

	DO_LDS();

	GPR[rt] = result;
    END_OPF;

    //
    // Memory access instructions(besides the coprocessor ones) follow:
    //

    //
    // LB - Load Byte
    //
    BEGIN_OPF(LB, 0x20, 0);
	ITYPE;
	uint32 address = GPR[rs] + immediate;

	DO_LDS();

	LDWhich = rt;
	LDValue = (int32)ReadMemory<int8>(timestamp, address);
    END_OPF;

    //
    // LBU - Load Byte Unsigned
    //
    BEGIN_OPF(LBU, 0x24, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	DO_LDS();

        LDWhich = rt;
	LDValue = ReadMemory<uint8>(timestamp, address);
    END_OPF;

    //
    // LH - Load Halfword
    //
    BEGIN_OPF(LH, 0x21, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	DO_LDS();

	if(address & 1)
	{
	 new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else
	{
	 LDWhich = rt;
         LDValue = (int32)ReadMemory<int16>(timestamp, address);
	}
    END_OPF;

    //
    // LHU - Load Halfword Unsigned
    //
    BEGIN_OPF(LHU, 0x25, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	DO_LDS();

        if(address & 1)
	{
         new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else
	{
	 LDWhich = rt;
         LDValue = ReadMemory<uint16>(timestamp, address);
	}
    END_OPF;


    //
    // LW - Load Word
    //
    BEGIN_OPF(LW, 0x23, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	DO_LDS();

        if(address & 3)
	{
         new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask);
         new_PC_mask = 0;
	}
        else
	{
	 LDWhich = rt;
         LDValue = ReadMemory<uint32>(timestamp, address);
	}
    END_OPF;

    //
    // SB - Store Byte
    //
    BEGIN_OPF(SB, 0x28, 0);
	ITYPE;
	uint32 address = GPR[rs] + immediate;

	WriteMemory<uint8>(timestamp, address, GPR[rt]);

	DO_LDS();
    END_OPF;

    // 
    // SH - Store Halfword
    //
    BEGIN_OPF(SH, 0x29, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	if(address & 0x1)
	{
	 new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else
	 WriteMemory<uint16>(timestamp, address, GPR[rt]);

	DO_LDS();
    END_OPF;

    // 
    // SW - Store Word
    //
    BEGIN_OPF(SW, 0x2B, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	if(address & 0x3)
	{
	 new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask);
         new_PC_mask = 0;
	}
	else
	 WriteMemory<uint32>(timestamp, address, GPR[rt]);

	DO_LDS();
    END_OPF;

    // LWL and LWR load delay slot tomfoolery appears to apply even to MFC0! (and probably MFCn and CFCn as well, though they weren't explicitly tested)

    //
    // LWL - Load Word Left
    //
    BEGIN_OPF(LWL, 0x22, 0);
	ITYPE;
	uint32 address = GPR[rs] + immediate;
	uint32 v = GPR[rt];

	if(LDWhich == rt)
	{
	 v = LDValue;
	}
	else
	{
	 DO_LDS();
	}

	LDWhich = rt;
	switch(address & 0x3)
	{
	 case 0: LDValue = (v & ~(0xFF << 24)) | (ReadMemory<uint8>(timestamp, address & ~3) << 24);
		 break;

	 case 1: LDValue = (v & ~(0xFFFF << 16)) | (ReadMemory<uint16>(timestamp, address & ~3) << 16);
	         break;

	 case 2: LDValue = (v & ~(0xFFFFFF << 8)) | (ReadMemory<uint32>(timestamp, address & ~3, true) << 8);
		 break;

	 case 3: LDValue = (v & ~(0xFFFFFFFF << 0)) | (ReadMemory<uint32>(timestamp, address & ~3) << 0);
		 break;
	}
    END_OPF;

    //
    // SWL - Store Word Left
    //
    BEGIN_OPF(SWL, 0x2A, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	switch(address & 0x3)
	{
	 case 0: WriteMemory<uint8>(timestamp, address & ~3, GPR[rt] >> 24);
		 break;

	 case 1: WriteMemory<uint16>(timestamp, address & ~3, GPR[rt] >> 16);
	         break;

	 case 2: WriteMemory<uint32>(timestamp, address & ~3, GPR[rt] >> 8, true);
		 break;

	 case 3: WriteMemory<uint32>(timestamp, address & ~3, GPR[rt] >> 0);
		 break;
	}
	DO_LDS();

    END_OPF;

    //
    // LWR - Load Word Right
    //
    BEGIN_OPF(LWR, 0x26, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;
	uint32 v = GPR[rt];

	if(LDWhich == rt)
	{
	 v = LDValue;
	}
	else
	{
	 DO_LDS();
	}

	LDWhich = rt;
	switch(address & 0x3)
	{
	 case 0: LDValue = (v & ~(0xFFFFFFFF)) | ReadMemory<uint32>(timestamp, address);
		 break;

	 case 1: LDValue = (v & ~(0xFFFFFF)) | ReadMemory<uint32>(timestamp, address, true);
		 break;

	 case 2: LDValue = (v & ~(0xFFFF)) | ReadMemory<uint16>(timestamp, address);
	         break;

	 case 3: LDValue = (v & ~(0xFF)) | ReadMemory<uint8>(timestamp, address);
		 break;
	}
    END_OPF;

    //
    // SWR - Store Word Right
    //
    BEGIN_OPF(SWR, 0x2E, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	switch(address & 0x3)
	{
	 case 0: WriteMemory<uint32>(timestamp, address, GPR[rt]);
		 break;

	 case 1: WriteMemory<uint32>(timestamp, address, GPR[rt], true);
		 break;

	 case 2: WriteMemory<uint16>(timestamp, address, GPR[rt]);
	         break;

	 case 3: WriteMemory<uint8>(timestamp, address, GPR[rt]);
		 break;
	}

	DO_LDS();

    END_OPF;

    //
    // Mednafen special instruction
    //
    BEGIN_OPF(INTERRUPT, 0x3F, 0);
	if(Halted)
	{
	 goto SkipNPCStuff;
	}
	else
	{
 	 DO_LDS();

	 new_PC = Exception(EXCEPTION_INT, PC, new_PC_mask);
         new_PC_mask = 0;
	}
    END_OPF;
   }

   OpDone: ;

   PC = (PC & new_PC_mask) + new_PC;
   new_PC_mask = ~0U;
   new_PC = 4;

   SkipNPCStuff:	;

   //printf("\n");
  }
 } while(PSX_EventHandler(timestamp));

 if(gte_ts_done > 0)
  gte_ts_done -= timestamp;

 ACTIVE_TO_BACKING;

 return(timestamp);
}

pscpu_timestamp_t PS_CPU::Run(pscpu_timestamp_t timestamp_in, bool ILHMode)
{
 if(CPUHook || ADDBT)
  return(RunReal<true, false>(timestamp_in));
 else
 {
  if(ILHMode)
   return(RunReal<false, true>(timestamp_in));
  else
   return(RunReal<false, false>(timestamp_in));
 }
}

void PS_CPU::SetCPUHook(void (*cpuh)(uint32 pc), void (*addbt)(uint32 from, uint32 to, bool exception))
{
 ADDBT = addbt;
 CPUHook = cpuh;
}

uint32 PS_CPU::GetRegister(unsigned int which, char *special, const uint32 special_len)
{
 uint32 ret = 0;

 if(which >= GSREG_GPR && which < (GSREG_GPR + 32))
  ret = GPR[which];
 else switch(which)
 {
  case GSREG_PC:
	ret = BACKED_PC;
	break;

  case GSREG_PC_NEXT:
	ret = BACKED_new_PC;
	break;

  case GSREG_IN_BD_SLOT:
	ret = !(BACKED_new_PC_mask & 3);
	break;

  case GSREG_LO:
	ret = LO;
	break;

  case GSREG_HI:
	ret = HI;
	break;

  case GSREG_SR:
	ret = CP0.SR;
	break;

  case GSREG_CAUSE:
	ret = CP0.CAUSE;
	break;

  case GSREG_EPC:
	ret = CP0.EPC;
	break;

 }

 return(ret);
}

void PS_CPU::SetRegister(unsigned int which, uint32 value)
{
 if(which >= GSREG_GPR && which < (GSREG_GPR + 32))
 {
  if(which != (GSREG_GPR + 0))
   GPR[which] = value;
 }
 else switch(which)
 {
  case GSREG_PC:
        BACKED_PC = value & ~0x3;	// Remove masking if we ever add proper misaligned PC exception
        break;

  case GSREG_LO:
        LO = value;
        break;

  case GSREG_HI:
        HI = value;
        break;

  case GSREG_SR:
        CP0.SR = value;		// TODO: mask
        break;

  case GSREG_CAUSE:
	CP0.CAUSE = value;
	break;

  case GSREG_EPC:
	CP0.EPC = value & ~0x3U;
	break;


 }
}

#undef BEGIN_OPF
#undef END_OPF
#undef MK_OPF

#define MK_OPF(op, funct)	((op) ? (0x40 | (op)) : (funct))
#define BEGIN_OPF(op, funct) case MK_OPF(op, funct): {
#define END_OPF } break;

// FIXME: should we breakpoint on an illegal address?  And with LWC2/SWC2 if CP2 isn't enabled?
void PS_CPU::CheckBreakpoints(void (*callback)(bool write, uint32 address, unsigned int len), uint32 instr)
{
 uint32 opf;

 opf = instr & 0x3F;

 if(instr & (0x3F << 26))
  opf = 0x40 | (instr >> 26);


 switch(opf)
 {
  default:
	break;

    //
    // LB - Load Byte
    //
    BEGIN_OPF(0x20, 0);
	ITYPE;
	uint32 address = GPR[rs] + immediate;

        callback(false, address, 1);
    END_OPF;

    //
    // LBU - Load Byte Unsigned
    //
    BEGIN_OPF(0x24, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        callback(false, address, 1);
    END_OPF;

    //
    // LH - Load Halfword
    //
    BEGIN_OPF(0x21, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        callback(false, address, 2);
    END_OPF;

    //
    // LHU - Load Halfword Unsigned
    //
    BEGIN_OPF(0x25, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        callback(false, address, 2);
    END_OPF;


    //
    // LW - Load Word
    //
    BEGIN_OPF(0x23, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        callback(false, address, 4);
    END_OPF;

    //
    // SB - Store Byte
    //
    BEGIN_OPF(0x28, 0);
	ITYPE;
	uint32 address = GPR[rs] + immediate;

        callback(true, address, 1);
    END_OPF;

    // 
    // SH - Store Halfword
    //
    BEGIN_OPF(0x29, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        callback(true, address, 2);
    END_OPF;

    // 
    // SW - Store Word
    //
    BEGIN_OPF(0x2B, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        callback(true, address, 4);
    END_OPF;

    //
    // LWL - Load Word Left
    //
    BEGIN_OPF(0x22, 0);
	ITYPE;
	uint32 address = GPR[rs] + immediate;

	do
	{
         callback(false, address, 1);
	} while((address--) & 0x3);

    END_OPF;

    //
    // SWL - Store Word Left
    //
    BEGIN_OPF(0x2A, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        do
        {
	 callback(true, address, 1);
        } while((address--) & 0x3);

    END_OPF;

    //
    // LWR - Load Word Right
    //
    BEGIN_OPF(0x26, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        do
        {
	 callback(false, address, 1);
        } while((++address) & 0x3);

    END_OPF;

    //
    // SWR - Store Word Right
    //
    BEGIN_OPF(0x2E, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

        do
        {
	 callback(true, address, 1);
        } while((++address) & 0x3);

    END_OPF;

    //
    // LWC2
    //
    BEGIN_OPF(0x32, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	callback(false, address, 4);
    END_OPF;

    //
    // SWC2
    //
    BEGIN_OPF(0x3A, 0);
        ITYPE;
        uint32 address = GPR[rs] + immediate;

	callback(true, address, 4);
    END_OPF;

 }
}


}
