#include "fpse.h"
#include "ppc.h"

#include <powerpc/powerpc.h>
#include <powerpc/powerpc_protos.h>

/* Defining this will stop most stdout as well as adding the register cache */
#define USE_REG_CACHE

/*
  PPC Registers:
  0       : Free
  1 - 2   : Do NOT touch
  3 - 4   : Used to pass/get arguments to/from functions
  5 - 9   : Work registers, functions may modify them
  10 - 12 : Used as temporary register cache, functions may modify them
  13      : Do NOT touch
  14 - 29 : Used as register cache, functions do not modify them
  30      : CR stack, also used as R3 stack
  31      : Pointer to R3000 structure

  NOTE: This dynarec is written for a system using the System V ABI, if you
        intend to use it for any other system, like the Mac which uses the
        PowerOpen ABI, some modifications may be required.
*/

#define FETCH_REG 0x01
#define STORE_REG 0x02

#define idxCPR0 35*4
#define idxCCR0 (35+32)*4
#define idxCPR2 (35+64)*4
#define idxCCR2 (35+96)*4
#define idxrLO  33*4
#define idxrHI  34*4

#define COMPILERSIZE (4*1024*1024)

/*
  Register cache, based upon statistics from
  Manta, Trex, Ridge Racer and V-Rally.
*/

#ifdef USE_REG_CACHE
static UINT32 reg_cache[32] = {
  0, 14, 15, 16, 17, 18, 19,  0,
 20, 21, 22,  0,  0,  0, 23, 24,
 25, 26,  0,  0,  0,  0,  0,  0,
 27,  0,  0,  0,  0, 28,  0,  29};
#endif

static void swl(UINT32 adr,UINT32 r)
{
  UINT32 data = read32(adr&~3);
  switch(adr&3) {
    case 3: data = r; break;
    case 2: data = (data & 0xff000000) | (r>> 8); break;
    case 1: data = (data & 0xffff0000) | (r>>16); break;
    case 0: data = (data & 0xffffff00) | (r>>24); break;
  }
  write32(adr&~3,data);
}

static void swr(UINT32 adr,UINT32 r)
{
  UINT32 data = read32(adr&~3);
  switch(adr&3) {
    case 3: data = (data & 0x00ffffff) | (r<<24); break;
    case 2: data = (data & 0x0000ffff) | (r<<16); break;
    case 1: data = (data & 0x000000ff) | (r<< 8); break;
    case 0: data = r; break;
  }
  write32(adr&~3,data);
}

static UINT32 lwl(UINT32 adr,UINT32 r)
{
  UINT32 data = read32(adr&~3);
  switch(adr&3) {
    case 3: r = data; break;
    case 2: r = (r & 0x000000ff) | (data<< 8); break;
    case 1: r = (r & 0x0000ffff) | (data<<16); break;
    case 0: r = (r & 0x00ffffff) | (data<<24); break;
  }
  return r;
}

static UINT32 lwr(UINT32 adr,UINT32 r)
{
  UINT32 data = read32(adr&~3);
  switch(adr&3) {
    case 3: r = (r & 0xffffff00) | (data>>24); break;
    case 2: r = (r & 0xffff0000) | (data>>16); break;
    case 1: r = (r & 0xff000000) | (data>> 8); break;
    case 0: r = data; break;
  }
  return r;
}

#define MAXMEMBLKS 1024

static UINT32 MemBlocksIdx = 0;
static UINT32 MemBlocks[MAXMEMBLKS*2];
static UINT32 *compilebuf;
static UINT32 *realpc;
static UINT32 *pcptr;
static int endflg = 0;

#define COMPILED(adr) realpc[((adr<0xbfc00000) ? ((adr>=0x1f000000 && adr<0x1f020000) ? (adr-0x1f000000+0x280000):(adr&0x1fffff)) : (adr-0xbfc00000+0x200000))/4]
#define NOT_IMPLEMENTED(str) printf("Not implemented %s : %08lx @ %08lx\n", str, code, Pc)

static int SearchFreeSlot()
{
  int x;

  for (x=0; x<MAXMEMBLKS; x++) {
    if (MemBlocks[x*2] > 0x1FFFFFFF) {
      return x;
    }
  }

  x = MemBlocksIdx;
  if (++MemBlocksIdx >= MAXMEMBLKS) {
    MemBlocksIdx = 0;
  }
  if (MemBlocks[x*2] < 0x1FFFFFFF) {
    memset(&COMPILED(MemBlocks[x*2]),0, MemBlocks[x*2+1] - MemBlocks[x*2]);
  }

  return x;
}

void CompileFlush(UINT32 start, UINT32 end)
{
  UINT32 x,mstart,mend;

  if (!compile) {
    return;
  }

  start &= 0x1FFFFFFF;
  end &= 0x1FFFFFFF;

  for (x=0; x<MAXMEMBLKS; x++) {
    mstart = MemBlocks[x*2];
    mend = MemBlocks[x*2+1];

    if ((start >= mstart && start <= mend) || (mstart >= start && mstart <= end)) {
      MemBlocks[x*2] = 0xFFFFFFF;
      MemBlocks[x*2+1] = 0xFFFFFFF;
      memset(&COMPILED(mstart),0,mend-mstart);
    }
  }
}

UINT32 jmppc(UINT32 Pc)
{
  static int first_time = 1;
  int pc,startptr,x;

  if (update_hw()) {
    if ((SR&0x401)==0x401) {
      EPC = Pc;
      CAUSE = 0x400;
      SR = (SR & ~0x3f)| ((SR<<2)&0x3f);
      Pc = 0x80000080;
    }
  }

  switch(Pc) {
  case 0:
    printf("PC = 0 at $%08x\n",(int)(PC));
    break;
#ifndef USE_REG_CACHE
  case 0xa0:
  case 0xb0:
  case 0xc0:
    PC = Pc;
    biosprint(Pc);
    Pc = PC;
    break;
#endif
  case 0x80000080:
    PC = Pc;
    exception_handler();
    Pc = PC;
    break;
  }

  if (COMPILED(Pc)) {
    return COMPILED(Pc);
  }

  if (first_time == 1) {
    /* Set register 31 to point to the R3000 structure */
    *pcptr++ = lis(31, ((UINT32)&reg >> 16));
    *pcptr++ = ori(31, 31, ((UINT32)&reg & 0xffff));

#ifdef USE_REG_CACHE
    /* Init register cache */
    for (x=0; x<32; x++) {
      if (reg_cache[x]) {
        *pcptr++ = lwz(reg_cache[x], (4*x), 31);
      }
    }
#endif
  }

  SETPC(Pc);
  endflg = 0;
  pc = Pc;

  if ((UINT32)pcptr > (UINT32)compilebuf+COMPILERSIZE-1024) {
    /* End of buffer is near, flush all */
    memset(MemBlocks,0xFF,MAXMEMBLKS*2*sizeof(UINT32));
    memset(realpc,0,0x200000+0x80000+0x20000);
    pcptr = compilebuf;
  }

  startptr = (int)pcptr;
  x = SearchFreeSlot();

  while(!endflg) {
    pc = compile1(pc);
  }

  MemBlocks[x*2] = (Pc & 0x1FFFFFFF);
  MemBlocks[x*2+1] = (pc & 0x1FFFFFFF);

  /* Flush the cache */
  SetCache(CACHE_ICACHEINV, 0, 0);
  SetCache(CACHE_DCACHEFLUSH, 0, 0);

  if (first_time == 1) {
    /* Execute the init code as well */
    first_time = 0;
    return (UINT32)compilebuf;
  }

  return COMPILED(Pc);
}

UINT32 regmap[3] = {0, 0, 0};
UINT32 regmod[3] = {0, 0, 0};
UINT32 regptr = 0;

UINT32 get_reg(UINT32 reg, int action) {
  UINT32 retreg;
  int i;

#ifdef USE_REG_CACHE
  if (reg_cache[reg]) {
    return reg_cache[reg];
  }
#endif

  /* Register already mapped ? */
  for (i=0; i<regptr; i++) {
    if (regmap[i] == reg) {
      if (action & STORE_REG) {
        regmod[i] = 1;
      }
      return 10+i;
    }
  }

  if (action & STORE_REG) {
    regmod[regptr] = 1;
  } else {
    regmod[regptr] = 0;
  }

  regmap[regptr] = reg;
  retreg = 10+regptr;

  if (action & FETCH_REG) {
    if (reg == 0) {
      /* Register 0 is ALWAYS 0 */
      *pcptr++ = li(retreg, 0);
    } else {
      *pcptr++ = lwz(retreg, (4*reg), 31);
    }
  }

  regptr++;

  return retreg;
}

void set_reg(void) {
  int i;

  for (i=0; i<regptr; i++) {
    if (regmod[i]) {
      /* Do NEVER write to register 0 */
      if (regmap[i] != 0) {
        *pcptr++ = stw((10+i), (4*regmap[i]), 31);
      }
    }
    regmod[i] = 0;
  }
  regptr = 0;
}

static void compile2(UINT32 Pc)
{
  FPSE_Flags |= IN_SLOT;
  compile1(Pc);
  FPSE_Flags &= ~IN_SLOT;
}

#define reno ((code>>6)&31)

int compile1(UINT32 Pc)
{
  UINT32 code, regt, regd, regs, regval, regsrc, regdst, new_pc;
  cop2func func = NULL;

  if (COMPILED(Pc)) {
    *pcptr++ = lis(6, ((COMPILED(Pc)) >> 16));
    *pcptr++ = ori(6, 6, ((COMPILED(Pc)) & 0xffff));
    *pcptr++ = mtlr(6);
    *pcptr++ = blr();
    Pc += 4;
    endflg = 1;
    return Pc;
  }

  if (!in_slot) {
    COMPILED(Pc) = (long)pcptr;
  }

  code = FETCH(Pc);
  Pc+=4;

  if (code) {
    switch(code>>26) {
    case SPECIAL:
      switch(code&63) {
        case SLL:
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = li(6, reno);
          *pcptr++ = slw(regd, regt, 6);
          set_reg();
          break;

        case SRL:
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = li(6, reno);
          *pcptr++ = srw(regd, regt, 6);
          set_reg();
          break;

        case SRA:
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = li(6, reno);
          *pcptr++ = sraw(regd, regt, 6);
          set_reg();
          break;

        case SLLV:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = slw(regd, regt, regs);
          set_reg();
          break;

        case SRLV:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = srw(regd, regt, regs);
          set_reg();
          break;

        case SRAV:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = sraw(regd, regt, regs);
          set_reg();
          break;

        case ADD:
        case ADDU:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = add(regd, regs, regt);
          set_reg();
          break;

        case SUB:
        case SUBU:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = subf(regd, regt, regs);
          set_reg();
          break;

        case AND:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = and(regd, regs, regt);
          set_reg();
          break;

        case OR:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = or(regd, regs, regt);
          set_reg();
          break;

        case XOR:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = xor(regd, regs, regt);
          set_reg();
          break;

        case NOR:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = nor(regd, regs, regt);
          set_reg();
          break;

        case SLT:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = cmp(0, regs, regt);
          *pcptr++ = mfcr(regd);
          *pcptr++ = rlwinm(regd, regd, 1, 31, 31);
          set_reg();
          break;

        case SLTU:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = subfc(regd, regt, regs);
          *pcptr++ = subfe(regd, regd, regd);
          *pcptr++ = neg(regd, regd);
          set_reg();
          break;

        case DIV:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = divw(6, regs, regt); /* 6 = quotient */
          *pcptr++ = mullw(7, 6, regt);   /* 7 = quotient * divisor */
          *pcptr++ = subf(7, 7, regs);    /* 7 = remainder */
          *pcptr++ = stw(7, idxrHI, 31);
          *pcptr++ = stw(6, idxrLO, 31);
          set_reg();
          break;

        case DIVU:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = divwu(6, regs, regt); /* 6 = quotient */
          *pcptr++ = mullw(7, 6, regt);    /* 7 = quotient * divisor */
          *pcptr++ = subf(7, 7, regs);     /* 7 = remainder */
          *pcptr++ = stw(7, idxrHI, 31);
          *pcptr++ = stw(6, idxrLO, 31);
          set_reg();
          break;

        case MULT:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = mulhw(6, regs, regt);
          *pcptr++ = mullw(7, regs, regt);
          *pcptr++ = stw(6, idxrHI, 31);
          *pcptr++ = stw(7, idxrLO, 31);
          set_reg();
          break;

        case MULTU:
          regs = get_reg(rsno, FETCH_REG);
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = mulhwu(6, regs, regt);
          *pcptr++ = mullw(7, regs, regt);
          *pcptr++ = stw(6, idxrHI, 31);
          *pcptr++ = stw(7, idxrLO, 31);
          set_reg();
          break;

        case MFHI:
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = lwz(regd, idxrHI, 31);
          set_reg();
          break;

        case MFLO:
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = lwz(regd, idxrLO, 31);
          set_reg();
          break;

        case MTHI:
          regs = get_reg(rsno, FETCH_REG);
          *pcptr++ = stw(regs, idxrHI, 31);
          set_reg();
          break;

        case MTLO:
          regs = get_reg(rsno, FETCH_REG);
          *pcptr++ = stw(regs, idxrLO, 31);
          set_reg();
          break;

        case JALR:
          regd = get_reg(rdno, STORE_REG);
          *pcptr++ = lis(regd, ((Pc+4) >> 16));
          *pcptr++ = ori(regd, regd, ((Pc+4) & 0xffff));
          set_reg();
          regs = get_reg(rsno, FETCH_REG);
          *pcptr++ = mr(30, regs);
          set_reg();
          compile2(Pc);
          *pcptr++ = mr(3, 30);
          *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
          *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
          *pcptr++ = mtlr(6);
          *pcptr++ = blrl();
          *pcptr++ = mtlr(3);
          *pcptr++ = blr();
          Pc += 4;
          break;

        case JR:
          endflg = 1;
          regs = get_reg(rsno, FETCH_REG);
          *pcptr++ = mr(30, regs);
          set_reg();
          compile2(Pc);
          *pcptr++ = mr(3, 30);
          *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
          *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
          *pcptr++ = mtlr(6);
          *pcptr++ = blrl();
          *pcptr++ = mtlr(3);
          *pcptr++ = blr();
          Pc += 4;
          break;

        case BREAK:
          *pcptr++ = lis(3, ((E_Bp*4) >> 16));
          *pcptr++ = ori(3, 3, ((E_Bp*4) & 0xffff));
          *pcptr++ = lis(4, ((Pc-4) >> 16));
          *pcptr++ = ori(4, 4, ((Pc-4) & 0xffff));
          *pcptr++ = lis(6, ((UINT32)&exception >> 16));
          *pcptr++ = ori(6, 6, ((UINT32)&exception & 0xffff));
          *pcptr++ = mtlr(6);
          *pcptr++ = blrl();
          *pcptr++ = mtlr(3);
          *pcptr++ = blr();
          break;

        case SYSCALL:
          *pcptr++ = lis(3, ((E_Sys*4) >> 16));
          *pcptr++ = ori(3, 3, ((E_Sys*4) & 0xffff));
          *pcptr++ = lis(4, ((Pc-4) >> 16));
          *pcptr++ = ori(4, 4, ((Pc-4) & 0xffff));
          *pcptr++ = lis(6, ((UINT32)&exception >> 16));
          *pcptr++ = ori(6, 6, ((UINT32)&exception & 0xffff));
          *pcptr++ = mtlr(6);
          *pcptr++ = blrl();
          *pcptr++ = mtlr(3);
          *pcptr++ = blr();
          break;

        default:
          NOT_IMPLEMENTED("SPECIAL");
          break;
        }
      break;

    case BCOND:
      switch(rtno) {
        case BLTZAL:
          regd = get_reg(31, STORE_REG);
          *pcptr++ = lis(regd, ((Pc+4) >> 16));
          *pcptr++ = ori(regd, regd, ((Pc+4) & 0xffff));
          set_reg();

        case BLTZ:
          regs = get_reg(rsno, FETCH_REG);
          *pcptr++ = cmpi(0, regs, 0);
          set_reg();
          *pcptr++ = mfcr(30);
          new_pc = Pc + immS*4;
          compile2(Pc);
          *pcptr++ = mtcr(30);
          *pcptr++ = bc(4, 0, 36); /* BGE */
          *pcptr++ = lis(3, (new_pc >> 16));
          *pcptr++ = ori(3, 3, (new_pc & 0xffff));
          *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
          *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
          *pcptr++ = mtlr(6);
          *pcptr++ = blrl();
          *pcptr++ = mtlr(3);
          *pcptr++ = blr();
          Pc += 4;
          break;

        case BGEZAL:
          regd = get_reg(31, STORE_REG);
          *pcptr++ = lis(regd, ((Pc+4) >> 16));
          *pcptr++ = ori(regd, regd, ((Pc+4) & 0xffff));
          set_reg();

        case BGEZ:
          regs = get_reg(rsno, FETCH_REG);
          *pcptr++ = cmpi(0, regs, 0);
          set_reg();
          *pcptr++ = mfcr(30);
          new_pc = Pc + immS*4;
          compile2(Pc);
          *pcptr++ = mtcr(30);
          *pcptr++ = bc(12, 0, 36); /* BLT */
          *pcptr++ = lis(3, (new_pc >> 16));
          *pcptr++ = ori(3, 3, (new_pc & 0xffff));
          *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
          *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
          *pcptr++ = mtlr(6);
          *pcptr++ = blrl();
          *pcptr++ = mtlr(3);
          *pcptr++ = blr();
          Pc += 4;
          break;

        default:
          NOT_IMPLEMENTED("BCOND");
          break;
      }
      break;

    case J:
      endflg = 1;
      compile2(Pc);
      new_pc = (Pc&0xf0000000)|((code&0x03ffffff)<<2);
      *pcptr++ = lis(3, (new_pc >> 16));
      *pcptr++ = ori(3, 3, (new_pc & 0xffff));
      *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mtlr(3);
      *pcptr++ = blr();
      Pc += 4;
      break;

    case JAL:
      regd = get_reg(31, STORE_REG);
      *pcptr++ = lis(regd, ((Pc+4) >> 16));
      *pcptr++ = ori(regd, regd, ((Pc+4) & 0xffff));
      set_reg();
      compile2(Pc);
      new_pc = (Pc&0xf0000000)|((code&0x03ffffff)<<2);
      *pcptr++ = lis(3, (new_pc >> 16));
      *pcptr++ = ori(3, 3, (new_pc & 0xffff));
      *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mtlr(3);
      *pcptr++ = blr();
      Pc += 4;
      break;

    case BNE:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, FETCH_REG);
      *pcptr++ = cmp(0, regs, regt);
      set_reg();
      *pcptr++ = mfcr(30);
      new_pc = Pc + immS*4;
      compile2(Pc);
      *pcptr++ = mtcr(30);
      *pcptr++ = bc(12, 2, 36); /* BEQ */
      *pcptr++ = lis(3, (new_pc >> 16));
      *pcptr++ = ori(3, 3, (new_pc & 0xffff));
      *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mtlr(3);
      *pcptr++ = blr();
      Pc += 4;
      break;

    case BEQ:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, FETCH_REG);
      *pcptr++ = cmp(0, regs, regt);
      set_reg();
      *pcptr++ = mfcr(30);
      new_pc = Pc + immS*4;
      compile2(Pc);
      *pcptr++ = mtcr(30);
      *pcptr++ = bc(4, 2, 36); /* BNE */
      *pcptr++ = lis(3, (new_pc >> 16));
      *pcptr++ = ori(3, 3, (new_pc & 0xffff));
      *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mtlr(3);
      *pcptr++ = blr();
      Pc += 4;
      break;

    case BLEZ:
      regs = get_reg(rsno, FETCH_REG);
      *pcptr++ = cmpi(0, regs, 0);
      set_reg();
      *pcptr++ = mfcr(30);
      new_pc = Pc + immS*4;
      compile2(Pc);
      *pcptr++ = mtcr(30);
      *pcptr++ = bc(12, 1, 36); /* BGT */
      *pcptr++ = lis(3, (new_pc >> 16));
      *pcptr++ = ori(3, 3, (new_pc & 0xffff));
      *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mtlr(3);
      *pcptr++ = blr();
      Pc += 4;
      break;

    case BGTZ:
      regs = get_reg(rsno, FETCH_REG);
      *pcptr++ = cmpi(0, regs, 0);
      set_reg();
      *pcptr++ = mfcr(30);
      new_pc = Pc + immS*4;
      compile2(Pc);
      *pcptr++ = mtcr(30);
      *pcptr++ = bc(4, 1, 36); /* BLE */
      *pcptr++ = lis(3, (new_pc >> 16));
      *pcptr++ = ori(3, 3, (new_pc & 0xffff));
      *pcptr++ = lis(6, ((UINT32)&jmppc >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&jmppc & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mtlr(3);
      *pcptr++ = blr();
      Pc += 4;
      break;

    case ADDI:
    case ADDIU:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = addi(regt, regs, immS);
      set_reg();
      break;

    case ANDI:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = andi(regt, regs, immU);
      set_reg();
      break;

    case ORI:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = ori(regt, regs, immU);
      set_reg();
      break;

    case XORI:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = xori(regt, regs, immU);
      set_reg();
      break;

    case LUI:
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = lis(regt, immU);
      set_reg();
      break;

    case SLTI:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = li(6, immS);
      *pcptr++ = cmp(0, regs, 6);
      *pcptr++ = mfcr(regt);
      *pcptr++ = rlwinm(regt, regt, 1, 31, 31);
      set_reg();
      break;

    case SLTIU:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, STORE_REG);
      *pcptr++ = li(6, immS);
      *pcptr++ = subfc(regt, 6, regs);
      *pcptr++ = subfe(regt, regt, regt);
      *pcptr++ = neg(regt, regt);
      set_reg();
      break;

    case COP0:
      switch(rsno) {
        case MFC:
          regt = get_reg(rtno, STORE_REG);
          *pcptr++ = lwz(regt, (idxCPR0+(4*rdno)), 31);
          set_reg();
          break;

        case CFC:
          regt = get_reg(rtno, STORE_REG);
          *pcptr++ = lwz(regt, (idxCCR0+(4*rdno)), 31);
          set_reg();
          break;

        case MTC:
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = stw(regt, (idxCPR0+(4*rdno)), 31);
          set_reg();
          break;

        case CTC:
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = stw(regt, (idxCCR0+(4*rdno)), 31);
          set_reg();
          break;

        case 16:
          if ((code&31) == 16) {
            *pcptr++ = lis(6, ((UINT32)&rfe >> 16));
            *pcptr++ = ori(6, 6, ((UINT32)&rfe & 0xffff));
            *pcptr++ = mtlr(6);
            *pcptr++ = blrl();
            break;
          }

        default:
          NOT_IMPLEMENTED("COP0");
          break;
      }
      break;

    case COP2:
      switch(rsno) {
        case MFC:
          func = cop2readfuncptr(rdno);
          if (func) {
            *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
            *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
            *pcptr++ = lis(6, ((UINT32)func >> 16));
            *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
            *pcptr++ = mtlr(6);
            *pcptr++ = blrl();
          }
          regt = get_reg(rtno, STORE_REG);
          *pcptr++ = lwz(regt, (idxCPR2+(4*rdno)), 31);
          set_reg();
          break;

        case CFC:
          func = cop2readfuncptr(rdno+32);
          if (func) {
            *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
            *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
            *pcptr++ = lis(6, ((UINT32)func >> 16));
            *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
            *pcptr++ = mtlr(6);
            *pcptr++ = blrl();
          }
          regt = get_reg(rtno, STORE_REG);
          *pcptr++ = lwz(regt, (idxCCR2+(4*rdno)), 31);
          set_reg();
          break;

        case MTC:
          func = cop2writefuncptr(rdno);
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = stw(regt, (idxCPR2+(4*rdno)), 31);
          set_reg();
          if (func) {
            *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
            *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
            *pcptr++ = lis(6, ((UINT32)func >> 16));
            *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
            *pcptr++ = mtlr(6);
            *pcptr++ = blrl();
          }
          break;

        case CTC:
          func = cop2writefuncptr(rdno+32);
          regt = get_reg(rtno, FETCH_REG);
          *pcptr++ = stw(regt, (idxCCR2+(4*rdno)), 31);
          set_reg();
          if (func) {
            *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
            *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
            *pcptr++ = lis(6, ((UINT32)func >> 16));
            *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
            *pcptr++ = mtlr(6);
            *pcptr++ = blrl();
          }
          break;

        default:
          func = cop2funcptr(code&0x1ffffff);
          if (func) {
            *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
            *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
            *pcptr++ = lis(6, ((UINT32)func >> 16));
            *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
            *pcptr++ = mtlr(6);
            *pcptr++ = blrl();
          }
          break;
      }
      break;

    case SB:
      regval = get_reg(rtno, FETCH_REG);
      regdst = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regdst);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regval);
      *pcptr++ = lis(6, ((UINT32)&write8 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&write8 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case SH:
      regval = get_reg(rtno, FETCH_REG);
      regdst = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regdst);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regval);
      *pcptr++ = lis(6, ((UINT32)&write16 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&write16 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case SW:
      regval = get_reg(rtno, FETCH_REG);
      regdst = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regdst);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regval);
      *pcptr++ = lis(6, ((UINT32)&write32 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&write32 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case SWL:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, FETCH_REG);
      *pcptr++ = mr(3, regs);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regt);
      *pcptr++ = lis(6, ((UINT32)&swl >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&swl & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case SWR:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, FETCH_REG);
      *pcptr++ = mr(3, regs);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regt);
      *pcptr++ = lis(6, ((UINT32)&swr >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&swr & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case LB:
      regsrc = get_reg(rsno, FETCH_REG);
      regdst = get_reg(rtno, STORE_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read8 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read8 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = extsb(3, 3);
      *pcptr++ = mr(regdst, 3);
      set_reg();
      break;

    case LBU:
      regsrc = get_reg(rsno, FETCH_REG);
      regdst = get_reg(rtno, STORE_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read8 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read8 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mr(regdst, 3);
      set_reg();
      break;

    case LH:
      regsrc = get_reg(rsno, FETCH_REG);
      regdst = get_reg(rtno, STORE_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read16 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read16 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = extsh(3, 3);
      *pcptr++ = mr(regdst, 3);
      set_reg();
      break;

    case LHU:
      regsrc = get_reg(rsno, FETCH_REG);
      regdst = get_reg(rtno, STORE_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read16 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read16 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mr(regdst, 3);
      set_reg();
      break;

    case LW:
      regsrc = get_reg(rsno, FETCH_REG);
      regdst = get_reg(rtno, STORE_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read32 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read32 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mr(regdst, 3);
      set_reg();
      break;

    case LWL:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, FETCH_REG|STORE_REG);
      *pcptr++ = mr(3, regs);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regt);
      *pcptr++ = lis(6, ((UINT32)&lwl >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&lwl & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mr(regt, 3);
      set_reg();
      break;

    case LWR:
      regs = get_reg(rsno, FETCH_REG);
      regt = get_reg(rtno, FETCH_REG|STORE_REG);
      *pcptr++ = mr(3, regs);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = mr(4, regt);
      *pcptr++ = lis(6, ((UINT32)&lwr >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&lwr & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = mr(regt, 3);
      set_reg();
      break;

    case SWC0:
      regdst = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regdst);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lwz(4, (idxCPR0+(4*rtno)), 31);
      *pcptr++ = lis(6, ((UINT32)&write32 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&write32 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case LWC0:
      regsrc = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read32 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read32 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = stw(3, (idxCPR0+(4*rtno)), 31);
      set_reg();
      break;

    case SWC2:
      func = cop2readfuncptr(rtno);
      if (func) {
        *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
        *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
        *pcptr++ = lis(6, ((UINT32)func >> 16));
        *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
        *pcptr++ = mtlr(6);
        *pcptr++ = blrl();
      }
      regdst = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regdst);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lwz(4, (idxCPR2+(4*rtno)), 31);
      *pcptr++ = lis(6, ((UINT32)&write32 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&write32 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      set_reg();
      break;

    case LWC2:
      regsrc = get_reg(rsno, FETCH_REG);
      *pcptr++ = mr(3, regsrc);
      *pcptr++ = addi(3, 3, immS);
      *pcptr++ = lis(6, ((UINT32)&read32 >> 16));
      *pcptr++ = ori(6, 6, ((UINT32)&read32 & 0xffff));
      *pcptr++ = mtlr(6);
      *pcptr++ = blrl();
      *pcptr++ = stw(3, (idxCPR2+(4*rtno)), 31);
      set_reg();
      func = cop2writefuncptr(rtno);
      if (func) {
        *pcptr++ = lis(3, ((UINT32)&reg.cpr2[0] >> 16));
        *pcptr++ = ori(3, 3, ((UINT32)&reg.cpr2[0] & 0xffff));
        *pcptr++ = lis(6, ((UINT32)func >> 16));
        *pcptr++ = ori(6, 6, ((UINT32)func & 0xffff));
        *pcptr++ = mtlr(6);
        *pcptr++ = blrl();
      }
      break;

      default:
        NOT_IMPLEMENTED("??");
        break;
    }
  }

  return Pc;
}

void compile_init(void)
{
  compilebuf = malloc(COMPILERSIZE);
  realpc = malloc(0x200000+0x80000+0x20000);
  memset(realpc,0,0x200000+0x80000+0x20000);
  memset(MemBlocks,0xFF,MAXMEMBLKS*2*sizeof(UINT32));
  pcptr = compilebuf;
}