#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
/*
* Alpha-specific debugger interface
*/
static char *alphaexcep(Map*, Rgetter);
static int alphafoll(Map*, uvlong, Rgetter, uvlong*);
static int alphainst(Map*, uvlong, char, char*, int);
static int alphadas(Map*, uvlong, char*, int);
static int alphainstlen(Map*, uvlong);
/*
* Debugger interface
*/
Machdata alphamach =
{
{0x80, 0, 0, 0}, /* break point */
4, /* break point size */
leswab, /* short to local byte order */
leswal, /* long to local byte order */
leswav, /* vlong to local byte order */
risctrace, /* C traceback */
riscframe, /* Frame finder */
alphaexcep, /* print exception */
0, /* breakpoint fixup */
leieeesftos, /* single precision float printer */
leieeedftos, /* double precisioin float printer */
alphafoll, /* following addresses */
alphainst, /* print instruction */
alphadas, /* dissembler */
alphainstlen, /* instruction size */
};
static char *illegaltype[] = {
"breakpoint",
"bugchk",
"gentrap",
"fen",
"illegal instruction",
};
static char *
alphaexcep(Map *map, Rgetter rget)
{
ulong type, a0, a1;
static char buf[256];
type = (*rget)(map, "TYPE");
a0 = (*rget)(map, "A0");
a1 = (*rget)(map, "A1");
/* a2 = (*rget)(map, "A2"); */
switch (type) {
case 1: /* arith */
sprint(buf, "trap: arithmetic trap 0x%lux", a0);
break;
case 2: /* bad instr or FEN */
if (a0 <= 4)
return illegaltype[a0];
else
sprint(buf, "illegal instr trap, unknown type %lud", a0);
break;
case 3: /* intr */
sprint(buf, "interrupt type %lud", a0);
break;
case 4: /* memory fault */
sprint(buf, "fault %s addr=0x%lux", (a1&1)?"write":"read", a0);
break;
case 5: /* syscall() */
return "system call";
case 6: /* alignment fault */
sprint(buf, "unaligned op 0x%lux addr 0x%lux", a1, a0);
break;
default: /* cannot happen */
sprint(buf, "unknown exception type %lud", type);
break;
}
return buf;
}
/* alpha disassembler and related functions */
static char FRAMENAME[] = ".frame";
typedef struct {
uvlong addr;
uchar op; /* bits 31-26 */
uchar ra; /* bits 25-21 */
uchar rb; /* bits 20-16 */
uchar rc; /* bits 4-0 */
long mem; /* bits 15-0 */
long branch; /* bits 20-0 */
uchar function; /* bits 11-5 */
uchar literal; /* bits 20-13 */
uchar islit; /* bit 12 */
uchar fpfn; /* bits 10-5 */
uchar fpmode; /* bits 15-11 */
long w0;
long w1;
int size; /* instruction size */
char *curr; /* fill point in buffer */
char *end; /* end of buffer */
char *err; /* error message */
} Instr;
static Map *mymap;
static int
decode(uvlong pc, Instr *i)
{
ulong w;
if (get4(mymap, pc, &w) < 0) {
werrstr("can't read instruction: %r");
return -1;
}
i->addr = pc;
i->size = 1;
i->op = (w >> 26) & 0x3F;
i->ra = (w >> 21) & 0x1F;
i->rb = (w >> 16) & 0x1F;
i->rc = w & 0x1F;
i->function = (w >> 5) & 0x7F;
i->mem = w & 0xFFFF;
if (i->mem & 0x8000)
i->mem -= 0x10000;
i->branch = w & 0x1FFFFF;
if (i->branch & 0x100000)
i->branch -= 0x200000;
i->function = (w >> 5) & 0x7F;
i->literal = (w >> 13) & 0xFF;
i->islit = (w >> 12) & 0x01;
i->fpfn = (w >> 5) & 0x3F;
i->fpmode = (w >> 11) & 0x1F;
i->w0 = w;
return 1;
}
static int
mkinstr(uvlong pc, Instr *i)
{
/* Instr x; */
if (decode(pc, i) < 0)
return -1;
#ifdef frommips
/* we probably want to do something like this for alpha... */
/*
* if it's a LUI followed by an ORI,
* it's an immediate load of a large constant.
* fix the LUI immediate in any case.
*/
if (i->op == 0x0F) {
if (decode(pc+4, &x) < 0)
return 0;
i->immediate <<= 16;
if (x.op == 0x0D && x.rs == x.rt && x.rt == i->rt) {
i->immediate |= (x.immediate & 0xFFFF);
i->w1 = x.w0;
i->size++;
return 1;
}
}
#endif
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);
}
typedef struct Opcode Opcode;
struct Opcode {
char *mnemonic;
void (*f)(Opcode *, Instr *);
char *ken;
};
static void format(char *, Instr *, char *);
static int
plocal(Instr *i, char *m, char r, int store)
{
int offset;
char *reg;
Symbol s;
if (!findsym(i->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s))
return 0;
if (s.value > i->mem) {
if(!getauto(&s, s.value-i->mem, CAUTO, &s))
return 0;
reg = "(SP)";
offset = i->mem;
} else {
offset = i->mem-s.value-8;
if (!getauto(&s, offset, CPARAM, &s))
return 0;
reg = "(FP)";
}
if (store)
bprint(i, "%s\t%c%d,%s+%d%s", m, r, i->ra, s.name, offset, reg);
else
bprint(i, "%s\t%s+%d%s,%c%d", m, s.name, offset, reg, r, i->ra);
return 1;
}
static void
_load(Opcode *o, Instr *i, char r)
{
char *m;
m = o->mnemonic;
if (i->rb == 30 && plocal(i, m, r, 0))
return;
if (i->rb == 29 && mach->sb) {
bprint(i, "%s\t", m);
i->curr += symoff(i->curr, i->end-i->curr, i->mem+mach->sb, CANY);
bprint(i, "(SB),%c%d", r, i->ra);
return;
}
format(m, i, o->ken);
}
static void
load(Opcode *o, Instr *i)
{
_load(o, i, 'R');
}
static void
loadf(Opcode *o, Instr *i)
{
_load(o, i, 'F');
}
static void
_store(Opcode *o, Instr *i, char r)
{
char *m;
m = o->mnemonic;
if (i->rb == 30 && plocal(i, m, r, 1))
return;
if (i->rb == 29 && mach->sb) {
bprint(i, "%s\t%c%d,", m, r, i->ra);
i->curr += symoff(i->curr, i->end-i->curr, i->mem+mach->sb, CANY);
bprint(i, "(SB)");
return;
}
format(o->mnemonic, i, o->ken);
}
static void
store(Opcode *o, Instr *i)
{
_store(o, i, 'R');
}
static void
storef(Opcode *o, Instr *i)
{
_store(o, i, 'F');
}
static void
misc(Opcode *o, Instr *i)
{
char *f;
USED(o);
switch (i->mem&0xFFFF) {
case 0x0000:
f = "TRAPB";
break;
case 0x4000:
f = "MB";
break;
case 0x8000:
f = "FETCH\t0(R%b)";
break;
case 0xA000:
f = "FETCH_M\t0(R%b)";
break;
case 0xC000:
f = "RPCC\tR%a";
break;
case 0xE000:
f = "RC\tR%a";
break;
case 0xF000:
f = "RS\tR%a";
break;
default:
f = "%w";
}
format(0, i, f);
}
static char *jmpcode[4] = { "JMP", "JSR", "RET", "JSR_COROUTINE" };
static void
jmp(Opcode *o, Instr *i)
{
int hint;
char *m;
USED(o);
hint = (i->mem >> 14) & 3;
m = jmpcode[hint];
if (i->ra == 31) {
if (hint == 2 && i->rb == 29)
bprint(i, m);
else
format(m, i, "(R%b)");
}
else
format(m, i, "R%a,(R%b)");
}
static void
br(Opcode *o, Instr *i)
{
if (i->ra == 31)
format(o->mnemonic, i, "%B");
else
format(o->mnemonic, i, o->ken);
}
static void
bsr(Opcode *o, Instr *i)
{
if (i->ra == 26)
format(o->mnemonic, i, "%B");
else
format(o->mnemonic, i, o->ken);
}
static void
mult(Opcode *o, Instr *i)
{
char *m;
switch (i->function) {
case 0x00:
m = "MULL";
break;
case 0x20:
m = "MULQ";
break;
case 0x40:
m = "MULL/V";
break;
case 0x60:
m = "MULQ/V";
break;
case 0x30:
m = "UMULH";
break;
default:
format("???", i, "%w");
return;
}
format(m, i, o->ken);
}
static char alphaload[] = "%l,R%a";
static char alphafload[] = "%l,F%a";
static char alphastore[] = "R%a,%l";
static char alphafstore[] = "F%a,%l";
static char alphabranch[] = "R%a,%B";
static char alphafbranch[] = "F%a,%B";
static char alphaint[] = "%v,R%a,R%c";
static char alphafp[] = "F%b,F%a,F%c";
static char alphafp2[] = "F%b,F%c";
static char alphaxxx[] = "%w";
static Opcode opcodes[64] = {
"PAL", 0, alphaxxx,
"OPC01", 0, alphaxxx,
"OPC02", 0, alphaxxx,
"OPC03", 0, alphaxxx,
"OPC04", 0, alphaxxx,
"OPC05", 0, alphaxxx,
"OPC06", 0, alphaxxx,
"OPC07", 0, alphaxxx,
"MOVQA", load, alphaload,
"MOVQAH", load, alphaload,
"MOVBU", load, alphaload, /* v 3 */
"MOVQU", load, alphaload,
"MOVWU", load, alphaload, /* v 3 */
"MOVWU", store, alphastore, /* v 3 */
"MOVBU", store, alphastore, /* v 3 */
"MOVQU", store, alphastore,
0, 0, 0, /* int arith */
0, 0, 0, /* logical */
0, 0, 0, /* shift */
0, mult, alphaint,
"OPC14", 0, alphaxxx,
"vax", 0, alphafp, /* vax */
0, 0, 0, /* ieee */
0, 0, 0, /* fp */
0, misc, alphaxxx,
"PAL19 [HW_MFPR]",0, alphaxxx,
"JSR", jmp, 0,
"PAL1B [HW_LD]",0, alphaxxx,
"OPC1C", 0, alphaxxx,
"PAL1D [HW_MTPR]",0, alphaxxx,
"PAL1E [HW_REI]",0, alphaxxx,
"PAL1F [HW_ST]",0, alphaxxx,
"MOVF", loadf, alphafload,
"MOVG", loadf, alphafload,
"MOVS", loadf, alphafload,
"MOVT", loadf, alphafload,
"MOVF", storef, alphafstore,
"MOVG", storef, alphafstore,
"MOVS", storef, alphafstore,
"MOVT", storef, alphafstore,
"MOVL", load, alphaload,
"MOVQ", load, alphaload,
"MOVLL", load, alphaload,
"MOVQL", load, alphaload,
"MOVL", store, alphastore,
"MOVQ", store, alphastore,
"MOVLC", store, alphastore,
"MOVQC", store, alphastore,
"JMP", br, alphabranch,
"FBEQ", 0, alphafbranch,
"FBLT", 0, alphafbranch,
"FBLE", 0, alphafbranch,
"JSR", bsr, alphabranch,
"FBNE", 0, alphafbranch,
"FBGE", 0, alphafbranch,
"FBGT", 0, alphafbranch,
"BLBC", 0, alphafbranch,
"BEQ", 0, alphabranch,
"BLT", 0, alphabranch,
"BLE", 0, alphabranch,
"BLBS", 0, alphabranch,
"BNE", 0, alphabranch,
"BGE", 0, alphabranch,
"BGT", 0, alphabranch,
};
static Opcode fpopcodes[64] = {
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"CVTLQ", 0, alphafp2,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"CPYS", 0, alphafp,
"CPYSN", 0, alphafp,
"CPYSE", 0, alphafp,
"???", 0, alphaxxx,
"MOVT", 0, "FPCR,F%a",
"MOVT", 0, "F%a,FPCR",
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"FCMOVEQ", 0, alphafp,
"FCMOVNE", 0, alphafp,
"FCMOVLT", 0, alphafp,
"FCMOVGE", 0, alphafp,
"FCMOVLE", 0, alphafp,
"FCMOVGT", 0, alphafp,
"CVTQL", 0, alphafp2,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
};
static Opcode ieeeopcodes[64] = {
"ADDS", 0, alphafp,
"SUBS", 0, alphafp,
"MULS", 0, alphafp,
"DIVS", 0, alphafp,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"ADDT", 0, alphafp,
"SUBT", 0, alphafp,
"MULT", 0, alphafp,
"DIVT", 0, alphafp,
"CMPTUN", 0, alphafp,
"CMPTEQ", 0, alphafp,
"CMPTLT", 0, alphafp,
"CMPTLE", 0, alphafp,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"CVTTS", 0, alphafp2,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"CVTTQ", 0, alphafp2,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"???", 0, alphaxxx,
"CVTQS", 0, alphafp2,
"???", 0, alphaxxx,
"CVTQT", 0, alphafp2,
"???", 0, alphaxxx,
};
static uchar amap[128] = {
[0x00] 1,
[0x40] 2,
[0x20] 3,
[0x60] 4,
[0x09] 5,
[0x49] 6,
[0x29] 7,
[0x69] 8,
[0x2D] 9,
[0x4D] 10,
[0x6D] 11,
[0x1D] 12,
[0x3D] 13,
[0x0F] 14,
[0x02] 15,
[0x0B] 16,
[0x12] 17,
[0x1B] 18,
[0x22] 19,
[0x2B] 20,
[0x32] 21,
[0x3B] 22,
};
static Opcode arithopcodes[64] = {
"???", 0, alphaxxx,
"ADDL", 0, alphaint,
"ADDL/V", 0, alphaint,
"ADDQ", 0, alphaint,
"ADDQ/V", 0, alphaint,
"SUBL", 0, alphaint,
"SUBL/V", 0, alphaint,
"SUBQ", 0, alphaint,
"SUBQ/V", 0, alphaint,
"CMPEQ", 0, alphaint,
"CMPLT", 0, alphaint,
"CMPLE", 0, alphaint,
"CMPULT", 0, alphaint,
"CMPULE", 0, alphaint,
"CMPBGE", 0, alphaint,
"S4ADDL", 0, alphaint,
"S4SUBL", 0, alphaint,
"S8ADDL", 0, alphaint,
"S8SUBL", 0, alphaint,
"S4ADDQ", 0, alphaint,
"S4SUBQ", 0, alphaint,
"S8ADDQ", 0, alphaint,
"S8SUBQ", 0, alphaint,
};
static uchar lmap[128] = {
[0x00] 1,
[0x20] 2,
[0x40] 3,
[0x08] 4,
[0x28] 5,
[0x48] 6,
[0x24] 7,
[0x44] 8,
[0x64] 9,
[0x26] 7,
[0x46] 8,
[0x66] 9,
[0x14] 10,
[0x16] 11,
};
static Opcode logicalopcodes[64] = {
"???", 0, alphaxxx,
"AND", 0, alphaint,
"OR", 0, alphaint,
"XOR", 0, alphaint,
"ANDNOT", 0, alphaint,
"ORNOT", 0, alphaint,
"XORNOT", 0, alphaint,
"CMOVEQ", 0, alphaint,
"CMOVLT", 0, alphaint,
"CMOVLE", 0, alphaint,
"CMOVNE", 0, alphaint,
"CMOVGE", 0, alphaint,
"CMOVGT", 0, alphaint,
"CMOVLBS", 0, alphaint,
"CMOVLBC", 0, alphaint,
};
static uchar smap[128] = {
[0x39] 1,
[0x3C] 2,
[0x34] 3,
[0x06] 4,
[0x16] 5,
[0x26] 6,
[0x36] 7,
[0x5A] 8,
[0x6A] 9,
[0x7A] 10,
[0x0B] 11,
[0x1B] 12,
[0x2B] 13,
[0x3B] 14,
[0x57] 15,
[0x67] 16,
[0x77] 17,
[0x02] 18,
[0x12] 19,
[0x22] 20,
[0x32] 21,
[0x52] 22,
[0x62] 23,
[0x72] 24,
[0x30] 25,
[0x31] 26,
};
static Opcode shiftopcodes[64] = {
"???", 0, alphaxxx,
"SLLQ", 0, alphaint,
"SRAQ", 0, alphaint,
"SRLQ", 0, alphaint,
"EXTBL", 0, alphaint,
"EXTWL", 0, alphaint,
"EXTLL", 0, alphaint,
"EXTQL", 0, alphaint,
"EXTWH", 0, alphaint,
"EXTLH", 0, alphaint,
"EXTQH", 0, alphaint,
"INSBL", 0, alphaint,
"INSWL", 0, alphaint,
"INSLL", 0, alphaint,
"INSQL", 0, alphaint,
"INSWH", 0, alphaint,
"INSLH", 0, alphaint,
"INSQH", 0, alphaint,
"MSKBL", 0, alphaint,
"MSKWL", 0, alphaint,
"MSKLL", 0, alphaint,
"MSKQL", 0, alphaint,
"MSKWH", 0, alphaint,
"MSKLH", 0, alphaint,
"MSKQH", 0, alphaint,
"ZAP", 0, alphaint,
"ZAPNOT", 0, alphaint,
};
static void
format(char *mnemonic, Instr *i, char *f)
{
if (mnemonic)
format(0, i, mnemonic);
if (f == 0)
return;
if (mnemonic)
if (i->curr < i->end)
*i->curr++ = '\t';
for ( ; *f && i->curr < i->end; f++) {
if (*f != '%') {
*i->curr++ = *f;
continue;
}
switch (*++f) {
case 'a':
bprint(i, "%d", i->ra);
break;
case 'b':
bprint(i, "%d", i->rb);
break;
case 'c':
bprint(i, "%d", i->rc);
break;
case 'v':
if (i->islit)
bprint(i, "$%ux", i->literal);
else
bprint(i, "R%d", i->rb);
break;
case 'l':
bprint(i, "%lx(R%d)", i->mem, i->rb);
break;
case 'i':
bprint(i, "$%lx", i->mem);
break;
case 'B':
i->curr += symoff(i->curr, i->end-i->curr,
(i->branch<<2)+i->addr+4, CANY);
break;
case 'w':
bprint(i, "[%lux]", i->w0);
break;
case '\0':
*i->curr++ = '%';
return;
default:
bprint(i, "%%%c", *f);
break;
}
}
*i->curr = 0;
}
static int
printins(Map *map, uvlong pc, char *buf, int n)
{
Instr i;
Opcode *o;
uchar op;
i.curr = buf;
i.end = buf+n-1;
mymap = map;
if (mkinstr(pc, &i) < 0)
return -1;
switch (i.op) {
case 0x10: /* INTA */
o = arithopcodes;
op = amap[i.function];
break;
case 0x11: /* INTL */
o = logicalopcodes;
op = lmap[i.function];
break;
case 0x12: /* INTS */
o = shiftopcodes;
op = smap[i.function];
break;
case 0x16: /* FLTI */
o = ieeeopcodes;
op = i.fpfn;
break;
case 0x17: /* FLTL */
o = fpopcodes;
op = i.fpfn;
break;
default:
o = opcodes;
op = i.op;
break;
}
if (o[op].f)
(*o[op].f)(&o[op], &i);
else
format(o[op].mnemonic, &i, o[op].ken);
return i.size*4;
}
static int
alphainst(Map *map, uvlong pc, char modifier, char *buf, int n)
{
USED(modifier);
return printins(map, pc, buf, n);
}
static int
alphadas(Map *map, uvlong pc, char *buf, int n)
{
Instr i;
i.curr = buf;
i.end = buf+n;
mymap = map;
if (mkinstr(pc, &i) < 0)
return -1;
if (i.end-i.curr > 8)
i.curr = _hexify(buf, i.w0, 7);
if (i.size == 2 && i.end-i.curr > 9) {
*i.curr++ = ' ';
i.curr = _hexify(i.curr, i.w1, 7);
}
*i.curr = 0;
return i.size*4;
}
static int
alphainstlen(Map *map, uvlong pc)
{
Instr i;
mymap = map;
if (mkinstr(pc, &i) < 0)
return -1;
return i.size*4;
}
static int
alphafoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
{
char buf[8];
Instr i;
mymap = map;
if (mkinstr(pc, &i) < 0)
return -1;
switch(i.op) {
case 0x1A: /* JMP/JSR/RET */
sprint(buf, "R%d", i.rb);
foll[0] = (*rget)(map, buf);
return 1;
case 0x30: /* BR */
case 0x34: /* BSR */
foll[0] = pc+4 + (i.branch<<2);
return 1;
default:
if (i.op > 0x30) { /* cond */
foll[0] = pc+4;
foll[1] = pc+4 + (i.branch<<2);
return 2;
}
foll[0] = pc+i.size*4;
return 1;
}
}
|