/*
    MIPS disassembler
    =================

    Written by BERO
*/

#include "fpse.h"

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

static char *cop0regname[32] = {
     /* from mips 4400 manual
    "Index","Random","EntryLo0","EntryLo1","Context","PageMask","Wired","$7",
    "BadVAddr","Count","EntryHi","Compare","Status","Cause","EPC","PRId",
    "Config","LLAddr","WatchLo","WatchHi","XContext","$21","$22","$23",
    "$24","$25","ECC","CacheErr","TagLo","TagHi","ErrorEPC","$31"
     */
    "INDEX","RAND","$2","BPC","$3","BDA","TAR","DCIC",
    "BadVAddr","BDAM","$10","BPCM","SR","CAUSE","EPC","PRId",
    "$16","$17","$18","$19","$20","$21","$22","$23",
    "$24","$25","$26","$27","$28","$29","$30","$31"
};

static char *cop2cregname[32] = {
     "R11R12","R13R21","R22R23","R31R32","R33","TRX","TRY","TRZ",
     "L11L12","L13L21","L22L23","L31L32","L33","RBK","GBK","BBK",
     "LR1LR2","LR3LG1","LG2LG3","LB1LB2","LB3","RFC","GFC","BFC",
     "OFX","OFY","H","DQA","DQB","ZSF3","ZSF4","FLAG"
};

static char *cop2regname[32] = {
     "VXY0","VZ0","VXY1","VZ1","VXY2","VZ2","RGB","OTZ",
     "IR0","IR1","IR2","IR3","SXY0","SXY1","SXY2","SXYP",
     "SZ0","SZ1","SZ2","SZ3","RGB0","RGB1","RGB2","$23",
     "MAC0","MAC1","MAC2","MAC3","IRGB","ORGB","LZCS","LZCR"
};

static char *cregname[32] = {
     "$0","$1","$2","$3","$4","$5","$6","$7",
     "$8","$9","$10","$11","$12","$13","$14","$15",
     "$16","$17","$18","$19","$20","$21","$22","$23",
     "$24","$25","$26","$27","$28","$29","$30","$31"
};

static char *nemonic[64] = {
     "special","bcond","j","jal","beq","bne","blez","bgtz",
     "addi","addiu","slti","sltiu","andi","ori","xori","lui",
     "cop0","cop1","cop2","cop3","","","","",
     "","","","","","","","",
     "lb","lh","lwl","lw","lbu","lhu","lwr","",
     "sb","sh","swl","sw","","","swr","",
     "lwc0","lwc1","lwc2","lwc3","","","","",
     "swc0","swc1","swc2","swc3","","","",""
};

static char *special[64] = {
     "sll","","srl","sra","sllv","","srlv","srav",
     "jr","jalr","","","syscall","break","","",
     "mfhi","mthi","mflo","mtlo","","","","",
     "mult","multu","div","divu","","","","",
     "add","addu","sub","subu","and","or","xor","nor",
     "","","slt","sltu","","","","",
     "","","","","","","","",
     "","","","","","","",""
};

static char *bcond[32] = {
     "bltz","bgez","","","","","","",
     "","","","","","","","",
     "bltzal","bgezal","","","","","","",
     "","","","","","","",""
};

static char *cop[16] = {
     "mfc","","cfc","","mtc","","ctc","",
     "bc","","","","bc","","",""
};

static char *cop0[32] = {
     "","tlbr","tlbwi","","","","tlbwr","",
     "tlbp","","","","","","","",
     "rfe","","","","","","","",
     "","","","","","","",""
};

#define   MASK(from,n)   (((1<<(n))-1)<<(from))
#define   CHECK(a)

#define rdna regname[rdno]
#define rtna regname[rtno]
#define rsna regname[rsno]

int dumpreg = 1;
int rlist[4] = { 0,0,0,0 };

void watch(int num)
{
    int i;

    if (num < 0) {
        for(i=0;i<32;i++) 
            printf("%-4s = %08x ",regname[i],(int)reg.r[i]);
        printf("\n");
    } else printf("%-4s = %08x\n",regname[num],(int)reg.r[num]);
}

void watchcop(int num)
{
    int i;

    switch (num) {
    case 2:
        for(i=0;i<32;i++) 
            printf("%-4s = %08x ",cop2cregname[i],(int)reg.ccr2[i]);
        break;
    case 3:
        for(i=0;i<32;i++) 
            printf("%-4s = %08x ",cop2regname[i],(int)reg.cpr2[i]);
        break;
    }
    printf("\n");
}

static int reduce(int n)
{
 int x,y;

 for (x=0;x<n;)
    {
        if (rlist[x]==0 || rlist[x+1]==rlist[x]) {
            for (y=x;y<n;y++) rlist[y] = rlist[y+1];
            n--;
        } else x++;
    }
 return n;
}

#ifdef MAKEDIS
#define DUMPREG(r1,r2,r3)
#else
#define DUMPREG(r1,r2,r3)   if (dumpreg) {                           \
                                int nreg;                            \
                                rlist[0] = r1;                       \
                                rlist[1] = r2;                       \
                                rlist[2] = r3;                       \
                                nreg = reduce(3);                    \
                                while (nreg--)                       \
                                    sprintf(buf,"%s %s=%08x",buf,    \
                                            regname[rlist[nreg]],    \
                                       (int)reg.r[rlist[nreg]]);     \
                            }
#endif

void disasm(char *buf, UINT32 code, UINT32 addr)
{
    int op,func; // ,rs,rt,rd,immS;

    if (!code) {
        strcpy(buf,"nop");
        return;
    }

    op = code>>26;
/*    rs = (code>>21)&31;
    rt = (code>>16)&31;
    rd = (code>>11)&31;
    immS = (short)code;
*/
    switch(op){
    case SPECIAL:
        func = code&63;
        switch(func){
        case SLL:
        case SRL:
        case SRA:
            CHECK(rsno) /* err */
            sprintf(buf,"%-5s %s,%s,%d\t",
                    special[func],rdna,rtna,(int)((code>>6)&31));
            DUMPREG(rdno,rtno,0)
            break;
        case SLLV:
        case SRLV:
        case SRAV:
            CHECK(code & MASK(6,5)) /* err */
            sprintf(buf,"%-5s %s,%s,%s\t",special[func],rdna,rtna,rsna);
            DUMPREG(rdno,rtno,rsno)
            break;
        case JR:
            CHECK(code & MASK(6,15)) /* err */
            sprintf(buf,"%-5s %s\t\t",special[func],rsna);
            DUMPREG(rsno,0,0)
            break;
        case JALR:
            CHECK(code & (MASK(6,5)|MASK(16,5))) /* err */
            sprintf(buf,"%-5s %s,%s\t",special[func],rsna,rdna);
            DUMPREG(rdno,rsno,0)
            break;
        case SYSCALL:
        case BREAK:
            sprintf(buf,"%-5s %08x",special[func],(int)((code>>6)&0xfffff));
            break;
        case MFHI:
        case MFLO:
            CHECK(code & (MASK(6,5)|MASK(16,10))) /* err */
            sprintf(buf,"%-5s %s\t",special[func],rdna);
            DUMPREG(rdno,0,0)
            break;
        case MTHI:
        case MTLO:
            CHECK(code & MASK(6,15)) /* err */
            sprintf(buf,"%-5s %s\t",special[func],rsna);
            DUMPREG(rsno,0,0)
            break;
        case MULT:
        case MULTU:
        case DIV:
        case DIVU:
            CHECK(code & MASK(6,10)) /* error */
            sprintf(buf,"%-5s %s,%s\t",special[func],rsna,rtna);
            DUMPREG(rsno,rtno,0)
            break;
        case ADD:
        case ADDU:
        case SUB:
        case SUBU:
        case AND:
        case OR:
        case XOR:
        case NOR:
        case SLT:
        case SLTU:
            CHECK(code & MASK(6,5)) /* err */
            sprintf(buf,"%-5s %s,%s,%s\t",special[func],rdna,rsna,rtna);
            DUMPREG(rdno,rtno,rsno)
            break;
        default:
            strcpy(buf,"unknown special");
            break;
        }
        break;
    case BCOND:
        sprintf(buf,"%-5s %s,%08x\t",bcond[rtno],rsna,(int)(addr+(immS+1)*4));
        DUMPREG(rsno,0,0)
        break;
    case J:
    case JAL:
        sprintf(buf,"%-5s %08x",nemonic[op],(int)((addr&0xf0000000)|(code&0x3ffffff)*4));
        break;
    case BEQ:
    case BNE:
        sprintf(buf,"%-5s %s,%s,%08x\t",nemonic[op],rsna,rtna,(int)(addr+(immS+1)*4));
        DUMPREG(rsno,rtno,0)
        break;
    case BLEZ:
    case BGTZ:
        CHECK(rtno) /* err */
        sprintf(buf,"%-5s %s,%08x\t",nemonic[op],rsna,(int)(addr+(immS+1)*4));
        DUMPREG(rsno,0,0)
        break;
    case ADDI:
    case ADDIU:
    case SLTI:
    case SLTIU:
    case ANDI:
    case ORI:
    case XORI:
    case LUI:
        sprintf(buf,"%-5s %s,%s,%d\t# %04x",nemonic[op],rtna,rsna,(int)immS,(int)(code&0xffff));
        DUMPREG(rsno,0,0)
        break;
    case COP0:
        if (rsno&0x10) {
            CHECK(code && MASK(5,20)) /* err */
            sprintf(buf,"%s",cop0[code&31]);
        } else {
            CHECK(code & MASK(0,11)) /* err */
            switch(rsno) {
                case MFC:
                case MTC:
                    sprintf(buf,"%s0  %s,%s",cop[rsno],rtna,cop0regname[rdno]);
                    break;
                case CFC:
                case CTC:
                    sprintf(buf,"%s0  %s,%s",cop[rsno],rtna,cregname[rdno]);
                    break;
            }
        }
        break;
    case COP2:
        if (rsno&0x10) {
            sprintf(buf,"cop%d  %08x",op&3,(int)(code&0x1ffffff));
        } else {
            CHECK(code & MASK(0,11)) /* err */
            switch(rsno) {
            case MFC:
            case MTC:
                sprintf(buf,"%s2  %s,%s",cop[rsno],rtna,cop2regname[rdno]);
                break;
            case CFC:
            case CTC:
                sprintf(buf,"%s2  %s,%s",cop[rsno],rtna,cop2cregname[rdno]);
                break;
            }
        }
        break;
    case LB:
    case LH:
    case LWL:
    case LW:
    case LBU:
    case LHU:
    case LWR:
    case SB:
    case SH:
    case SWL:
    case SW:
    case SWR:
        sprintf(buf,"%-5s %s,%d(%s)\t",nemonic[op],rtna,immS,rsna);
        DUMPREG(rsno,0,0)
        break;
    case LWC0:
    case SWC0:
        sprintf(buf,"%-5s %s,%d(%s)",nemonic[op],cop0regname[rtno],(int)immS,rsna);
        break;
    case SWC2:
    case LWC2:
        sprintf(buf,"%-5s %s,%d(%s)",nemonic[op],cop2regname[rtno],(int)immS,rsna);
        break;
    case LWC1:
    case LWC3:
    case SWC1:
    case SWC3:
        sprintf(buf,"%-5s %s,%d(%s)",nemonic[op],cregname[rtno],(int)immS,rsna);
        break;
    default:
        strcpy(buf,"unknown");
        break;
    }
}

#ifdef MAKEDIS

int main(int argc,char *argv[])
{
    FILE *fp;
    char *buf,*mem;
    UINT32 addr,startaddr,fsize;
    int   offset,i;
    char inputbuf[256];

    EXE_HEADER *head;
    char strbuf[256];

    fp = fopen(argv[1],"rb");
    fseek(fp,0,SEEK_END);
    fsize = ftell(fp);
    fseek(fp,0,SEEK_SET);
    buf = malloc(fsize);
    fread(buf,1,fsize,fp);
    fclose(fp);

    head = (void*)buf;
    mem = buf;
    if (memcmp(head->id,"PS-X EXE",8)==0) {
        startaddr = head->t_addr;
        mem+=2048;
    } else {
        if (argc > 2) sscanf(argv[2],"%x",&startaddr);
                 else startaddr = 0xbfc00000;
    }

    mem-=startaddr;
    addr = startaddr;

    for (i=0;i<fsize/4;i++)
        {
            UINT32 code = *(UINT32 *)(mem+addr);
            disasm(strbuf,code,addr);
            printf("%08x %08x %s\n",addr,code,strbuf);
            addr+=4;
        }  
/*
   while(1) {
     for(i=0;i<16;i++) {
          u_long code = *(u_long*)(mem+addr);
          disasm(strbuf,code,addr);
          printf("%08x %08x %s\n",addr,code,strbuf);
          addr+=4;
     }
     printf(">");
     gets(inputbuf);
     if (strcmp(inputbuf,"q")==0) break;
    switch(inputbuf[0]){
    case 'a': { char *endp; addr=strtoul(inputbuf+1,&endp,16); }
    }
   }
*/
    return 0;
}
#endif