#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
/* mips native disassembler */
typedef struct {
uvlong addr; /* pc of instr */
uchar op; /* bits 31-26 */
uchar rs; /* bits 25-21 */
uchar rt; /* bits 20-16 */
uchar rd; /* bits 15-11 */
uchar sa; /* bits 10-6 */
uchar function; /* bits 5-0 */
long immediate; /* bits 15-0 */
ulong cofun; /* bits 24-0 */
ulong target; /* bits 25-0 */
long w0;
char *curr; /* current fill point */
char *end; /* end of buffer */
char *err;
} Instr;
typedef struct {
char *mnemonic;
char *mipsco;
} Opcode;
static char mipscoload[] = "r%t,%l";
static char mipscoalui[] = "r%t,r%s,%i";
static char mipscoalu3op[] = "r%d,r%s,r%t";
static char mipscoboc[] = "r%s,r%t,%b";
static char mipscoboc0[] = "r%s,%b";
static char mipscorsrt[] = "r%s,r%t";
static char mipscorsi[] = "r%s,%i";
static char mipscoxxx[] = "%w";
static char mipscofp3[] = "f%a,f%d,f%t"; /* fd,fs,ft */
static char mipscofp2[] = "f%a,f%d"; /* fd,fs */
static char mipscofpc[] = "f%d,f%t"; /* fs,ft */
static Opcode opcodes[64] = {
0, 0,
0, 0,
"j", "%j",
"jal", "%j",
"beq", mipscoboc,
"bne", mipscoboc,
"blez", mipscoboc0,
"bgtz", mipscoboc0,
"addi", mipscoalui,
"addiu", mipscoalui,
"slti", mipscoalui,
"sltiu", mipscoalui,
"andi", mipscoalui,
"ori", mipscoalui,
"xori", mipscoalui,
"lui", "r%t,%u",
"cop0", 0,
"cop1", 0,
"cop2", 0,
"cop3", 0,
"beql", mipscoboc,
"bnel", mipscoboc,
"blezl", mipscoboc0,
"bgtzl", mipscoboc0,
"instr18", mipscoxxx,
"instr19", mipscoxxx,
"instr1A", mipscoxxx,
"instr1B", mipscoxxx,
"instr1C", mipscoxxx,
"instr1D", mipscoxxx,
"instr1E", mipscoxxx,
"instr1F", mipscoxxx,
"lb", mipscoload,
"lh", mipscoload,
"lwl", mipscoload,
"lw", mipscoload,
"lbu", mipscoload,
"lhu", mipscoload,
"lwr", mipscoload,
"instr27", mipscoxxx,
"sb", mipscoload,
"sh", mipscoload,
"swl", mipscoload,
"sw", mipscoload,
"instr2C", mipscoxxx,
"instr2D", mipscoxxx,
"swr", mipscoload,
"cache", "",
"ll", mipscoload,
"lwc1", mipscoload,
"lwc2", mipscoload,
"lwc3", mipscoload,
"instr34", mipscoxxx,
"ld", mipscoload,
"ld", mipscoload,
"ld", mipscoload,
"sc", mipscoload,
"swc1", mipscoload,
"swc2", mipscoload,
"swc3", mipscoload,
"instr3C", mipscoxxx,
"sd", mipscoload,
"sd", mipscoload,
"sd", mipscoload,
};
static Opcode sopcodes[64] = {
"sll", "r%d,r%t,$%a",
"special01", mipscoxxx,
"srl", "r%d,r%t,$%a",
"sra", "r%d,r%t,$%a",
"sllv", "r%d,r%t,R%s",
"special05", mipscoxxx,
"srlv", "r%d,r%t,r%s",
"srav", "r%d,r%t,r%s",
"jr", "r%s",
"jalr", "r%d,r%s",
"special0A", mipscoxxx,
"special0B", mipscoxxx,
"syscall", "",
"break", "",
"special0E", mipscoxxx,
"sync", "",
"mfhi", "r%d",
"mthi", "r%s",
"mflo", "r%d",
"mtlo", "r%s",
"special14", mipscoxxx,
"special15", mipscoxxx,
"special16", mipscoxxx,
"special17", mipscoxxx,
"mult", mipscorsrt,
"multu", mipscorsrt,
"div", mipscorsrt,
"divu", mipscorsrt,
"special1C", mipscoxxx,
"special1D", mipscoxxx,
"special1E", mipscoxxx,
"special1F", mipscoxxx,
"add", mipscoalu3op,
"addu", mipscoalu3op,
"sub", mipscoalu3op,
"subu", mipscoalu3op,
"and", mipscoalu3op,
"or", mipscoalu3op,
"xor", mipscoalu3op,
"nor", mipscoalu3op,
"special28", mipscoxxx,
"special29", mipscoxxx,
"slt", mipscoalu3op,
"sltu", mipscoalu3op,
"special2C", mipscoxxx,
"special2D", mipscoxxx,
"special2E", mipscoxxx,
"special2F", mipscoxxx,
"tge", mipscorsrt,
"tgeu", mipscorsrt,
"tlt", mipscorsrt,
"tltu", mipscorsrt,
"teq", mipscorsrt,
"special35", mipscoxxx,
"tne", mipscorsrt,
"special37", mipscoxxx,
"special38", mipscoxxx,
"special39", mipscoxxx,
"special3A", mipscoxxx,
"special3B", mipscoxxx,
"special3C", mipscoxxx,
"special3D", mipscoxxx,
"special3E", mipscoxxx,
"special3F", mipscoxxx,
};
static Opcode ropcodes[32] = {
"bltz", mipscoboc0,
"bgez", mipscoboc0,
"bltzl", mipscoboc0,
"bgezl", mipscoboc0,
"regimm04", mipscoxxx,
"regimm05", mipscoxxx,
"regimm06", mipscoxxx,
"regimm07", mipscoxxx,
"tgei", mipscorsi,
"tgeiu", mipscorsi,
"tlti", mipscorsi,
"tltiu", mipscorsi,
"teqi", mipscorsi,
"regimm0D", mipscoxxx,
"tnei", mipscorsi,
"regimm0F", mipscoxxx,
"bltzal", mipscoboc0,
"bgezal", mipscoboc0,
"bltzall", mipscoboc0,
"bgezall", mipscoboc0,
"regimm14", mipscoxxx,
"regimm15", mipscoxxx,
"regimm16", mipscoxxx,
"regimm17", mipscoxxx,
"regimm18", mipscoxxx,
"regimm19", mipscoxxx,
"regimm1A", mipscoxxx,
"regimm1B", mipscoxxx,
"regimm1C", mipscoxxx,
"regimm1D", mipscoxxx,
"regimm1E", mipscoxxx,
"regimm1F", mipscoxxx,
};
static Opcode fopcodes[64] = {
"add.%f", mipscofp3,
"sub.%f", mipscofp3,
"mul.%f", mipscofp3,
"div.%f", mipscofp3,
"sqrt.%f", mipscofp2,
"abs.%f", mipscofp2,
"mov.%f", mipscofp2,
"neg.%f", mipscofp2,
"finstr08", mipscoxxx,
"finstr09", mipscoxxx,
"finstr0A", mipscoxxx,
"finstr0B", mipscoxxx,
"round.w.%f", mipscofp2,
"trunc.w%f", mipscofp2,
"ceil.w%f", mipscofp2,
"floor.w%f", mipscofp2,
"finstr10", mipscoxxx,
"finstr11", mipscoxxx,
"finstr12", mipscoxxx,
"finstr13", mipscoxxx,
"finstr14", mipscoxxx,
"finstr15", mipscoxxx,
"finstr16", mipscoxxx,
"finstr17", mipscoxxx,
"finstr18", mipscoxxx,
"finstr19", mipscoxxx,
"finstr1A", mipscoxxx,
"finstr1B", mipscoxxx,
"finstr1C", mipscoxxx,
"finstr1D", mipscoxxx,
"finstr1E", mipscoxxx,
"finstr1F", mipscoxxx,
"cvt.s.%f", mipscofp2,
"cvt.d.%f", mipscofp2,
"cvt.e.%f", mipscofp2,
"cvt.q.%f", mipscofp2,
"cvt.w.%f", mipscofp2,
"finstr25", mipscoxxx,
"finstr26", mipscoxxx,
"finstr27", mipscoxxx,
"finstr28", mipscoxxx,
"finstr29", mipscoxxx,
"finstr2A", mipscoxxx,
"finstr2B", mipscoxxx,
"finstr2C", mipscoxxx,
"finstr2D", mipscoxxx,
"finstr2E", mipscoxxx,
"finstr2F", mipscoxxx,
"c.f.%f", mipscofpc,
"c.un.%f", mipscofpc,
"c.eq.%f", mipscofpc,
"c.ueq.%f", mipscofpc,
"c.olt.%f", mipscofpc,
"c.ult.%f", mipscofpc,
"c.ole.%f", mipscofpc,
"c.ule.%f", mipscofpc,
"c.sf.%f", mipscofpc,
"c.ngle.%f", mipscofpc,
"c.seq.%f", mipscofpc,
"c.ngl.%f", mipscofpc,
"c.lt.%f", mipscofpc,
"c.nge.%f", mipscofpc,
"c.le.%f", mipscofpc,
"c.ngt.%f", mipscofpc,
};
static char fsub[16] = {
's', 'd', 'e', 'q', 'w', '?', '?', '?',
'?', '?', '?', '?', '?', '?', '?', '?'
};
static int
mkinstr(Instr *i, Map *map, uvlong pc)
{
ulong w;
if (get4(map, pc, &w) < 0) {
werrstr("can't read instruction: %r");
return -1;
}
i->addr = pc;
i->op = (w >> 26) & 0x3F;
i->rs = (w >> 21) & 0x1F;
i->rt = (w >> 16) & 0x1F;
i->rd = (w >> 11) & 0x1F;
i->sa = (w >> 6) & 0x1F;
i->function = w & 0x3F;
i->immediate = w & 0x0000FFFF;
if (i->immediate & 0x8000)
i->immediate |= ~0x0000FFFF;
i->cofun = w & 0x01FFFFFF;
i->target = w & 0x03FFFFFF;
i->w0 = w;
return 1;
}
#pragma varargck argpos bprint 2
static void
bprint(Instr *i, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
i->curr = vseprint(i->curr, i->end, fmt, arg);
va_end(arg);
}
static void
format(char *mnemonic, Instr *i, char *f)
{
if (mnemonic)
format(0, i, mnemonic);
if (f == 0)
return;
if (i->curr < i->end)
*i->curr++ = '\t';
for ( ; *f && i->curr < i->end; f++) {
if (*f != '%') {
*i->curr++ = *f;
continue;
}
switch (*++f) {
case 's':
bprint(i, "%d", i->rs);
break;
case 't':
bprint(i, "%d", i->rt);
break;
case 'd':
bprint(i, "%d", i->rd);
break;
case 'a':
bprint(i, "%d", i->sa);
break;
case 'l':
if (i->rs == 30) {
i->curr += symoff(i->curr, i->end-i->curr, i->immediate+mach->sb, CANY);
bprint(i, "(SB)");
} else
bprint(i, "%lx(r%d)", i->immediate, i->rs);
break;
case 'i':
bprint(i, "$%lx", i->immediate);
break;
case 'u':
*i->curr++ = '$';
i->curr += symoff(i->curr, i->end-i->curr, i->immediate, CANY);
bprint(i, "(SB)");
break;
case 'j':
i->curr += symoff(i->curr, i->end-i->curr,
(i->target<<2)|(i->addr & 0xF0000000), CANY);
bprint(i, "(SB)");
break;
case 'b':
i->curr += symoff(i->curr, i->end-i->curr,
(i->immediate<<2)+i->addr+4, CANY);
break;
case 'c':
bprint(i, "%lux", i->cofun);
break;
case 'w':
bprint(i, "[%lux]", i->w0);
break;
case 'f':
*i->curr++ = fsub[i->rs & 0x0F];
break;
case '\0':
*i->curr++ = '%';
return;
default:
bprint(i, "%%%c", *f);
break;
}
}
}
static void
copz(int cop, Instr *i)
{
char *f, *m, buf[16];
m = buf;
f = "%t,%d";
switch (i->rs) {
case 0:
sprint(buf, "mfc%d", cop);
break;
case 2:
sprint(buf, "cfc%d", cop);
break;
case 4:
sprint(buf, "mtc%d", cop);
break;
case 6:
sprint(buf, "ctc%d", cop);
break;
case 8:
f = "%b";
switch (i->rt) {
case 0:
sprint(buf, "bc%df", cop);
break;
case 1:
sprint(buf, "bc%dt", cop);
break;
case 2:
sprint(buf, "bc%dfl", cop);
break;
case 3:
sprint(buf, "bc%dtl", cop);
break;
default:
sprint(buf, "cop%d", cop);
f = mipscoxxx;
break;
}
break;
default:
sprint(buf, "cop%d", cop);
if (i->rs & 0x10)
f = "function %c";
else
f = mipscoxxx;
break;
}
format(m, i, f);
}
static void
cop0(Instr *i)
{
char *m = 0;
if (i->rs >= 0x10) {
switch (i->cofun) {
case 1:
m = "tlbr";
break;
case 2:
m = "tlbwi";
break;
case 6:
m = "tlbwr";
break;
case 8:
m = "tlbp";
break;
case 16:
m = "rfe";
break;
case 24:
m = "eret";
break;
case 32:
m = "wait";
break;
}
if (m) {
format(m, i, 0);
if (i->curr < i->end)
*i->curr++ = 0;
return;
}
}
copz(0, i);
}
int
_mipscoinst(Map *map, uvlong pc, char *buf, int n)
{
Instr i;
Opcode *o;
uchar op;
i.curr = buf;
i.end = buf+n-1;
if (mkinstr(&i, map, pc) < 0)
return -1;
switch (i.op) {
case 0x00: /* SPECIAL */
o = sopcodes;
op = i.function;
break;
case 0x01: /* REGIMM */
o = ropcodes;
op = i.rt;
break;
case 0x10: /* COP0 */
cop0(&i);
return 4;
case 0x11: /* COP1 */
if (i.rs & 0x10) {
o = fopcodes;
op = i.function;
break;
}
/*FALLTHROUGH*/
case 0x12: /* COP2 */
case 0x13: /* COP3 */
copz(i.op-0x10, &i);
return 4;
default:
o = opcodes;
op = i.op;
break;
}
format(o[op].mnemonic, &i, o[op].mipsco);
return 4;
}
|