#include "fpse.h"

#undef PRINTF
#undef rs
#undef rt
#undef rd

#define PRINTF  if (1==0) printf
#define   rs   rsno*4
#define   rt   rtno*4
#define   rd   rdno*4

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

#define   PP   0    /* Pentium optimize */

#define COMPILERSIZE    (1024*1024)

typedef void (*funcptr)();

/* runtime function */

int breakpoint = 0; /* 0x1a1c; */
int writebreak = 0; /*xa000e28c;*/
/*
void watch(void)
{
static char *regname[32] = {
     "zero","at","v0","v1","a0","a1","a2","a3",
     "t0","t1","t2","t3","t4","t5","t6","t7",
     "s0","s1","s2","s3","s4","s5","s6","s7",
     "t8","t9","k0","k1","gp","sp","fp","ra"
};

     int i;
     for(i=0;i<32;i++) 
          printf("%-4s = %08x ",regname[i],(int)reg.r[i]);
     printf("\n");
}
*/
static void swl(UINT32 adr,UINT32 r)
{
          UINT32    data = read32(adr&~3);
          switch(adr&3) {
#if TARGET_BIG_ENDIAN
          case 0: data = r; break;
          case 1: data = (data & 0xff000000) | (r>> 8); break;
          case 2: data = (data & 0xffff0000) | (r>>16); break;
          case 3: data = (data & 0xffffff00) | (r>>24); break;
#else
          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;
#endif
          }
          write32(adr&~3,data);
}

static void swr(UINT32 adr,UINT32 r)
{
          UINT32    data = read32(adr&~3);
          switch(adr&3) {
#if TARGET_BIG_ENDIAN
          case 0: data = (data & 0x00ffffff) | (r<<24); break;
          case 1: data = (data & 0x0000ffff) | (r<<16); break;
          case 2: data = (data & 0x000000ff) | (r<< 8); break;
          case 3: data = r; break;
#else
          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;
#endif
          }
          write32(adr&~3,data);
}

static UINT32 lwl(UINT32 adr,UINT32 r)
{
          UINT32    data = read32(adr&~3);
          switch(adr&3) {
#if TARGET_BIG_ENDIAN
          case 0: r = data; break;
          case 1: r = (r & 0x000000ff) | (data<< 8); break;
          case 2: r = (r & 0x0000ffff) | (data<<16); break;
          case 3: r = (r & 0x00ffffff) | (data<<24); break;
#else
          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;
#endif
          }
          return r;
}

static UINT32 lwr(UINT32 adr,UINT32 r)
{
          UINT32    data = read32(adr&~3);
          switch(adr&3) {
#if TARGET_BIG_ENDIAN
          case 0: r = (r & 0xffffff00) | (data>>24); break;
          case 1: r = (r & 0xffff0000) | (data>>16); break;
          case 2: r = (r & 0xff000000) | (data>> 8); break;
          case 3: r = data; break;
#else
          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;
#endif
          }
          return r;
}

#define   NOT_IMPREMENT(cd)   printf("not imprement %s: %08x\n",cd,(int)code);

static int lastax = -1;

#define MAXMEMBLKS      1024

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

#define   COMPILED(adr)  realpc[( (adr<0xbfc00000) ?                          \
                                ((adr>=0x1f000000 && adr<0x1f020000) ?       \
                                 (adr-0x1f000000+0x280000):(adr&0x1fffff)) : \
                                 (adr-0xbfc00000+0x200000) )/4]

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;
}

// int dmact = 0;

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

    if (!compile) return;

    start &= 0x1FFFFFFF;
    end   &= 0x1FFFFFFF;
/*
    if (start == 0x1E000)
    {
        printf("Found CDMA - ACT=%08x-%08x\n",start,end);

        dmact= 1;
    }
*/
    for (x=0; x<MAXMEMBLKS; x++)
    {
        mstart = MemBlocks[x*2];
        mend   = MemBlocks[x*2+1];
/*
        if (dmact) {
            printf("-> %03x) %08x-%08x\n", x, mstart, mend);
            if (x == 0x199)
                printf("reached limit \n");
        }
*/
        if ((start >= mstart && start <= mend) ||
            (mstart >= start && mstart <= end))
        {
//            printf("invalidate blk %08x-%08x\n",mstart,mend);
            MemBlocks[x*2]   = 0xFFFFFFF;
            MemBlocks[x*2+1] = 0xFFFFFFF;
            memset(&COMPILED(mstart),0,mend-mstart);
        }

    }
//    dmact = 0;
}

UINT32 jmppc(UINT32 Pc)
{
    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:
// Oooppsss!! We need also to release everything
// (otherwise DirectDraw will surely kill your PC!)
        printf("PC = 0 at $%08x\n",(int)(PC));
        if (exitflg)
        {
            hw_close();
            memfree();
            exit(-1);
        }
        break;
    case 0xa0:
    case 0xb0:
    case 0xc0:
        PC = Pc;
        biosprint(Pc);
        Pc = PC;
        break;
    case 0x80000080:
        PC = Pc;
        exception_handler();
        Pc = PC;
        break;
    }

    if (COMPILED(Pc)) return COMPILED(Pc);

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

    if (pcptr > 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);

    PRINTF("compile : psx = %08x-%08x(%08x) pc = %08x-%08x(%08x)\n",
           (int)Pc,(int)pc,(int)(pc-Pc),(int)startptr,(int)pcptr,(int)(pcptr-startptr));

    return COMPILED(Pc);
}

#if 1

#define   GENCODE1(c0)   *pcptr++=c0
#define   GENCODE2(c0,c1)     pcptr[0]=c0;pcptr[1]=c1; pcptr+=2
#define   GENCODE3(c0,c1,c2)  pcptr[0]=c0;pcptr[1]=c1;pcptr[2]=c2; pcptr+=3
#define   GENCODE4(c0,c1,c2,c3)    pcptr[0]=c0;pcptr[1]=c1;pcptr[2]=c2;pcptr[3]=c3;pcptr+=4
#define   GENCODE1i(c0,imm)   pcptr[0]=c0; *(long*)&pcptr[1]=(long)imm; pcptr+=5
#define   GENCODE2i(c0,c1,imm)     pcptr[0]=c0;pcptr[1]=c1; *(long*)&pcptr[2]=(long)imm; pcptr+=6
#define   GENCODE3i(c0,c1,c2,imm)  pcptr[0]=c0;pcptr[1]=c1;pcptr[2]=c2; *(long*)&pcptr[3]=(long)imm; pcptr+=7

//#define DISASM(code,pc)
#define   DISASM    if (verbose) disasm2
static void disasm2(long code,long addr)
{
     char buf[256];
     disasm(buf,code,addr);
     printf(";%08lx %08lx   %s\n",addr,code,buf);
}

#else

#define   DISASM    if (verbose) disasm2
static void disasm2(long code,long addr)
{
     char buf[256];
     disasm(buf,code,addr);
     printf(";%08lx %08lx   %s\n",addr,code,buf);
}

static void GENCODE1(unsigned char  c0)
{
     PRINTF(" %p %02x                ",pcptr,c0);
     *pcptr++=c0;
}
static void GENCODE2(unsigned char  c0,unsigned char  c1)
{
     PRINTF(" %p %02x %02x             ",pcptr,c0,c1);
     pcptr[0]=c0;pcptr[1]=c1;
     pcptr+=2;
}
static void GENCODE3(unsigned char  c0,unsigned char  c1,unsigned char  c2)
{
     PRINTF(" %p %02x %02x %02x          ",pcptr,c0,c1,c2);
     pcptr[0]=c0;pcptr[1]=c1;pcptr[2]=c2;
     pcptr+=3;
}
static void GENCODE4(unsigned char  c0,unsigned char  c1,unsigned char  c2,unsigned char  c3)
{
     PRINTF(" %p %02x %02x %02x %02x       ",pcptr,c0,c1,c2,c3);
     pcptr[0]=c0;pcptr[1]=c1;pcptr[2]=c2;pcptr[3]=c3;
     pcptr+=4;
}

static void GENCODE1i(unsigned char  c0,int imm)
{
     PRINTF(" %p %02x %08x       ",pcptr,c0,imm);
     pcptr[0]=c0;
     *(long*)&pcptr[1] = imm;
     pcptr+=5;
}
static void GENCODE2i(unsigned char  c0,unsigned char  c1,int imm)
{
     PRINTF(" %p %02x %02x %08x    ",pcptr,c0,c1,imm);
     pcptr[0]=c0;pcptr[1]=c1;
     *(long*)&pcptr[2] = imm;
     pcptr+=6;
}
static void GENCODE3i(unsigned char  c0,unsigned char  c1,unsigned char  c2,int imm)
{
     PRINTF(" %p %02x %02x %02x %08x ",pcptr,c0,c1,c2,imm);
     pcptr[0]=c0;pcptr[1]=c1;pcptr[2]=c2;
     *(long*)&pcptr[3] = imm;
     pcptr+=7;
}


#endif

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;
}

static void load_ax(int r)
{
     if (!r) {
          GENCODE2(0x31,0xc0);
          PRINTF("xor eax,eax\n");
     } else {
          GENCODE3(0x8b,0x46,r);
          PRINTF("mov eax,[esi+%d]\n",r);
     }
     lastax = r;
}

#if 0
static void load_dx(int r)
{
     GENCODE3(0x8b,0x56,r);
     PRINTF("mov edx,[esi+%d]\n",r);
}
#endif

static void store_ax(int r)
{
     if (!r) return;
     GENCODE3(0x89,0x46,r);
     PRINTF("mov [esi+%d],eax\n",r);
     //isimm[r/4] = 0;
     lastax = r;
}

static void store_imm(int r,int imm)
{
     GENCODE3i(0xc7,0x46,r,imm);
     PRINTF("mov dword [esi+%d],0%xh\n",r,imm);
     //isimm[r/4] = imm;
     lastax = -1;
}

static void push_imm(int imm)
{
     GENCODE1i(0x68,imm);
     PRINTF("push 0%xh\n",imm);
}

static void push_ax(void)
{
     GENCODE1(0x50);
     PRINTF("push eax\n");
}

static void push_r(int r)
{
     if (!r) {
          load_ax(r);
          push_ax();
     } else {
#if PP
          load_ax(r);
          push_ax();
#else
          GENCODE3(0xff,0x76,r);
          PRINTF("push dword [esi+%d]\n",r);
#endif
     }
}

/* ofset > 127 */
static void load2_ax(int r)
{
     GENCODE2i(0x8b,0x86,r);
     PRINTF("mov eax,[esi+%d]\n",r);
}

static void store2_ax(int r)
{
     GENCODE2i(0x89,0x86,r);
     PRINTF("mov [esi+%d],eax\n",r);
}

static void store2_dx(int r)
{
     GENCODE2i(0x89,0x96,r);
     PRINTF("mov [esi+%d],edx\n",r);
}

static void push2_r(int r)
{
     GENCODE2i(0xff,0xb6,r);
     PRINTF("push dword [esi+%d]\n",r);
}


static void alu_i(UINT32 code,char *str,int op_r,int op_m)
{
     int imm = immU;
/*
     05 ii ii ii ii add  eax,imm
     81 46 xx ii ii ii ii add dword [esi+rt],imm
     25 ii ii ii ii and  eax,imm
     81 66 xx ii ii ii ii and dword [esi+rt],imm
     0d ii ii ii ii or   eax,imm
     81 4e xx ii ii ii ii or  dword [esi+rt],imm
     35 ii ii ii ii xor  eax,imm
     81 76 xx ii ii ii ii xor dword [esi+rt],imm
     3d ii ii ii ii cmp  eax,imm
     81 7e xx ii ii ii ii cmp dword [esi+rt],imm
     b8 ii ii ii ii mov  eax,imm
     c7 46 xx ii ii ii ii mov dword [esi+rt],imm
*/

     /* 8b 46 xx mov eax,[esi+xx] */
     /* 89 46 xx mov [esi+xx],eax */

     if (!rt) return;

     if (rs) {
          if (rs==rt) {
               GENCODE3i(0x81,op_m,rs,imm);
               PRINTF("%s dword [esi+%d],0%xh\n",str,(int)rs,imm);
               /*if (rt==lastax)*/ lastax=-1;
          } else {
               load_ax(rs);
               GENCODE1i(op_r,imm);
               PRINTF("%s eax,%d\n",str,imm);
               store_ax(rt);
          }
     } else {
          store_imm(rt,imm);
     }
}

static void add_imm(int imm)
{
     if (!imm) return;

     if (imm<-128 || imm>127) {
          GENCODE1i(0x05,imm);
          PRINTF("add eax,0%xh\n",imm);
     } else if (imm==1) {
          GENCODE1(0x40);
          PRINTF("inc eax\n");
     } else if (imm==-1) {
          GENCODE1(0x48);
          PRINTF("dec eax\n");
     } else {
          GENCODE3(0x8d,0x40,imm);
          PRINTF("lea eax,[eax+%d]\n",imm);
     }
}

static void add_i(UINT32 code)
{
/*
     05 ii ii ii ii add  eax,imm
     81 46 xx ii ii ii ii add dword [esi+rt],imm
*/
     int imm = immS;

     if (!rt) return;

     if (rs) {
          if (rs==rt) {
               if (imm==1) {
                    GENCODE3(0xff,0x46,rs);
                    PRINTF("inc [esi+%d]\n",(int)rs);
               } else if (imm==-1) {
                    GENCODE3(0xff,0x4e,rs);
                    PRINTF("dec [esi+%d]\n",(int)rs);
               } else if (imm) {
                    GENCODE3i(0x81,0x46,rs,imm);
                    PRINTF("add dword [esi+%d],0%xh\n",(int)rs,imm);
               }
          } else {
               load_ax(rs);
               add_imm(imm);
               store_ax(rt);
          }
     } else {
          store_imm(rt,imm);
     }
}

static void alu_r(UINT32 code,char *str,int op_1,int op_2)
{
/*
     03 46 xx add   eax,[esi+rt]
     01 46 xx add   [esi+rt],eax
     2b 46 xx sub   eax,[esi+rt]
     29 46 xx sub   [esi+rt],eax
     23 46 xx and   eax,[esi+rt]
     21 46 xx and   [esi+rt],eax
     0b 46 xx or    eax,[esi+rt]
     09 46 xx or    [esi+rt],eax
     33 46 xx xor   eax,[esi+rt]
     31 46 xx xor   [esi+rt],eax
     3b 46 xx cmp   eax,[esi+rt]
     39 46 xx cmp   [esi+rt],eax
*/
     if (!rd) return;

     if (rs) {
#if !PP
          if (rs==rd) {
               load_ax(rt);
               GENCODE3(op_2,0x46,rd);
               PRINTF("%s [esi+%d],eax\n",str,(int)rd);
               //isimm[rdno] = 0;
               return;
          } else
#endif
          {
               load_ax(rs);
               if (rt) {
                    GENCODE3(op_1,0x46,rt);
                    PRINTF("%s eax,[esi+%d]\n",str,(int)rt);
               }
          }
     } else if (rt) {
          load_ax(rt);
     } else {
          store_imm(rd,0);
          return;
     }
     store_ax(rd);
}

static void subu_r(UINT32 code)
{
     if (!rd) return;

     if (rs) {
          if (rs==rd) {
               if (rt) {
                    load_ax(rt);
                    GENCODE3(0x29,0x46,rd);
                    PRINTF("sub [esi+%d],eax\n",(int)rd);
                    //isimm[rdno] = 0;
               }
               return;
          }
          load_ax(rs);
          if (rt) {
               GENCODE3(0x2b,0x46,rt);
               PRINTF("sub eax,[esi+%d]\n",(int)rt);
          }
     } else if (rt) {
          load_ax(rt);
          GENCODE2(0xf7,0xd8);
          PRINTF("neg eax\n");
     } else {
          store_imm(rd,0);
          return;
     }
     store_ax(rd);
}

static void nor_r(UINT32 code)
{
     /* f7 d0 not eax */
     if (!rd) return;

     if (rs) {
          load_ax(rs);
          if (rt) {
               GENCODE3(0x0b,0x46,rt);
               PRINTF("or eax,[esi+%d]\n",(int)rt);
          }
     } else if (rt) {
          load_ax(rt);
     } else {
          store_imm(rd,-1);
          return;
     }
     GENCODE2(0xf7,0xd0);
     PRINTF("not eax\n");
     store_ax(rd);
}

static void and_r(UINT32 code)
{
     if (rd) {
          if (rs && rt) {
               load_ax(rs);
               GENCODE3(0x23,0x46,rt);
               PRINTF("and eax,[esi+%d]\n",(int)rt);
               store_ax(rd);
          } else {
               store_imm(rd,0);
          }
     }
}

/*
     50 push   eax
     ff 76 xx push  dword [esi+xx]
     40 inc    eax
     48 dec    eax
     ff 46 xx inc   dword [esi+rs]
     ff 4e xx dec   dword [esi+rs]
     8d 40 xx lea   eax,[eax+imm]
     31 c0 xor eax,eax
     f7 d8 neg eax
*/

/*
     9c pushf
     9d popf
     e9 xx xx xx xx jmp
     eb xx jmp last
     74 xx je  last
     75 xx jne last
     7c xx jl  last
     7d xx jge last
     7e xx jle last
     7f xx jg  last
*/

static void cmp_i(UINT32 code,int imm)
{
     GENCODE3i(0x81,0x7e,rs,imm);
     PRINTF("cmp [esi+%d],0%xh\n",(int)rs,imm);
}

static void cmp_r(UINT32 code)
{
     if (!rt) { cmp_i(code,0); return; }
     load_ax(rs);

     GENCODE3(0x3b,0x46,rt);
     PRINTF("cmp eax,[esi+%d]\n",(int)rt);
}

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

static void push_addr(UINT32 code)
{
     if (rs==0) {
          push_imm(immS);
     } else 
     if (immS) {
          load_ax(rs);
          add_imm(immS);
          push_ax();
     } else {
          push_r(rs);
     }
}

static void call(char *str,funcptr func,int argn)
{
/*
     b8 xx xx xx xx    mov eax,xx
     ff e0             jmp eax
    ff 20             jmp [eax]
     ff d0             call eax
    ff 10             call [eax]
     ff 15 xx xx xx xx call [xx]
     e8 xx xx xx xx    call xx (offset)
     81 c4 xx xx xx xx add esp,2
     8d 64 24 xx lea esp,[esp+2]
*/

     GENCODE1i(0xe8,(UINT32)func-(UINT32)(pcptr+5));
     PRINTF("call %s\n",str);

/*
     59 pop ecx
     5a pop edx
*/
     lastax = -1;
     if (argn==4) {
          GENCODE1(0x5a);
          PRINTF("pop edx\n");
     } else if (argn==8) {
          GENCODE1(0x5a);
          PRINTF("pop edx\n");
          GENCODE1(0x5a);
          PRINTF("pop edx\n");
     } else
     if (argn) {
#if 0
          GENCODE4(0x8d,0x64,0x24,argn);
          PRINTF("lea esp,[esp+%d]\n",argn);
#else
          GENCODE2i(0x81,0xc4,argn);
          PRINTF("add esp,%d\n",argn);
#endif
     }
}

static void jump(void)
{
     call("jmppc",(funcptr)jmppc,4); /* 5 + 5 */
     GENCODE2(0xff,0xe0); /* 2 */
     PRINTF("jmp eax\n");
     //endflg = 1;
}

static void jump_imm(UINT32 newpc)
{
     push_imm(newpc);
     jump();
}

static void bxx(UINT32 code,UINT32 Pc,char *str,int op)
{
     UINT32 newpc;
     char *base;

     if (FETCH(Pc)==0) {
          /* nop */
          PRINTF("%08x %08x %s\n",(int)Pc,0,"nop");
     } else { 
          GENCODE1(0x9c);
          PRINTF("pushf\n");
          compile2(Pc);
          GENCODE1(0x9d);
          PRINTF("popf\n");
     } 

     GENCODE2(op,0);
     PRINTF("%s @skip\n",str);
     base = pcptr;

     newpc = Pc + immS*4;
#if 0
     if (COMPILED(newpc)) {
          GENCODE1i(0xe9,COMPILED(newpc)-(UINT32)(pcptr+5));
          PRINTF("jmp %p\n",COMPILED(newpc));
     } else 
#endif
     {
          push_imm(newpc); /* 5 */
          jump(); /* 5 + 1 + 2 */
     }
     base[-1] = (UINT32)pcptr-(UINT32)base;
     PRINTF("@skip:\n");
/*
          push_imm(PC+4);
          jump();
          endflg = 1;
*/
}

static void store_pc(int pc)
{
     /* デバッグ用  */
     GENCODE2i(0xc7,0x86,rPC);
     PRINTF("mov dword [esi+%d],0%xh\n",rPC,pc-4);
     PRINTF(" %p %08x\n",pcptr,pc-4);
     *(long*)pcptr = pc-4;
     pcptr+=4;
}

static void store(UINT32 code,char *str,funcptr func)
{
     push_r(rt);
     push_addr(code);
     call(str,func,8);
}

static void load(UINT32 code,char *str,funcptr func)
{
     push_addr(code);
     call(str,func,4);
}

void sxx(UINT32 code,char *str,int op_r,int op_m)
{
     /* 8b 46 xx mov eax,[esi+xx] */
     /* c1 e0 xx shl eax,xx */
     /* c1 e8 xx shr eax,xx */
     /* c1 f8 xx sar eax,xx */
     /* c1 66 xx yy shl [esi+xx],yy */
     /* c1 6e xx yy shr [esi+xx],yy */
     /* c1 7e xx yy sar [esi+xx],yy */
     /* 89 46 xx mov [esi+xx],eax */

     int shamt = (code>>6)&31;
     if (rd && shamt) {
          if (rd==rt) {
               GENCODE4(0xc1,op_m,rd,shamt);
               PRINTF("%s dword [esi+%d],%d\n",str,(int)rd,shamt);
               //isimm[rdno] = 0;
               /*if (rd==lastax)*/ lastax = -1;
          } else {
               load_ax(rt);
               GENCODE3(0xc1,op_r,shamt);
               PRINTF("%s eax,%d\n",str,shamt);
               store_ax(rd);
          }
     }
}

void sxxv(UINT32 code,char *str,int op_r,int op_m)
{
     /* 8b 46 xx mov eax,[esi+xx] */
     /* 8b 4e xx mov ecx,[esi+xx] */
     /* 8a 4e xx mov cl,[esi+xx] */
     /* d3 e0    shl eax,cl */
     /* d3 e8    shr eax,cl */
     /* d3 f8    sar eax,cl */
     /* d3 66 xx shl [esi+xx],cl */
     /* d3 6e xx shr [esi+xx],cl */
     /* d3 7e xx sar [esi+xx],cl */
     /* 89 46 xx mov [esi+xx],eax */

     if (rd && rs) {
          GENCODE3(0x8b,0x4e,rs);
          PRINTF("mov ecx,[esi+%d]\n",(int)rs);
          if (rd==rt) {
               GENCODE3(0xd3,op_m,rd);
               PRINTF("%s dword [esi+%d],cl\n",str,(int)rd);
               /*if (rd==lastax)*/ lastax = -1;
          } else {
               load_ax(rt);
               GENCODE2(0xd3,op_r);
               PRINTF("%s eax,cl\n",str);
               store_ax(rd);
          }
     }
}

static void cop2gen(cop2func func)
{
     if (!func) return;
     GENCODE2i(0x8d,0x86,idxCPR2);
     PRINTF("lea eax,[esi+CPR2]\n");
     push_ax();
     call("cop2func",(funcptr)func,4);
}

int compile1(UINT32 Pc)
{
     UINT32 code;
//   static char strbuf[256];

     if (COMPILED(Pc)) {
          PRINTF("JUMP %x %x %x\n",(int)Pc,(int)pcptr,(int)COMPILED(Pc));
          GENCODE1i(0xe9,COMPILED(Pc)-(UINT32)(pcptr+5));
          PRINTF("jmp %p\n",(void *)COMPILED(Pc));
          Pc+=4;
          endflg = 1;
          return Pc;
     }

     if (!in_slot) COMPILED(Pc) = (long)pcptr;
        if (!(FPSE_Flags & SET_ESI)) {
            GENCODE1i(0xBE,(int)&reg);
            PRINTF("mov esi,reg\n");
            FPSE_Flags |= SET_ESI;
        }

/*
     if (Pc==breakpoint) {
          call("watch",watch,0);
     }
*/

     code = FETCH(Pc);

     DISASM(code,Pc);

     Pc+=4;
     if (code)
     switch(code>>26) {

     case SPECIAL:
          switch(code&63) {
     /* c1 e0 xx shl eax,xx */
     /* c1 e8 xx shr eax,xx */
     /* c1 f8 xx sar eax,xx */
     /* c1 66 xx yy shl [esi+xx],yy */
     /* c1 6e xx yy shr [esi+xx],yy */
     /* c1 7e xx yy sar [esi+xx],yy */

          case SLL: sxx(code,"shl",0xe0,0x66); break;
          case SRL: sxx(code,"shr",0xe8,0x6e); break;
          case SRA: sxx(code,"sar",0xf8,0x7e); break;

     /* d3 e0    shl eax,cl */
     /* d3 e8    shr eax,cl */
     /* d3 f8    sar eax,cl */
     /* d3 66 xx shl [esi+xx],cl */
     /* d3 6e xx shr [esi+xx],cl */
     /* d3 7e xx sar [esi+xx],cl */

          case SLLV:     sxxv(code,"shl",0xe0,0x66); break;
          case SRLV:     sxxv(code,"shr",0xe8,0x6e); break;
          case SRAV:     sxxv(code,"sar",0xf8,0x7e); break;

/*
     03 46 xx add   eax,[esi+rt]
     01 46 xx add   [esi+rt],eax
     2b 46 xx sub   eax,[esi+rt]
     29 46 xx sub   [esi+rt],eax
     23 46 xx and   eax,[esi+rt]
     21 46 xx and   [esi+rt],eax
     0b 46 xx or    eax,[esi+rt]
     09 46 xx or    [esi+rt],eax
     33 46 xx xor   eax,[esi+rt]
     31 46 xx xor   [esi+rt],eax
*/

          case ADD: alu_r(code,"add",0x03,0x01); break;
          case ADDU:     alu_r(code,"add",0x03,0x01); break;
          case SUBU:     subu_r(code); break;
          case SUB: subu_r(code); break;
          case AND: and_r(code); break;
          case OR:  alu_r(code,"or" ,0x0b,0x09); break;
          case XOR: alu_r(code,"xor",0x33,0x31); break;
          case NOR: nor_r(code); break;

          case SLT:
               if (rd==0) break;
               cmp_r(code);
               GENCODE3(0xf,0x9c,0xc0);
               PRINTF("setl al\n");
               GENCODE1i(0x25,1);
               PRINTF("and eax,1\n");
               store_ax(rd);
               break;
          case SLTU:
               if (rd==0) break;
               cmp_r(code);
               GENCODE3(0xf,0x92,0xc0);
               PRINTF("setb al\n");
               GENCODE1i(0x25,1);
               PRINTF("and eax,1\n");
               store_ax(rd);
               break;
          case DIV:
               load_ax(rs);
               GENCODE1(0x99);
               PRINTF("cdq\n");
               GENCODE3(0xf7,0x7e,rt);
               PRINTF("idiv dword [esi+%d]\n",(int)rt);
               store2_ax(idxrLO);
               store2_dx(idxrHI);
               lastax = -1;
               break;
          case DIVU:
               load_ax(rs);
               GENCODE2(0x31,0xd2);
               PRINTF("xor edx,edx\n");
               GENCODE3(0xf7,0x76,rt);
               PRINTF("div dword [esi+%d]\n",(int)rt);
               store2_ax(idxrLO);
               store2_dx(idxrHI);
               lastax = -1;
               break;
          case MULT:
               load_ax(rs);
               GENCODE3(0xf7,0x6e,rt);
               PRINTF("imul dword [esi+%d]\n",(int)rt);
               store2_ax(idxrLO);
               store2_dx(idxrHI);
               lastax = -1;
               break;
          case MULTU:
               load_ax(rs);
               GENCODE3(0xf7,0x66,rt);
               PRINTF("mul dword [esi+%d]\n",(int)rt);
               store2_ax(idxrLO);
               store2_dx(idxrHI);
               lastax = -1;
               break;
          case MFHI: if (rd) { load2_ax(idxrHI); store_ax(rd); } break;
          case MFLO: if (rd) { load2_ax(idxrLO); store_ax(rd); } break;
          case MTHI: load_ax(rs); store2_ax(idxrHI); break;
          case MTLO: load_ax(rs); store2_ax(idxrLO); break;

          case JALR:
               store_imm(31*4,Pc+4);
               push_r(rs);
               compile2(Pc);
               jump();
               Pc+=4;
               break;
          case JR   :
               endflg = 1;
               push_r(rs);
               compile2(Pc);
               jump();
               Pc+=4;
               break;
          case BREAK:
               push_imm(Pc-4);
               push_imm(E_Bp*4);
               call("exception",(funcptr)exception,8);
               GENCODE2(0xff,0xe0);
               PRINTF("jmp eax\n");
               break;
          case SYSCALL:
               push_imm(Pc-4);
               push_imm(E_Sys*4);
               call("exception",(funcptr)exception,8);
               GENCODE2(0xff,0xe0);
               PRINTF("jmp eax\n");
               break;
          default:  NOT_IMPREMENT("special"); break;
          }
          break;

     case BCOND:
          switch(rtno){
          case BLTZAL:store_imm(31*4,Pc+4);
          case BLTZ:     cmp_i(code,0); bxx(code,Pc,"jge",0x7d); Pc+=4; break;
          case BGEZAL:store_imm(31*4,Pc+4);
          case BGEZ:     cmp_i(code,0); bxx(code,Pc,"jl",0x7c); Pc+=4; break;
          default:  NOT_IMPREMENT("bcond"); break;
          }
          break;

     /* BRANCH/JUMP */
     case J:
          endflg = 1;
          compile2(Pc);
          jump_imm((Pc&0xf0000000)|((code&0x03ffffff)<<2));
          Pc+=4;
          break;
     case JAL:
          store_imm(31*4,Pc+4);
          compile2(Pc);
          jump_imm((Pc&0xf0000000)|((code&0x03ffffff)<<2));
          Pc+=4;
          break;
     case BNE: cmp_r(code); bxx(code,Pc,"je",0x74); Pc+=4; break;
     case BEQ:
          if (rs==0 && rt==0) {
               compile2(Pc);
               jump_imm(Pc+immS*4);
               endflg = 1;
          } else {
               cmp_r(code); bxx(code,Pc,"jne",0x75);
          }
          Pc+=4;
          break;
     case BLEZ:     cmp_i(code,0); bxx(code,Pc,"jg",0x7f); Pc+=4; break;
     case BGTZ:     cmp_i(code,0); bxx(code,Pc,"jle",0x7e); Pc+=4; break;

     /* ALU */
/*
     05 ii ii ii ii add  eax,imm
     81 46 xx ii ii ii ii add dword [esi+rt],imm
     25 ii ii ii ii and  eax,imm
     81 66 xx ii ii ii ii and dword [esi+rt],imm
     0d ii ii ii ii or   eax,imm
     81 4e xx ii ii ii ii or  dword [esi+rt],imm
     35 ii ii ii ii xor  eax,imm
     81 76 xx ii ii ii ii xor dword [esi+rt],imm
     3d ii ii ii ii cmp  eax,imm
     81 7e xx ii ii ii ii cmp dword [esi+rt],imm
     b8 ii ii ii ii mov  eax,imm
*/
     case ADDI:     add_i(code); break;
     case ADDIU:    add_i(code); break;
     case ANDI:     alu_i(code,"and",0x25,0x66); break;
     case ORI: alu_i(code,"or" ,0x0d,0x4e); break;
     case XORI:     alu_i(code,"xor",0x35,0x76); break;
     case LUI:
          {
          UINT32 imm = code<<16;
          if (!in_slot) {
               UINT32 code2 = FETCH(Pc);
            if (((code2>>16)&0x3ff) == ((rtno<<5)|rtno)) {
               switch(code2>>26) {
               case ORI:
               case XORI:
                    DISASM(code2,Pc);
                    Pc+=4;
                    imm |= code2&0xffff;
                    break;
               case ADDI:
               case ADDIU:
                    DISASM(code2,Pc);
                    Pc+=4;
                    imm += (INT16)code2;
                    break;
               }
            }
          }
          store_imm(rt,imm);
          }
          break;
     case SLTI:
          cmp_i(code,immS);
          GENCODE3(0xf,0x9c,0xc0);
          PRINTF("setl al\n");
          GENCODE1i(0x25,1);
          PRINTF("and eax,1\n");
          store_ax(rt);
          break;
     case SLTIU:
          cmp_i(code,immS);
          GENCODE3(0xf,0x92,0xc0);
          PRINTF("setb al\n");
          GENCODE1i(0x25,1);
          PRINTF("and eax,1\n");
          store_ax(rt);
          break;

     /* COP */
     case COP0:
          switch(rsno) {
          case MFC: load2_ax(idxCPR0+rd); store_ax(rt); break; /* MFC */
          case CFC: load2_ax(idxCCR0+rd); store_ax(rt); break; /* CFC */
          case MTC: load_ax(rt); store2_ax(idxCPR0+rd); break; /* MTC */
          case CTC: load_ax(rt); store2_ax(idxCCR0+rd); break; /* CTC */
          case 16:
               if ((code&31)==16) { /* RFE */
                    call("rfe",(funcptr)rfe,0);
                    break;
               }
          default: NOT_IMPREMENT("COP0");    break;
          }
          break;
     case COP2:
          switch(rsno) {
          case MFC:
               cop2gen(cop2readfuncptr(rdno));
               load2_ax(idxCPR2+rd); store_ax(rt);
               break; /* MFC */
          case CFC:
               cop2gen(cop2readfuncptr(rdno+32));
               load2_ax(idxCCR2+rd); store_ax(rt);
               break; /* CFC */
          case MTC:
               load_ax(rt); store2_ax(idxCPR2+rd);
               cop2gen(cop2writefuncptr(rdno));
               break; /* MTC */
          case CTC:
               load_ax(rt); store2_ax(idxCCR2+rd);
               cop2gen(cop2writefuncptr(rdno+32));
               break; /* CTC */
          default:
               cop2gen(cop2funcptr(code&0x1ffffff));
               break;
          }
          break;

     /* LOAD/STORE */
/*
     8b 46 xx mov   eax,[esi+xx]
     8b 56 xx mov   edx,[esi+xx]
     03 05 ii ii ii ii add    eax,[imm]
     8b 80 ii ii ii ii mov    eax,[eax+imm2]
     8a 80 ii ii ii ii mov    al,[eax+imm2]
     66 8b ii ii ii ii mov    ax,[eax+imm2]
     0f b6 80 ii ii ii ii movzx    eax,byte [eax+imm2]
     0f b7 80 ii ii ii ii movzx    eax,word [eax+imm2]
     0f be 80 ii ii ii ii movsx    eax,byte [eax+imm2]
     0f bf 80 ii ii ii ii movsx    eax,word [eax+imm2]
     89 80 ii ii ii ii mov    [eax+imm2],eax
     66 89 80 ii ii ii ii mov [eax+imm2],ax
     88 80 ii ii ii ii mov    [eax+imm2],al
     89 82 ii ii ii ii mov    [edx+imm2],eax
     66 89 82 ii ii ii ii mov [edx+imm2],ax
     88 82 ii ii ii ii mov    [edx+imm2],al
*/

     case SB:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_dx(rs);
               load_ax(rt);
               GENCODE2i(0x88,0x82,base);
               PRINTF("mov [edx+0%xh],al\n",base);
               break;
          } }
#endif
          store(code,"write8",(funcptr)write8); break;
     case SH:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_dx(rs);
               load_ax(rt);
               GENCODE3i(0x66,0x89,0x82,base);
               PRINTF("mov [edx+0%xh],ax\n",base);
               break;
          } }
#endif
          store(code,"write16",(funcptr)write16); break;
     case SW:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_dx(rs);
               load_ax(rt);
               GENCODE2i(0x89,0x82,base);
               PRINTF("mov [edx+0%xh],eax\n",base);
               break;
          } }
#endif
          store(code,"write32",(funcptr)write32); break;
     case SWL: store(code,"swl",(funcptr)swl); break;
     case SWR: store(code,"swr",(funcptr)swr); break;

     case LB:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_ax(rs);
               GENCODE3i(0x0f,0xbe,0x80,base);
               PRINTF("movsx eax,byte [eax+0%xh]\n",base);
               store_ax(rt);
               break;
          } }
#endif
          load(code,"read8",(funcptr)read8);
          if (rt) {
               GENCODE3(0x0f,0xbe,0xc0); PRINTF("movsx eax,al\n");
               store_ax(rt);
          }
          break;
     case LBU:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_ax(rs);
               GENCODE3i(0x0f,0xb6,0x80,base);
               PRINTF("movzx eax,word [eax+0%xh]\n",base);
               store_ax(rt);
               break;
          } }
#endif
          load(code,"read8",(funcptr)read8);
          store_ax(rt);
          break;
     case LH:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_ax(rs);
               GENCODE3i(0x0f,0xbf,0x80,base);
               PRINTF("movsx eax,word [eax+0%xh]\n",base);
               store_ax(rt);
               break;
          } }
#endif
          load(code,"read16",(funcptr)read16);
          if (rt) {
               GENCODE3(0x0f,0xbf,0xc0); PRINTF("movsx eax,ax\n");
               store_ax(rt);
          }
          break;
     case LHU:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_ax(rs);
               GENCODE3i(0x0f,0xb7,0x80,base);
               PRINTF("movzx eax,word [eax+0%xh]\n",base);
               store_ax(rt);
               break;
          } }
#endif
          load(code,"read16",(funcptr)read16);
          store_ax(rt);
          break;

     case LW:
          store_pc(Pc);
#if 0
          { int base;
          if (isimm[rsno] && (base = (int)baseaddr2(isimm[rsno]+immS)+immS)!=0) {
               load_ax(rs);
               GENCODE2i(0x8b,0x80,base);
               PRINTF("mov eax,[eax+0%xh]\n",base);
               store_ax(rt);
               break;
          } }
#endif
          load(code,"read32",(funcptr)read32);
          store_ax(rt);
          break;
     case LWL:
          push_r(rt);
          push_addr(code);
          call("lwl",(funcptr)lwl,8);
          store_ax(rt);
          break;
     case LWR:
          push_r(rt);
          push_addr(code);
          call("lwr",(funcptr)lwr,8);
          store_ax(rt);
          break;
     case SWC0:
          push2_r(rt+idxCPR0);
          push_addr(code);
          call("write32",(funcptr)write32,8);
          lastax = -1;
          break;
     case LWC0:
          load(code,"read32",(funcptr)read32);
          store2_ax(rt+idxCPR0);
          break;
     case SWC2:
          cop2gen(cop2readfuncptr(rtno));
          push2_r(rt+idxCPR2);
          push_addr(code);
          call("write32",(funcptr)write32,8);
          lastax = -1;
          break;
     case LWC2:
          load(code,"read32",(funcptr)read32);
          store2_ax(rt+idxCPR2);
          cop2gen(cop2writefuncptr(rtno));
          break;
     default:  NOT_IMPREMENT("??"); break;
     }
     return Pc;
}

#if 0
int compiled(UINT32 Pc)
{
     return COMPILED(Pc);
}

void compile(UINT32 Pc)
{
     if (COMPILED(Pc)) return;
     pcbase = baseaddr(Pc);
     endflg = 0;
     memset(isimm,0,sizeof(isimm));
     while(!endflg) {
          Pc = compile1(Pc);
     }
}

#endif