| // Inferno libmach/5db.c | 
 | // http://code.google.com/p/inferno-os/source/browse/utils/libmach/5db.c | 
 | // | 
 | //	Copyright © 1994-1999 Lucent Technologies Inc. | 
 | //	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net). | 
 | //	Portions Copyright © 1997-1999 Vita Nuova Limited. | 
 | //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). | 
 | //	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others. | 
 | //	Portions Copyright © 2009 The Go Authors.  All rights reserved. | 
 | // | 
 | // Permission is hereby granted, free of charge, to any person obtaining a copy | 
 | // of this software and associated documentation files (the "Software"), to deal | 
 | // in the Software without restriction, including without limitation the rights | 
 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 | // copies of the Software, and to permit persons to whom the Software is | 
 | // furnished to do so, subject to the following conditions: | 
 | // | 
 | // The above copyright notice and this permission notice shall be included in | 
 | // all copies or substantial portions of the Software. | 
 | // | 
 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
 | // THE SOFTWARE. | 
 |  | 
 | #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include "ureg_arm.h" | 
 | #include <mach.h> | 
 |  | 
 | static int debug = 0; | 
 |  | 
 | #define	BITS(a, b)	((1<<(b+1))-(1<<a)) | 
 |  | 
 | #define LSR(v, s)	((ulong)(v) >> (s)) | 
 | #define ASR(v, s)	((long)(v) >> (s)) | 
 | #define ROR(v, s)	(LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s)))) | 
 |  | 
 |  | 
 |  | 
 | typedef struct	Instr	Instr; | 
 | struct	Instr | 
 | { | 
 | 	Map	*map; | 
 | 	ulong	w; | 
 | 	ulong	addr; | 
 | 	uchar	op;			/* super opcode */ | 
 |  | 
 | 	uchar	cond;			/* bits 28-31 */ | 
 | 	uchar	store;			/* bit 20 */ | 
 |  | 
 | 	uchar	rd;			/* bits 12-15 */ | 
 | 	uchar	rn;			/* bits 16-19 */ | 
 | 	uchar	rs;			/* bits 0-11 (shifter operand) */ | 
 |  | 
 | 	long	imm;			/* rotated imm */ | 
 | 	char*	curr;			/* fill point in buffer */ | 
 | 	char*	end;			/* end of buffer */ | 
 | 	char*	err;			/* error message */ | 
 | }; | 
 |  | 
 | typedef struct Opcode Opcode; | 
 | struct Opcode | 
 | { | 
 | 	char*	o; | 
 | 	void	(*fmt)(Opcode*, Instr*); | 
 | 	ulong	(*foll)(Map*, Rgetter, Instr*, ulong); | 
 | 	char*	a; | 
 | }; | 
 |  | 
 | static	void	format(char*, Instr*, char*); | 
 | static	char	FRAMENAME[] = ".frame"; | 
 |  | 
 | /* | 
 |  * Arm-specific debugger interface | 
 |  */ | 
 |  | 
 | extern	char	*armexcep(Map*, Rgetter); | 
 | static	int	armfoll(Map*, uvlong, Rgetter, uvlong*); | 
 | static	int	arminst(Map*, uvlong, char, char*, int); | 
 | static	int	armdas(Map*, uvlong, char*, int); | 
 | static	int	arminstlen(Map*, uvlong); | 
 |  | 
 | /* | 
 |  *	Debugger interface | 
 |  */ | 
 | Machdata armmach = | 
 | { | 
 | 	{0, 0, 0, 0xD},		/* break point */ | 
 | 	4,			/* break point size */ | 
 |  | 
 | 	leswab,			/* short to local byte order */ | 
 | 	leswal,			/* long to local byte order */ | 
 | 	leswav,			/* long to local byte order */ | 
 | 	risctrace,		/* C traceback */ | 
 | 	riscframe,		/* Frame finder */ | 
 | 	armexcep,			/* print exception */ | 
 | 	0,			/* breakpoint fixup */ | 
 | 	0,			/* single precision float printer */ | 
 | 	0,			/* double precision float printer */ | 
 | 	armfoll,		/* following addresses */ | 
 | 	arminst,		/* print instruction */ | 
 | 	armdas,			/* dissembler */ | 
 | 	arminstlen,		/* instruction size */ | 
 | }; | 
 |  | 
 | char* | 
 | armexcep(Map *map, Rgetter rget) | 
 | { | 
 | 	long c; | 
 |  | 
 | 	c = (*rget)(map, "TYPE"); | 
 | 	switch (c&0x1f) { | 
 | 	case 0x11: | 
 | 		return "Fiq interrupt"; | 
 | 	case 0x12: | 
 | 		return "Mirq interrupt"; | 
 | 	case 0x13: | 
 | 		return "SVC/SWI Exception"; | 
 | 	case 0x17: | 
 | 		return "Prefetch Abort/Data Abort"; | 
 | 	case 0x18: | 
 | 		return "Data Abort"; | 
 | 	case 0x1b: | 
 | 		return "Undefined instruction/Breakpoint"; | 
 | 	case 0x1f: | 
 | 		return "Sys trap"; | 
 | 	default: | 
 | 		return "Undefined trap"; | 
 | 	} | 
 | } | 
 |  | 
 | static | 
 | char*	cond[16] = | 
 | { | 
 | 	"EQ",	"NE",	"CS",	"CC", | 
 | 	"MI",	"PL",	"VS",	"VC", | 
 | 	"HI",	"LS",	"GE",	"LT", | 
 | 	"GT",	"LE",	0,	"NV" | 
 | }; | 
 |  | 
 | static | 
 | char*	shtype[4] = | 
 | { | 
 | 	"<<",	">>",	"->",	"@>" | 
 | }; | 
 |  | 
 | static | 
 | char *hb[4] = | 
 | { | 
 | 	"???",	"HU", "B", "H" | 
 | }; | 
 |  | 
 | static | 
 | char*	addsub[2] = | 
 | { | 
 | 	"-",	"+", | 
 | }; | 
 |  | 
 | int | 
 | armclass(long w) | 
 | { | 
 | 	int op; | 
 |  | 
 | 	op = (w >> 25) & 0x7; | 
 | 	switch(op) { | 
 | 	case 0:	/* data processing r,r,r */ | 
 | 		op = ((w >> 4) & 0xf); | 
 | 		if(op == 0x9) { | 
 | 			op = 48+16;		/* mul */ | 
 | 			if(w & (1<<24)) { | 
 | 				op += 2; | 
 | 				if(w & (1<<22)) | 
 | 					op++;	/* swap */ | 
 | 				break; | 
 | 			} | 
 | 			if(w & (1<<21)) | 
 | 				op++;		/* mla */ | 
 | 			break; | 
 | 		} | 
 | 		if ((op & 0x9) == 0x9)		/* ld/st byte/half s/u */ | 
 | 		{ | 
 | 			op = (48+16+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2); | 
 | 			break; | 
 | 		} | 
 | 		op = (w >> 21) & 0xf; | 
 | 		if(w & (1<<4)) | 
 | 			op += 32; | 
 | 		else | 
 | 		if(w & (31<<7)) | 
 | 			op += 16; | 
 | 		break; | 
 | 	case 1:	/* data processing i,r,r */ | 
 | 		op = (48) + ((w >> 21) & 0xf); | 
 | 		break; | 
 | 	case 2:	/* load/store byte/word i(r) */ | 
 | 		op = (48+24) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2); | 
 | 		break; | 
 | 	case 3:	/* load/store byte/word (r)(r) */ | 
 | 		op = (48+24+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2); | 
 | 		break; | 
 | 	case 4:	/* block data transfer (r)(r) */ | 
 | 		op = (48+24+4+4) + ((w >> 20) & 0x1); | 
 | 		break; | 
 | 	case 5:	/* branch / branch link */ | 
 | 		op = (48+24+4+4+2) + ((w >> 24) & 0x1); | 
 | 		break; | 
 | 	case 7:	/* coprocessor crap */ | 
 | 		op = (48+24+4+4+2+2) + ((w >> 3) & 0x2) + ((w >> 20) & 0x1); | 
 | 		break; | 
 | 	default: | 
 | 		op = (48+24+4+4+2+2+4); | 
 | 		break; | 
 | 	} | 
 | 	return op; | 
 | } | 
 |  | 
 | static int | 
 | decode(Map *map, ulong pc, Instr *i) | 
 | { | 
 | 	uint32 w; | 
 |  | 
 | 	if(get4(map, pc, &w) < 0) { | 
 | 		werrstr("can't read instruction: %r"); | 
 | 		return -1; | 
 | 	} | 
 | 	i->w = w; | 
 | 	i->addr = pc; | 
 | 	i->cond = (w >> 28) & 0xF; | 
 | 	i->op = armclass(w); | 
 | 	i->map = map; | 
 | 	return 1; | 
 | } | 
 |  | 
 | 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 int | 
 | plocal(Instr *i) | 
 | { | 
 | 	char *reg; | 
 | 	Symbol s; | 
 | 	char *fn; | 
 | 	int class; | 
 | 	int offset; | 
 |  | 
 | 	if(!findsym(i->addr, CTEXT, &s)) { | 
 | 		if(debug)fprint(2,"fn not found @%lux: %r\n", i->addr); | 
 | 		return 0; | 
 | 	} | 
 | 	fn = s.name; | 
 | 	if (!findlocal(&s, FRAMENAME, &s)) { | 
 | 		if(debug)fprint(2,"%s.%s not found @%s: %r\n", fn, FRAMENAME, s.name); | 
 | 			return 0; | 
 | 	} | 
 | 	if(s.value > i->imm) { | 
 | 		class = CAUTO; | 
 | 		offset = s.value-i->imm; | 
 | 		reg = "(SP)"; | 
 | 	} else { | 
 | 		class = CPARAM; | 
 | 		offset = i->imm-s.value-4; | 
 | 		reg = "(FP)"; | 
 | 	} | 
 | 	if(!getauto(&s, offset, class, &s)) { | 
 | 		if(debug)fprint(2,"%s %s not found @%ux: %r\n", fn, | 
 | 			class == CAUTO ? " auto" : "param", offset); | 
 | 		return 0; | 
 | 	} | 
 | 	bprint(i, "%s%c%d%s", s.name, class == CPARAM ? '+' : '-', s.value, reg); | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* | 
 |  * Print value v as name[+offset] | 
 |  */ | 
 | int | 
 | gsymoff(char *buf, int n, long v, int space) | 
 | { | 
 | 	Symbol s; | 
 | 	int r; | 
 | 	long delta; | 
 |  | 
 | 	r = delta = 0;		/* to shut compiler up */ | 
 | 	if (v) { | 
 | 		r = findsym(v, space, &s); | 
 | 		if (r) | 
 | 			delta = v-s.value; | 
 | 		if (delta < 0) | 
 | 			delta = -delta; | 
 | 	} | 
 | 	if (v == 0 || r == 0 || delta >= 4096) | 
 | 		return snprint(buf, n, "#%lux", v); | 
 | 	if (strcmp(s.name, ".string") == 0) | 
 | 		return snprint(buf, n, "#%lux", v); | 
 | 	if (!delta) | 
 | 		return snprint(buf, n, "%s", s.name); | 
 | 	if (s.type != 't' && s.type != 'T') | 
 | 		return snprint(buf, n, "%s+%lux", s.name, v-s.value); | 
 | 	else | 
 | 		return snprint(buf, n, "#%lux", v); | 
 | } | 
 |  | 
 | static void | 
 | armdps(Opcode *o, Instr *i) | 
 | { | 
 | 	i->store = (i->w >> 20) & 1; | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->rd = (i->w >> 12) & 0xf; | 
 | 	i->rs = (i->w >> 0) & 0xf; | 
 | 	if(i->rn == 15 && i->rs == 0) { | 
 | 		if(i->op == 8) { | 
 | 			format("MOVW", i,"CPSR, R%d"); | 
 | 			return; | 
 | 		} else | 
 | 		if(i->op == 10) { | 
 | 			format("MOVW", i,"SPSR, R%d"); | 
 | 			return; | 
 | 		} | 
 | 	} else | 
 | 	if(i->rn == 9 && i->rd == 15) { | 
 | 		if(i->op == 9) { | 
 | 			format("MOVW", i, "R%s, CPSR"); | 
 | 			return; | 
 | 		} else | 
 | 		if(i->op == 11) { | 
 | 			format("MOVW", i, "R%s, SPSR"); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armdpi(Opcode *o, Instr *i) | 
 | { | 
 | 	ulong v; | 
 | 	int c; | 
 |  | 
 | 	v = (i->w >> 0) & 0xff; | 
 | 	c = (i->w >> 8) & 0xf; | 
 | 	while(c) { | 
 | 		v = (v<<30) | (v>>2); | 
 | 		c--; | 
 | 	} | 
 | 	i->imm = v; | 
 | 	i->store = (i->w >> 20) & 1; | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->rd = (i->w >> 12) & 0xf; | 
 | 	i->rs = i->w&0x0f; | 
 |  | 
 | 		/* RET is encoded as ADD #0,R14,R15 */ | 
 | 	if((i->w & 0x0fffffff) == 0x028ef000){ | 
 | 		format("RET%C", i, ""); | 
 | 		return; | 
 | 	} | 
 | 	if((i->w & 0x0ff0ffff) == 0x0280f000){ | 
 | 		format("B%C", i, "0(R%n)"); | 
 | 		return; | 
 | 	} | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armsdti(Opcode *o, Instr *i) | 
 | { | 
 | 	ulong v; | 
 |  | 
 | 	v = i->w & 0xfff; | 
 | 	if(!(i->w & (1<<23))) | 
 | 		v = -v; | 
 | 	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1); | 
 | 	i->imm = v; | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->rd = (i->w >> 12) & 0xf; | 
 | 		/* RET is encoded as LW.P x,R13,R15 */ | 
 | 	if ((i->w & 0x0ffff000) == 0x049df000) | 
 | 	{ | 
 | 		format("RET%C%p", i, "%I"); | 
 | 		return; | 
 | 	} | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | /* arm V4 ld/st halfword, signed byte */ | 
 | static void | 
 | armhwby(Opcode *o, Instr *i) | 
 | { | 
 | 	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1); | 
 | 	i->imm = (i->w & 0xf) | ((i->w >> 8) & 0xf); | 
 | 	if (!(i->w & (1 << 23))) | 
 | 		i->imm = - i->imm; | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->rd = (i->w >> 12) & 0xf; | 
 | 	i->rs = (i->w >> 0) & 0xf; | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armsdts(Opcode *o, Instr *i) | 
 | { | 
 | 	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1); | 
 | 	i->rs = (i->w >> 0) & 0xf; | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->rd = (i->w >> 12) & 0xf; | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armbdt(Opcode *o, Instr *i) | 
 | { | 
 | 	i->store = (i->w >> 21) & 0x3;		/* S & W bits */ | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->imm = i->w & 0xffff; | 
 | 	if(i->w == 0xe8fd8000) | 
 | 		format("RFE", i, ""); | 
 | 	else | 
 | 		format(o->o, i, o->a); | 
 | } | 
 |  | 
 | /* | 
 | static void | 
 | armund(Opcode *o, Instr *i) | 
 | { | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armcdt(Opcode *o, Instr *i) | 
 | { | 
 | 	format(o->o, i, o->a); | 
 | } | 
 | */ | 
 |  | 
 | static void | 
 | armunk(Opcode *o, Instr *i) | 
 | { | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armb(Opcode *o, Instr *i) | 
 | { | 
 | 	ulong v; | 
 |  | 
 | 	v = i->w & 0xffffff; | 
 | 	if(v & 0x800000) | 
 | 		v |= ~0xffffff; | 
 | 	i->imm = (v<<2) + i->addr + 8; | 
 | 	format(o->o, i, o->a); | 
 | } | 
 |  | 
 | static void | 
 | armco(Opcode *o, Instr *i)		/* coprocessor instructions */ | 
 | { | 
 | 	int op, p, cp; | 
 |  | 
 | 	char buf[1024]; | 
 |  | 
 | 	i->rn = (i->w >> 16) & 0xf; | 
 | 	i->rd = (i->w >> 12) & 0xf; | 
 | 	i->rs = i->w&0xf; | 
 | 	cp = (i->w >> 8) & 0xf; | 
 | 	p = (i->w >> 5) & 0x7; | 
 | 	if(i->w&(1<<4)) { | 
 | 		op = (i->w >> 21) & 0x07; | 
 | 		snprint(buf, sizeof(buf), "#%x, #%x, R%d, C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p); | 
 | 	} else { | 
 | 		op = (i->w >> 20) & 0x0f; | 
 | 		snprint(buf, sizeof(buf), "#%x, #%x, C(%d), C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p); | 
 | 	} | 
 | 	format(o->o, i, buf); | 
 | } | 
 |  | 
 | static int | 
 | armcondpass(Map *map, Rgetter rget, uchar cond) | 
 | { | 
 | 	ulong psr; | 
 | 	uchar n; | 
 | 	uchar z; | 
 | 	uchar c; | 
 | 	uchar v; | 
 |  | 
 | 	psr = rget(map, "PSR"); | 
 | 	n = (psr >> 31) & 1; | 
 | 	z = (psr >> 30) & 1; | 
 | 	c = (psr >> 29) & 1; | 
 | 	v = (psr >> 28) & 1; | 
 |  | 
 | 	switch(cond) { | 
 | 		case 0:		return z; | 
 | 		case 1:		return !z; | 
 | 		case 2:		return c; | 
 | 		case 3:		return !c; | 
 | 		case 4:		return n; | 
 | 		case 5:		return !n; | 
 | 		case 6:		return v; | 
 | 		case 7:		return !v; | 
 | 		case 8:		return c && !z; | 
 | 		case 9:		return !c || z; | 
 | 		case 10:	return n == v; | 
 | 		case 11:	return n != v; | 
 | 		case 12:	return !z && (n == v); | 
 | 		case 13:	return z && (n != v); | 
 | 		case 14:	return 1; | 
 | 		case 15:	return 0; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static ulong | 
 | armshiftval(Map *map, Rgetter rget, Instr *i) | 
 | { | 
 | 	if(i->w & (1 << 25)) {				/* immediate */ | 
 | 		ulong imm = i->w & BITS(0, 7); | 
 | 		ulong s = (i->w & BITS(8, 11)) >> 7; /* this contains the *2 */ | 
 | 		return ROR(imm, s); | 
 | 	} else { | 
 | 		char buf[8]; | 
 | 		ulong v; | 
 | 		ulong s = (i->w & BITS(7,11)) >> 7; | 
 |  | 
 | 		sprint(buf, "R%ld", i->w & 0xf); | 
 | 		v = rget(map, buf); | 
 |  | 
 | 		switch((i->w & BITS(4, 6)) >> 4) { | 
 | 		case 0:					/* LSLIMM */ | 
 | 			return v << s; | 
 | 		case 1:					/* LSLREG */ | 
 | 			sprint(buf, "R%lud", s >> 1); | 
 | 			s = rget(map, buf) & 0xFF; | 
 | 			if(s >= 32) return 0; | 
 | 			return v << s; | 
 | 		case 2:					/* LSRIMM */ | 
 | 			return LSR(v, s); | 
 | 		case 3:					/* LSRREG */ | 
 | 			sprint(buf, "R%ld", s >> 1); | 
 | 			s = rget(map, buf) & 0xFF; | 
 | 			if(s >= 32) return 0; | 
 | 			return LSR(v, s); | 
 | 		case 4:					/* ASRIMM */ | 
 | 			if(s == 0) { | 
 | 				if((v & (1U<<31)) == 0) | 
 | 					return 0; | 
 | 				return 0xFFFFFFFF; | 
 | 			} | 
 | 			return ASR(v, s); | 
 | 		case 5:					/* ASRREG */ | 
 | 			sprint(buf, "R%ld", s >> 1); | 
 | 			s = rget(map, buf) & 0xFF; | 
 | 			if(s >= 32) { | 
 | 				if((v & (1U<<31)) == 0) | 
 | 					return 0; | 
 | 				return 0xFFFFFFFF; | 
 | 			} | 
 | 			return ASR(v, s); | 
 | 		case 6:					/* RORIMM */ | 
 | 			if(s == 0) { | 
 | 				ulong c = (rget(map, "PSR") >> 29) & 1; | 
 |  | 
 | 				return (c << 31) | LSR(v, 1); | 
 | 			} | 
 | 			return ROR(v, s); | 
 | 		case 7:					/* RORREG */ | 
 | 			sprint(buf, "R%ld", (s>>1)&0xF); | 
 | 			s = rget(map, buf); | 
 | 			if(s == 0 || (s & 0xF) == 0) | 
 | 				return v; | 
 | 			return ROR(v, s & 0xF); | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | nbits(ulong v) | 
 | { | 
 | 	int n = 0; | 
 | 	int i; | 
 |  | 
 | 	for(i=0; i < 32 ; i++) { | 
 | 		if(v & 1) ++n; | 
 | 		v >>= 1; | 
 | 	} | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | static ulong | 
 | armmaddr(Map *map, Rgetter rget, Instr *i) | 
 | { | 
 | 	ulong v; | 
 | 	ulong nb; | 
 | 	char buf[8]; | 
 | 	ulong rn; | 
 |  | 
 | 	rn = (i->w >> 16) & 0xf; | 
 | 	sprint(buf,"R%ld", rn); | 
 |  | 
 | 	v = rget(map, buf); | 
 | 	nb = nbits(i->w & ((1 << 15) - 1)); | 
 |  | 
 | 	switch((i->w >> 23) & 3) { | 
 | 		case 0: return (v - (nb*4)) + 4; | 
 | 		case 1: return v; | 
 | 		case 2: return v - (nb*4); | 
 | 		case 3: return v + 4; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static ulong | 
 | armaddr(Map *map, Rgetter rget, Instr *i) | 
 | { | 
 | 	char buf[8]; | 
 | 	ulong rn; | 
 |  | 
 | 	sprint(buf, "R%ld", (i->w >> 16) & 0xf); | 
 | 	rn = rget(map, buf); | 
 |  | 
 | 	if((i->w & (1<<24)) == 0) {			/* POSTIDX */ | 
 | 		sprint(buf, "R%ld", rn); | 
 | 		return rget(map, buf); | 
 | 	} | 
 |  | 
 | 	if((i->w & (1<<25)) == 0) {			/* OFFSET */ | 
 | 		sprint(buf, "R%ld", rn); | 
 | 		if(i->w & (1U<<23)) | 
 | 			return rget(map, buf) + (i->w & BITS(0,11)); | 
 | 		return rget(map, buf) - (i->w & BITS(0,11)); | 
 | 	} else {					/* REGOFF */ | 
 | 		ulong index = 0; | 
 | 		uchar c; | 
 | 		uchar rm; | 
 |  | 
 | 		sprint(buf, "R%ld", i->w & 0xf); | 
 | 		rm = rget(map, buf); | 
 |  | 
 | 		switch((i->w & BITS(5,6)) >> 5) { | 
 | 		case 0: index = rm << ((i->w & BITS(7,11)) >> 7);	break; | 
 | 		case 1: index = LSR(rm, ((i->w & BITS(7,11)) >> 7));	break; | 
 | 		case 2: index = ASR(rm, ((i->w & BITS(7,11)) >> 7));	break; | 
 | 		case 3: | 
 | 			if((i->w & BITS(7,11)) == 0) { | 
 | 				c = (rget(map, "PSR") >> 29) & 1; | 
 | 				index = c << 31 | LSR(rm, 1); | 
 | 			} else { | 
 | 				index = ROR(rm, ((i->w & BITS(7,11)) >> 7)); | 
 | 			} | 
 | 			break; | 
 | 		} | 
 | 		if(i->w & (1<<23)) | 
 | 			return rn + index; | 
 | 		return rn - index; | 
 | 	} | 
 | } | 
 |  | 
 | static ulong | 
 | armfadd(Map *map, Rgetter rget, Instr *i, ulong pc) | 
 | { | 
 | 	char buf[8]; | 
 | 	int r; | 
 |  | 
 | 	r = (i->w >> 12) & 0xf; | 
 | 	if(r != 15 || !armcondpass(map, rget, (i->w >> 28) & 0xf)) | 
 | 		return pc+4; | 
 |  | 
 | 	r = (i->w >> 16) & 0xf; | 
 | 	sprint(buf, "R%d", r); | 
 |  | 
 | 	return rget(map, buf) + armshiftval(map, rget, i); | 
 | } | 
 |  | 
 | static ulong | 
 | armfmovm(Map *map, Rgetter rget, Instr *i, ulong pc) | 
 | { | 
 | 	uint32 v; | 
 | 	ulong addr; | 
 |  | 
 | 	v = i->w & 1<<15; | 
 | 	if(!v || !armcondpass(map, rget, (i->w>>28)&0xf)) | 
 | 		return pc+4; | 
 |  | 
 | 	addr = armmaddr(map, rget, i) + nbits(i->w & BITS(0,15)); | 
 | 	if(get4(map, addr, &v) < 0) { | 
 | 		werrstr("can't read addr: %r"); | 
 | 		return -1; | 
 | 	} | 
 | 	return v; | 
 | } | 
 |  | 
 | static ulong | 
 | armfbranch(Map *map, Rgetter rget, Instr *i, ulong pc) | 
 | { | 
 | 	if(!armcondpass(map, rget, (i->w >> 28) & 0xf)) | 
 | 		return pc+4; | 
 |  | 
 | 	return pc + (((signed long)i->w << 8) >> 6) + 8; | 
 | } | 
 |  | 
 | static ulong | 
 | armfmov(Map *map, Rgetter rget, Instr *i, ulong pc) | 
 | { | 
 | 	ulong rd; | 
 | 	uint32 v; | 
 |  | 
 | 	rd = (i->w >> 12) & 0xf; | 
 | 	if(rd != 15 || !armcondpass(map, rget, (i->w>>28)&0xf)) | 
 | 		return pc+4; | 
 |  | 
 | 	 /* LDR */ | 
 | 	/* BUG: Needs LDH/B, too */ | 
 | 	if(((i->w>>26)&0x3) == 1) { | 
 | 		if(get4(map, armaddr(map, rget, i), &v) < 0) { | 
 | 			werrstr("can't read instruction: %r"); | 
 | 			return pc+4; | 
 | 		} | 
 | 		return v; | 
 | 	} | 
 |  | 
 | 	 /* MOV */ | 
 | 	return armshiftval(map, rget, i); | 
 | } | 
 |  | 
 | static Opcode opcodes[] = | 
 | { | 
 | 	"AND%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"EOR%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"SUB%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"RSB%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"ADD%C%S",	armdps, armfadd,	"R%s,R%n,R%d", | 
 | 	"ADC%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"SBC%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"RSC%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"TST%C%S",	armdps, 0,	"R%s,R%n", | 
 | 	"TEQ%C%S",	armdps, 0,	"R%s,R%n", | 
 | 	"CMP%C%S",	armdps, 0,	"R%s,R%n", | 
 | 	"CMN%C%S",	armdps, 0,	"R%s,R%n", | 
 | 	"ORR%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"MOVW%C%S",	armdps, armfmov,	"R%s,R%d", | 
 | 	"BIC%C%S",	armdps, 0,	"R%s,R%n,R%d", | 
 | 	"MVN%C%S",	armdps, 0,	"R%s,R%d", | 
 |  | 
 | /* 16 */ | 
 | 	"AND%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"EOR%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"SUB%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"RSB%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"ADD%C%S",	armdps, armfadd,	"(R%s%h%m),R%n,R%d", | 
 | 	"ADC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"SBC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"RSC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"TST%C%S",	armdps, 0,	"(R%s%h%m),R%n", | 
 | 	"TEQ%C%S",	armdps, 0,	"(R%s%h%m),R%n", | 
 | 	"CMP%C%S",	armdps, 0,	"(R%s%h%m),R%n", | 
 | 	"CMN%C%S",	armdps, 0,	"(R%s%h%m),R%n", | 
 | 	"ORR%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"MOVW%C%S",	armdps, armfmov,	"(R%s%h%m),R%d", | 
 | 	"BIC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d", | 
 | 	"MVN%C%S",	armdps, 0,	"(R%s%h%m),R%d", | 
 |  | 
 | /* 32 */ | 
 | 	"AND%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"EOR%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"SUB%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"RSB%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"ADD%C%S",	armdps, armfadd,	"(R%s%hR%M),R%n,R%d", | 
 | 	"ADC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"SBC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"RSC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"TST%C%S",	armdps, 0,	"(R%s%hR%M),R%n", | 
 | 	"TEQ%C%S",	armdps, 0,	"(R%s%hR%M),R%n", | 
 | 	"CMP%C%S",	armdps, 0,	"(R%s%hR%M),R%n", | 
 | 	"CMN%C%S",	armdps, 0,	"(R%s%hR%M),R%n", | 
 | 	"ORR%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"MOVW%C%S",	armdps, armfmov,	"(R%s%hR%M),R%d", | 
 | 	"BIC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d", | 
 | 	"MVN%C%S",	armdps, 0,	"(R%s%hR%M),R%d", | 
 |  | 
 | /* 48 */ | 
 | 	"AND%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"EOR%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"SUB%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"RSB%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"ADD%C%S",	armdpi, armfadd,	"$#%i,R%n,R%d", | 
 | 	"ADC%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"SBC%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"RSC%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"TST%C%S",	armdpi, 0,	"$#%i,R%n", | 
 | 	"TEQ%C%S",	armdpi, 0,	"$#%i,R%n", | 
 | 	"CMP%C%S",	armdpi, 0,	"$#%i,R%n", | 
 | 	"CMN%C%S",	armdpi, 0,	"$#%i,R%n", | 
 | 	"ORR%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"MOVW%C%S",	armdpi, armfmov,	"$#%i,R%d", | 
 | 	"BIC%C%S",	armdpi, 0,	"$#%i,R%n,R%d", | 
 | 	"MVN%C%S",	armdpi, 0,	"$#%i,R%d", | 
 |  | 
 | /* 48+16 */ | 
 | 	"MUL%C%S",	armdpi, 0,	"R%s,R%M,R%n", | 
 | 	"MULA%C%S",	armdpi, 0,	"R%s,R%M,R%n,R%d", | 
 | 	"SWPW",		armdpi, 0,	"R%s,(R%n),R%d", | 
 | 	"SWPB",		armdpi, 0,	"R%s,(R%n),R%d", | 
 |  | 
 | /* 48+16+4 */ | 
 | 	"MOV%u%C%p",	armhwby, 0,	"R%d,(R%n%UR%M)", | 
 | 	"MOV%u%C%p",	armhwby, 0,	"R%d,%I", | 
 | 	"MOV%u%C%p",	armhwby, armfmov,	"(R%n%UR%M),R%d", | 
 | 	"MOV%u%C%p",	armhwby, armfmov,	"%I,R%d", | 
 |  | 
 | /* 48+24 */ | 
 | 	"MOVW%C%p",	armsdti, 0,	"R%d,%I", | 
 | 	"MOVB%C%p",	armsdti, 0,	"R%d,%I", | 
 | 	"MOVW%C%p",	armsdti, armfmov,	"%I,R%d", | 
 | 	"MOVBU%C%p",	armsdti, armfmov,	"%I,R%d", | 
 |  | 
 | 	"MOVW%C%p",	armsdts, 0,	"R%d,(R%s%h%m)(R%n)", | 
 | 	"MOVB%C%p",	armsdts, 0,	"R%d,(R%s%h%m)(R%n)", | 
 | 	"MOVW%C%p",	armsdts, armfmov,	"(R%s%h%m)(R%n),R%d", | 
 | 	"MOVBU%C%p",	armsdts, armfmov,	"(R%s%h%m)(R%n),R%d", | 
 |  | 
 | 	"MOVM%C%P%a",	armbdt, armfmovm,		"[%r],(R%n)", | 
 | 	"MOVM%C%P%a",	armbdt, armfmovm,		"(R%n),[%r]", | 
 |  | 
 | 	"B%C",		armb, armfbranch,		"%b", | 
 | 	"BL%C",		armb, armfbranch,		"%b", | 
 |  | 
 | 	"CDP%C",	armco, 0,		"", | 
 | 	"CDP%C",	armco, 0,		"", | 
 | 	"MCR%C",	armco, 0,		"", | 
 | 	"MRC%C",	armco, 0,		"", | 
 |  | 
 | 	"UNK",		armunk, 0,	"", | 
 | }; | 
 |  | 
 | static void | 
 | gaddr(Instr *i) | 
 | { | 
 | 	*i->curr++ = '$'; | 
 | 	i->curr += gsymoff(i->curr, i->end-i->curr, i->imm, CANY); | 
 | } | 
 |  | 
 | static	char *mode[] = { 0, "IA", "DB", "IB" }; | 
 | static	char *pw[] = { "P", "PW", 0, "W" }; | 
 | static	char *sw[] = { 0, "W", "S", "SW" }; | 
 |  | 
 | static void | 
 | format(char *mnemonic, Instr *i, char *f) | 
 | { | 
 | 	int j, k, m, n; | 
 | 	int g; | 
 | 	char *fmt; | 
 |  | 
 | 	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 'C':	/* .CONDITION */ | 
 | 			if(cond[i->cond]) | 
 | 				bprint(i, ".%s", cond[i->cond]); | 
 | 			break; | 
 |  | 
 | 		case 'S':	/* .STORE */ | 
 | 			if(i->store) | 
 | 				bprint(i, ".S"); | 
 | 			break; | 
 |  | 
 | 		case 'P':	/* P & U bits for block move */ | 
 | 			n = (i->w >>23) & 0x3; | 
 | 			if (mode[n]) | 
 | 				bprint(i, ".%s", mode[n]); | 
 | 			break; | 
 |  | 
 | 		case 'p':	/* P & W bits for single data xfer*/ | 
 | 			if (pw[i->store]) | 
 | 				bprint(i, ".%s", pw[i->store]); | 
 | 			break; | 
 |  | 
 | 		case 'a':	/* S & W bits for single data xfer*/ | 
 | 			if (sw[i->store]) | 
 | 				bprint(i, ".%s", sw[i->store]); | 
 | 			break; | 
 |  | 
 | 		case 's': | 
 | 			bprint(i, "%d", i->rs & 0xf); | 
 | 			break; | 
 |  | 
 | 		case 'M': | 
 | 			bprint(i, "%d", (i->w>>8) & 0xf); | 
 | 			break; | 
 |  | 
 | 		case 'm': | 
 | 			bprint(i, "%d", (i->w>>7) & 0x1f); | 
 | 			break; | 
 |  | 
 | 		case 'h': | 
 | 			bprint(i, shtype[(i->w>>5) & 0x3]); | 
 | 			break; | 
 |  | 
 | 		case 'u':		/* Signed/unsigned Byte/Halfword */ | 
 | 			bprint(i, hb[(i->w>>5) & 0x3]); | 
 | 			break; | 
 |  | 
 | 		case 'I': | 
 | 			if (i->rn == 13) { | 
 | 				if (plocal(i)) | 
 | 					break; | 
 | 			} | 
 | 			g = 0; | 
 | 			fmt = "#%lx(R%d)"; | 
 | 			if (i->rn == 15) { | 
 | 				/* convert load of offset(PC) to a load immediate */ | 
 | 				uint32 x; | 
 | 				if (get4(i->map, i->addr+i->imm+8, &x) > 0) | 
 | 				{ | 
 | 					i->imm = (int32)x; | 
 | 					g = 1; | 
 | 					fmt = ""; | 
 | 				} | 
 | 			} | 
 | 			if (mach->sb) | 
 | 			{ | 
 | 				if (i->rd == 11) { | 
 | 					uint32 nxti; | 
 |  | 
 | 					if (get4(i->map, i->addr+4, &nxti) > 0) { | 
 | 						if ((nxti & 0x0e0f0fff) == 0x060c000b) { | 
 | 							i->imm += mach->sb; | 
 | 							g = 1; | 
 | 							fmt = "-SB"; | 
 | 						} | 
 | 					} | 
 | 				} | 
 | 				if (i->rn == 12) | 
 | 				{ | 
 | 					i->imm += mach->sb; | 
 | 					g = 1; | 
 | 					fmt = "-SB(SB)"; | 
 | 				} | 
 | 			} | 
 | 			if (g) | 
 | 			{ | 
 | 				gaddr(i); | 
 | 				bprint(i, fmt, i->rn); | 
 | 			} | 
 | 			else | 
 | 				bprint(i, fmt, i->imm, i->rn); | 
 | 			break; | 
 | 		case 'U':		/* Add/subtract from base */ | 
 | 			bprint(i, addsub[(i->w >> 23) & 1]); | 
 | 			break; | 
 |  | 
 | 		case 'n': | 
 | 			bprint(i, "%d", i->rn); | 
 | 			break; | 
 |  | 
 | 		case 'd': | 
 | 			bprint(i, "%d", i->rd); | 
 | 			break; | 
 |  | 
 | 		case 'i': | 
 | 			bprint(i, "%lux", i->imm); | 
 | 			break; | 
 |  | 
 | 		case 'b': | 
 | 			i->curr += symoff(i->curr, i->end-i->curr, | 
 | 				i->imm, CTEXT); | 
 | 			break; | 
 |  | 
 | 		case 'g': | 
 | 			i->curr += gsymoff(i->curr, i->end-i->curr, | 
 | 				i->imm, CANY); | 
 | 			break; | 
 |  | 
 | 		case 'r': | 
 | 			n = i->imm&0xffff; | 
 | 			j = 0; | 
 | 			k = 0; | 
 | 			while(n) { | 
 | 				m = j; | 
 | 				while(n&0x1) { | 
 | 					j++; | 
 | 					n >>= 1; | 
 | 				} | 
 | 				if(j != m) { | 
 | 					if(k) | 
 | 						bprint(i, ","); | 
 | 					if(j == m+1) | 
 | 						bprint(i, "R%d", m); | 
 | 					else | 
 | 						bprint(i, "R%d-R%d", m, j-1); | 
 | 					k = 1; | 
 | 				} | 
 | 				j++; | 
 | 				n >>= 1; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case '\0': | 
 | 			*i->curr++ = '%'; | 
 | 			return; | 
 |  | 
 | 		default: | 
 | 			bprint(i, "%%%c", *f); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	*i->curr = 0; | 
 | } | 
 |  | 
 | static int | 
 | printins(Map *map, ulong pc, char *buf, int n) | 
 | { | 
 | 	Instr i; | 
 |  | 
 | 	i.curr = buf; | 
 | 	i.end = buf+n-1; | 
 | 	if(decode(map, pc, &i) < 0) | 
 | 		return -1; | 
 |  | 
 | 	(*opcodes[i.op].fmt)(&opcodes[i.op], &i); | 
 | 	return 4; | 
 | } | 
 |  | 
 | static int | 
 | arminst(Map *map, uvlong pc, char modifier, char *buf, int n) | 
 | { | 
 | 	USED(modifier); | 
 | 	return printins(map, pc, buf, n); | 
 | } | 
 |  | 
 | static int | 
 | armdas(Map *map, uvlong pc, char *buf, int n) | 
 | { | 
 | 	Instr i; | 
 |  | 
 | 	i.curr = buf; | 
 | 	i.end = buf+n; | 
 | 	if(decode(map, pc, &i) < 0) | 
 | 		return -1; | 
 | 	if(i.end-i.curr > 8) | 
 | 		i.curr = _hexify(buf, i.w, 7); | 
 | 	*i.curr = 0; | 
 | 	return 4; | 
 | } | 
 |  | 
 | static int | 
 | arminstlen(Map *map, uvlong pc) | 
 | { | 
 | 	Instr i; | 
 |  | 
 | 	if(decode(map, pc, &i) < 0) | 
 | 		return -1; | 
 | 	return 4; | 
 | } | 
 |  | 
 | static int | 
 | armfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll) | 
 | { | 
 | 	ulong d; | 
 | 	Instr i; | 
 |  | 
 | 	if(decode(map, pc, &i) < 0) | 
 | 		return -1; | 
 |  | 
 | 	if(opcodes[i.op].foll) { | 
 | 		d = (*opcodes[i.op].foll)(map, rget, &i, pc); | 
 | 		if(d == -1) | 
 | 			return -1; | 
 | 	} else | 
 | 		d = pc+4; | 
 |  | 
 | 	foll[0] = d; | 
 | 	return 1; | 
 | } |