#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern extern
#include "mips.h"

void    Iaddi(ulong);
void    Isw(ulong);
void    Ilui(ulong);
void    Iori(ulong);
void    Ixori(ulong);
void    Ilw(ulong);
void    Ijal(ulong);
void    Ispecial(ulong);
void    Ibeq(ulong);
void    Iaddiu(ulong);
void    Ilb(ulong);
void    Iandi(ulong);
void    Ij(ulong);
void    Ibne(ulong);
void    Isb(ulong);
void    Islti(ulong);
void    Ibcond(ulong);
void    Ibgtz(ulong);
void    Ilbu(ulong);
void    Ilhu(ulong);
void    Ish(ulong);
void    Ilh(ulong);
void    Iblez(ulong);
void    Isltiu(ulong);
void    Iswc1(ulong);
void    Ilwc1(ulong);
void    Icop1(ulong);

Inst itab[] = {
       { Ispecial,     0 },
       { Ibcond,       "bcond",        Ibranch },
       { Ij,           "j",            Ibranch },
       { Ijal,         "jal",          Ibranch },
       { Ibeq,         "beq",          Ibranch },
       { Ibne,         "bne",          Ibranch },
       { Iblez,        "blez",         Ibranch },
       { Ibgtz,        "bgtz",         Ibranch },
       { Iaddi,        "addi",         Iarith },       /* 8 */
       { Iaddiu,       "addiu",        Iarith },
       { Islti,        "slti",         Iarith },
       { Isltiu,       "sltiu",        Iarith },
       { Iandi,        "andi",         Iarith },
       { Iori,         "ori",          Iarith },
       { Ixori,        "xori",         Iarith },
       { Ilui,         "lui",          Iload },        /* 15 */
       { undef,        "" },
       { Icop1,        "cop1",         Ifloat },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { Ilb,          "lb",           Iload },
       { Ilh,          "lh",           Iload },
       { undef,        "" },
       { Ilw,          "lw",           Iload },
       { Ilbu,         "lbu",          Iload },
       { Ilhu,         "lhu",          Iload },
       { undef,        "" },
       { undef,        "" },
       { Isb,          "sb",           Istore },
       { Ish,          "sh",           Istore },
       { undef,        "" },
       { Isw,          "sw",           Istore },       /* 43 */
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { Ilwc1,                "lwc1",         Ifloat },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { Iswc1,        "swc1",         Ifloat },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { undef,        "" },
       { 0 }
};

void
run(void)
{
       do {
               reg.r[0] = 0;
               reg.ir = ifetch(reg.pc);
               Iexec(reg.ir);
               reg.pc += 4;
               if(bplist)
                       brkchk(reg.pc, Instruction);
       }while(--count);
}

void
undef(ulong inst)
{
/*
       if((reg.ir>>26) == 0)
               Bprint(bioout, "special=%d,%d table=%d\n",
               (reg.ir>>3)&0x7, reg.ir&0x7, reg.ir&0x3f);
       else
               Bprint(bioout, "code=%d,%d table=%d\n",
               reg.ir>>29, (reg.ir>>26)&0x7, reg.ir>>26);
*/
       Bprint(bioout, "Undefined Instruction Trap IR %.8lux\n", inst);
       longjmp(errjmp, 0);
}

void
Iaddi(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = (short)(inst&0xffff);

       if(trace)
               itrace("addi\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = reg.r[rs] + imm;
}

void
Iandi(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = inst&0xffff;

       if(trace)
               itrace("andi\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = reg.r[rs] & imm;
}

void
Isw(ulong inst)
{
       int rt, rb;
       int off;
       ulong value;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       value = reg.r[rt];
       if(trace)
               itrace("sw\tr%d,0x%x(r%d) %lux=%lux",
                               rt, off, rb, reg.r[rb]+off, value);

       putmem_w(reg.r[rb]+off, value);
}

void
Isb(ulong inst)
{
       int rt, rb;
       int off;
       uchar value;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       value = reg.r[rt];
       if(trace)
               itrace("sb\tr%d,0x%x(r%d) %lux=%lux", rt, off, rb, reg.r[rb]+off, value);

       putmem_b(reg.r[rb]+off, value);
}

void
Ish(ulong inst)
{
       int rt, rb;
       int off;
       ushort value;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       value = reg.r[rt];
       if(trace)
               itrace("sh\tr%d,0x%x(r%d) %lux=%lux",
                               rt, off, rb, reg.r[rb]+off, value&0xffff);

       putmem_h(reg.r[rb]+off, value);
}

void
Ilui(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       USED(rs);
       imm = inst<<16;

       if(trace)
               itrace("lui\tr%d,#0x%x", rt, imm);

       reg.r[rt] = imm;
}

void
Iori(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = inst&0xffff;

       if(trace)
               itrace("ori\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = reg.r[rs] | imm;
}

void
Ixori(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = inst&0xffff;

       if(trace)
               itrace("xori\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = reg.r[rs] ^ imm;
}

void
Ilw(ulong inst)
{
       int rt, rb;
       int off;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       if(trace)
               itrace("lw\tr%d,0x%x(r%d) ea=%lux", rt, off, rb, reg.r[rb]+off);

       reg.r[rt] = getmem_w(reg.r[rb]+off);
}

void
Ilh(ulong inst)
{
       int rt, rb;
       int off;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       if(trace)
               itrace("lh\tr%d,0x%x(r%d) ea=%lux", rt, off, rb, reg.r[rb]+off);

       reg.r[rt] = (short)getmem_h(reg.r[rb]+off);
}

void
Ijal(ulong inst)
{
       ulong npc;
       Symbol s;

       npc = (reg.pc&0xF0000000)|((inst&0x3FFFFFF)<<2);
       if(trace)
               itrace("jal\t0x%lux", npc);

       reg.r[31] = reg.pc+8;
       /* Do the delay slot */
       reg.ir = ifetch(reg.pc+4);
       Statbra();
       Iexec(reg.ir);

       if(calltree) {
               findsym(npc, CTEXT, &s);
               Bprint(bioout, "%8lux %s(", reg.pc, s.name);
               printparams(&s, reg.r[29]);
               Bprint(bioout, "from ");
               printsource(reg.pc);
               Bputc(bioout, '\n');
       }

       reg.pc = npc-4;
}

void
Ij(ulong inst)
{
       ulong npc;

       npc = (reg.pc&0xF0000000)|((inst&0x3FFFFFF)<<2);
       if(trace)
               itrace("j\t0x%lux", npc);

       /* Do the delay slot */
       reg.ir = ifetch(reg.pc+4);
       Statbra();
       Iexec(reg.ir);
       reg.pc = npc-4;
}

void
Ibeq(ulong inst)
{
       int rt, rs;
       int off;
       ulong npc;

       Getrsrt(rs, rt, inst);
       off = (short)(inst&0xffff);

       npc = reg.pc + (off<<2) + 4;
       if(trace)
               itrace("beq\tr%d,r%d,0x%lux", rs, rt, npc);

       if(reg.r[rs] == reg.r[rt]) {
               /* Do the delay slot */
               reg.ir = ifetch(reg.pc+4);
               Statbra();
               Iexec(reg.ir);
               reg.pc = npc-4;
       }
}

void
Ibgtz(ulong inst)
{
       int rs;
       int off;
       ulong npc, r;

       rs = (inst>>21)&0x1f;
       off = (short)(inst&0xffff);

       npc = reg.pc + (off<<2) + 4;
       if(trace)
               itrace("bgtz\tr%d,0x%lux", rs, npc);

       r = reg.r[rs];
       if(!(r&SIGNBIT) && r != 0) {
               /* Do the delay slot */
               reg.ir = ifetch(reg.pc+4);
               Iexec(reg.ir);
               reg.pc = npc-4;
       }
}

void
Iblez(ulong inst)
{
       int rs;
       int off;
       ulong npc, r;

       rs = (inst>>21)&0x1f;
       off = (short)(inst&0xffff);

       npc = reg.pc + (off<<2) + 4;
       if(trace)
               itrace("blez\tr%d,0x%lux", rs, npc);

       r = reg.r[rs];
       if((r&SIGNBIT) || r == 0) {
               /* Do the delay slot */
               reg.ir = ifetch(reg.pc+4);
               Statbra();
               Iexec(reg.ir);
               reg.pc = npc-4;
       }
}

void
Ibne(ulong inst)
{
       int rt, rs;
       int off;
       ulong npc;

       Getrsrt(rs, rt, inst);
       off = (short)(inst&0xffff);

       npc = reg.pc + (off<<2) + 4;
       if(trace)
               itrace("bne\tr%d,r%d,0x%lux", rs, rt, npc);

       if(reg.r[rs] != reg.r[rt]) {
               /* Do the delay slot */
               reg.ir = ifetch(reg.pc+4);
               Statbra();
               Iexec(reg.ir);
               reg.pc = npc-4;
       }
}

void
Iaddiu(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = (short)(inst&0xffff);

       if(trace)
               itrace("addiu\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = reg.r[rs]+imm;
}

void
Islti(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = (short)(inst&0xffff);

       if(trace)
               itrace("slti\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = reg.r[rs] < imm ? 1 : 0;
}

void
Isltiu(ulong inst)
{
       int rs, rt;
       int imm;

       Getrsrt(rs, rt, inst);
       imm = (short)(inst&0xffff);

       if(trace)
               itrace("sltiu\tr%d,r%d,#0x%x", rt, rs, imm);

       reg.r[rt] = (ulong)reg.r[rs] < (ulong)imm ? 1 : 0;
}

void
Ilb(ulong inst)
{
       int rt, rb;
       int off;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       if(trace)
               itrace("lb\tr%d,0x%x(r%d) ea=%lux", rt, off, rb, reg.r[rb]+off);

       reg.r[rt] = (schar)getmem_b(reg.r[rb]+off);
}

void
Ilbu(ulong inst)
{
       int rt, rb;
       int off;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       if(trace)
               itrace("lbu\tr%d,0x%x(r%d) ea=%lux", rt, off, rb, reg.r[rb]+off);

       reg.r[rt] = getmem_b(reg.r[rb]+off) & 0xff;
}

void
Ilhu(ulong inst)
{
       int rt, rb;
       int off;

       Getrbrt(rb, rt, inst);
       off = (short)(inst&0xffff);

       if(trace)
               itrace("lhu\tr%d,0x%x(r%d) ea=%lux", rt, off, rb, reg.r[rb]+off);

       reg.r[rt] = getmem_h(reg.r[rb]+off) & 0xffff;
}

enum
{
       Bltz    = 0,
       Bgez    = 1,
       Bltzal  = 0x10,
       Bgezal  = 0x11,
};

static char *sbcond[] =
{
       [Bltz]          "ltz",
       [Bgez]          "gez",
       [Bltzal]        "ltzal",
       [Bgezal]        "gezal",
};

void
Ibcond(ulong inst)
{
       int rs, bran;
       int off, doit;
       ulong npc;

       rs = (inst>>21)&0x1f;
       bran = (inst>>16)&0x1f;
       off = (short)(inst&0xffff);
       doit = 0;

       npc = reg.pc + (off<<2) + 4;
       switch(bran) {
       default:
               Bprint(bioout, "bcond=%d\n", bran);
               undef(inst);
       case Bltz:
               if(reg.r[rs]&SIGNBIT)
                       doit = 1;
               break;
       case Bgez:
               if(!(reg.r[rs]&SIGNBIT))
                       doit = 1;
               break;
       case Bltzal:
               if(reg.r[rs]&SIGNBIT) {
                       doit = 1;
                       reg.r[31] = reg.pc+8;
               }
               break;
       case Bgezal:
               if(reg.r[rs] >= 0) {
                       doit = 1;
                       reg.r[31] = reg.pc+8;
               }
               break;
       }

       if(trace)
               itrace("b%s\tr%d,0x%lux", sbcond[bran], rs, npc);

       if(doit) {
               /* Do the delay slot */
               reg.ir = ifetch(reg.pc+4);
               Statbra();
               Iexec(reg.ir);
               reg.pc = npc-4;
       }

}