/*
 * UPSE: the unix playstation sound emulator.
 *
 * Filename: upse_r3000_bytecode_evaluator.c
 * Purpose: libupse: r3K machine bytecode evaluator
 *
 * Copyright (c) 2007 William Pitcock <nenolod@sacredspiral.co.uk>
 * Portions copyright (c) 1999-2002 Pcsx Team
 * Portions copyright (c) 2004 "Xodnizel"
 *
 * UPSE is free software, released under the GNU General Public License,
 * version 2.
 *
 * A copy of the GNU General Public License, version 2, is included in
 * the UPSE source kit as COPYING.
 *
 * UPSE is offered without any warranty of any kind, explicit or implicit.
 */

#include <stdlib.h>

#include "upse-internal.h"

static int branch;
static int branch2;
static u32 branchPC;

// These macros are used to assemble the repassembler functions

//        if(!PSXM(upse_r3000_cpu_regs.pc)) puts("Whoops");   
//        Fix this...

//        printf("%08x ", upse_r3000_cpu_regs.pc);

#define execI() { \
	upse_r3000_cpu_regs.code = BFLIP32(PSXMu32(upse_r3000_cpu_regs.pc)); \
        	\
	upse_r3000_cpu_regs.pc+= 4; upse_r3000_cpu_regs.cycle++; \
	psxBSC[upse_r3000_cpu_regs.code >> 26](); \
}

// Subsets
static void (*psxBSC[64]) ();
static void (*psxSPC[64]) ();
static void (*psxREG[32]) ();
static void (*psxCP0[32]) ();

static void delayRead(int reg, u32 bpc)
{
    u32 rold, rnew;

//      SysPrintf("delayRead at %x!\n", upse_r3000_cpu_regs.pc);

    rold = upse_r3000_cpu_regs.GPR.r[reg];
    psxBSC[upse_r3000_cpu_regs.code >> 26] ();	// branch delay load
    rnew = upse_r3000_cpu_regs.GPR.r[reg];

    upse_r3000_cpu_regs.pc = bpc;

    psxBranchTest();

    upse_r3000_cpu_regs.GPR.r[reg] = rold;
    execI();			// first branch opcode
    upse_r3000_cpu_regs.GPR.r[reg] = rnew;

    branch = 0;
}

static void delayWrite(int reg, u32 bpc)
{

/*	SysPrintf("delayWrite at %x!\n", upse_r3000_cpu_regs.pc);

//	SysPrintf("%s\n", disR3000AF(upse_r3000_cpu_regs.code, upse_r3000_cpu_regs.pc-4));
//	SysPrintf("%s\n", disR3000AF(PSXMu32(bpc), bpc));*/

    // no changes from normal behavior

    psxBSC[upse_r3000_cpu_regs.code >> 26] ();

    branch = 0;
    upse_r3000_cpu_regs.pc = bpc;

    psxBranchTest();
}

static void delayReadWrite(int reg, u32 bpc)
{

//      SysPrintf("delayReadWrite at %x!\n", upse_r3000_cpu_regs.pc);

    // the branch delay load is skipped

    branch = 0;
    upse_r3000_cpu_regs.pc = bpc;

    psxBranchTest();
}

// this defines shall be used with the tmp 
// of the next func (instead of _Funct_...)
#define _tFunct_  ((tmp      ) & 0x3F)	// The funct part of the instruction register
#define _tRd_     ((tmp >> 11) & 0x1F)	// The rd part of the instruction register
#define _tRt_     ((tmp >> 16) & 0x1F)	// The rt part of the instruction register
#define _tRs_     ((tmp >> 21) & 0x1F)	// The rs part of the instruction register
#define _tSa_     ((tmp >>  6) & 0x1F)	// The sa part of the instruction register

static void psxDelayTest(u32 reg, u32 bpc)
{
    u32 tmp;

    tmp = BFLIP32(PSXMu32(bpc));
    branch = 1;

    switch (tmp >> 26)
    {
      case 0x00:		// SPECIAL
	  switch (_tFunct_)
	  {
	    case 0x00:		// SLL
		if (!tmp)
		    break;	// NOP
	    case 0x02:
	    case 0x03:		// SRL/SRA
		if (_tRd_ == reg && _tRt_ == reg)
		{
		    delayReadWrite(reg, bpc);
		    return;
		}
		else if (_tRt_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		else if (_tRd_ == reg)
		{
		    delayWrite(reg, bpc);
		    return;
		}
		break;

	    case 0x08:		// JR
		if (_tRs_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		break;
	    case 0x09:		// JALR
		if (_tRd_ == reg && _tRs_ == reg)
		{
		    delayReadWrite(reg, bpc);
		    return;
		}
		else if (_tRs_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		else if (_tRd_ == reg)
		{
		    delayWrite(reg, bpc);
		    return;
		}
		break;

		// SYSCALL/BREAK just a break;

	    case 0x20:
	    case 0x21:
	    case 0x22:
	    case 0x23:
	    case 0x24:
	    case 0x25:
	    case 0x26:
	    case 0x27:
	    case 0x2a:
	    case 0x2b:		// ADD/ADDU...
	    case 0x04:
	    case 0x06:
	    case 0x07:		// SLLV...
		if (_tRd_ == reg && (_tRt_ == reg || _tRs_ == reg))
		{
		    delayReadWrite(reg, bpc);
		    return;
		}
		else if (_tRt_ == reg || _tRs_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		else if (_tRd_ == reg)
		{
		    delayWrite(reg, bpc);
		    return;
		}
		break;

	    case 0x10:
	    case 0x12:		// MFHI/MFLO
		if (_tRd_ == reg)
		{
		    delayWrite(reg, bpc);
		    return;
		}
		break;
	    case 0x11:
	    case 0x13:		// MTHI/MTLO
		if (_tRs_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		break;

	    case 0x18:
	    case 0x19:
	    case 0x1a:
	    case 0x1b:		// MULT/DIV...
		if (_tRt_ == reg || _tRs_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		break;
	  }
	  break;

      case 0x01:		// REGIMM
	  switch (_tRt_)
	  {
	    case 0x00:
	    case 0x02:
	    case 0x10:
	    case 0x12:		// BLTZ/BGEZ...
		if (_tRs_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		break;
	  }
	  break;

	  // J would be just a break;
      case 0x03:		// JAL
	  if (31 == reg)
	  {
	      delayWrite(reg, bpc);
	      return;
	  }
	  break;

      case 0x04:
      case 0x05:		// BEQ/BNE
	  if (_tRs_ == reg || _tRt_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  break;

      case 0x06:
      case 0x07:		// BLEZ/BGTZ
	  if (_tRs_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  break;

      case 0x08:
      case 0x09:
      case 0x0a:
      case 0x0b:
      case 0x0c:
      case 0x0d:
      case 0x0e:		// ADDI/ADDIU...
	  if (_tRt_ == reg && _tRs_ == reg)
	  {
	      delayReadWrite(reg, bpc);
	      return;
	  }
	  else if (_tRs_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  else if (_tRt_ == reg)
	  {
	      delayWrite(reg, bpc);
	      return;
	  }
	  break;

      case 0x0f:		// LUI
	  if (_tRt_ == reg)
	  {
	      delayWrite(reg, bpc);
	      return;
	  }
	  break;

      case 0x10:		// COP0
	  switch (_tFunct_)
	  {
	    case 0x00:		// MFC0
		if (_tRt_ == reg)
		{
		    delayWrite(reg, bpc);
		    return;
		}
		break;
	    case 0x02:		// CFC0
		if (_tRt_ == reg)
		{
		    delayWrite(reg, bpc);
		    return;
		}
		break;
	    case 0x04:		// MTC0
		if (_tRt_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		break;
	    case 0x06:		// CTC0
		if (_tRt_ == reg)
		{
		    delayRead(reg, bpc);
		    return;
		}
		break;
		// RFE just a break;
	  }
	  break;

      case 0x22:
      case 0x26:		// LWL/LWR
	  if (_tRt_ == reg)
	  {
	      delayReadWrite(reg, bpc);
	      return;
	  }
	  else if (_tRs_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  break;

      case 0x20:
      case 0x21:
      case 0x23:
      case 0x24:
      case 0x25:		// LB/LH/LW/LBU/LHU
	  if (_tRt_ == reg && _tRs_ == reg)
	  {
	      delayReadWrite(reg, bpc);
	      return;
	  }
	  else if (_tRs_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  else if (_tRt_ == reg)
	  {
	      delayWrite(reg, bpc);
	      return;
	  }
	  break;

      case 0x28:
      case 0x29:
      case 0x2a:
      case 0x2b:
      case 0x2e:		// SB/SH/SWL/SW/SWR
	  if (_tRt_ == reg || _tRs_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  break;

      case 0x32:
      case 0x3a:		// LWC2/SWC2
	  if (_tRs_ == reg)
	  {
	      delayRead(reg, bpc);
	      return;
	  }
	  break;
    }
    psxBSC[upse_r3000_cpu_regs.code >> 26] ();

    branch = 0;
    upse_r3000_cpu_regs.pc = bpc;

    psxBranchTest();
}

static void psxNULL(void);

static INLINE void doBranch(u32 tar)
{
    u32 tmp;

    branch2 = branch = 1;
    branchPC = tar;

    upse_r3000_cpu_regs.code = BFLIP32(PSXMu32(upse_r3000_cpu_regs.pc));

    upse_r3000_cpu_regs.pc += 4;
    upse_r3000_cpu_regs.cycle++;

    // check for load delay
    tmp = upse_r3000_cpu_regs.code >> 26;
    switch (tmp)
    {
      case 0x10:		// COP0
	  switch (_Rs_)
	  {
	    case 0x00:		// MFC0
	    case 0x02:		// CFC0
		psxDelayTest(_Rt_, branchPC);
		return;
	  }
	  break;
      case 0x32:		// LWC2
	  psxDelayTest(_Rt_, branchPC);
	  return;
      default:
	  if (tmp >= 0x20 && tmp <= 0x26)
	  {			// LB/LH/LWL/LW/LBU/LHU/LWR
	      psxDelayTest(_Rt_, branchPC);
	      return;
	  }
	  break;
    }

    psxBSC[upse_r3000_cpu_regs.code >> 26] ();

    if ((upse_r3000_cpu_regs.pc - 8) == branchPC && !(upse_r3000_cpu_regs.code >> 26))
    {
	//printf("%08x\n",upse_r3000_cpu_regs.code>>26);
	CounterDeadLoopSkip();
    }
    branch = 0;
    upse_r3000_cpu_regs.pc = branchPC;

    psxBranchTest();
}

/*********************************************************
* Arithmetic with immediate operand                      *
* Format:  OP rt, rs, immediate                          *
*********************************************************/
static void psxADDI()
{
    if (!_Rt_)
	return;
    _rRt_ = _u32(_rRs_) + _Imm_;
}				// Rt = Rs + Im         (Exception on Integer Overflow)
static void psxADDIU()
{
    if (!_Rt_)
	return;
    _rRt_ = _u32(_rRs_) + _Imm_;
}				// Rt = Rs + Im
static void psxANDI()
{
    if (!_Rt_)
	return;
    _rRt_ = _u32(_rRs_) & _ImmU_;
}				// Rt = Rs And Im
static void psxORI()
{
    if (!_Rt_)
	return;
    _rRt_ = _u32(_rRs_) | _ImmU_;
}				// Rt = Rs Or  Im
static void psxXORI()
{
    if (!_Rt_)
	return;
    _rRt_ = _u32(_rRs_) ^ _ImmU_;
}				// Rt = Rs Xor Im
static void psxSLTI()
{
    if (!_Rt_)
	return;
    _rRt_ = _i32(_rRs_) < _Imm_;
}				// Rt = Rs < Im         (Signed)
static void psxSLTIU()
{
    if (!_Rt_)
	return;
    _rRt_ = _u32(_rRs_) < ((u32) _ImmU_);
}				// Rt = Rs < Im         (Unsigned)

/*********************************************************
* Register arithmetic                                    *
* Format:  OP rd, rs, rt                                 *
*********************************************************/
static void psxADD()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) + _u32(_rRt_);
}				// Rd = Rs + Rt         (Exception on Integer Overflow)
static void psxADDU()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) + _u32(_rRt_);
}				// Rd = Rs + Rt
static void psxSUB()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) - _u32(_rRt_);
}				// Rd = Rs - Rt         (Exception on Integer Overflow)
static void psxSUBU()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) - _u32(_rRt_);
}				// Rd = Rs - Rt
static void psxAND()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) & _u32(_rRt_);
}				// Rd = Rs And Rt
static void psxOR()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) | _u32(_rRt_);
}				// Rd = Rs Or  Rt
static void psxXOR()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) ^ _u32(_rRt_);
}				// Rd = Rs Xor Rt
static void psxNOR()
{
    if (!_Rd_)
	return;
    _rRd_ = ~(_u32(_rRs_) | _u32(_rRt_));
}				// Rd = Rs Nor Rt
static void psxSLT()
{
    if (!_Rd_)
	return;
    _rRd_ = _i32(_rRs_) < _i32(_rRt_);
}				// Rd = Rs < Rt         (Signed)
static void psxSLTU()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRs_) < _u32(_rRt_);
}				// Rd = Rs < Rt         (Unsigned)

/*********************************************************
* Register mult/div & Register trap logic                *
* Format:  OP rs, rt                                     *
*********************************************************/
static void psxDIV()
{
    if (_i32(_rRt_) != 0)
    {
	_rLo_ = _i32(_rRs_) / _i32(_rRt_);
	_rHi_ = _i32(_rRs_) % _i32(_rRt_);
    }
}

static void psxDIVU()
{
    if (_rRt_ != 0)
    {
	_rLo_ = _rRs_ / _rRt_;
	_rHi_ = _rRs_ % _rRt_;
    }
}

static void psxMULT()
{
    u64 res = (s64) ((s64) _i32(_rRs_) * (s64) _i32(_rRt_));

    upse_r3000_cpu_regs.GPR.n.lo = (u32) (res & 0xffffffff);
    upse_r3000_cpu_regs.GPR.n.hi = (u32) ((res >> 32) & 0xffffffff);
}

static void psxMULTU()
{
    u64 res = (u64) ((u64) _u32(_rRs_) * (u64) _u32(_rRt_));

    upse_r3000_cpu_regs.GPR.n.lo = (u32) (res & 0xffffffff);
    upse_r3000_cpu_regs.GPR.n.hi = (u32) ((res >> 32) & 0xffffffff);
}

/*********************************************************
* Register branch logic                                  *
* Format:  OP rs, offset                                 *
*********************************************************/
#define RepZBranchi32(op)      if(_i32(_rRs_) op 0) doBranch(_BranchTarget_);
#define RepZBranchLinki32(op)  if(_i32(_rRs_) op 0) { _SetLink(31); doBranch(_BranchTarget_); }

static void psxBGEZ()
{
RepZBranchi32(>=)}		// Branch if Rs >= 0
static void psxBGEZAL()
{
RepZBranchLinki32(>=)}		// Branch if Rs >= 0 and link
static void psxBGTZ()
{
RepZBranchi32(>)}		// Branch if Rs >  0
static void psxBLEZ()
{
RepZBranchi32(<=)}		// Branch if Rs <= 0
static void psxBLTZ()
{
RepZBranchi32(<)}		// Branch if Rs <  0
static void psxBLTZAL()
{
RepZBranchLinki32(<)}		// Branch if Rs <  0 and link

/*********************************************************
* Shift arithmetic with constant shift                   *
* Format:  OP rd, rt, sa                                 *
*********************************************************/
static void psxSLL()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRt_) << _Sa_;
}				// Rd = Rt << sa
static void psxSRA()
{
    if (!_Rd_)
	return;
    _rRd_ = _i32(_rRt_) >> _Sa_;
}				// Rd = Rt >> sa (arithmetic)
static void psxSRL()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRt_) >> _Sa_;
}				// Rd = Rt >> sa (logical)

/*********************************************************
* Shift arithmetic with variant register shift           *
* Format:  OP rd, rt, rs                                 *
*********************************************************/
static void psxSLLV()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRt_) << _u32(_rRs_);
}				// Rd = Rt << rs
static void psxSRAV()
{
    if (!_Rd_)
	return;
    _rRd_ = _i32(_rRt_) >> _u32(_rRs_);
}				// Rd = Rt >> rs (arithmetic)
static void psxSRLV()
{
    if (!_Rd_)
	return;
    _rRd_ = _u32(_rRt_) >> _u32(_rRs_);
}				// Rd = Rt >> rs (logical)

/*********************************************************
* Load higher 16 bits of the first word in GPR with imm  *
* Format:  OP rt, immediate                              *
*********************************************************/
static void psxLUI()
{
    if (!_Rt_)
	return;
    _rRt_ = upse_r3000_cpu_regs.code << 16;
}				// Upper halfword of Rt = Im

/*********************************************************
* Move from HI/LO to GPR                                 *
* Format:  OP rd                                         *
*********************************************************/
static void psxMFHI()
{
    if (!_Rd_)
	return;
    _rRd_ = _rHi_;
}				// Rd = Hi
static void psxMFLO()
{
    if (!_Rd_)
	return;
    _rRd_ = _rLo_;
}				// Rd = Lo

/*********************************************************
* Move to GPR to HI/LO & Register jump                   *
* Format:  OP rs                                         *
*********************************************************/
static void psxMTHI()
{
    _rHi_ = _rRs_;
}				// Hi = Rs
static void psxMTLO()
{
    _rLo_ = _rRs_;
}				// Lo = Rs

/*********************************************************
* Special purpose instructions                           *
* Format:  OP                                            *
*********************************************************/
static void psxBREAK()
{
    // Break exception - psx rom doens't handles this
}

static void psxSYSCALL()
{
    upse_r3000_cpu_regs.pc -= 4;
    psxException(0x20, branch);
}

static void psxRFE()
{
    upse_r3000_cpu_regs.CP0.n.Status = (upse_r3000_cpu_regs.CP0.n.Status & 0xfffffff0) | ((upse_r3000_cpu_regs.CP0.n.Status & 0x3c) >> 2);
}

/*********************************************************
* Register branch logic                                  *
* Format:  OP rs, rt, offset                             *
*********************************************************/
#define RepBranchi32(op)      if(_i32(_rRs_) op _i32(_rRt_)) { doBranch(_BranchTarget_); }

static void psxBEQ()
{
RepBranchi32( ==)}		// Branch if Rs == Rt
static void psxBNE()
{
RepBranchi32(!=)}		// Branch if Rs != Rt

/*********************************************************
* Jump to target                                         *
* Format:  OP target                                     *
*********************************************************/
static void psxJ()
{
    doBranch(_JumpTarget_);
}
static void psxJAL()
{
    _SetLink(31);
    doBranch(_JumpTarget_);
}

/*********************************************************
* Register jump                                          *
* Format:  OP rs, rd                                     *
*********************************************************/
static void psxJR()
{
    doBranch(_u32(_rRs_));
}
static void psxJALR()
{
    if (_Rd_)
    {
	_SetLink(_Rd_);
    }
    doBranch(_u32(_rRs_));
}

/*********************************************************
* Load and store for GPR                                 *
* Format:  OP rt, offset(base)                           *
*********************************************************/

#define _oB_ (_u32(_rRs_) + _Imm_)

static void psxLB()
{
    if (_Rt_)
    {
	_rRt_ = (s8) upse_ps1_memory_read_8(_oB_);
    }
    else
    {
	upse_ps1_memory_read_8(_oB_);
    }
}

static void psxLBU()
{
    if (_Rt_)
    {
	_rRt_ = upse_ps1_memory_read_8(_oB_);
    }
    else
    {
	upse_ps1_memory_read_8(_oB_);
    }
}

static void psxLH()
{
    if (_Rt_)
    {
	_rRt_ = (s16) upse_ps1_memory_read_16(_oB_);
    }
    else
    {
	upse_ps1_memory_read_16(_oB_);
    }
}

static void psxLHU()
{
    if (_Rt_)
    {
	_rRt_ = upse_ps1_memory_read_16(_oB_);
    }
    else
    {
	upse_ps1_memory_read_16(_oB_);
    }
}

static void psxLW()
{
    if (_Rt_)
    {
	_rRt_ = upse_ps1_memory_read_32(_oB_);
    }
    else
    {
	upse_ps1_memory_read_32(_oB_);
    }
}

static u32 LWL_MASK[4] = { 0xffffff, 0xffff, 0xff, 0 };
static u32 LWL_SHIFT[4] = { 24, 16, 8, 0 };

static void psxLWL()
{
    u32 addr = _oB_;
    u32 shift = addr & 3;
    u32 mem = upse_ps1_memory_read_32(addr & ~3);

    if (!_Rt_)
	return;
    _rRt_ = (_u32(_rRt_) & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift]);

    /*
       Mem = 1234.  Reg = abcd

       0   4bcd   (mem << 24) | (reg & 0x00ffffff)
       1   34cd   (mem << 16) | (reg & 0x0000ffff)
       2   234d   (mem <<  8) | (reg & 0x000000ff)
       3   1234   (mem      ) | (reg & 0x00000000)
     */
}

static u32 LWR_MASK[4] = { 0, 0xff000000, 0xffff0000, 0xffffff00 };
static u32 LWR_SHIFT[4] = { 0, 8, 16, 24 };

static void psxLWR()
{
    u32 addr = _oB_;
    u32 shift = addr & 3;
    u32 mem = upse_ps1_memory_read_32(addr & ~3);

    if (!_Rt_)
	return;
    _rRt_ = (_u32(_rRt_) & LWR_MASK[shift]) | (mem >> LWR_SHIFT[shift]);

    /*
       Mem = 1234.  Reg = abcd

       0   1234   (mem      ) | (reg & 0x00000000)
       1   a123   (mem >>  8) | (reg & 0xff000000)
       2   ab12   (mem >> 16) | (reg & 0xffff0000)
       3   abc1   (mem >> 24) | (reg & 0xffffff00)
     */
}

static void psxSB()
{
    upse_ps1_memory_write_8(_oB_, _u8(_rRt_));
}
static void psxSH()
{
    upse_ps1_memory_write_16(_oB_, _u16(_rRt_));
}
static void psxSW()
{
    upse_ps1_memory_write_32(_oB_, _u32(_rRt_));
}

static const u32 SWL_MASK[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0 };
static const u32 SWL_SHIFT[4] = { 24, 16, 8, 0 };

static void psxSWL()
{
    u32 addr = _oB_;
    u32 shift = addr & 3;
    u32 mem = upse_ps1_memory_read_32(addr & ~3);

    upse_ps1_memory_write_32(addr & ~3, (_u32(_rRt_) >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift]));
    /*
       Mem = 1234.  Reg = abcd

       0   123a   (reg >> 24) | (mem & 0xffffff00)
       1   12ab   (reg >> 16) | (mem & 0xffff0000)
       2   1abc   (reg >>  8) | (mem & 0xff000000)
       3   abcd   (reg      ) | (mem & 0x00000000)
     */
}

static const u32 SWR_MASK[4] = { 0, 0xff, 0xffff, 0xffffff };
static const u32 SWR_SHIFT[4] = { 0, 8, 16, 24 };

static void psxSWR()
{
    u32 addr = _oB_;
    u32 shift = addr & 3;
    u32 mem = upse_ps1_memory_read_32(addr & ~3);

    upse_ps1_memory_write_32(addr & ~3, (_u32(_rRt_) << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift]));

    /*
       Mem = 1234.  Reg = abcd

       0   abcd   (reg      ) | (mem & 0x00000000)
       1   bcd4   (reg <<  8) | (mem & 0x000000ff)
       2   cd34   (reg << 16) | (mem & 0x0000ffff)
       3   d234   (reg << 24) | (mem & 0x00ffffff)
     */
}

/*********************************************************
* Moves between GPR and COPx                             *
* Format:  OP rt, fs                                     *
*********************************************************/
static void psxMFC0()
{
    if (!_Rt_)
	return;
    _rRt_ = (int) _rFs_;
}
static void psxCFC0()
{
    if (!_Rt_)
	return;
    _rRt_ = (int) _rFs_;
}

static INLINE void MTC0(int reg, u32 val)
{
    switch (reg)
    {
      case 13:			// Cause
	  upse_r3000_cpu_regs.CP0.n.Cause = val & ~(0xfc00);

	  // the next code is untested, if u know please
	  // tell me if it works ok or not (linuzappz)
	  if (upse_r3000_cpu_regs.CP0.n.Cause & upse_r3000_cpu_regs.CP0.n.Status & 0x0300 && upse_r3000_cpu_regs.CP0.n.Status & 0x1)
	  {
	      psxException(upse_r3000_cpu_regs.CP0.n.Cause, 0);
	  }
	  break;

      default:
	  upse_r3000_cpu_regs.CP0.r[reg] = val;
	  break;
    }
}

static void psxMTC0()
{
    MTC0(_Rd_, _u32(_rRt_));
}
static void psxCTC0()
{
    MTC0(_Rd_, _u32(_rRt_));
}

/*********************************************************
* Unknow instruction (would generate an exception)       *
* Format:  ?                                             *
*********************************************************/
static void psxNULL()
{
#ifdef PSXCPU_LOG
    PSXCPU_LOG("psx: Unimplemented op %x\n", upse_r3000_cpu_regs.code);
#endif
}

static void psxSPECIAL()
{
    psxSPC[_Funct_] ();
}

static void psxREGIMM()
{
    psxREG[_Rt_] ();
}

static void psxCOP0()
{
    psxCP0[_Rs_] ();
}

static void psxHLE()
{
    psxHLEt[upse_r3000_cpu_regs.code & 0xff] ();
}

static void (*psxBSC[64]) () =
{
psxSPECIAL, psxREGIMM, psxJ, psxJAL, psxBEQ, psxBNE, psxBLEZ, psxBGTZ,
	psxADDI, psxADDIU, psxSLTI, psxSLTIU, psxANDI, psxORI, psxXORI, psxLUI,
	psxCOP0, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
	psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
	psxLB, psxLH, psxLWL, psxLW, psxLBU, psxLHU, psxLWR, psxNULL,
	psxSB, psxSH, psxSWL, psxSW, psxNULL, psxNULL, psxSWR, psxNULL,
	psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxHLE, psxNULL, psxNULL, psxNULL, psxNULL};


static void (*psxSPC[64]) () =
{
psxSLL, psxNULL, psxSRL, psxSRA, psxSLLV, psxNULL, psxSRLV, psxSRAV,
	psxJR, psxJALR, psxNULL, psxNULL, psxSYSCALL, psxBREAK, psxNULL, psxNULL,
	psxMFHI, psxMTHI, psxMFLO, psxMTLO, psxNULL, psxNULL, psxNULL, psxNULL,
	psxMULT, psxMULTU, psxDIV, psxDIVU, psxNULL, psxNULL, psxNULL, psxNULL,
	psxADD, psxADDU, psxSUB, psxSUBU, psxAND, psxOR, psxXOR, psxNOR,
	psxNULL, psxNULL, psxSLT, psxSLTU, psxNULL, psxNULL, psxNULL, psxNULL,
	psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL};

static void (*psxREG[32]) () =
{
psxBLTZ, psxBGEZ, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
	psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
	psxBLTZAL, psxBGEZAL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL};

static void (*psxCP0[32]) () =
{
psxMFC0, psxNULL, psxCFC0, psxNULL, psxMTC0, psxNULL, psxCTC0, psxNULL,
	psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
	psxRFE, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL};


///////////////////////////////////////////

int upse_r3000_cpu_init(void)
{
    return 0;
}

void upse_r3000_cpu_reset(void)
{
    branch = branch2 = 0;
}

void upse_r3000_cpu_execute(void)
{
    for (;;)
    {
	if (!CounterSPURun())
	{
	    psxShutdown();
	    return;
	}
	upse_ps1_spu_finalize();
	execI();
    }
}

void upse_r3000_cpu_execute_block(void)
{
    branch2 = 0;
    while (!branch2)
	execI();
}

void upse_r3000_cpu_clear(u32 Addr, u32 Size)
{
}

void upse_r3000_cpu_shutdown(void)
{
}
