/*  Pcsx - Pc Psx Emulator
*  Copyright (C) 1999-2003  Pcsx 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA
*/


#ifdef _MSC_VER_
#pragma warning(disable:4244)
#pragma warning(disable:4761)
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>

#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

#include "../psxcommon.h"
#include "ppc.h"
#include "reguse.h"
#include "../r3000a.h"
#include "../psxhle.h"

#define malloc balloc
#define free bfree

//#define NO_CONSTANT

u32 *psxRecLUT;

#undef _Op_
#define _Op_     _fOp_(psxRegs.code)
#undef _Funct_
#define _Funct_  _fFunct_(psxRegs.code)
#undef _Rd_
#define _Rd_     _fRd_(psxRegs.code)
#undef _Rt_
#define _Rt_     _fRt_(psxRegs.code)
#undef _Rs_
#define _Rs_     _fRs_(psxRegs.code)
#undef _Sa_
#define _Sa_     _fSa_(psxRegs.code)
#undef _Im_
#define _Im_     _fIm_(psxRegs.code)
#undef _Target_
#define _Target_ _fTarget_(psxRegs.code)

#undef _Imm_
#define _Imm_     _fImm_(psxRegs.code)
#undef _ImmU_
#define _ImmU_     _fImmU_(psxRegs.code)

#undef PC_REC
#undef PC_REC8
#undef PC_REC16
#undef PC_REC32
#define PC_REC(x)    (psxRecLUT[x >> 16] + (x & 0xffff))
#define PC_REC8(x)    (*(u8 *)PC_REC(x))
#define PC_REC16(x) (*(u16*)PC_REC(x))
#define PC_REC32(x) (*(u32*)PC_REC(x))

#define OFFSET(X,Y) ((u32)(Y)-(u32)(X))

#define RECMEM_SIZE        (12*1024*1024)

static char *recMem;    /* the recompiled blocks will be here */
static char *recRAM;    /* and the ptr to the blocks here */
static char *recROM;    /* and here */

static u32 pc;            /* recompiler pc */
static u32 pcold;        /* recompiler oldpc */
static int count;        /* recompiler intruction count */
static int branch;        /* set for branch */
static u32 target;        /* branch target */
static u32 resp;

u32 cop2readypc = 0;
u32 idlecyclecount = 0;

#define NUM_REGISTERS    34
typedef struct {
    int state;
    u32 k;
    int reg;
} iRegisters;

static iRegisters iRegs[34];

#define ST_UNK      0x00
#define ST_CONST    0x01
#define ST_MAPPED   0x02

#ifdef NO_CONSTANT
#define IsConst(reg) 0
#else
#define IsConst(reg)  (iRegs[reg].state & ST_CONST)
#endif
#define IsMapped(reg) (iRegs[reg].state & ST_MAPPED)

static void (*recBSC[64])();
static void (*recSPC[64])();
static void (*recREG[32])();
static void (*recCP0[32])();
static void (*recCP2[64])();
static void (*recCP2BSC[32])();

#define REG_LO            32
#define REG_HI            33

// Hardware register usage
#define HWUSAGE_NONE     0x00

#define HWUSAGE_READ     0x01
#define HWUSAGE_WRITE    0x02
#define HWUSAGE_CONST    0x04
#define HWUSAGE_ARG      0x08    /* used as an argument for a function call */

#define HWUSAGE_RESERVED 0x10    /* won't get flushed when flushing all regs */
#define HWUSAGE_SPECIAL  0x20    /* special purpose register */
#define HWUSAGE_HARDWIRED 0x40    /* specific hardware register mapping that is never disposed */
#define HWUSAGE_INITED    0x80
#define HWUSAGE_PSXREG    0x100

// Remember to invalidate the special registers if they are modified by compiler
enum {
   ARG1 = 3,
   ARG2 = 4,
   ARG3 = 5,
   PSXREGS,    // ptr
     PSXMEM,        // ptr
   CYCLECOUNT,    // ptr
   PSXPC,    // ptr
   TARGETPTR,    // ptr
   TARGET,    // ptr
   RETVAL,
   REG_RZERO,
   REG_WZERO
};

typedef struct {
   int code;
   u32 k;
   int usage;
   int lastUsed;
   
   void (*flush)(int hwreg);
   int private;
} HWRegister;
static HWRegister HWRegisters[NUM_HW_REGISTERS];
static int HWRegUseCount;
static int DstCPUReg;
static int UniqueRegAlloc;

static int GetFreeHWReg();
static void InvalidateCPURegs();
static void DisposeHWReg(int index);
static void FlushHWReg(int index);
static void FlushAllHWReg();
static void MapPsxReg32(int reg);
static void FlushPsxReg32(int hwreg);
static int UpdateHWRegUsage(int hwreg, int usage);
static int GetHWReg32(int reg);
static int PutHWReg32(int reg);
static int GetSpecialIndexFromHWRegs(int which);
static int GetHWRegFromCPUReg(int cpureg);
static int MapRegSpecial(int which);
static void FlushRegSpecial(int hwreg);
static int GetHWRegSpecial(int which);
static int PutHWRegSpecial(int which);
static void recRecompile();
static void recError();

static int GetFreeHWReg()
{
    int i, least, index;
    
    if (DstCPUReg != -1) {
        index = GetHWRegFromCPUReg(DstCPUReg);
        DstCPUReg = -1;
    } else {
        // LRU algorith with a twist ;)
        for (i=0; i<NUM_HW_REGISTERS; i++) {
            if (!(HWRegisters[i].usage & HWUSAGE_RESERVED)) {
                break;
            }
        }
   
        least = HWRegisters[i].lastUsed; index = i;
        for (; i<NUM_HW_REGISTERS; i++) {
            if (!(HWRegisters[i].usage & HWUSAGE_RESERVED)) {
                if (HWRegisters[i].usage == HWUSAGE_NONE && HWRegisters[i].code >= 13) {
                    index = i;
                    break;
                }
                else if (HWRegisters[i].lastUsed < least) {
                    least = HWRegisters[i].lastUsed;
                    index = i;
                }
            }
        }
         
         // Cycle the registers
         if (HWRegisters[index].usage == HWUSAGE_NONE) {
            for (; i<NUM_HW_REGISTERS; i++) {
                if (!(HWRegisters[i].usage & HWUSAGE_RESERVED)) {
                    if (HWRegisters[i].usage == HWUSAGE_NONE && 
                         HWRegisters[i].code >= 13 && 
                         HWRegisters[i].lastUsed < least) {
                        least = HWRegisters[i].lastUsed;
                        index = i;
                        break;
                    }
                }
            }
         }
    }
    
/*    if (HWRegisters[index].code < 13 && HWRegisters[index].code > 3) {
        SysPrintf("Allocating volatile register %i\n", HWRegisters[index].code);
    }
    if (HWRegisters[index].usage != HWUSAGE_NONE) {
        SysPrintf("RegUse too big. Flushing %i\n", HWRegisters[index].code);
    }*/
    if (HWRegisters[index].usage & (HWUSAGE_RESERVED | HWUSAGE_HARDWIRED)) {
        if (HWRegisters[index].usage & HWUSAGE_RESERVED) {
            SysPrintf("Error! Trying to map a new register to a reserved register (r%i)", 
                        HWRegisters[index].code);
        }
        if (HWRegisters[index].usage & HWUSAGE_HARDWIRED) {
            SysPrintf("Error! Trying to map a new register to a hardwired register (r%i)", 
                        HWRegisters[index].code);
        }
    }
    
    if (HWRegisters[index].lastUsed != 0) {
        UniqueRegAlloc = 0;
    }
    
    // Make sure the register is really flushed!
    FlushHWReg(index);
    HWRegisters[index].usage = HWUSAGE_NONE;
    HWRegisters[index].flush = NULL;
    
    return index;
}

static void FlushHWReg(int index)
{
    if (index < 0) return;
    if (HWRegisters[index].usage == HWUSAGE_NONE) return;
    
    if (HWRegisters[index].flush) {
        HWRegisters[index].usage |= HWUSAGE_RESERVED;
        HWRegisters[index].flush(index);
        HWRegisters[index].flush = NULL;
    }
    
    if (HWRegisters[index].usage & HWUSAGE_HARDWIRED) {
        HWRegisters[index].usage &= ~(HWUSAGE_READ | HWUSAGE_WRITE);
    } else {
        HWRegisters[index].usage = HWUSAGE_NONE;
    }
}

// get rid of a mapped register without flushing the contents to the memory
static void DisposeHWReg(int index)
{
    if (index < 0) return;
    if (HWRegisters[index].usage == HWUSAGE_NONE) return;
    
    HWRegisters[index].usage &= ~(HWUSAGE_READ | HWUSAGE_WRITE);
    if (HWRegisters[index].usage == HWUSAGE_NONE) {
        SysPrintf("Error! not correctly disposing register (r%i)", HWRegisters[index].code);
    }
    
    FlushHWReg(index);
}

// operated on cpu registers
__inline static void FlushCPURegRange(int start, int end)
{
    int i;
    
    if (end <= 0) end = 31;
    if (start <= 0) start = 0;
    
    for (i=0; i<NUM_HW_REGISTERS; i++) {
        if (HWRegisters[i].code >= start && HWRegisters[i].code <= end)
            if (HWRegisters[i].flush)
                FlushHWReg(i);
    }

    for (i=0; i<NUM_HW_REGISTERS; i++) {
        if (HWRegisters[i].code >= start && HWRegisters[i].code <= end)
            FlushHWReg(i);
    }
}

static void FlushAllHWReg()
{
    FlushCPURegRange(0,31);
}

static void InvalidateCPURegs()
{
    FlushCPURegRange(0,12);
}

#pragma mark --- Mapping utility functions ---

static void MoveHWRegToCPUReg(int cpureg, int hwreg)
{
    int dstreg;
    
    if (HWRegisters[hwreg].code == cpureg)
        return;
    
    dstreg = GetHWRegFromCPUReg(cpureg);
    
    HWRegisters[dstreg].usage &= ~(HWUSAGE_HARDWIRED | HWUSAGE_ARG);
    if (HWRegisters[hwreg].usage & (HWUSAGE_READ | HWUSAGE_WRITE)) {
        FlushHWReg(dstreg);
        MR(HWRegisters[dstreg].code, HWRegisters[hwreg].code);
    } else {
        if (HWRegisters[dstreg].usage & (HWUSAGE_READ | HWUSAGE_WRITE)) {
            MR(HWRegisters[hwreg].code, HWRegisters[dstreg].code);
        }
        else if (HWRegisters[dstreg].usage != HWUSAGE_NONE) {
            FlushHWReg(dstreg);
        }
    }
    
    HWRegisters[dstreg].code = HWRegisters[hwreg].code;
    HWRegisters[hwreg].code = cpureg;
}

static int UpdateHWRegUsage(int hwreg, int usage)
{
    HWRegisters[hwreg].lastUsed = ++HWRegUseCount;    
    if (usage & HWUSAGE_WRITE) {
        HWRegisters[hwreg].usage &= ~HWUSAGE_CONST;
    }
    if (!(usage & HWUSAGE_INITED)) {
        HWRegisters[hwreg].usage &= ~HWUSAGE_INITED;
    }
    HWRegisters[hwreg].usage |= usage;
    
    return HWRegisters[hwreg].code;
}

static int GetHWRegFromCPUReg(int cpureg)
{
    int i;
    for (i=0; i<NUM_HW_REGISTERS; i++) {
        if (HWRegisters[i].code == cpureg) {
            return i;
        }
    }
    
    SysPrintf("Error! Register location failure (r%i)", cpureg);
    return 0;
}

// this function operates on cpu registers
void SetDstCPUReg(int cpureg)
{
    DstCPUReg = cpureg;
}

static void ReserveArgs(int args)
{
    int index, i;
    
    for (i=0; i<args; i++) {
        index = GetHWRegFromCPUReg(3+i);
        HWRegisters[index].usage |= HWUSAGE_RESERVED | HWUSAGE_HARDWIRED | HWUSAGE_ARG;
    }
}

static void ReleaseArgs()
{
    int i;
    
    for (i=0; i<NUM_HW_REGISTERS; i++) {
        if (HWRegisters[i].usage & HWUSAGE_ARG) {
            //HWRegisters[i].usage = HWUSAGE_NONE;
            //HWRegisters[i].flush = NULL;
            HWRegisters[i].usage &= ~(HWUSAGE_RESERVED | HWUSAGE_HARDWIRED | HWUSAGE_ARG);
            FlushHWReg(i);
        }
    }
}

#pragma mark --- Psx register mapping ---

static void MapPsxReg32(int reg)
{
   int hwreg = GetFreeHWReg();
   HWRegisters[hwreg].flush = FlushPsxReg32;
   HWRegisters[hwreg].private = reg;
   
   if (iRegs[reg].reg != -1) {
       SysPrintf("error: double mapped psx register");
   }
   
   iRegs[reg].reg = hwreg;
   iRegs[reg].state |= ST_MAPPED;
}

static void FlushPsxReg32(int hwreg)
{
    int reg = HWRegisters[hwreg].private;
    
    if (iRegs[reg].reg == -1) {
        SysPrintf("error: flushing unmapped psx register");
    }
    
    if (HWRegisters[hwreg].usage & HWUSAGE_WRITE) {
        if (branch) {
            /*int reguse = nextPsxRegUse(pc-8, reg);
            if (reguse == REGUSE_NONE || (reguse & REGUSE_READ))*/ {
                STW(HWRegisters[hwreg].code, OFFSET(&psxRegs, &psxRegs.GPR.r[reg]), GetHWRegSpecial(PSXREGS));
            }
        } else {
            int reguse = nextPsxRegUse(pc-4, reg);
            if (reguse == REGUSE_NONE || (reguse & REGUSE_READ)) {
                STW(HWRegisters[hwreg].code, OFFSET(&psxRegs, &psxRegs.GPR.r[reg]), GetHWRegSpecial(PSXREGS));
            }
        }
    }
    
    iRegs[reg].reg = -1;
    iRegs[reg].state = ST_UNK;
}

static int GetHWReg32(int reg)
{
    int usage = HWUSAGE_PSXREG | HWUSAGE_READ;
    
    if (reg == 0) {
        return GetHWRegSpecial(REG_RZERO);
    }
    if (!IsMapped(reg)) {
        usage |= HWUSAGE_INITED;
        MapPsxReg32(reg);
        
        HWRegisters[iRegs[reg].reg].usage |= HWUSAGE_RESERVED;
        if (IsConst(reg)) {
            LIW(HWRegisters[iRegs[reg].reg].code, iRegs[reg].k);
            usage |= HWUSAGE_WRITE | HWUSAGE_CONST;
            //iRegs[reg].state &= ~ST_CONST;
        }
        else {
            LWZ(HWRegisters[iRegs[reg].reg].code, OFFSET(&psxRegs, &psxRegs.GPR.r[reg]), GetHWRegSpecial(PSXREGS));
        }
        HWRegisters[iRegs[reg].reg].usage &= ~HWUSAGE_RESERVED;
    }
    else if (DstCPUReg != -1) {
        int dst = DstCPUReg;
        DstCPUReg = -1;
        
        if (HWRegisters[iRegs[reg].reg].code < 13) {
            MoveHWRegToCPUReg(dst, iRegs[reg].reg);
        } else {
            MR(DstCPUReg, HWRegisters[iRegs[reg].reg].code);
        }
    }
    
    DstCPUReg = -1;
    
    return UpdateHWRegUsage(iRegs[reg].reg, usage);
}

static int PutHWReg32(int reg)
{
    int usage = HWUSAGE_PSXREG | HWUSAGE_WRITE;
    if (reg == 0) {
        return PutHWRegSpecial(REG_WZERO);
    }
    
    if (DstCPUReg != -1 && IsMapped(reg)) {
        if (HWRegisters[iRegs[reg].reg].code != DstCPUReg) {
            int tmp = DstCPUReg;
            DstCPUReg = -1;
            DisposeHWReg(iRegs[reg].reg);
            DstCPUReg = tmp;
        }
    }
    if (!IsMapped(reg)) {
        usage |= HWUSAGE_INITED;
        MapPsxReg32(reg);
    }
    
    DstCPUReg = -1;
    iRegs[reg].state &= ~ST_CONST;

    return UpdateHWRegUsage(iRegs[reg].reg, usage);
}

#pragma mark --- Special register mapping ---

static int GetSpecialIndexFromHWRegs(int which)
{
    int i;
    for (i=0; i<NUM_HW_REGISTERS; i++) {
        if (HWRegisters[i].usage & HWUSAGE_SPECIAL) {
            if (HWRegisters[i].private == which) {
                return i;
            }
        }
    }
    return -1;
}

static int MapRegSpecial(int which)
{
    int hwreg = GetFreeHWReg();
    HWRegisters[hwreg].flush = FlushRegSpecial;
    HWRegisters[hwreg].private = which;
    
    return hwreg;
}

static void FlushRegSpecial(int hwreg)
{
    int which = HWRegisters[hwreg].private;
    
    if (!(HWRegisters[hwreg].usage & HWUSAGE_WRITE))
        return;
    
    switch (which) {
        case CYCLECOUNT:
            STW(HWRegisters[hwreg].code, OFFSET(&psxRegs, &psxRegs.cycle), GetHWRegSpecial(PSXREGS));
            break;
        case PSXPC:
            STW(HWRegisters[hwreg].code, OFFSET(&psxRegs, &psxRegs.pc), GetHWRegSpecial(PSXREGS));
            break;
        case TARGET:
            STW(HWRegisters[hwreg].code, 0, GetHWRegSpecial(TARGETPTR));
            break;
    }
}

static int GetHWRegSpecial(int which)
{
    int index = GetSpecialIndexFromHWRegs(which);
    int usage = HWUSAGE_READ | HWUSAGE_SPECIAL;
    
    if (index == -1) {
        usage |= HWUSAGE_INITED;
        index = MapRegSpecial(which);
        
        HWRegisters[index].usage |= HWUSAGE_RESERVED;
        switch (which) {
            case PSXREGS:
            case PSXMEM:
                SysPrintf("error! shouldn't be here!\n");
                //HWRegisters[index].flush = NULL;
                //LIW(HWRegisters[index].code, (u32)&psxRegs);
                break;
            case TARGETPTR:
                HWRegisters[index].flush = NULL;
                LIW(HWRegisters[index].code, (u32)&target);
                break;
            case REG_RZERO:
                HWRegisters[index].flush = NULL;
                LIW(HWRegisters[index].code, 0);
                break;
            case RETVAL:
                MoveHWRegToCPUReg(3, index);
                /*reg = GetHWRegFromCPUReg(3);
                HWRegisters[reg].code = HWRegisters[index].code;
                HWRegisters[index].code = 3;*/
                HWRegisters[index].flush = NULL;
                
                usage |= HWUSAGE_RESERVED;
                break;

            case CYCLECOUNT:
                LWZ(HWRegisters[index].code, OFFSET(&psxRegs, &psxRegs.cycle), GetHWRegSpecial(PSXREGS));
                break;
            case PSXPC:
                LWZ(HWRegisters[index].code, OFFSET(&psxRegs, &psxRegs.pc), GetHWRegSpecial(PSXREGS));
                break;
            case TARGET:
                LWZ(HWRegisters[index].code, 0, GetHWRegSpecial(TARGETPTR));
                break;
            default:
                SysPrintf("Error: Unknown special register in GetHWRegSpecial()\n");
                break;
        }
        HWRegisters[index].usage &= ~HWUSAGE_RESERVED;
    }
    else if (DstCPUReg != -1) {
        int dst = DstCPUReg;
        DstCPUReg = -1;
        
        MoveHWRegToCPUReg(dst, index);
    }

    return UpdateHWRegUsage(index, usage);
}

static int PutHWRegSpecial(int which)
{
    int index = GetSpecialIndexFromHWRegs(which);
    int usage = HWUSAGE_WRITE | HWUSAGE_SPECIAL;
    
    if (DstCPUReg != -1 && index != -1) {
        if (HWRegisters[index].code != DstCPUReg) {
            int tmp = DstCPUReg;
            DstCPUReg = -1;
            DisposeHWReg(index);
            DstCPUReg = tmp;
        }
    }
    switch (which) {
        case PSXREGS:
        case TARGETPTR:
            SysPrintf("Error: Read-only special register in PutHWRegSpecial()\n");
        case REG_WZERO:
            if (index >= 0) {
                    if (HWRegisters[index].usage & HWUSAGE_WRITE)
                        break;
            }
            index = MapRegSpecial(which);
            HWRegisters[index].flush = NULL;
            break;
        default:
            if (index == -1) {
                usage |= HWUSAGE_INITED;
                index = MapRegSpecial(which);
                
                HWRegisters[index].usage |= HWUSAGE_RESERVED;
                switch (which) {
                    case ARG1:
                    case ARG2:
                    case ARG3:
                        MoveHWRegToCPUReg(3+(which-ARG1), index);
                        /*reg = GetHWRegFromCPUReg(3+(which-ARG1));
                        
                        if (HWRegisters[reg].usage != HWUSAGE_NONE) {
                            HWRegisters[reg].usage &= ~(HWUSAGE_HARDWIRED | HWUSAGE_ARG);
                            if (HWRegisters[reg].flush != NULL && HWRegisters[reg].usage & (HWUSAGE_WRITE | HWUSAGE_READ)) {
                                MR(HWRegisters[index].code, HWRegisters[reg].code);
                            } else {
                                FlushHWReg(reg);
                            }
                        }
                        HWRegisters[reg].code = HWRegisters[index].code;
                        if (!(HWRegisters[index].code >= 3 && HWRegisters[index].code <=31))
                            SysPrintf("Error! Register allocation");
                        HWRegisters[index].code = 3+(which-ARG1);*/
                        HWRegisters[index].flush = NULL;
                        
                        usage |= HWUSAGE_RESERVED | HWUSAGE_HARDWIRED | HWUSAGE_ARG;
                        break;
                }
            }
            HWRegisters[index].usage &= ~HWUSAGE_RESERVED;
            break;
    }
    
    DstCPUReg = -1;

    return UpdateHWRegUsage(index, usage);
}

#pragma mark --- ---

static void MapConst(int reg, u32 _const) {
    if (reg == 0)
        return;
    if (IsConst(reg) && iRegs[reg].k == _const)
        return;
    
    DisposeHWReg(iRegs[reg].reg);
    iRegs[reg].k = _const;
    iRegs[reg].state = ST_CONST;
}

static void MapCopy(int dst, int src)
{
   // do it the lazy way for now
   MR(PutHWReg32(dst), GetHWReg32(src));
}

static void iFlushReg(u32 nextpc, int reg) {
    if (!IsMapped(reg) && IsConst(reg)) {
        GetHWReg32(reg);
    }
    if (IsMapped(reg)) {
        if (nextpc) {
            int use = nextPsxRegUse(nextpc, reg);
            if ((use & REGUSE_RW) == REGUSE_WRITE) {
                DisposeHWReg(iRegs[reg].reg);
            } else {
                FlushHWReg(iRegs[reg].reg);
            }
        } else {
            FlushHWReg(iRegs[reg].reg);
        }
    }
}

static void iFlushRegs(u32 nextpc) {
    int i;

    for (i=1; i<NUM_REGISTERS; i++) {
        iFlushReg(nextpc, i);
    }
}

static void Return()
{
    iFlushRegs(0);
    FlushAllHWReg();
    if (((u32)returnPC & 0x1fffffc) == (u32)returnPC) {
        BA((u32)returnPC);
    }
    else {
        LIW(0, (u32)returnPC);
        MTLR(0);
        BLR();
    }
}

static void iRet() {
   /* store cycle */
   count = (idlecyclecount + (pc - pcold) / 4) * BIAS;
   ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);
   Return();
}

static int iLoadTest() {
    u32 tmp;

    // check for load delay
    tmp = psxRegs.code >> 26;
    switch (tmp) {
        case 0x10: // COP0
            switch (_Rs_) {
                case 0x00: // MFC0
                case 0x02: // CFC0
                    return 1;
            }
            break;
        case 0x12: // COP2
            switch (_Funct_) {
                case 0x00:
                    switch (_Rs_) {
                        case 0x00: // MFC2
                        case 0x02: // CFC2
                            return 1;
                    }
                    break;
            }
            break;
        case 0x32: // LWC2
            return 1;
        default:
            if (tmp >= 0x20 && tmp <= 0x26) { // LB/LH/LWL/LW/LBU/LHU/LWR
                return 1;
            }
            break;
    }
    return 0;
}

/* set a pending branch */
static void SetBranch() {
    int treg;
    branch = 1;
    psxRegs.code = PSXMu32(pc);
    pc+=4;

    if (iLoadTest() == 1) {
        iFlushRegs(0);
        LIW(0, psxRegs.code);
        STW(0, OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS));
        /* store cycle */
        count = (idlecyclecount + (pc - pcold) / 4) * BIAS;
        ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);
        
        treg = GetHWRegSpecial(TARGET);
        MR(PutHWRegSpecial(ARG2), treg);
        DisposeHWReg(GetHWRegFromCPUReg(treg));
        LIW(PutHWRegSpecial(ARG1), _Rt_);
               LIW(GetHWRegSpecial(PSXPC), pc);
        FlushAllHWReg();
        CALLFunc((u32)psxDelayTest);

        Return();
        return;
    }

    recBSC[psxRegs.code>>26]();

    iFlushRegs(0);
    treg = GetHWRegSpecial(TARGET);
    MR(PutHWRegSpecial(PSXPC), GetHWRegSpecial(TARGET)); // FIXME: this line should not be needed
    DisposeHWReg(GetHWRegFromCPUReg(treg));
    FlushAllHWReg();

    count = (idlecyclecount + (pc - pcold) / 4) * BIAS;
       ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);
    FlushAllHWReg();
    CALLFunc((u32)psxBranchTest);
    
    // TODO: don't return if target is compiled
    Return();
}

static void iJump(u32 branchPC) {
    u32 *b1, *b2;
    branch = 1;
    psxRegs.code = PSXMu32(pc);
    pc+=4;

    if (iLoadTest() == 1) {
        iFlushRegs(0);
        LIW(0, psxRegs.code);
        STW(0, OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS));
        /* store cycle */
        count = (idlecyclecount + (pc - pcold) / 4) * BIAS;
        ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);

        LIW(PutHWRegSpecial(ARG2), branchPC);
        LIW(PutHWRegSpecial(ARG1), _Rt_);
        LIW(GetHWRegSpecial(PSXPC), pc);
        FlushAllHWReg();
        CALLFunc((u32)psxDelayTest);
               
        Return();
        return;
    }

    recBSC[psxRegs.code>>26]();

    iFlushRegs(branchPC);
    LIW(PutHWRegSpecial(PSXPC), branchPC);
    FlushAllHWReg();

    count = (idlecyclecount + (pc - pcold) / 4) * BIAS;
       //if (/*psxRegs.code == 0 &&*/ count == 2 && branchPC == pcold) {
       //    LIW(PutHWRegSpecial(CYCLECOUNT), 0);
       //} else {
           ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);
       //}
    FlushAllHWReg();
    CALLFunc((u32)psxBranchTest);

    if (!Config.HLE && Config.PsxOut &&
        ((branchPC & 0x1fffff) == 0xa0 ||
         (branchPC & 0x1fffff) == 0xb0 ||
         (branchPC & 0x1fffff) == 0xc0))
      CALLFunc((u32)psxJumpTest);

    // always return for now...
    //Return();
        
    // maybe just happened an interruption, check so
    LIW(0, branchPC);
    CMPLW(GetHWRegSpecial(PSXPC), 0);
    BNE_L(b1);
    
    LIW(3, PC_REC(branchPC));
    LWZ(3, 0, 3);
    CMPLWI(3, 0);
    BNE_L(b2);
    
    B_DST(b1);
    Return();
    
    // next bit is already compiled - jump right to it
    B_DST(b2);
    MTCTR(3);
    BCTR();
}

static void iBranch(u32 branchPC, int savectx) {
    HWRegister HWRegistersS[NUM_HW_REGISTERS];
    iRegisters iRegsS[NUM_REGISTERS];
    int HWRegUseCountS = 0;
    u32 respold=0;
    u32 *b1, *b2;

    if (savectx) {
        respold = resp;
        memcpy(iRegsS, iRegs, sizeof(iRegs));
        memcpy(HWRegistersS, HWRegisters, sizeof(HWRegisters));
        HWRegUseCountS = HWRegUseCount;
    }
    
    branch = 1;
    psxRegs.code = PSXMu32(pc);

    // the delay test is only made when the branch is taken
    // savectx == 0 will mean that :)
    if (savectx == 0 && iLoadTest() == 1) {
        iFlushRegs(0);
        LIW(0, psxRegs.code);
        STW(0, OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS));
        /* store cycle */
        count = (idlecyclecount + ((pc+4) - pcold) / 4) * BIAS;
        ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);

        LIW(PutHWRegSpecial(ARG2), branchPC);
        LIW(PutHWRegSpecial(ARG1), _Rt_);
               LIW(GetHWRegSpecial(PSXPC), pc);
        FlushAllHWReg();
        CALLFunc((u32)psxDelayTest);

        Return();
        return;
    }

    pc+= 4;
    recBSC[psxRegs.code>>26]();
    
    iFlushRegs(branchPC);
    LIW(PutHWRegSpecial(PSXPC), branchPC);
    FlushAllHWReg();

    /* store cycle */
    count = (idlecyclecount + (pc - pcold) / 4) * BIAS;
       //if (/*psxRegs.code == 0 &&*/ count == 2 && branchPC == pcold) {
       //    LIW(PutHWRegSpecial(CYCLECOUNT), 0);
       //} else {
           ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);
       //}
    FlushAllHWReg();
    CALLFunc((u32)psxBranchTest);
    
    // always return for now...
    //Return();

    LIW(0, branchPC);
    CMPLW(GetHWRegSpecial(PSXPC), 0);
    BNE_L(b1);
    
    LIW(3, PC_REC(branchPC));
    LWZ(3, 0, 3);
    CMPLWI(3, 0);
    BNE_L(b2);
    
    B_DST(b1);
    Return();
    
    B_DST(b2);
    MTCTR(3);
    BCTR();

    // maybe just happened an interruption, check so
/*    CMP32ItoM((u32)&psxRegs.pc, branchPC);
    j8Ptr[1] = JE8(0);
    RET();

    x86SetJ8(j8Ptr[1]);
    MOV32MtoR(EAX, PC_REC(branchPC));
    TEST32RtoR(EAX, EAX);
    j8Ptr[2] = JNE8(0);
    RET();

    x86SetJ8(j8Ptr[2]);
    JMP32R(EAX);*/

    pc-= 4;
    if (savectx) {
        resp = respold;
        memcpy(iRegs, iRegsS, sizeof(iRegs));
        memcpy(HWRegisters, HWRegistersS, sizeof(HWRegisters));
        HWRegUseCount = HWRegUseCountS;
    }
}


static void iDumpRegs() {
    int i, j;

    printf("%lx %lx\n", psxRegs.pc, psxRegs.cycle);
    for (i=0; i<4; i++) {
        for (j=0; j<8; j++)
            printf("%lx ", psxRegs.GPR.r[j*i]);
        printf("\n");
    }
}

void iDumpBlock(char *ptr) {
/*    FILE *f;
    u32 i;

    SysPrintf("dump1 %x:%x, %x\n", psxRegs.pc, pc, psxCurrentCycle);

    for (i = psxRegs.pc; i < pc; i+=4)
        SysPrintf("%s\n", disR3000AF(PSXMu32(i), i));

    fflush(stdout);
    f = fopen("dump1", "w");
    fwrite(ptr, 1, (u32)x86Ptr - (u32)ptr, f);
    fclose(f);
    system("ndisasmw -u dump1");
    fflush(stdout);*/
}

#define REC_FUNC(f) \
void psx##f(); \
static void rec##f() { \
    iFlushRegs(0); \
    LIW(PutHWRegSpecial(ARG1), (u32)psxRegs.code); \
    STW(GetHWRegSpecial(ARG1), OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS)); \
    LIW(PutHWRegSpecial(PSXPC), (u32)pc); \
    FlushAllHWReg(); \
    CALLFunc((u32)psx##f); \
/*    branch = 2; */\
}

#define REC_SYS(f) \
void psx##f(); \
static void rec##f() { \
    iFlushRegs(0); \
       LIW(PutHWRegSpecial(ARG1), (u32)psxRegs.code); \
       STW(GetHWRegSpecial(ARG1), OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS)); \
       LIW(PutHWRegSpecial(PSXPC), (u32)pc); \
       FlushAllHWReg(); \
    CALLFunc((u32)psx##f); \
    branch = 2; \
    iRet(); \
}

#define REC_BRANCH(f) \
void psx##f(); \
static void rec##f() { \
    iFlushRegs(0); \
       LIW(PutHWRegSpecial(ARG1), (u32)psxRegs.code); \
       STW(GetHWRegSpecial(ARG1), OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS)); \
       LIW(PutHWRegSpecial(PSXPC), (u32)pc); \
       FlushAllHWReg(); \
    CALLFunc((u32)psx##f); \
    branch = 2; \
    iRet(); \
}

static void freeMem(int all)
{
   if (recMem) free(recMem);
   if (recRAM) free(recRAM);
   if (recROM) free(recROM);
   recMem = recRAM = recROM = 0;
   
   if (all && psxRecLUT) {
       free(psxRecLUT); psxRecLUT = NULL;
   }
}

static int allocMem() {
    int i;

    freeMem(0);
       
    if (psxRecLUT==NULL)
        psxRecLUT = (u32*) malloc(0x010000 * 4);

    recMem = (char*) malloc(RECMEM_SIZE);
       //recMem = mmap(NULL, RECMEM_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1,  0);
    recRAM = (char*) malloc(0x200000);
    recROM = (char*) malloc(0x080000);
    if (recRAM == NULL || recROM == NULL || recMem == NULL/*(void *)-1*/ || psxRecLUT == NULL) {
               freeMem(1);
        SysMessage("Error allocating memory"); return -1;
    }

    for (i=0; i<0x80; i++) psxRecLUT[i + 0x0000] = (u32)&recRAM[(i & 0x1f) << 16];
    memcpy(psxRecLUT + 0x8000, psxRecLUT, 0x80 * 4);
    memcpy(psxRecLUT + 0xa000, psxRecLUT, 0x80 * 4);

    for (i=0; i<0x08; i++) psxRecLUT[i + 0xbfc0] = (u32)&recROM[i << 16];
    
    return 0;
}

static int recInit() {
    return allocMem();
}

static void recReset() {
    memset(recRAM, 0, 0x200000);
    memset(recROM, 0, 0x080000);

    ppcInit();
    ppcSetPtr((u32 *)recMem);

    branch = 0;
    memset(iRegs, 0, sizeof(iRegs));
    iRegs[0].state = ST_CONST;
    iRegs[0].k     = 0;
}

static void recShutdown() {
       freeMem(1);
    ppcShutdown();
}

static void recError() {
    SysReset();
    ClosePlugins();
    SysMessage("Unrecoverable error while running recompiler\n");
    SysRunGui();
}

__inline static void execute() {
    void (**recFunc)();
    char *p;

    p =    (char*)PC_REC(psxRegs.pc);
    /*if (p != NULL)*/ recFunc = (void (**)()) (u32)p;
    /*else { recError(); return; }*/

    if (*recFunc == 0) {
        recRecompile();
    }
    recRun(*recFunc, (u32)&psxRegs, (u32)&psxM);
}

static void recExecute() {
    for (;;) execute();
}

static void recExecuteBlock() {
    execute();
}

static void recClear(u32 Addr, u32 Size) {
    memset((void*)PC_REC(Addr), 0, Size * 4);
}

static void recNULL() {
//    SysMessage("recUNK: %8.8x\n", psxRegs.code);
}

/*********************************************************
* goes to opcodes tables...                              *
* Format:  table[something....]                          *
*********************************************************/

//REC_SYS(SPECIAL);
static void recSPECIAL() {
    recSPC[_Funct_]();
}

static void recREGIMM() {
    recREG[_Rt_]();
}

static void recCOP0() {
    recCP0[_Rs_]();
}

//REC_SYS(COP2);
static void recCOP2() {
    recCP2[_Funct_]();
}

static void recBASIC() {
    recCP2BSC[_Rs_]();
}

//end of Tables opcodes...

#pragma mark - Arithmetic with immediate operand -
/*********************************************************
* Arithmetic with immediate operand                      *
* Format:  OP rt, rs, immediate                          *
*********************************************************/

#if 0
/*REC_FUNC(ADDI);
REC_FUNC(ADDIU);
REC_FUNC(ANDI);
REC_FUNC(ORI);
REC_FUNC(XORI);
REC_FUNC(SLTI);
REC_FUNC(SLTIU);*/
#else
static void recADDIU()  {
// Rt = Rs + Im
    if (!_Rt_) return;

    if (IsConst(_Rs_)) {
        MapConst(_Rt_, iRegs[_Rs_].k + _Imm_);
    } else {
        if (_Imm_ == 0) {
            MapCopy(_Rt_, _Rs_);
        } else {
            ADDI(PutHWReg32(_Rt_), GetHWReg32(_Rs_), _Imm_);
        }
    }
}

static void recADDI()  {
// Rt = Rs + Im
    recADDIU();
}

//REC_FUNC(SLTI);
//REC_FUNC(SLTIU);
//CR0:    SIGN      | POSITIVE | ZERO  | SOVERFLOW | SOVERFLOW | OVERFLOW | CARRY
static void recSLTI() {
// Rt = Rs < Im (signed)
    if (!_Rt_) return;

    if (IsConst(_Rs_)) {
        MapConst(_Rt_, (s32)iRegs[_Rs_].k < _Imm_);
    } else {
        if (_Imm_ == 0) {
            SRWI(PutHWReg32(_Rt_), GetHWReg32(_Rs_), 31);
        } else {
            int reg;
            CMPWI(GetHWReg32(_Rs_), _Imm_);
            reg = PutHWReg32(_Rt_);
            LI(reg, 1);
            BLT(1);
            LI(reg, 0);
        }
    }
}

static void recSLTIU() {
// Rt = Rs < Im (unsigned)
    if (!_Rt_) return;

    if (IsConst(_Rs_)) {
        MapConst(_Rt_, iRegs[_Rs_].k < _ImmU_);
    } else {
        int reg;
        CMPLWI(GetHWReg32(_Rs_), _Imm_);
        reg = PutHWReg32(_Rt_);
        LI(reg, 1);
        BLT(1);
        LI(reg, 0);
    }
}

static void recANDI() {
// Rt = Rs And Im
   if (!_Rt_) return;

   if (IsConst(_Rs_)) {
       MapConst(_Rt_, iRegs[_Rs_].k & _ImmU_);
   } else {
       ANDI_(PutHWReg32(_Rt_), GetHWReg32(_Rs_), _ImmU_);
   }
}

static void recORI() {
// Rt = Rs Or Im
    if (!_Rt_) return;

    if (IsConst(_Rs_)) {
        MapConst(_Rt_, iRegs[_Rs_].k | _ImmU_);
    } else {
        if (_Imm_ == 0) {
            MapCopy(_Rt_, _Rs_);
        } else {
            ORI(PutHWReg32(_Rt_), GetHWReg32(_Rs_), _ImmU_);
        }
    }
}

static void recXORI() {
// Rt = Rs Xor Im
   if (!_Rt_) return;

   if (IsConst(_Rs_)) {
       MapConst(_Rt_, iRegs[_Rs_].k ^ _ImmU_);
   } else {
       XORI(PutHWReg32(_Rt_), GetHWReg32(_Rs_), _ImmU_);
   }
}
#endif
//end of * Arithmetic with immediate operand  

/*********************************************************
* Load higher 16 bits of the first word in GPR with imm  *
* Format:  OP rt, immediate                              *
*********************************************************/
//REC_FUNC(LUI);
//#if 0*/
static void recLUI()  {
// Rt = Imm << 16
    if (!_Rt_) return;

    MapConst(_Rt_, psxRegs.code << 16);
}
//#endif
//End of Load Higher .....

#pragma mark - Register arithmetic -
/*********************************************************
* Register arithmetic                                    *
* Format:  OP rd, rs, rt                                 *
*********************************************************/

#if 0
/*REC_FUNC(ADD);
REC_FUNC(ADDU);
REC_FUNC(SUB);
REC_FUNC(SUBU);
REC_FUNC(AND);
REC_FUNC(OR);
REC_FUNC(XOR);
REC_FUNC(NOR);
REC_FUNC(SLT);
REC_FUNC(SLTU);*/
#else
static void recADDU() {
// Rd = Rs + Rt 
    if (!_Rd_) return;

    if (IsConst(_Rs_) && IsConst(_Rt_)) {
        MapConst(_Rd_, iRegs[_Rs_].k + iRegs[_Rt_].k);
    } else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
        if ((s32)(s16)iRegs[_Rs_].k == (s32)iRegs[_Rs_].k) {
            ADDI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), (s16)iRegs[_Rs_].k);
        } else if ((iRegs[_Rs_].k & 0xffff) == 0) {
            ADDIS(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k>>16);
        } else {
            ADD(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
    } else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
        if ((s32)(s16)iRegs[_Rt_].k == (s32)iRegs[_Rt_].k) {
            ADDI(PutHWReg32(_Rd_), GetHWReg32(_Rs_), (s16)iRegs[_Rt_].k);
        } else if ((iRegs[_Rt_].k & 0xffff) == 0) {
            ADDIS(PutHWReg32(_Rd_), GetHWReg32(_Rs_), iRegs[_Rt_].k>>16);
        } else {
            ADD(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
    } else {
        ADD(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
    }
}

static void recADD() {
// Rd = Rs + Rt
    recADDU();
}

static void recSUBU() {
// Rd = Rs - Rt
   if (!_Rd_) return;

   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rs_].k - iRegs[_Rt_].k);
   } else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
       if ((s32)(s16)(-iRegs[_Rt_].k) == (s32)(-iRegs[_Rt_].k)) {
           ADDI(PutHWReg32(_Rd_), GetHWReg32(_Rs_), -iRegs[_Rt_].k);
       } else if (((-iRegs[_Rt_].k) & 0xffff) == 0) {
           ADDIS(PutHWReg32(_Rd_), GetHWReg32(_Rs_), (-iRegs[_Rt_].k)>>16);
       } else {
           SUB(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } else {
       SUB(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
   }
}   

static void recSUB() {
// Rd = Rs - Rt
    recSUBU();
}

static void recAND() {
// Rd = Rs And Rt
   if (!_Rd_) return;
   
   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rs_].k & iRegs[_Rt_].k);
   } else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
       // TODO: implement shifted (ANDIS) versions of these
       if ((iRegs[_Rs_].k & 0xffff) == iRegs[_Rs_].k) {
           ANDI_(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
       } else {
           AND(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
       if ((iRegs[_Rt_].k & 0xffff) == iRegs[_Rt_].k) {
           ANDI_(PutHWReg32(_Rd_), GetHWReg32(_Rs_), iRegs[_Rt_].k);
       } else {
           AND(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } else {
       AND(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
   }
}   

static void recOR() {
// Rd = Rs Or Rt
   if (!_Rd_) return;
   
   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rs_].k | iRegs[_Rt_].k);
   }
   else {
       if (_Rs_ == _Rt_) {
           MapCopy(_Rd_, _Rs_);
       }
       else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
           if ((iRegs[_Rs_].k & 0xffff) == iRegs[_Rs_].k) {
               ORI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
           } else {
               OR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
           }
       }
       else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
           if ((iRegs[_Rt_].k & 0xffff) == iRegs[_Rt_].k) {
               ORI(PutHWReg32(_Rd_), GetHWReg32(_Rs_), iRegs[_Rt_].k);
           } else {
               OR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
           }
       } else {
           OR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   }
}

static void recXOR() {
// Rd = Rs Xor Rt
   if (!_Rd_) return;
   
   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rs_].k ^ iRegs[_Rt_].k);
   } else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
       if ((iRegs[_Rs_].k & 0xffff) == iRegs[_Rs_].k) {
           XORI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
       } else {
           XOR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
       if ((iRegs[_Rt_].k & 0xffff) == iRegs[_Rt_].k) {
           XORI(PutHWReg32(_Rd_), GetHWReg32(_Rs_), iRegs[_Rt_].k);
       } else {
           XOR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } else {
       XOR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
   }
}

static void recNOR() {
// Rd = Rs Nor Rt
   if (!_Rd_) return;
   
   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, ~(iRegs[_Rs_].k | iRegs[_Rt_].k));
   } /*else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
       if ((iRegs[_Rs_].k & 0xffff) == iRegs[_Rs_].k) {
           NORI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
       } else {
           NOR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
       if ((iRegs[_Rt_].k & 0xffff) == iRegs[_Rt_].k) {
           NORI(PutHWReg32(_Rd_), GetHWReg32(_Rs_), iRegs[_Rt_].k);
       } else {
           NOR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       }
   } */else {
       NOR(PutHWReg32(_Rd_), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
   }
}

static void recSLT() {
// Rd = Rs < Rt (signed)
   if (!_Rd_) return;

   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, (s32)iRegs[_Rs_].k < (s32)iRegs[_Rt_].k);
   } else { // TODO: add immidiate cases
       int reg;
       CMPW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
       reg = PutHWReg32(_Rd_);
       LI(reg, 1);
       BLT(1);
       LI(reg, 0);
   }
}

static void recSLTU() { 
// Rd = Rs < Rt (unsigned)
   if (!_Rd_) return;

   if (IsConst(_Rs_) && IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rs_].k < iRegs[_Rt_].k);
   } else { // TODO: add immidiate cases
       SUBFC(PutHWReg32(_Rd_), GetHWReg32(_Rt_), GetHWReg32(_Rs_));
       SUBFE(PutHWReg32(_Rd_), GetHWReg32(_Rd_), GetHWReg32(_Rd_));
       NEG(PutHWReg32(_Rd_), GetHWReg32(_Rd_));
   }
}
#endif
//End of * Register arithmetic

#pragma mark - mult/div & Register trap logic -
/*********************************************************
* Register mult/div & Register trap logic                *
* Format:  OP rs, rt                                     *
*********************************************************/

#if 0
REC_FUNC(MULT);
REC_FUNC(MULTU);
REC_FUNC(DIV);
REC_FUNC(DIVU);
#else

int DoShift(u32 k)
{
    u32 i;
    for (i=0; i<30; i++) {
        if (k == (1ul << i))
            return i;
    }
    return -1;
}

//REC_FUNC(MULT);

// FIXME: doesn't work in GT - wrong way marker
static void recMULT() {
// Lo/Hi = Rs * Rt (signed)
    s32 k; int r;
    int usehi, uselo;
    
    if ((IsConst(_Rs_) && iRegs[_Rs_].k == 0) ||
        (IsConst(_Rt_) && iRegs[_Rt_].k == 0)) {
        MapConst(REG_LO, 0);
        MapConst(REG_HI, 0);
        return;
    }
    
    if (IsConst(_Rs_) && IsConst(_Rt_)) {
        u64 res = (s64)((s64)(s32)iRegs[_Rs_].k * (s64)(s32)iRegs[_Rt_].k);
        MapConst(REG_LO, (res & 0xffffffff));
        MapConst(REG_HI, ((res >> 32) & 0xffffffff));
        return;
    }
    
    if (IsConst(_Rs_)) {
        k = (s32)iRegs[_Rs_].k;
        r = _Rt_;
    } else if (IsConst(_Rt_)) {
        k = (s32)iRegs[_Rt_].k;
        r = _Rs_;
    } else {
        r = -1;
        k = 0;
    }
    
    // FIXME: this should not be needed!!!
//    uselo = isPsxRegUsed(pc, REG_LO);
//    usehi = isPsxRegUsed(pc, REG_HI);
    uselo = 1; //isPsxRegUsed(pc, REG_LO);
    usehi = 1; //isPsxRegUsed(pc, REG_HI);


    if (r != -1) {
        int shift = DoShift(k);
        if (shift != -1) {
            if (uselo) {
                SLWI(PutHWReg32(REG_LO), GetHWReg32(r), shift)
            }
            if (usehi) {
                SRAWI(PutHWReg32(REG_HI), GetHWReg32(r), 31-shift);
            }
        } else {
            //if ((s32)(s16)k == k) {
            //    MULLWI(PutHWReg32(REG_LO), GetHWReg32(r), k);
            //    MULHWI(PutHWReg32(REG_HI), GetHWReg32(r), k);
            //} else
            {
                if (uselo) {
                    MULLW(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
                }
                if (usehi) {
                    MULHW(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
                }
            }
        }
    } else {
        if (uselo) {
            MULLW(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
        if (usehi) {
            MULHW(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
    }
}

static void recMULTU() {
// Lo/Hi = Rs * Rt (unsigned)
    u32 k; int r;
    int usehi, uselo;
    
    if ((IsConst(_Rs_) && iRegs[_Rs_].k == 0) ||
        (IsConst(_Rt_) && iRegs[_Rt_].k == 0)) {
        MapConst(REG_LO, 0);
        MapConst(REG_HI, 0);
        return;
    }
    
    if (IsConst(_Rs_) && IsConst(_Rt_)) {
        u64 res = (u64)((u64)(u32)iRegs[_Rs_].k * (u64)(u32)iRegs[_Rt_].k);
        MapConst(REG_LO, (res & 0xffffffff));
        MapConst(REG_HI, ((res >> 32) & 0xffffffff));
        return;
    }
    
    if (IsConst(_Rs_)) {
        k = (s32)iRegs[_Rs_].k;
        r = _Rt_;
    } else if (IsConst(_Rt_)) {
        k = (s32)iRegs[_Rt_].k;
        r = _Rs_;
    } else {
        r = -1;
        k = 0;
    }
    
    uselo = isPsxRegUsed(pc, REG_LO);
    usehi = isPsxRegUsed(pc, REG_HI);

    if (r != -1) {
        int shift = DoShift(k);
        if (shift != -1) {
            if (uselo) {
                SLWI(PutHWReg32(REG_LO), GetHWReg32(r), shift);
            }
            if (usehi) {
                SRWI(PutHWReg32(REG_HI), GetHWReg32(r), 31-shift);
            }
        } else {
            {
                if (uselo) {
                    MULLW(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
                }
                if (usehi) {
                    MULHWU(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
                }
            }
        }
    } else {
        if (uselo) {
            MULLW(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
        if (usehi) {
            MULHWU(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
    }
}

static void recDIV() {
// Lo/Hi = Rs / Rt (signed)
    int usehi;

    if (IsConst(_Rs_) && iRegs[_Rs_].k == 0) {
        MapConst(REG_LO, 0);
        MapConst(REG_HI, 0);
        return;
    }
    if (IsConst(_Rt_) && IsConst(_Rs_)) {
        MapConst(REG_LO, (s32)iRegs[_Rs_].k / (s32)iRegs[_Rt_].k);
        MapConst(REG_HI, (s32)iRegs[_Rs_].k % (s32)iRegs[_Rt_].k);
        return;
    }
    
    usehi = isPsxRegUsed(pc, REG_HI);
    
    if (IsConst(_Rt_)) {
        int shift = DoShift(iRegs[_Rt_].k);
        if (shift != -1) {
            SRAWI(PutHWReg32(REG_LO), GetHWReg32(_Rs_), shift);
            ADDZE(PutHWReg32(REG_LO), GetHWReg32(REG_LO));
            if (usehi) {
                RLWINM(PutHWReg32(REG_HI), GetHWReg32(_Rs_), 0, 31-shift, 31);
            }
        } else if (iRegs[_Rt_].k == 3) {
            // http://the.wall.riscom.net/books/proc/ppc/cwg/code2.html
            LIS(PutHWReg32(REG_HI), 0x5555);
            ADDI(PutHWReg32(REG_HI), GetHWReg32(REG_HI), 0x5556);
            MULHW(PutHWReg32(REG_LO), GetHWReg32(REG_HI), GetHWReg32(_Rs_));
            SRWI(PutHWReg32(REG_HI), GetHWReg32(_Rs_), 31);
            ADD(PutHWReg32(REG_LO), GetHWReg32(REG_LO), GetHWReg32(REG_HI));
            if (usehi) {
                MULLI(PutHWReg32(REG_HI), GetHWReg32(REG_LO), 3);
                SUB(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(REG_HI));
            }
        } else {
            DIVW(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
            if (usehi) {
                if ((iRegs[_Rt_].k & 0x7fff) == iRegs[_Rt_].k) {
                    MULLI(PutHWReg32(REG_HI), GetHWReg32(REG_LO), iRegs[_Rt_].k);
                } else {
                    MULLW(PutHWReg32(REG_HI), GetHWReg32(REG_LO), GetHWReg32(_Rt_));
                }
                SUB(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(REG_HI));
            }
        }
    } else {
        DIVW(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        if (usehi) {
            MULLW(PutHWReg32(REG_HI), GetHWReg32(REG_LO), GetHWReg32(_Rt_));
            SUB(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(REG_HI));
        }
    }
}

static void recDIVU() {
// Lo/Hi = Rs / Rt (unsigned)
    int usehi;

    if (IsConst(_Rs_) && iRegs[_Rs_].k == 0) {
        MapConst(REG_LO, 0);
        MapConst(REG_HI, 0);
        return;
    }
    if (IsConst(_Rt_) && IsConst(_Rs_)) {
        MapConst(REG_LO, (u32)iRegs[_Rs_].k / (u32)iRegs[_Rt_].k);
        MapConst(REG_HI, (u32)iRegs[_Rs_].k % (u32)iRegs[_Rt_].k);
        return;
    }
    
    usehi = isPsxRegUsed(pc, REG_HI);

    if (IsConst(_Rt_)) {
        int shift = DoShift(iRegs[_Rt_].k);
        if (shift != -1) {
            SRWI(PutHWReg32(REG_LO), GetHWReg32(_Rs_), shift);
            if (usehi) {
                RLWINM(PutHWReg32(REG_HI), GetHWReg32(_Rs_), 0, 31-shift, 31);
            }
        } else {
            DIVWU(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
            if (usehi) {
                MULLW(PutHWReg32(REG_HI), GetHWReg32(_Rt_), GetHWReg32(REG_LO));
                SUB(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(REG_HI));
            }
        }
    } else {
        DIVWU(PutHWReg32(REG_LO), GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        if (usehi) {
            MULLW(PutHWReg32(REG_HI), GetHWReg32(_Rt_), GetHWReg32(REG_LO));
            SUB(PutHWReg32(REG_HI), GetHWReg32(_Rs_), GetHWReg32(REG_HI));
        }
    }
}
#endif
//End of * Register mult/div & Register trap logic  

#pragma mark - memory access -

#if 0
REC_FUNC(LB);
REC_FUNC(LBU);
REC_FUNC(LH);
REC_FUNC(LHU);
REC_FUNC(LW);

REC_FUNC(SB);
REC_FUNC(SH);
REC_FUNC(SW);

REC_FUNC(LWL);
REC_FUNC(LWR);
REC_FUNC(SWL);
REC_FUNC(SWR);
#else
static void preMemRead()
{
    int rs;
    
    ReserveArgs(1);
    if (_Rs_ != _Rt_) {
        DisposeHWReg(iRegs[_Rt_].reg);
    }
    rs = GetHWReg32(_Rs_);
    if (rs != 3 || _Imm_ != 0) {
        ADDI(PutHWRegSpecial(ARG1), rs, _Imm_);
    }
    if (_Rs_ == _Rt_) {
        DisposeHWReg(iRegs[_Rt_].reg);
    }
    InvalidateCPURegs();
    //FlushAllHWReg();
}

static void preMemWrite(int size)
{
    int rs;
    
    ReserveArgs(2);
    rs = GetHWReg32(_Rs_);
    if (rs != 3 || _Imm_ != 0) {
        ADDI(PutHWRegSpecial(ARG1), rs, _Imm_);
    }
    if (size == 1) {
        RLWINM(PutHWRegSpecial(ARG2), GetHWReg32(_Rt_), 0, 24, 31);
        //ANDI_(PutHWRegSpecial(ARG2), GetHWReg32(_Rt_), 0xff);
    } else if (size == 2) {
        RLWINM(PutHWRegSpecial(ARG2), GetHWReg32(_Rt_), 0, 16, 31);
        //ANDI_(PutHWRegSpecial(ARG2), GetHWReg32(_Rt_), 0xffff);
    } else {
        MR(PutHWRegSpecial(ARG2), GetHWReg32(_Rt_));
    }
    
    InvalidateCPURegs();
    //FlushAllHWReg();
}

static void recLB() {
// Rt = mem[Rs + Im] (signed)
    
   /*if (IsConst(_Rs_)) {
       u32 addr = iRegs[_Rs_].k + _Imm_;
       int t = addr >> 16;
   
       if ((t & 0xfff0)  == 0xbfc0) {
           if (!_Rt_) return;
           // since bios is readonly it won't change
           MapConst(_Rt_, psxRs8(addr));
           return;
       }
       if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
           if (!_Rt_) return;
               
           addr = (u32)&psxM[addr & 0x1fffff];
           LIW(PutHWReg32(_Rt_), ((addr>>16)<<16)+(addr&0x8000<<1)); // FIXME: is this correct?
           LBZ(PutHWReg32(_Rt_), addr&0xffff, GetHWReg32(_Rt_));
           EXTSB(PutHWReg32(_Rt_), GetHWReg32(_Rt_));
           return;
       }
       if (t == 0x1f80 && addr < 0x1f801000) {
           if (!_Rt_) return;
   
           addr = (u32)&psxH[addr & 0xfff];
           LIW(PutHWReg32(_Rt_), ((addr>>16)<<16)+(addr&0x8000<<1)); // FIXME: is this correct?
           LBZ(PutHWReg32(_Rt_), addr&0xffff, GetHWReg32(_Rt_));
           EXTSB(PutHWReg32(_Rt_), GetHWReg32(_Rt_));
           return;
       }
   //    SysPrintf("unhandled r8 %x\n", addr);
   }*/
    
    preMemRead();
    CALLFunc((u32)psxMemRead8);
    if (_Rt_) {
        EXTSB(PutHWReg32(_Rt_), GetHWRegSpecial(RETVAL));
        DisposeHWReg(GetSpecialIndexFromHWRegs(RETVAL));
    }
}

static void recLBU() {
// Rt = mem[Rs + Im] (unsigned)

   /*if (IsConst(_Rs_)) {
       u32 addr = iRegs[_Rs_].k + _Imm_;
       int t = addr >> 16;
   
       if ((t & 0xfff0)  == 0xbfc0) {
           if (!_Rt_) return;
           // since bios is readonly it won't change
           MapConst(_Rt_, psxRu8(addr));
           return;
       }
       if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
           if (!_Rt_) return;
               
           addr = (u32)&psxM[addr & 0x1fffff];
           LIW(PutHWReg32(_Rt_), ((addr>>16)<<16)+(addr&0x8000<<1)); // FIXME: is this correct?
           LBZ(PutHWReg32(_Rt_), addr&0xffff, GetHWReg32(_Rt_));
           return;
       }
       if (t == 0x1f80 && addr < 0x1f801000) {
           if (!_Rt_) return;
   
           addr = (u32)&psxH[addr & 0xfff];
           LIW(PutHWReg32(_Rt_), ((addr>>16)<<16)+(addr&0x8000<<1)); // FIXME: is this correct?
           LBZ(PutHWReg32(_Rt_), addr&0xffff, GetHWReg32(_Rt_));
           return;
       }
   //    SysPrintf("unhandled r8 %x\n", addr);
   }*/
       
    preMemRead();
    CALLFunc((u32)psxMemRead8);
    
    if (_Rt_) {
        SetDstCPUReg(3);
        PutHWReg32(_Rt_);
    }
}

static void recLH() {
// Rt = mem[Rs + Im] (signed)

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;
    
        if ((t & 0xfff0)  == 0xbfc0) {
            if (!_Rt_) return;
            // since bios is readonly it won't change
            MapConst(_Rt_, psxRs16(addr));
            return;
        }
        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            if (!_Rt_) return;

            LIW(PutHWReg32(_Rt_), (u32)&psxM[addr & 0x1fffff]);
            LHBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
            EXTSH(PutHWReg32(_Rt_), GetHWReg32(_Rt_));
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            if (!_Rt_) return;

            LIW(PutHWReg32(_Rt_), (u32)&psxH[addr & 0xfff]);
            LHBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
            EXTSH(PutHWReg32(_Rt_), GetHWReg32(_Rt_));
            return;
        }
    //    SysPrintf("unhandled r16 %x\n", addr);
    }
   
    preMemRead();
    CALLFunc((u32)psxMemRead16);
    if (_Rt_) {
        EXTSH(PutHWReg32(_Rt_), GetHWRegSpecial(RETVAL));
        DisposeHWReg(GetSpecialIndexFromHWRegs(RETVAL));
    }
}

static void recLHU() {
// Rt = mem[Rs + Im] (unsigned)

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;
    
        if ((t & 0xfff0) == 0xbfc0) {
            if (!_Rt_) return;
            // since bios is readonly it won't change
            MapConst(_Rt_, psxRu16(addr));
            return;
        }
        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            if (!_Rt_) return;

            LIW(PutHWReg32(_Rt_), (u32)&psxM[addr & 0x1fffff]);
            LHBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            if (!_Rt_) return;

            LIW(PutHWReg32(_Rt_), (u32)&psxH[addr & 0xfff]);
            LHBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
            return;
        }
        if (t == 0x1f80) {
            if (addr >= 0x1f801c00 && addr < 0x1f801e00) {
                    if (!_Rt_) return;
                    
                    ReserveArgs(1);
                    LIW(PutHWRegSpecial(ARG1), addr);
                    DisposeHWReg(iRegs[_Rt_].reg);
                    InvalidateCPURegs();
                    CALLFunc((u32)SPU_readRegister);
                    
                    SetDstCPUReg(3);
                    PutHWReg32(_Rt_);
                    return;
            }
            switch (addr) {
                    case 0x1f801100: case 0x1f801110: case 0x1f801120:
                        if (!_Rt_) return;
                        
                        ReserveArgs(1);
                        LIW(PutHWRegSpecial(ARG1), (addr >> 4) & 0x3);
                        DisposeHWReg(iRegs[_Rt_].reg);
                        InvalidateCPURegs();
                        CALLFunc((u32)psxRcntRcount);
                        
                        SetDstCPUReg(3);
                        PutHWReg32(_Rt_);
                        return;

                    case 0x1f801104: case 0x1f801114: case 0x1f801124:
                        if (!_Rt_) return;
                        
                       ReserveArgs(1);
                       LIW(PutHWRegSpecial(ARG1), (addr >> 4) & 0x3);
                       DisposeHWReg(iRegs[_Rt_].reg);
                       InvalidateCPURegs();
                       CALLFunc((u32)psxRcntRmode);
                       
                       SetDstCPUReg(3);
                       PutHWReg32(_Rt_);
                        return;
    
                    case 0x1f801108: case 0x1f801118: case 0x1f801128:
                        if (!_Rt_) return;

                       ReserveArgs(1);
                       LIW(PutHWRegSpecial(ARG1), (addr >> 4) & 0x3);
                       DisposeHWReg(iRegs[_Rt_].reg);
                       InvalidateCPURegs();
                       CALLFunc((u32)psxRcntRtarget);
                       
                       SetDstCPUReg(3);
                       PutHWReg32(_Rt_);
                        return;
                    }
        }
    //    SysPrintf("unhandled r16u %x\n", addr);
    }
    
    preMemRead();
    CALLFunc((u32)psxMemRead16);
    if (_Rt_) {
        SetDstCPUReg(3);
        PutHWReg32(_Rt_);
    }
}

static void recLW() {
// Rt = mem[Rs + Im] (unsigned)

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0xfff0) == 0xbfc0) {
            if (!_Rt_) return;
            // since bios is readonly it won't change
            MapConst(_Rt_, psxRu32(addr));
            return;
        }
        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            if (!_Rt_) return;

            LIW(PutHWReg32(_Rt_), (u32)&psxM[addr & 0x1fffff]);
            LWBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            if (!_Rt_) return;

            LIW(PutHWReg32(_Rt_), (u32)&psxH[addr & 0xfff]);
            LWBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
            return;
        }
        if (t == 0x1f80) {
            switch (addr) {
                case 0x1f801080: case 0x1f801084: case 0x1f801088: 
                case 0x1f801090: case 0x1f801094: case 0x1f801098: 
                case 0x1f8010a0: case 0x1f8010a4: case 0x1f8010a8: 
                case 0x1f8010b0: case 0x1f8010b4: case 0x1f8010b8: 
                case 0x1f8010c0: case 0x1f8010c4: case 0x1f8010c8: 
                case 0x1f8010d0: case 0x1f8010d4: case 0x1f8010d8: 
                case 0x1f8010e0: case 0x1f8010e4: case 0x1f8010e8: 
                case 0x1f801070: case 0x1f801074:
                case 0x1f8010f0: case 0x1f8010f4:
                    if (!_Rt_) return;
                    
                    LIW(PutHWReg32(_Rt_), (u32)&psxH[addr & 0xffff]);
                    LWBRX(PutHWReg32(_Rt_), 0, GetHWReg32(_Rt_));
                    return;

                case 0x1f801810:
                    if (!_Rt_) return;

                    DisposeHWReg(iRegs[_Rt_].reg);
                    InvalidateCPURegs();
                    CALLFunc((u32)GPU_readData);
                    
                    SetDstCPUReg(3);
                    PutHWReg32(_Rt_);
                    return;

                case 0x1f801814:
                    if (!_Rt_) return;

                    DisposeHWReg(iRegs[_Rt_].reg);
                    InvalidateCPURegs();
                    CALLFunc((u32)GPU_readStatus);
                    
                    SetDstCPUReg(3);
                    PutHWReg32(_Rt_);
                    return;
            }
        }
//        SysPrintf("unhandled r32 %x\n", addr);
    }

    preMemRead();
    CALLFunc((u32)psxMemRead32);
    if (_Rt_) {
        SetDstCPUReg(3);
        PutHWReg32(_Rt_);
    }
}

REC_FUNC(LWL);
REC_FUNC(LWR);
REC_FUNC(SWL);
REC_FUNC(SWR);
/*extern u32 LWL_MASK[4];
extern u32 LWL_SHIFT[4];

void iLWLk(u32 shift) {
    if (IsConst(_Rt_)) {
        MOV32ItoR(ECX, iRegs[_Rt_].k);
    } else {
        MOV32MtoR(ECX, (u32)&psxRegs.GPR.r[_Rt_]);
    }
    AND32ItoR(ECX, LWL_MASK[shift]);
    SHL32ItoR(EAX, LWL_SHIFT[shift]);
    OR32RtoR (EAX, ECX);
}

void recLWL() {
// Rt = Rt Merge mem[Rs + Im]

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            MOV32MtoR(EAX, (u32)&psxM[addr & 0x1ffffc]);
            iLWLk(addr & 3);

            iRegs[_Rt_].state = ST_UNK;
            MOV32RtoM((u32)&psxRegs.GPR.r[_Rt_], EAX);
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            MOV32MtoR(EAX, (u32)&psxH[addr & 0xffc]);
            iLWLk(addr & 3);

            iRegs[_Rt_].state = ST_UNK;
            MOV32RtoM((u32)&psxRegs.GPR.r[_Rt_], EAX);
            return;
        }
    }

    if (IsConst(_Rs_)) MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_);
    else {
        MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_Rs_]);
        if (_Imm_) ADD32ItoR(EAX, _Imm_);
    }
    PUSH32R  (EAX);
    AND32ItoR(EAX, ~3);
    PUSH32R  (EAX);
    CALLFunc((u32)psxMemRead32);

    if (_Rt_) {
        ADD32ItoR(ESP, 4);
        POP32R   (EDX);
        AND32ItoR(EDX, 0x3); // shift = addr & 3;

        MOV32ItoR(ECX, (u32)LWL_SHIFT);
        MOV32RmStoR(ECX, ECX, EDX, 2);
        SHL32CLtoR(EAX); // mem(EAX) << LWL_SHIFT[shift]

        MOV32ItoR(ECX, (u32)LWL_MASK);
        MOV32RmStoR(ECX, ECX, EDX, 2);
        if (IsConst(_Rt_)) {
            MOV32ItoR(EDX, iRegs[_Rt_].k);
        } else {
            MOV32MtoR(EDX, (u32)&psxRegs.GPR.r[_Rt_]);
        }
        AND32RtoR(EDX, ECX); // _rRt_ & LWL_MASK[shift]

        OR32RtoR(EAX, EDX);

        iRegs[_Rt_].state = ST_UNK;
        MOV32RtoM((u32)&psxRegs.GPR.r[_Rt_], EAX);
    } else {
//        ADD32ItoR(ESP, 8);
        resp+= 8;
    }
}

static void recLWBlock(int count) {
    u32 *code = PSXM(pc);
    int i, respsave;
// Rt = mem[Rs + Im] (unsigned)

//    iFlushRegs(0);

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0xfff0) == 0xbfc0) {
            // since bios is readonly it won't change
            for (i=0; i<count; i++, code++, addr+=4) {
                if (_fRt_(*code)) {
                    MapConst(_fRt_(*code), psxRu32(addr));
                }
            }
            return;
        }
        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            for (i=0; i<count; i++, code++, addr+=4) {
                if (!_fRt_(*code)) return;
                iRegs[_fRt_(*code)].state = ST_UNK;

                MOV32MtoR(EAX, (u32)&psxM[addr & 0x1fffff]);
                MOV32RtoM((u32)&psxRegs.GPR.r[_fRt_(*code)], EAX);
            }
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            for (i=0; i<count; i++, code++, addr+=4) {
                if (!_fRt_(*code)) return;
                iRegs[_fRt_(*code)].state = ST_UNK;

                MOV32MtoR(EAX, (u32)&psxH[addr & 0xfff]);
                MOV32RtoM((u32)&psxRegs.GPR.r[_fRt_(*code)], EAX);
            }
            return;
        }
    }

    SysPrintf("recLWBlock %d: %d\n", count, IsConst(_Rs_));
    iPushOfB();
    CALLFunc((u32)psxMemPointer);
//    ADD32ItoR(ESP, 4);
    resp+= 4;

    respsave = resp; resp = 0;
    TEST32RtoR(EAX, EAX);
    j32Ptr[4] = JZ32(0);
    XOR32RtoR(ECX, ECX);
    for (i=0; i<count; i++, code++) {
        if (_fRt_(*code)) {
            iRegs[_fRt_(*code)].state = ST_UNK;
            
            MOV32RmStoR(EDX, EAX, ECX, 2);
            MOV32RtoM((u32)&psxRegs.GPR.r[_fRt_(*code)], EDX);
        }
        if (i != (count-1)) INC32R(ECX);
    }
    j32Ptr[5] = JMP32(0);
    x86SetJ32(j32Ptr[4]);
    for (i=0, code = PSXM(pc); i<count; i++, code++) {
        psxRegs.code = *code;
        recLW();
    }
    ADD32ItoR(ESP, resp);
    x86SetJ32(j32Ptr[5]);
    resp = respsave;
}

extern u32 LWR_MASK[4];
extern u32 LWR_SHIFT[4];

void iLWRk(u32 shift) {
    if (IsConst(_Rt_)) {
        MOV32ItoR(ECX, iRegs[_Rt_].k);
    } else {
        MOV32MtoR(ECX, (u32)&psxRegs.GPR.r[_Rt_]);
    }
    AND32ItoR(ECX, LWR_MASK[shift]);
    SHR32ItoR(EAX, LWR_SHIFT[shift]);
    OR32RtoR (EAX, ECX);
}

void recLWR() {
// Rt = Rt Merge mem[Rs + Im]

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            MOV32MtoR(EAX, (u32)&psxM[addr & 0x1ffffc]);
            iLWRk(addr & 3);

            iRegs[_Rt_].state = ST_UNK;
            MOV32RtoM((u32)&psxRegs.GPR.r[_Rt_], EAX);
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            MOV32MtoR(EAX, (u32)&psxH[addr & 0xffc]);
            iLWRk(addr & 3);

            iRegs[_Rt_].state = ST_UNK;
            MOV32RtoM((u32)&psxRegs.GPR.r[_Rt_], EAX);
            return;
        }
    }

    if (IsConst(_Rs_)) MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_);
    else {
        MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_Rs_]);
        if (_Imm_) ADD32ItoR(EAX, _Imm_);
    }
    PUSH32R  (EAX);
    AND32ItoR(EAX, ~3);
    PUSH32R  (EAX);
    CALLFunc((u32)psxMemRead32);

    if (_Rt_) {
        ADD32ItoR(ESP, 4);
        POP32R   (EDX);
        AND32ItoR(EDX, 0x3); // shift = addr & 3;

        MOV32ItoR(ECX, (u32)LWR_SHIFT);
        MOV32RmStoR(ECX, ECX, EDX, 2);
        SHR32CLtoR(EAX); // mem(EAX) >> LWR_SHIFT[shift]

        MOV32ItoR(ECX, (u32)LWR_MASK);
        MOV32RmStoR(ECX, ECX, EDX, 2);

        if (IsConst(_Rt_)) {
            MOV32ItoR(EDX, iRegs[_Rt_].k);
        } else {
            MOV32MtoR(EDX, (u32)&psxRegs.GPR.r[_Rt_]);
        }
        AND32RtoR(EDX, ECX); // _rRt_ & LWR_MASK[shift]

        OR32RtoR(EAX, EDX);

        iRegs[_Rt_].state = ST_UNK;
        MOV32RtoM((u32)&psxRegs.GPR.r[_Rt_], EAX);
    } else {
//        ADD32ItoR(ESP, 8);
        resp+= 8;
    }
}*/

static void recSB() {
// mem[Rs + Im] = Rt

    /*if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            if (IsConst(_Rt_)) {
                MOV8ItoM((u32)&psxM[addr & 0x1fffff], (u8)iRegs[_Rt_].k);
            } else {
                MOV8MtoR(EAX, (u32)&psxRegs.GPR.r[_Rt_]);
                MOV8RtoM((u32)&psxM[addr & 0x1fffff], EAX);
            }
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            if (IsConst(_Rt_)) {
                MOV8ItoM((u32)&psxH[addr & 0xfff], (u8)iRegs[_Rt_].k);
            } else {
                MOV8MtoR(EAX, (u32)&psxRegs.GPR.r[_Rt_]);
                MOV8RtoM((u32)&psxH[addr & 0xfff], EAX);
            }
            return;
        }
//        SysPrintf("unhandled w8 %x\n", addr);
    }*/

    preMemWrite(1);
    CALLFunc((u32)psxMemWrite8);
}

static void recSH() {
// mem[Rs + Im] = Rt

    /*if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            if (IsConst(_Rt_)) {
                MOV16ItoM((u32)&psxM[addr & 0x1fffff], (u16)iRegs[_Rt_].k);
            } else {
                MOV16MtoR(EAX, (u32)&psxRegs.GPR.r[_Rt_]);
                MOV16RtoM((u32)&psxM[addr & 0x1fffff], EAX);
            }
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            if (IsConst(_Rt_)) {
                MOV16ItoM((u32)&psxH[addr & 0xfff], (u16)iRegs[_Rt_].k);
            } else {
                MOV16MtoR(EAX, (u32)&psxRegs.GPR.r[_Rt_]);
                MOV16RtoM((u32)&psxH[addr & 0xfff], EAX);
            }
            return;
        }
        if (t == 0x1f80) {
            if (addr >= 0x1f801c00 && addr < 0x1f801e00) {
                if (IsConst(_Rt_)) {
                    PUSH32I(iRegs[_Rt_].k);
                } else {
                    PUSH32M((u32)&psxRegs.GPR.r[_Rt_]);
                }
                PUSH32I  (addr);
                CALL32M  ((u32)&SPU_writeRegister);
#ifndef __WIN32__
                resp+= 8;
#endif
                return;
            }
        }
//        SysPrintf("unhandled w16 %x\n", addr);
    }*/

    preMemWrite(2);
    CALLFunc((u32)psxMemWrite16);
}

static void recSW() {
// mem[Rs + Im] = Rt
    u32 *b1, *b2;
#if 0
    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            LIW(0, addr & 0x1fffff);
            STWBRX(GetHWReg32(_Rt_), GetHWRegSpecial(PSXMEM), 0);
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            LIW(0, (u32)&psxH[addr & 0xfff]);
            STWBRX(GetHWReg32(_Rt_), 0, 0);
            return;
        }
        if (t == 0x1f80) {
            switch (addr) {
                case 0x1f801080: case 0x1f801084: 
                case 0x1f801090: case 0x1f801094: 
                case 0x1f8010a0: case 0x1f8010a4: 
                case 0x1f8010b0: case 0x1f8010b4: 
                case 0x1f8010c0: case 0x1f8010c4: 
                case 0x1f8010d0: case 0x1f8010d4: 
                case 0x1f8010e0: case 0x1f8010e4: 
                case 0x1f801074:
                case 0x1f8010f0:
                    LIW(0, (u32)&psxH[addr & 0xffff]);
                    STWBRX(GetHWReg32(_Rt_), 0, 0);
                    return;

/*                case 0x1f801810:
                    if (IsConst(_Rt_)) {
                        PUSH32I(iRegs[_Rt_].k);
                    } else {
                        PUSH32M((u32)&psxRegs.GPR.r[_Rt_]);
                    }
                    CALL32M((u32)&GPU_writeData);
#ifndef __WIN32__
                    resp+= 4;
#endif
                    return;

                case 0x1f801814:
                    if (IsConst(_Rt_)) {
                        PUSH32I(iRegs[_Rt_].k);
                    } else {
                        PUSH32M((u32)&psxRegs.GPR.r[_Rt_]);
                    }
                    CALL32M((u32)&GPU_writeStatus);
#ifndef __WIN32__
                    resp+= 4;
#endif*/
            }
        }
//        SysPrintf("unhandled w32 %x\n", addr);
    }
    
/*    LIS(0, 0x0079 + ((_Imm_ <= 0) ? 1 : 0));
    CMPLW(GetHWReg32(_Rs_), 0);
    BGE_L(b1);
    
    //SaveContext();
    ADDI(0, GetHWReg32(_Rs_), _Imm_);
    RLWINM(0, GetHWReg32(_Rs_), 0, 11, 31);
    STWBRX(GetHWReg32(_Rt_), GetHWRegSpecial(PSXMEM), 0);
    B_L(b2);
    
    B_DST(b1);*/
#endif
    preMemWrite(4);
    CALLFunc((u32)psxMemWrite32);
    
    //B_DST(b2);
}

/*
static void recSWBlock(int count) {
    u32 *code;
    int i, respsave;
// mem[Rs + Im] = Rt

//    iFlushRegs();

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;
        code = PSXM(pc);

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            for (i=0; i<count; i++, code++, addr+=4) {
                if (IsConst(_fRt_(*code))) {
                    MOV32ItoM((u32)&psxM[addr & 0x1fffff], iRegs[_fRt_(*code)].k);
                } else {
                    MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_fRt_(*code)]);
                    MOV32RtoM((u32)&psxM[addr & 0x1fffff], EAX);
                }
            }
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            for (i=0; i<count; i++, code++, addr+=4) {
                if (!_fRt_(*code)) return;
                iRegs[_fRt_(*code)].state = ST_UNK;

                MOV32MtoR(EAX, (u32)&psxH[addr & 0xfff]);
                MOV32RtoM((u32)&psxRegs.GPR.r[_fRt_(*code)], EAX);
            }
            return;
        }
    }

    SysPrintf("recSWBlock %d: %d\n", count, IsConst(_Rs_));
    iPushOfB();
    CALLFunc((u32)psxMemPointer);
//    ADD32ItoR(ESP, 4);
    resp+= 4;

    respsave = resp; resp = 0;
    TEST32RtoR(EAX, EAX);
    j32Ptr[4] = JZ32(0);
    XOR32RtoR(ECX, ECX);
    for (i=0, code = PSXM(pc); i<count; i++, code++) {
        if (IsConst(_fRt_(*code))) {
            MOV32ItoR(EDX, iRegs[_fRt_(*code)].k);
        } else {
            MOV32MtoR(EDX, (u32)&psxRegs.GPR.r[_fRt_(*code)]);
        }
        MOV32RtoRmS(EAX, ECX, 2, EDX);
        if (i != (count-1)) INC32R(ECX);
    }
    j32Ptr[5] = JMP32(0);
    x86SetJ32(j32Ptr[4]);
    for (i=0, code = PSXM(pc); i<count; i++, code++) {
        psxRegs.code = *code;
        recSW();
    }
    ADD32ItoR(ESP, resp);
    x86SetJ32(j32Ptr[5]);
    resp = respsave;
}

extern u32 SWL_MASK[4];
extern u32 SWL_SHIFT[4];

void iSWLk(u32 shift) {
    if (IsConst(_Rt_)) {
        MOV32ItoR(ECX, iRegs[_Rt_].k);
    } else {
        MOV32MtoR(ECX, (u32)&psxRegs.GPR.r[_Rt_]);
    }
    SHR32ItoR(ECX, SWL_SHIFT[shift]);
    AND32ItoR(EAX, SWL_MASK[shift]);
    OR32RtoR (EAX, ECX);
}

void recSWL() {
// mem[Rs + Im] = Rt Merge mem[Rs + Im]

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            MOV32MtoR(EAX, (u32)&psxM[addr & 0x1ffffc]);
            iSWLk(addr & 3);
            MOV32RtoM((u32)&psxM[addr & 0x1ffffc], EAX);
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            MOV32MtoR(EAX, (u32)&psxH[addr & 0xffc]);
            iSWLk(addr & 3);
            MOV32RtoM((u32)&psxH[addr & 0xffc], EAX);
            return;
        }
    }

    if (IsConst(_Rs_)) MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_);
    else {
        MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_Rs_]);
        if (_Imm_) ADD32ItoR(EAX, _Imm_);
    }
    PUSH32R  (EAX);
    AND32ItoR(EAX, ~3);
    PUSH32R  (EAX);

    CALLFunc((u32)psxMemRead32);

    ADD32ItoR(ESP, 4);
    POP32R   (EDX);
    AND32ItoR(EDX, 0x3); // shift = addr & 3;

    MOV32ItoR(ECX, (u32)SWL_MASK);
    MOV32RmStoR(ECX, ECX, EDX, 2);
    AND32RtoR(EAX, ECX); // mem & SWL_MASK[shift]

    MOV32ItoR(ECX, (u32)SWL_SHIFT);
    MOV32RmStoR(ECX, ECX, EDX, 2);
    if (IsConst(_Rt_)) {
        MOV32ItoR(EDX, iRegs[_Rt_].k);
    } else {
        MOV32MtoR(EDX, (u32)&psxRegs.GPR.r[_Rt_]);
    }
    SHR32CLtoR(EDX); // _rRt_ >> SWL_SHIFT[shift]

    OR32RtoR (EAX, EDX);
    PUSH32R  (EAX);

    if (IsConst(_Rs_)) MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_);
    else {
        MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_Rs_]);
        if (_Imm_) ADD32ItoR(EAX, _Imm_);
    }
    AND32ItoR(EAX, ~3);
    PUSH32R  (EAX);

    CALLFunc((u32)psxMemWrite32);
//    ADD32ItoR(ESP, 8);
    resp+= 8;
}

extern u32 SWR_MASK[4];
extern u32 SWR_SHIFT[4];

void iSWRk(u32 shift) {
    if (IsConst(_Rt_)) {
        MOV32ItoR(ECX, iRegs[_Rt_].k);
    } else {
        MOV32MtoR(ECX, (u32)&psxRegs.GPR.r[_Rt_]);
    }
    SHL32ItoR(ECX, SWR_SHIFT[shift]);
    AND32ItoR(EAX, SWR_MASK[shift]);
    OR32RtoR (EAX, ECX);
}

void recSWR() {
// mem[Rs + Im] = Rt Merge mem[Rs + Im]

    if (IsConst(_Rs_)) {
        u32 addr = iRegs[_Rs_].k + _Imm_;
        int t = addr >> 16;

        if ((t & 0x1fe0) == 0 && (t & 0x1fff) != 0) {
            MOV32MtoR(EAX, (u32)&psxM[addr & 0x1ffffc]);
            iSWRk(addr & 3);
            MOV32RtoM((u32)&psxM[addr & 0x1ffffc], EAX);
            return;
        }
        if (t == 0x1f80 && addr < 0x1f801000) {
            MOV32MtoR(EAX, (u32)&psxH[addr & 0xffc]);
            iSWRk(addr & 3);
            MOV32RtoM((u32)&psxH[addr & 0xffc], EAX);
            return;
        }
    }

    if (IsConst(_Rs_)) MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_);
    else {
        MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_Rs_]);
        if (_Imm_) ADD32ItoR(EAX, _Imm_);
    }
    PUSH32R  (EAX);
    AND32ItoR(EAX, ~3);
    PUSH32R  (EAX);

    CALLFunc((u32)psxMemRead32);

    ADD32ItoR(ESP, 4);
    POP32R   (EDX);
    AND32ItoR(EDX, 0x3); // shift = addr & 3;

    MOV32ItoR(ECX, (u32)SWR_MASK);
    MOV32RmStoR(ECX, ECX, EDX, 2);
    AND32RtoR(EAX, ECX); // mem & SWR_MASK[shift]

    MOV32ItoR(ECX, (u32)SWR_SHIFT);
    MOV32RmStoR(ECX, ECX, EDX, 2);
    if (IsConst(_Rt_)) {
        MOV32ItoR(EDX, iRegs[_Rt_].k);
    } else {
        MOV32MtoR(EDX, (u32)&psxRegs.GPR.r[_Rt_]);
    }
    SHL32CLtoR(EDX); // _rRt_ << SWR_SHIFT[shift]

    OR32RtoR (EAX, EDX);
    PUSH32R  (EAX);

    if (IsConst(_Rs_)) MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_);
    else {
        MOV32MtoR(EAX, (u32)&psxRegs.GPR.r[_Rs_]);
        if (_Imm_) ADD32ItoR(EAX, _Imm_);
    }
    AND32ItoR(EAX, ~3);
    PUSH32R  (EAX);

    CALLFunc((u32)psxMemWrite32);
//    ADD32ItoR(ESP, 8);
    resp+= 8;
}*/
#endif

#if 0
/*REC_FUNC(SLL);
REC_FUNC(SRL);
REC_FUNC(SRA);*/
#else
static void recSLL() {
// Rd = Rt << Sa
   if (!_Rd_) return;

   if (IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rt_].k << _Sa_);
   } else {
       SLWI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), _Sa_);
   }
}

static void recSRL() {
// Rd = Rt >> Sa
   if (!_Rd_) return;

   if (IsConst(_Rt_)) {
       MapConst(_Rd_, iRegs[_Rt_].k >> _Sa_);
   } else {
       SRWI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), _Sa_);
   }
}

static void recSRA() {
// Rd = Rt >> Sa
   if (!_Rd_) return;

   if (IsConst(_Rt_)) {
       MapConst(_Rd_, (s32)iRegs[_Rt_].k >> _Sa_);
   } else {
       SRAWI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), _Sa_);
   }
}
#endif

#pragma mark - shift ops -
#if 0
/*REC_FUNC(SLLV);
REC_FUNC(SRLV);
REC_FUNC(SRAV);*/
#else
static void recSLLV() {
// Rd = Rt << Rs
    if (!_Rd_) return;

    if (IsConst(_Rt_) && IsConst(_Rs_)) {
        MapConst(_Rd_, iRegs[_Rt_].k << iRegs[_Rs_].k);
    } else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
        SLWI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
    } else {
        SLW(PutHWReg32(_Rd_), GetHWReg32(_Rt_), GetHWReg32(_Rs_));
    }
}

static void recSRLV() {
// Rd = Rt >> Rs
    if (!_Rd_) return;

    if (IsConst(_Rt_) && IsConst(_Rs_)) {
        MapConst(_Rd_, iRegs[_Rt_].k >> iRegs[_Rs_].k);
    } else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
        SRWI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
    } else {
        SRW(PutHWReg32(_Rd_), GetHWReg32(_Rt_), GetHWReg32(_Rs_));
    }
}

static void recSRAV() {
// Rd = Rt >> Rs
    if (!_Rd_) return;

    if (IsConst(_Rt_) && IsConst(_Rs_)) {
        MapConst(_Rd_, (s32)iRegs[_Rt_].k >> iRegs[_Rs_].k);
    } else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
        SRAWI(PutHWReg32(_Rd_), GetHWReg32(_Rt_), iRegs[_Rs_].k);
    } else {
        SRAW(PutHWReg32(_Rd_), GetHWReg32(_Rt_), GetHWReg32(_Rs_));
    }
}
#endif

//REC_SYS(SYSCALL);
//REC_SYS(BREAK);

//#if 0*/
/*int dump;*/
static void recSYSCALL() {
//    dump=1;
    iFlushRegs(0);
    
    ReserveArgs(2);
    LIW(PutHWRegSpecial(PSXPC), pc - 4);
    LIW(PutHWRegSpecial(ARG1), 0x20);
    LIW(PutHWRegSpecial(ARG2), (branch == 1 ? 1 : 0));
    FlushAllHWReg();
    CALLFunc ((u32)psxException);

    branch = 2;
    iRet();
}

static void recBREAK() {
}
//#endif

#if 0
/*REC_FUNC(MFHI);
REC_FUNC(MTHI);
REC_FUNC(MFLO);
REC_FUNC(MTLO);*/
#else
static void recMFHI() {
// Rd = Hi
    if (!_Rd_) return;
    
    if (IsConst(REG_HI)) {
        MapConst(_Rd_, iRegs[REG_HI].k);
    } else {
        MapCopy(_Rd_, REG_HI);
    }
}

static void recMTHI() {
// Hi = Rs

    if (IsConst(_Rs_)) {
        MapConst(REG_HI, iRegs[_Rs_].k);
    } else {
        MapCopy(REG_HI, _Rs_);
    }
}

static void recMFLO() {
// Rd = Lo
    if (!_Rd_) return;

    if (IsConst(REG_LO)) {
        MapConst(_Rd_, iRegs[REG_LO].k);
    } else {
        MapCopy(_Rd_, REG_LO);
    }
}

static void recMTLO() {
// Lo = Rs

    if (IsConst(_Rs_)) {
        MapConst(REG_LO, iRegs[_Rs_].k);
    } else {
        MapCopy(REG_LO, _Rs_);
    }
}
#endif

#pragma mark - branch ops -
#if 0
/*REC_BRANCH(J);
REC_BRANCH(JR);
REC_BRANCH(JAL);
REC_BRANCH(JALR);
REC_BRANCH(BLTZ);
REC_BRANCH(BGTZ);
REC_BRANCH(BLTZAL);
REC_BRANCH(BGEZAL);
REC_BRANCH(BNE);
REC_BRANCH(BEQ);
REC_BRANCH(BLEZ);
REC_BRANCH(BGEZ);*/
#else
static void recBLTZ() {
// Branch if Rs < 0
    u32 bpc = _Imm_ * 4 + pc;
    u32 *b;

    if (IsConst(_Rs_)) {
        if ((s32)iRegs[_Rs_].k < 0) {
            iJump(bpc); return;
        } else {
            iJump(pc+4); return;
        }
    }

    CMPWI(GetHWReg32(_Rs_), 0);
    BLT_L(b);
    
    iBranch(pc+4, 1);

    B_DST(b);
    
    iBranch(bpc, 0);
    pc+=4;
}

static void recBGTZ() {
// Branch if Rs > 0
   u32 bpc = _Imm_ * 4 + pc;
   u32 *b;

   if (IsConst(_Rs_)) {
       if ((s32)iRegs[_Rs_].k > 0) {
           iJump(bpc); return;
       } else {
           iJump(pc+4); return;
       }
   }

   CMPWI(GetHWReg32(_Rs_), 0);
   BGT_L(b);
   
   iBranch(pc+4, 1);

   B_DST(b);

   iBranch(bpc, 0);
   pc+=4;
}

static void recBLTZAL() {
// Branch if Rs < 0
   u32 bpc = _Imm_ * 4 + pc;
   u32 *b;

   if (IsConst(_Rs_)) {
       if ((s32)iRegs[_Rs_].k < 0) {
           MapConst(31, pc + 4);

           iJump(bpc); return;
       } else {
           iJump(pc+4); return;
       }
   }

   CMPWI(GetHWReg32(_Rs_), 0);
   BLT_L(b);
   
   iBranch(pc+4, 1);

   B_DST(b);
   
   MapConst(31, pc + 4);

   iBranch(bpc, 0);
   pc+=4;
}

static void recBGEZAL() {
// Branch if Rs >= 0
   u32 bpc = _Imm_ * 4 + pc;
   u32 *b;

   if (IsConst(_Rs_)) {
       if ((s32)iRegs[_Rs_].k >= 0) {
           MapConst(31, pc + 4);

           iJump(bpc); return;
       } else {
           iJump(pc+4); return;
       }
   }

   CMPWI(GetHWReg32(_Rs_), 0);
   BGE_L(b);
   
   iBranch(pc+4, 1);

   B_DST(b);
   
   MapConst(31, pc + 4);

   iBranch(bpc, 0);
   pc+=4;
}

static void recJ() {
// j target

    iJump(_Target_ * 4 + (pc & 0xf0000000));
}

static void recJAL() {
// jal target
    MapConst(31, pc + 4);

    iJump(_Target_ * 4 + (pc & 0xf0000000));
}

static void recJR() {
// jr Rs

    if (IsConst(_Rs_)) {
        iJump(iRegs[_Rs_].k);
        //LIW(PutHWRegSpecial(TARGET), iRegs[_Rs_].k);
    } else {
        MR(PutHWRegSpecial(TARGET), GetHWReg32(_Rs_));
        SetBranch();
    }
}

static void recJALR() {
// jalr Rs

    if (_Rd_) {
        MapConst(_Rd_, pc + 4);
    }
    
    if (IsConst(_Rs_)) {
        iJump(iRegs[_Rs_].k);
        //LIW(PutHWRegSpecial(TARGET), iRegs[_Rs_].k);
    } else {
        MR(PutHWRegSpecial(TARGET), GetHWReg32(_Rs_));
        SetBranch();
    }
}

static void recBEQ() {
// Branch if Rs == Rt
    u32 bpc = _Imm_ * 4 + pc;
    u32 *b;

    if (_Rs_ == _Rt_) {
        iJump(bpc);
    }
    else {
        if (IsConst(_Rs_) && IsConst(_Rt_)) {
            if (iRegs[_Rs_].k == iRegs[_Rt_].k) {
                iJump(bpc); return;
            } else {
                iJump(pc+4); return;
            }
        }
        else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
            if ((iRegs[_Rs_].k & 0xffff) == iRegs[_Rs_].k) {
                CMPLWI(GetHWReg32(_Rt_), iRegs[_Rs_].k);
            }
            else if ((s32)(s16)iRegs[_Rs_].k == (s32)iRegs[_Rs_].k) {
                CMPWI(GetHWReg32(_Rt_), iRegs[_Rs_].k);
            }
            else {
                CMPLW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
            }
        }
        else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
            if ((iRegs[_Rt_].k & 0xffff) == iRegs[_Rt_].k) {
                CMPLWI(GetHWReg32(_Rs_), iRegs[_Rt_].k);
            }
            else if ((s32)(s16)iRegs[_Rt_].k == (s32)iRegs[_Rt_].k) {
                CMPWI(GetHWReg32(_Rs_), iRegs[_Rt_].k);
            }
            else {
                CMPLW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
            }
        }
        else {
            CMPLW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
        
        BEQ_L(b);
        
        iBranch(pc+4, 1);
    
        B_DST(b);
        
        iBranch(bpc, 0);
        pc+=4;
    }
}

static void recBNE() {
// Branch if Rs != Rt
    u32 bpc = _Imm_ * 4 + pc;
    u32 *b;

    if (_Rs_ == _Rt_) {
        iJump(pc+4);
    }
    else {
        if (IsConst(_Rs_) && IsConst(_Rt_)) {
            if (iRegs[_Rs_].k != iRegs[_Rt_].k) {
                iJump(bpc); return;
            } else {
                iJump(pc+4); return;
            }
        }
        else if (IsConst(_Rs_) && !IsMapped(_Rs_)) {
            if ((iRegs[_Rs_].k & 0xffff) == iRegs[_Rs_].k) {
                CMPLWI(GetHWReg32(_Rt_), iRegs[_Rs_].k);
            }
            else if ((s32)(s16)iRegs[_Rs_].k == (s32)iRegs[_Rs_].k) {
                CMPWI(GetHWReg32(_Rt_), iRegs[_Rs_].k);
            }
            else {
                CMPLW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
            }
        }
        else if (IsConst(_Rt_) && !IsMapped(_Rt_)) {
            if ((iRegs[_Rt_].k & 0xffff) == iRegs[_Rt_].k) {
                CMPLWI(GetHWReg32(_Rs_), iRegs[_Rt_].k);
            }
            else if ((s32)(s16)iRegs[_Rt_].k == (s32)iRegs[_Rt_].k) {
                CMPWI(GetHWReg32(_Rs_), iRegs[_Rt_].k);
            }
            else {
                CMPLW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
            }
        }
        else {
            CMPLW(GetHWReg32(_Rs_), GetHWReg32(_Rt_));
        }
        
        BNE_L(b);
        
        iBranch(pc+4, 1);
    
        B_DST(b);
        
        iBranch(bpc, 0);
        pc+=4;
    }
}

static void recBLEZ() {
// Branch if Rs <= 0
    u32 bpc = _Imm_ * 4 + pc;
    u32 *b;

    if (IsConst(_Rs_)) {
        if ((s32)iRegs[_Rs_].k <= 0) {
            iJump(bpc); return;
        } else {
            iJump(pc+4); return;
        }
    }

    CMPWI(GetHWReg32(_Rs_), 0);
    BLE_L(b);
    
    iBranch(pc+4, 1);

    B_DST(b);
    
    iBranch(bpc, 0);
    pc+=4;
}

static void recBGEZ() {
// Branch if Rs >= 0
    u32 bpc = _Imm_ * 4 + pc;
    u32 *b;

    if (IsConst(_Rs_)) {
        if ((s32)iRegs[_Rs_].k >= 0) {
            iJump(bpc); return;
        } else {
            iJump(pc+4); return;
        }
    }

    CMPWI(GetHWReg32(_Rs_), 0);
    BGE_L(b);
    
    iBranch(pc+4, 1);

    B_DST(b);
    
    iBranch(bpc, 0);
    pc+=4;
}
#endif

#if 1
//REC_FUNC(MFC0);
//REC_SYS(MTC0);
//REC_FUNC(CFC0);
//REC_SYS(CTC0);
REC_FUNC(RFE);
//#else
static void recMFC0() {
// Rt = Cop0->Rd
    if (!_Rt_) return;
    
    LWZ(PutHWReg32(_Rt_), OFFSET(&psxRegs, &psxRegs.CP0.r[_Rd_]), GetHWRegSpecial(PSXREGS));
}

static void recCFC0() {
// Rt = Cop0->Rd

    recMFC0();
}

static void recMTC0() {
// Cop0->Rd = Rt

    /*if (IsConst(_Rt_)) {
        switch (_Rd_) {
            case 12:
                MOV32ItoM((u32)&psxRegs.CP0.r[_Rd_], iRegs[_Rt_].k);
                break;
            case 13:
                MOV32ItoM((u32)&psxRegs.CP0.r[_Rd_], iRegs[_Rt_].k & ~(0xfc00));
                break;
            default:
                MOV32ItoM((u32)&psxRegs.CP0.r[_Rd_], iRegs[_Rt_].k);
                break;
        }
    } else*/ {
        switch (_Rd_) {
            case 13:
                RLWINM(0,GetHWReg32(_Rt_),0,22,15); // & ~(0xfc00)
                STW(0, OFFSET(&psxRegs, &psxRegs.CP0.r[_Rd_]), GetHWRegSpecial(PSXREGS));
                break;
            default:
                STW(GetHWReg32(_Rt_), OFFSET(&psxRegs, &psxRegs.CP0.r[_Rd_]), GetHWRegSpecial(PSXREGS));
                break;
        }
    }

    if (_Rd_ == 12 || _Rd_ == 13) {
        iFlushRegs(0);
        LIW(PutHWRegSpecial(PSXPC), (u32)pc);
        FlushAllHWReg();
        CALLFunc((u32)psxTestSWInts);
        if(_Rd_ == 12) {
          LWZ(0, OFFSET(&psxRegs, &psxRegs.interrupt), GetHWRegSpecial(PSXREGS));
          ORIS(0, 0, 0x8000);
          STW(0, OFFSET(&psxRegs, &psxRegs.interrupt), GetHWRegSpecial(PSXREGS));
        }
        branch = 2;
        iRet();
    }
}

static void recCTC0() {
// Cop0->Rd = Rt

    recMTC0();
}
#else
static void recRFE() {
    // TODO: implement multiple temp registers or cop0 registers
    RLWINM(t1,Status,0,0,27);
    RLWINM(Status,Status,30,28,31);
    OR(Status,t1,Status);
    
    MOV32MtoR(EAX, (u32)&psxRegs.CP0.n.Status);
    MOV32RtoR(ECX, EAX);
    AND32ItoR(EAX, 0xfffffff0);
    AND32ItoR(ECX, 0x3c);
    SHR32ItoR(ECX, 2);
    OR32RtoR (EAX, ECX);
    MOV32RtoM((u32)&psxRegs.CP0.n.Status, EAX);
    CALLFunc((u32)psxExceptionTest);
}
#endif

#if 0
#define CP2_FUNC(f) \
void gte##f(); \
static void rec##f() { \
    iFlushRegs(0); \
    LIW(0, (u32)psxRegs.code); \
    STW(0, OFFSET(&psxRegs, &psxRegs.code), GetHWRegSpecial(PSXREGS)); \
    FlushAllHWReg(); \
    CALLFunc ((u32)gte##f); \
}
CP2_FUNC(LWC2);
CP2_FUNC(SWC2);

#else
#include "pGte.h"
#endif
//

static void recHLE() {
    iFlushRegs(0);
    FlushAllHWReg();
    
    if ((psxRegs.code & 0x3ffffff) == (psxRegs.code & 0x7)) {
        CALLFunc((u32)psxHLEt[psxRegs.code & 0x7]);
    } else {
        // somebody else must have written to current opcode for this to happen!!!!
        CALLFunc((u32)psxHLEt[0]); // call dummy function
    }
    
    count = (idlecyclecount + (pc - pcold) / 4 + 20) * BIAS;
    ADDI(PutHWRegSpecial(CYCLECOUNT), GetHWRegSpecial(CYCLECOUNT), count);
    FlushAllHWReg();
    CALLFunc((u32)psxBranchTest);
    Return();
    
    branch = 2;
}

//

static void (*recBSC[64])() = {
    recSPECIAL, recREGIMM, recJ   , recJAL  , recBEQ , recBNE , recBLEZ, recBGTZ,
    recADDI   , recADDIU , recSLTI, recSLTIU, recANDI, recORI , recXORI, recLUI ,
    recCOP0   , recNULL  , recCOP2, recNULL , recNULL, recNULL, recNULL, recNULL,
    recNULL   , recNULL  , recNULL, recNULL , recNULL, recNULL, recNULL, recNULL,
    recLB     , recLH    , recLWL , recLW   , recLBU , recLHU , recLWR , recNULL,
    recSB     , recSH    , recSWL , recSW   , recNULL, recNULL, recSWR , recNULL,
    recNULL   , recNULL  , recLWC2, recNULL , recNULL, recNULL, recNULL, recNULL,
    recNULL   , recNULL  , recSWC2, recHLE  , recNULL, recNULL, recNULL, recNULL
};

static void (*recSPC[64])() = {
    recSLL , recNULL, recSRL , recSRA , recSLLV   , recNULL , recSRLV, recSRAV,
    recJR  , recJALR, recNULL, recNULL, recSYSCALL, recBREAK, recNULL, recNULL,
    recMFHI, recMTHI, recMFLO, recMTLO, recNULL   , recNULL , recNULL, recNULL,
    recMULT, recMULTU, recDIV, recDIVU, recNULL   , recNULL , recNULL, recNULL,
    recADD , recADDU, recSUB , recSUBU, recAND    , recOR   , recXOR , recNOR ,
    recNULL, recNULL, recSLT , recSLTU, recNULL   , recNULL , recNULL, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL   , recNULL , recNULL, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL   , recNULL , recNULL, recNULL
};

static void (*recREG[32])() = {
    recBLTZ  , recBGEZ  , recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recNULL  , recNULL  , recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recBLTZAL, recBGEZAL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recNULL  , recNULL  , recNULL, recNULL, recNULL, recNULL, recNULL, recNULL
};

static void (*recCP0[32])() = {
    recMFC0, recNULL, recCFC0, recNULL, recMTC0, recNULL, recCTC0, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recRFE , recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL
};

static void (*recCP2[64])() = {
    recBASIC, recRTPS , recNULL , recNULL, recNULL, recNULL , recNCLIP, recNULL, // 00
    recNULL , recNULL , recNULL , recNULL, recOP  , recNULL , recNULL , recNULL, // 08
    recDPCS , recINTPL, recMVMVA, recNCDS, recCDP , recNULL , recNCDT , recNULL, // 10
    recNULL , recNULL , recNULL , recNCCS, recCC  , recNULL , recNCS  , recNULL, // 18
    recNCT  , recNULL , recNULL , recNULL, recNULL, recNULL , recNULL , recNULL, // 20
    recSQR  , recDCPL , recDPCT , recNULL, recNULL, recAVSZ3, recAVSZ4, recNULL, // 28 
    recRTPT , recNULL , recNULL , recNULL, recNULL, recNULL , recNULL , recNULL, // 30
    recNULL , recNULL , recNULL , recNULL, recNULL, recGPF  , recGPL  , recNCCT  // 38
};

static void (*recCP2BSC[32])() = {
    recMFC2, recNULL, recCFC2, recNULL, recMTC2, recNULL, recCTC2, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL,
    recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL
};

void __declspec(naked) __icbi(int offset, const void * base)
{
	__asm {
        icbi r3,r4
        blr
    }
}

static void recRecompile() {
    //static int recCount = 0;
	u32 a;
    char *p;
    u32 *ptr;
    int i;
    
    cop2readypc = 0;
    idlecyclecount = 0;

    // initialize state variables
    UniqueRegAlloc = 1;
    HWRegUseCount = 0;
    DstCPUReg = -1;
    memset(HWRegisters, 0, sizeof(HWRegisters));
    for (i=0; i<NUM_HW_REGISTERS; i++)
        HWRegisters[i].code = cpuHWRegisters[NUM_HW_REGISTERS-i-1];
    
    // reserve the special psxReg register
    HWRegisters[0].usage = HWUSAGE_SPECIAL | HWUSAGE_RESERVED | HWUSAGE_HARDWIRED;
    HWRegisters[0].private = PSXREGS;
    HWRegisters[0].k = (u32)&psxRegs;

    HWRegisters[1].usage = HWUSAGE_SPECIAL | HWUSAGE_RESERVED | HWUSAGE_HARDWIRED;
    HWRegisters[1].private = PSXMEM;
    HWRegisters[1].k = (u32)&psxM;

    // reserve the special psxRegs.cycle register
    //HWRegisters[1].usage = HWUSAGE_SPECIAL | HWUSAGE_RESERVED | HWUSAGE_HARDWIRED;
    //HWRegisters[1].private = CYCLECOUNT;
    
    //memset(iRegs, 0, sizeof(iRegs));
    for (i=0; i<NUM_REGISTERS; i++) {
        iRegs[i].state = ST_UNK;
        iRegs[i].reg = -1;
    }
    iRegs[0].k = 0;
    iRegs[0].state = ST_CONST;
    
    /* if ppcPtr reached the mem limit reset whole mem */
    if (((u32)ppcPtr - (u32)recMem) >= (RECMEM_SIZE - 0x10000))
        recReset();

    ppcAlign(/*32*/4);
    ptr = ppcPtr;
    
    // give us write access
    //mprotect(recMem, RECMEM_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE);
    
    // tell the LUT where to find us
    PC_REC32(psxRegs.pc) = (u32)ppcPtr;

    pcold = pc = psxRegs.pc;
    
    //SysPrintf("RecCount: %i\n", recCount++);
    
    for (count=0; count<500;) {
        p = (char *)PSXM(pc);
        if (p == NULL) recError();
        psxRegs.code = SWAP32(*(u32 *)p);
/*
        if ((psxRegs.code >> 26) == 0x23) { // LW
            int i;
            u32 code;

            for (i=1;; i++) {
                p = (char *)PSXM(pc+i*4);
                if (p == NULL) recError();
                code = *(u32 *)p;

                if ((code >> 26) != 0x23 ||
                    _fRs_(code)  != _Rs_ ||
                    _fImm_(code) != (_Imm_+i*4))
                    break;
            }
            if (i > 1) {
                recLWBlock(i);
                pc = pc + i*4; continue;
            }
        }

        if ((psxRegs.code >> 26) == 0x2b) { // SW
            int i;
            u32 code;

            for (i=1;; i++) {
                p = (char *)PSXM(pc+i*4);
                if (p == NULL) recError();
                code = *(u32 *)p;

                if ((code >> 26) != 0x2b ||
                    _fRs_(code)  != _Rs_ ||
                    _fImm_(code) != (_Imm_+i*4))
                    break;
            }
            if (i > 1) {
                recSWBlock(i);
                pc = pc + i*4; continue;
            }
        }*/

        pc+=4; count++;
//        iFlushRegs(0); // test
        recBSC[psxRegs.code>>26]();

        if (branch) {
            branch = 0;
            //if (dump) iDumpBlock(ptr);
            goto done;
        }
    }

    iFlushRegs(pc);
    
    LIW(PutHWRegSpecial(PSXPC), pc);

    iRet();

done:;
#if 0
    MakeDataExecutable(ptr, ((u8*)ppcPtr)-((u8*)ptr));
#else
    a = (u32)(u8*)ptr;
	while(a < (u32)(u8*)ppcPtr) {
	  //__asm__ __volatile__("icbi 0,%0" : : "r" (a));
	  //__asm__ __volatile__("dcbst 0,%0" : : "r" (a));
		__icbi(0,a);
		__dcbst(0,a);
	  a += 4;
	}
	//__asm__ __volatile__("sync");
	//__asm__ __volatile__("isync");
	__emit(0x7c0004ac);//sync
	__emit(0x4C00012C);//isync
#endif
    
#if 1
    sprintf((char *)ppcPtr, "PC=%08x", pcold);
    ppcPtr += strlen((char *)ppcPtr);
#endif

    //mprotect(recMem, RECMEM_SIZE, PROT_EXEC|PROT_READ/*|PROT_WRITE*/);
}


R3000Acpu psxRec = {
    recInit,
    recReset,
    recExecute,
    recExecuteBlock,
    recClear,
    recShutdown
};