shithub: riscv

ref: 2ba46903fa70ff92d923d10ff71cd6a32bcfdf84
dir: /sys/src/libmach/7db.c/

View raw version
#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;
	}
}