xref: /inferno-os/utils/libmach/5db.c (revision e57c7e16a3789cd4de1a3c2560d49b1ee39cd10a)
1 #include <lib9.h>
2 #include <bio.h>
3 #include "mach.h"
4 
5 static int debug = 0;
6 
7 #define	BITS(a, b)	((1<<(b+1))-(1<<a))
8 
9 #define LSR(v, s)	((ulong)(v) >> (s))
10 #define ASR(v, s)	((long)(v) >> (s))
11 #define ROR(v, s)	(LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
12 
13 
14 
15 typedef struct	Instr	Instr;
16 struct	Instr
17 {
18 	Map	*map;
19 	ulong	w;
20 	uvlong	addr;
21 	uchar	op;			/* super opcode */
22 
23 	uchar	cond;			/* bits 28-31 */
24 	uchar	store;			/* bit 20 */
25 
26 	uchar	rd;			/* bits 12-15 */
27 	uchar	rn;			/* bits 16-19 */
28 	uchar	rs;			/* bits 0-11 (shifter operand) */
29 
30 	long	imm;			/* rotated imm */
31 	char*	curr;			/* fill point in buffer */
32 	char*	end;			/* end of buffer */
33 	char*	err;			/* error message */
34 };
35 
36 typedef struct Opcode Opcode;
37 struct Opcode
38 {
39 	char*	o;
40 	void	(*fmt)(Opcode*, Instr*);
41 	uvlong	(*foll)(Map*, Rgetter, Instr*, uvlong);
42 	char*	a;
43 };
44 
45 static	void	format(char*, Instr*, char*);
46 static	char	FRAMENAME[] = ".frame";
47 
48 /*
49  * Arm-specific debugger interface
50  */
51 
52 static	char	*armexcep(Map*, Rgetter);
53 static	int	armfoll(Map*, uvlong, Rgetter, uvlong*);
54 static	int	arminst(Map*, uvlong, char, char*, int);
55 static	int	armdas(Map*, uvlong, char*, int);
56 static	int	arminstlen(Map*, uvlong);
57 
58 /*
59  *	Debugger interface
60  */
61 Machdata armmach =
62 {
63 	{0, 0, 0, 0xD},		/* break point */
64 	4,			/* break point size */
65 
66 	leswab,			/* short to local byte order */
67 	leswal,			/* long to local byte order */
68 	leswav,			/* long to local byte order */
69 	risctrace,		/* C traceback */
70 	riscframe,		/* Frame finder */
71 	armexcep,			/* print exception */
72 	0,			/* breakpoint fixup */
73 	0,			/* single precision float printer */
74 	0,			/* double precision float printer */
75 	armfoll,		/* following addresses */
76 	arminst,		/* print instruction */
77 	armdas,			/* dissembler */
78 	arminstlen,		/* instruction size */
79 };
80 
81 static char*
82 armexcep(Map *map, Rgetter rget)
83 {
84 	uvlong c;
85 
86 	c = (*rget)(map, "TYPE");
87 	switch ((int)c&0x1f) {
88 	case 0x11:
89 		return "Fiq interrupt";
90 	case 0x12:
91 		return "Mirq interrupt";
92 	case 0x13:
93 		return "SVC/SWI Exception";
94 	case 0x17:
95 		return "Prefetch Abort/Data Abort";
96 	case 0x18:
97 		return "Data Abort";
98 	case 0x1b:
99 		return "Undefined instruction/Breakpoint";
100 	case 0x1f:
101 		return "Sys trap";
102 	default:
103 		return "Undefined trap";
104 	}
105 }
106 
107 static
108 char*	cond[16] =
109 {
110 	"EQ",	"NE",	"CS",	"CC",
111 	"MI",	"PL",	"VS",	"VC",
112 	"HI",	"LS",	"GE",	"LT",
113 	"GT",	"LE",	0,	"NV"
114 };
115 
116 static
117 char*	shtype[4] =
118 {
119 	"<<",	">>",	"->",	"@>"
120 };
121 
122 static
123 char *hb[4] =
124 {
125 	"???",	"HU", "B", "H"
126 };
127 
128 static
129 char*	addsub[2] =
130 {
131 	"-",	"+",
132 };
133 
134 int
135 armclass(long w)
136 {
137 	int op;
138 
139 	op = (w >> 25) & 0x7;
140 	switch(op) {
141 	case 0:	/* data processing r,r,r */
142 		op = ((w >> 4) & 0xf);
143 		if(op == 0x9) {
144 			op = 48+16;		/* mul */
145 			if(w & (1<<24)) {
146 				op += 2;
147 				if(w & (1<<22))
148 					op++;	/* swap */
149 				break;
150 			}
151 			if(w & (1<<23)) {	/* mullu */
152 				op = (48+24+4+4+2+2+4);
153 				if(w & (1<<22))	/* mull */
154 					op += 2;
155 			}
156 			if(w & (1<<21))
157 				op++;		/* mla */
158 			break;
159 		}
160 		if((op & 0x9) == 0x9)		/* ld/st byte/half s/u */
161 		{
162 			op = (48+16+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
163 			break;
164 		}
165 		op = (w >> 21) & 0xf;
166 		if(w & (1<<4))
167 			op += 32;
168 		else
169 		if((w & (31<<7)) || (w & (1<<5)))
170 			op += 16;
171 		break;
172 	case 1:	/* data processing i,r,r */
173 		op = (48) + ((w >> 21) & 0xf);
174 		break;
175 	case 2:	/* load/store byte/word i(r) */
176 		op = (48+24) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
177 		break;
178 	case 3:	/* load/store byte/word (r)(r) */
179 		op = (48+24+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
180 		break;
181 	case 4:	/* block data transfer (r)(r) */
182 		op = (48+24+4+4) + ((w >> 20) & 0x1);
183 		break;
184 	case 5:	/* branch / branch link */
185 		op = (48+24+4+4+2) + ((w >> 24) & 0x1);
186 		break;
187 	case 7:	/* coprocessor crap */
188 		op = (48+24+4+4+2+2) + ((w >> 3) & 0x2) + ((w >> 20) & 0x1);
189 		break;
190 	default:
191 		op = (48+24+4+4+2+2+4+4);
192 		break;
193 	}
194 	return op;
195 }
196 
197 static int
198 decode(Map *map, uvlong pc, Instr *i)
199 {
200 	ulong w;
201 
202 	if(get4(map, pc, &w) < 0) {
203 		werrstr("can't read instruction: %r");
204 		return -1;
205 	}
206 	i->w = w;
207 	i->addr = pc;
208 	i->cond = (w >> 28) & 0xF;
209 	i->op = armclass(w);
210 	i->map = map;
211 	return 1;
212 }
213 
214 #pragma	varargck	argpos	bprint		2
215 
216 static void
217 bprint(Instr *i, char *fmt, ...)
218 {
219 	va_list arg;
220 
221 	va_start(arg, fmt);
222 	i->curr = vseprint(i->curr, i->end, fmt, arg);
223 	va_end(arg);
224 }
225 
226 static int
227 plocal(Instr *i)
228 {
229 	char *reg;
230 	Symbol s;
231 	char *fn;
232 	int class;
233 	int offset;
234 
235 	if(!findsym(i->addr, CTEXT, &s)) {
236 		if(debug)fprint(2,"fn not found @%llux: %r\n", i->addr);
237 		return 0;
238 	}
239 	fn = s.name;
240 	if (!findlocal(&s, FRAMENAME, &s)) {
241 		if(debug)fprint(2,"%s.%s not found @%s: %r\n", fn, FRAMENAME, s.name);
242 			return 0;
243 	}
244 	if(s.value > i->imm) {
245 		class = CAUTO;
246 		offset = s.value-i->imm;
247 		reg = "(SP)";
248 	} else {
249 		class = CPARAM;
250 		offset = i->imm-s.value-4;
251 		reg = "(FP)";
252 	}
253 	if(!getauto(&s, offset, class, &s)) {
254 		if(debug)fprint(2,"%s %s not found @%ux: %r\n", fn,
255 			class == CAUTO ? " auto" : "param", offset);
256 		return 0;
257 	}
258 	bprint(i, "%s%c%lld%s", s.name, class == CPARAM ? '+' : '-', s.value, reg);
259 	return 1;
260 }
261 
262 /*
263  * Print value v as name[+offset]
264  */
265 static int
266 gsymoff(char *buf, int n, long v, int space)
267 {
268 	Symbol s;
269 	int r;
270 	long delta;
271 
272 	r = delta = 0;		/* to shut compiler up */
273 	if (v) {
274 		r = findsym(v, space, &s);
275 		if (r)
276 			delta = v-s.value;
277 		if (delta < 0)
278 			delta = -delta;
279 	}
280 	if (v == 0 || r == 0 || delta >= 4096)
281 		return snprint(buf, n, "#%lux", v);
282 	if (strcmp(s.name, ".string") == 0)
283 		return snprint(buf, n, "#%lux", v);
284 	if (!delta)
285 		return snprint(buf, n, "%s", s.name);
286 	if (s.type != 't' && s.type != 'T')
287 		return snprint(buf, n, "%s+%llux", s.name, v-s.value);
288 	else
289 		return snprint(buf, n, "#%lux", v);
290 }
291 
292 static void
293 armdps(Opcode *o, Instr *i)
294 {
295 	i->store = (i->w >> 20) & 1;
296 	i->rn = (i->w >> 16) & 0xf;
297 	i->rd = (i->w >> 12) & 0xf;
298 	i->rs = (i->w >> 0) & 0xf;
299 	if(i->rn == 15 && i->rs == 0) {
300 		if(i->op == 8) {
301 			format("MOVW", i,"CPSR, R%d");
302 			return;
303 		} else
304 		if(i->op == 10) {
305 			format("MOVW", i,"SPSR, R%d");
306 			return;
307 		}
308 	} else
309 	if(i->rn == 9 && i->rd == 15) {
310 		if(i->op == 9) {
311 			format("MOVW", i, "R%s, CPSR");
312 			return;
313 		} else
314 		if(i->op == 11) {
315 			format("MOVW", i, "R%s, SPSR");
316 			return;
317 		}
318 	}
319 	format(o->o, i, o->a);
320 }
321 
322 static void
323 armdpi(Opcode *o, Instr *i)
324 {
325 	ulong v;
326 	int c;
327 
328 	v = (i->w >> 0) & 0xff;
329 	c = (i->w >> 8) & 0xf;
330 	while(c) {
331 		v = (v<<30) | (v>>2);
332 		c--;
333 	}
334 	i->imm = v;
335 	i->store = (i->w >> 20) & 1;
336 	i->rn = (i->w >> 16) & 0xf;
337 	i->rd = (i->w >> 12) & 0xf;
338 	i->rs = i->w&0x0f;
339 
340 		/* RET is encoded as ADD #0,R14,R15 */
341 	if((i->w & 0x0fffffff) == 0x028ef000){
342 		format("RET%C", i, "");
343 		return;
344 	}
345 	if((i->w & 0x0ff0ffff) == 0x0280f000){
346 		format("B%C", i, "0(R%n)");
347 		return;
348 	}
349 	format(o->o, i, o->a);
350 }
351 
352 static void
353 armsdti(Opcode *o, Instr *i)
354 {
355 	ulong v;
356 
357 	v = i->w & 0xfff;
358 	if(!(i->w & (1<<23)))
359 		v = -v;
360 	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
361 	i->imm = v;
362 	i->rn = (i->w >> 16) & 0xf;
363 	i->rd = (i->w >> 12) & 0xf;
364 		/* RET is encoded as LW.P x,R13,R15 */
365 	if ((i->w & 0x0ffff000) == 0x049df000)
366 	{
367 		format("RET%C%p", i, "%I");
368 		return;
369 	}
370 	format(o->o, i, o->a);
371 }
372 
373 /* arm V4 ld/st halfword, signed byte */
374 static void
375 armhwby(Opcode *o, Instr *i)
376 {
377 	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
378 	i->imm = (i->w & 0xf) | ((i->w >> 8) & 0xf);
379 	if (!(i->w & (1 << 23)))
380 		i->imm = - i->imm;
381 	i->rn = (i->w >> 16) & 0xf;
382 	i->rd = (i->w >> 12) & 0xf;
383 	i->rs = (i->w >> 0) & 0xf;
384 	format(o->o, i, o->a);
385 }
386 
387 static void
388 armsdts(Opcode *o, Instr *i)
389 {
390 	i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
391 	i->rs = (i->w >> 0) & 0xf;
392 	i->rn = (i->w >> 16) & 0xf;
393 	i->rd = (i->w >> 12) & 0xf;
394 	format(o->o, i, o->a);
395 }
396 
397 static void
398 armbdt(Opcode *o, Instr *i)
399 {
400 	i->store = (i->w >> 21) & 0x3;		/* S & W bits */
401 	i->rn = (i->w >> 16) & 0xf;
402 	i->imm = i->w & 0xffff;
403 	if(i->w == 0xe8fd8000)
404 		format("RFE", i, "");
405 	else
406 		format(o->o, i, o->a);
407 }
408 
409 static void
410 armund(Opcode *o, Instr *i)
411 {
412 	format(o->o, i, o->a);
413 }
414 
415 static void
416 armcdt(Opcode *o, Instr *i)
417 {
418 	format(o->o, i, o->a);
419 }
420 
421 static void
422 armunk(Opcode *o, Instr *i)
423 {
424 	format(o->o, i, o->a);
425 }
426 
427 static void
428 armb(Opcode *o, Instr *i)
429 {
430 	ulong v;
431 
432 	v = i->w & 0xffffff;
433 	if(v & 0x800000)
434 		v |= ~0xffffff;
435 	i->imm = (v<<2) + i->addr + 8;
436 	format(o->o, i, o->a);
437 }
438 
439 static void
440 armco(Opcode *o, Instr *i)		/* coprocessor instructions */
441 {
442 	int op, p, cp;
443 
444 	char buf[1024];
445 
446 	i->rn = (i->w >> 16) & 0xf;
447 	i->rd = (i->w >> 12) & 0xf;
448 	i->rs = i->w&0xf;
449 	cp = (i->w >> 8) & 0xf;
450 	p = (i->w >> 5) & 0x7;
451 	if(i->w&(1<<4)) {
452 		op = (i->w >> 21) & 0x07;
453 		snprint(buf, sizeof(buf), "#%x, #%x, R%d, C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p);
454 	} else {
455 		op = (i->w >> 20) & 0x0f;
456 		snprint(buf, sizeof(buf), "#%x, #%x, C(%d), C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p);
457 	}
458 	format(o->o, i, buf);
459 }
460 
461 static int
462 armcondpass(Map *map, Rgetter rget, uchar cond)
463 {
464 	uvlong psr;
465 	uchar n;
466 	uchar z;
467 	uchar c;
468 	uchar v;
469 
470 	psr = rget(map, "PSR");
471 	n = (psr >> 31) & 1;
472 	z = (psr >> 30) & 1;
473 	c = (psr >> 29) & 1;
474 	v = (psr >> 28) & 1;
475 
476 	switch(cond) {
477 	default:
478 	case 0:		return z;
479 	case 1:		return !z;
480 	case 2:		return c;
481 	case 3:		return !c;
482 	case 4:		return n;
483 	case 5:		return !n;
484 	case 6:		return v;
485 	case 7:		return !v;
486 	case 8:		return c && !z;
487 	case 9:		return !c || z;
488 	case 10:	return n == v;
489 	case 11:	return n != v;
490 	case 12:	return !z && (n == v);
491 	case 13:	return z && (n != v);
492 	case 14:	return 1;
493 	case 15:	return 0;
494 	}
495 }
496 
497 static ulong
498 armshiftval(Map *map, Rgetter rget, Instr *i)
499 {
500 	if(i->w & (1 << 25)) {				/* immediate */
501 		ulong imm = i->w & BITS(0, 7);
502 		ulong s = (i->w & BITS(8, 11)) >> 7; /* this contains the *2 */
503 		return ROR(imm, s);
504 	} else {
505 		char buf[8];
506 		ulong v;
507 		ulong s = (i->w & BITS(7,11)) >> 7;
508 
509 		sprint(buf, "R%ld", i->w & 0xf);
510 		v = rget(map, buf);
511 
512 		switch((i->w & BITS(4, 6)) >> 4) {
513 		default:
514 		case 0:					/* LSLIMM */
515 			return v << s;
516 		case 1:					/* LSLREG */
517 			sprint(buf, "R%lud", s >> 1);
518 			s = rget(map, buf) & 0xFF;
519 			if(s >= 32) return 0;
520 			return v << s;
521 		case 2:					/* LSRIMM */
522 			return LSR(v, s);
523 		case 3:					/* LSRREG */
524 			sprint(buf, "R%ld", s >> 1);
525 			s = rget(map, buf) & 0xFF;
526 			if(s >= 32) return 0;
527 			return LSR(v, s);
528 		case 4:					/* ASRIMM */
529 			if(s == 0) {
530 				if((v & (1U<<31)) == 0)
531 					return 0;
532 				return 0xFFFFFFFF;
533 			}
534 			return ASR(v, s);
535 		case 5:					/* ASRREG */
536 			sprint(buf, "R%ld", s >> 1);
537 			s = rget(map, buf) & 0xFF;
538 			if(s >= 32) {
539 				if((v & (1U<<31)) == 0)
540 					return 0;
541 				return 0xFFFFFFFF;
542 			}
543 			return ASR(v, s);
544 		case 6:					/* RORIMM */
545 			if(s == 0) {
546 				ulong c = (rget(map, "PSR") >> 29) & 1;
547 
548 				return (c << 31) | LSR(v, 1);
549 			}
550 			return ROR(v, s);
551 		case 7:					/* RORREG */
552 			sprint(buf, "R%ld", (s>>1)&0xF);
553 			s = rget(map, buf);
554 			if(s == 0 || (s & 0xF) == 0)
555 				return v;
556 			return ROR(v, s & 0xF);
557 		}
558 	}
559 }
560 
561 static int
562 nbits(ulong v)
563 {
564 	int n = 0;
565 	int i;
566 
567 	for(i=0; i < 32 ; i++) {
568 		if(v & 1) ++n;
569 		v >>= 1;
570 	}
571 
572 	return n;
573 }
574 
575 static ulong
576 armmaddr(Map *map, Rgetter rget, Instr *i)
577 {
578 	ulong v;
579 	ulong nb;
580 	char buf[8];
581 	ulong rn;
582 
583 	rn = (i->w >> 16) & 0xf;
584 	sprint(buf,"R%ld", rn);
585 
586 	v = rget(map, buf);
587 	nb = nbits(i->w & ((1 << 15) - 1));
588 
589 	switch((i->w >> 23) & 3) {
590 	default:
591 	case 0: return (v - (nb*4)) + 4;
592 	case 1: return v;
593 	case 2: return v - (nb*4);
594 	case 3: return v + 4;
595 	}
596 }
597 
598 static uvlong
599 armaddr(Map *map, Rgetter rget, Instr *i)
600 {
601 	char buf[8];
602 	ulong rn;
603 
604 	sprint(buf, "R%ld", (i->w >> 16) & 0xf);
605 	rn = rget(map, buf);
606 
607 	if((i->w & (1<<24)) == 0) {			/* POSTIDX */
608 		sprint(buf, "R%ld", rn);
609 		return rget(map, buf);
610 	}
611 
612 	if((i->w & (1<<25)) == 0) {			/* OFFSET */
613 		sprint(buf, "R%ld", rn);
614 		if(i->w & (1U<<23))
615 			return rget(map, buf) + (i->w & BITS(0,11));
616 		return rget(map, buf) - (i->w & BITS(0,11));
617 	} else {					/* REGOFF */
618 		ulong index = 0;
619 		uchar c;
620 		uchar rm;
621 
622 		sprint(buf, "R%ld", i->w & 0xf);
623 		rm = rget(map, buf);
624 
625 		switch((i->w & BITS(5,6)) >> 5) {
626 		case 0: index = rm << ((i->w & BITS(7,11)) >> 7);	break;
627 		case 1: index = LSR(rm, ((i->w & BITS(7,11)) >> 7));	break;
628 		case 2: index = ASR(rm, ((i->w & BITS(7,11)) >> 7));	break;
629 		case 3:
630 			if((i->w & BITS(7,11)) == 0) {
631 				c = (rget(map, "PSR") >> 29) & 1;
632 				index = c << 31 | LSR(rm, 1);
633 			} else {
634 				index = ROR(rm, ((i->w & BITS(7,11)) >> 7));
635 			}
636 			break;
637 		}
638 		if(i->w & (1<<23))
639 			return rn + index;
640 		return rn - index;
641 	}
642 }
643 
644 static uvlong
645 armfadd(Map *map, Rgetter rget, Instr *i, uvlong pc)
646 {
647 	char buf[8];
648 	int r;
649 
650 	r = (i->w >> 12) & 0xf;
651 	if(r != 15 || !armcondpass(map, rget, (i->w >> 28) & 0xf))
652 		return pc+4;
653 
654 	r = (i->w >> 16) & 0xf;
655 	sprint(buf, "R%d", r);
656 
657 	return rget(map, buf) + armshiftval(map, rget, i);
658 }
659 
660 static uvlong
661 armfmovm(Map *map, Rgetter rget, Instr *i, uvlong pc)
662 {
663 	ulong v;
664 	ulong addr;
665 
666 	v = i->w & 1<<15;
667 	if(!v || !armcondpass(map, rget, (i->w>>28)&0xf))
668 		return pc+4;
669 
670 	addr = armmaddr(map, rget, i) + nbits(i->w & BITS(0,15));
671 	if(get4(map, addr, &v) < 0) {
672 		werrstr("can't read addr: %r");
673 		return -1;
674 	}
675 	return v;
676 }
677 
678 static uvlong
679 armfbranch(Map *map, Rgetter rget, Instr *i, uvlong pc)
680 {
681 	if(!armcondpass(map, rget, (i->w >> 28) & 0xf))
682 		return pc+4;
683 
684 	return pc + (((signed long)i->w << 8) >> 6) + 8;
685 }
686 
687 static uvlong
688 armfmov(Map *map, Rgetter rget, Instr *i, uvlong pc)
689 {
690 	ulong rd, v;
691 
692 	rd = (i->w >> 12) & 0xf;
693 	if(rd != 15 || !armcondpass(map, rget, (i->w>>28)&0xf))
694 		return pc+4;
695 
696 	 /* LDR */
697 	/* BUG: Needs LDH/B, too */
698 	if(((i->w>>26)&0x3) == 1) {
699 		if(get4(map, armaddr(map, rget, i), &v) < 0) {
700 			werrstr("can't read instruction: %r");
701 			return pc+4;
702 		}
703 		return v;
704 	}
705 
706 	 /* MOV */
707 	v = armshiftval(map, rget, i);
708 
709 	return v;
710 }
711 
712 static Opcode opcodes[] =
713 {
714 	"AND%C%S",	armdps, 0,	"R%s,R%n,R%d",
715 	"EOR%C%S",	armdps, 0,	"R%s,R%n,R%d",
716 	"SUB%C%S",	armdps, 0,	"R%s,R%n,R%d",
717 	"RSB%C%S",	armdps, 0,	"R%s,R%n,R%d",
718 	"ADD%C%S",	armdps, armfadd,	"R%s,R%n,R%d",
719 	"ADC%C%S",	armdps, 0,	"R%s,R%n,R%d",
720 	"SBC%C%S",	armdps, 0,	"R%s,R%n,R%d",
721 	"RSC%C%S",	armdps, 0,	"R%s,R%n,R%d",
722 	"TST%C%S",	armdps, 0,	"R%s,R%n",
723 	"TEQ%C%S",	armdps, 0,	"R%s,R%n",
724 	"CMP%C%S",	armdps, 0,	"R%s,R%n",
725 	"CMN%C%S",	armdps, 0,	"R%s,R%n",
726 	"ORR%C%S",	armdps, 0,	"R%s,R%n,R%d",
727 	"MOVW%C%S",	armdps, armfmov,	"R%s,R%d",
728 	"BIC%C%S",	armdps, 0,	"R%s,R%n,R%d",
729 	"MVN%C%S",	armdps, 0,	"R%s,R%d",
730 
731 /* 16 */
732 	"AND%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
733 	"EOR%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
734 	"SUB%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
735 	"RSB%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
736 	"ADD%C%S",	armdps, armfadd,	"(R%s%h%m),R%n,R%d",
737 	"ADC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
738 	"SBC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
739 	"RSC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
740 	"TST%C%S",	armdps, 0,	"(R%s%h%m),R%n",
741 	"TEQ%C%S",	armdps, 0,	"(R%s%h%m),R%n",
742 	"CMP%C%S",	armdps, 0,	"(R%s%h%m),R%n",
743 	"CMN%C%S",	armdps, 0,	"(R%s%h%m),R%n",
744 	"ORR%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
745 	"MOVW%C%S",	armdps, armfmov,	"(R%s%h%m),R%d",
746 	"BIC%C%S",	armdps, 0,	"(R%s%h%m),R%n,R%d",
747 	"MVN%C%S",	armdps, 0,	"(R%s%h%m),R%d",
748 
749 /* 32 */
750 	"AND%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
751 	"EOR%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
752 	"SUB%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
753 	"RSB%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
754 	"ADD%C%S",	armdps, armfadd,	"(R%s%hR%M),R%n,R%d",
755 	"ADC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
756 	"SBC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
757 	"RSC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
758 	"TST%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
759 	"TEQ%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
760 	"CMP%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
761 	"CMN%C%S",	armdps, 0,	"(R%s%hR%M),R%n",
762 	"ORR%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
763 	"MOVW%C%S",	armdps, armfmov,	"(R%s%hR%M),R%d",
764 	"BIC%C%S",	armdps, 0,	"(R%s%hR%M),R%n,R%d",
765 	"MVN%C%S",	armdps, 0,	"(R%s%hR%M),R%d",
766 
767 /* 48 */
768 	"AND%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
769 	"EOR%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
770 	"SUB%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
771 	"RSB%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
772 	"ADD%C%S",	armdpi, armfadd,	"$#%i,R%n,R%d",
773 	"ADC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
774 	"SBC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
775 	"RSC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
776 	"TST%C%S",	armdpi, 0,	"$#%i,R%n",
777 	"TEQ%C%S",	armdpi, 0,	"$#%i,R%n",
778 	"CMP%C%S",	armdpi, 0,	"$#%i,R%n",
779 	"CMN%C%S",	armdpi, 0,	"$#%i,R%n",
780 	"ORR%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
781 	"MOVW%C%S",	armdpi, armfmov,	"$#%i,R%d",
782 	"BIC%C%S",	armdpi, 0,	"$#%i,R%n,R%d",
783 	"MVN%C%S",	armdpi, 0,	"$#%i,R%d",
784 
785 /* 48+16 */
786 	"MUL%C%S",	armdpi, 0,	"R%M,R%s,R%n",
787 	"MULA%C%S",	armdpi, 0,	"R%M,R%s,R%n,R%d",
788 	"SWPW",		armdpi, 0,	"R%s,(R%n),R%d",
789 	"SWPB",		armdpi, 0,	"R%s,(R%n),R%d",
790 
791 /* 48+16+4 */
792 	"MOV%u%C%p",	armhwby, 0,	"R%d,(R%n%UR%M)",
793 	"MOV%u%C%p",	armhwby, 0,	"R%d,%I",
794 	"MOV%u%C%p",	armhwby, armfmov,	"(R%n%UR%M),R%d",
795 	"MOV%u%C%p",	armhwby, armfmov,	"%I,R%d",
796 
797 /* 48+24 */
798 	"MOVW%C%p",	armsdti, 0,	"R%d,%I",
799 	"MOVB%C%p",	armsdti, 0,	"R%d,%I",
800 	"MOVW%C%p",	armsdti, armfmov,	"%I,R%d",
801 	"MOVBU%C%p",	armsdti, armfmov,	"%I,R%d",
802 
803 	"MOVW%C%p",	armsdts, 0,	"R%d,(R%s%h%m)(R%n)",
804 	"MOVB%C%p",	armsdts, 0,	"R%d,(R%s%h%m)(R%n)",
805 	"MOVW%C%p",	armsdts, armfmov,	"(R%s%h%m)(R%n),R%d",
806 	"MOVBU%C%p",	armsdts, armfmov,	"(R%s%h%m)(R%n),R%d",
807 
808 	"MOVM%C%P%a",	armbdt, armfmovm,		"[%r],(R%n)",
809 	"MOVM%C%P%a",	armbdt, armfmovm,		"(R%n),[%r]",
810 
811 	"B%C",		armb, armfbranch,		"%b",
812 	"BL%C",		armb, armfbranch,		"%b",
813 
814 	"CDP%C",	armco, 0,		"",
815 	"CDP%C",	armco, 0,		"",
816 	"MCR%C",	armco, 0,		"",
817 	"MRC%C",	armco, 0,		"",
818 
819 /* 48+24+4+4+2+2+4 */
820 	"MULLU%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
821 	"MULALU%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
822 	"MULL%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
823 	"MULAL%C%S",	armdpi, 0,	"R%M,R%s,(R%n,R%d)",
824 
825 /* 48+24+4+4+2+2+4+4 */
826 	"UNK",		armunk, 0,	"",
827 };
828 
829 static void
830 gaddr(Instr *i)
831 {
832 	*i->curr++ = '$';
833 	i->curr += gsymoff(i->curr, i->end-i->curr, i->imm, CANY);
834 }
835 
836 static	char *mode[] = { 0, "IA", "DB", "IB" };
837 static	char *pw[] = { "P", "PW", 0, "W" };
838 static	char *sw[] = { 0, "W", "S", "SW" };
839 
840 static void
841 format(char *mnemonic, Instr *i, char *f)
842 {
843 	int j, k, m, n;
844 	int g;
845 	char *fmt;
846 
847 	if(mnemonic)
848 		format(0, i, mnemonic);
849 	if(f == 0)
850 		return;
851 	if(mnemonic)
852 		if(i->curr < i->end)
853 			*i->curr++ = '\t';
854 	for ( ; *f && i->curr < i->end; f++) {
855 		if(*f != '%') {
856 			*i->curr++ = *f;
857 			continue;
858 		}
859 		switch (*++f) {
860 
861 		case 'C':	/* .CONDITION */
862 			if(cond[i->cond])
863 				bprint(i, ".%s", cond[i->cond]);
864 			break;
865 
866 		case 'S':	/* .STORE */
867 			if(i->store)
868 				bprint(i, ".S");
869 			break;
870 
871 		case 'P':	/* P & U bits for block move */
872 			n = (i->w >>23) & 0x3;
873 			if (mode[n])
874 				bprint(i, ".%s", mode[n]);
875 			break;
876 
877 		case 'p':	/* P & W bits for single data xfer*/
878 			if (pw[i->store])
879 				bprint(i, ".%s", pw[i->store]);
880 			break;
881 
882 		case 'a':	/* S & W bits for single data xfer*/
883 			if (sw[i->store])
884 				bprint(i, ".%s", sw[i->store]);
885 			break;
886 
887 		case 's':
888 			bprint(i, "%d", i->rs & 0xf);
889 			break;
890 
891 		case 'M':
892 			bprint(i, "%lud", (i->w>>8) & 0xf);
893 			break;
894 
895 		case 'm':
896 			bprint(i, "%lud", (i->w>>7) & 0x1f);
897 			break;
898 
899 		case 'h':
900 			bprint(i, shtype[(i->w>>5) & 0x3]);
901 			break;
902 
903 		case 'u':		/* Signed/unsigned Byte/Halfword */
904 			bprint(i, hb[(i->w>>5) & 0x3]);
905 			break;
906 
907 		case 'I':
908 			if (i->rn == 13) {
909 				if (plocal(i))
910 					break;
911 			}
912 			g = 0;
913 			fmt = "#%lx(R%d)";
914 			if (i->rn == 15) {
915 				/* convert load of offset(PC) to a load immediate */
916 				if (get4(i->map, i->addr+i->imm+8, (ulong*)&i->imm) > 0)
917 				{
918 					g = 1;
919 					fmt = "";
920 				}
921 			}
922 			if (mach->sb)
923 			{
924 				if (i->rd == 11) {
925 					ulong nxti;
926 
927 					if (get4(i->map, i->addr+4, &nxti) > 0) {
928 						if ((nxti & 0x0e0f0fff) == 0x060c000b) {
929 							i->imm += mach->sb;
930 							g = 1;
931 							fmt = "-SB";
932 						}
933 					}
934 				}
935 				if (i->rn == 12)
936 				{
937 					i->imm += mach->sb;
938 					g = 1;
939 					fmt = "-SB(SB)";
940 				}
941 			}
942 			if (g)
943 			{
944 				gaddr(i);
945 				bprint(i, fmt, i->rn);
946 			}
947 			else
948 				bprint(i, fmt, i->imm, i->rn);
949 			break;
950 		case 'U':		/* Add/subtract from base */
951 			bprint(i, addsub[(i->w >> 23) & 1]);
952 			break;
953 
954 		case 'n':
955 			bprint(i, "%d", i->rn);
956 			break;
957 
958 		case 'd':
959 			bprint(i, "%d", i->rd);
960 			break;
961 
962 		case 'i':
963 			bprint(i, "%lux", i->imm);
964 			break;
965 
966 		case 'b':
967 			i->curr += symoff(i->curr, i->end-i->curr,
968 				i->imm, CTEXT);
969 			break;
970 
971 		case 'g':
972 			i->curr += gsymoff(i->curr, i->end-i->curr,
973 				i->imm, CANY);
974 			break;
975 
976 		case 'r':
977 			n = i->imm&0xffff;
978 			j = 0;
979 			k = 0;
980 			while(n) {
981 				m = j;
982 				while(n&0x1) {
983 					j++;
984 					n >>= 1;
985 				}
986 				if(j != m) {
987 					if(k)
988 						bprint(i, ",");
989 					if(j == m+1)
990 						bprint(i, "R%d", m);
991 					else
992 						bprint(i, "R%d-R%d", m, j-1);
993 					k = 1;
994 				}
995 				j++;
996 				n >>= 1;
997 			}
998 			break;
999 
1000 		case '\0':
1001 			*i->curr++ = '%';
1002 			return;
1003 
1004 		default:
1005 			bprint(i, "%%%c", *f);
1006 			break;
1007 		}
1008 	}
1009 	*i->curr = 0;
1010 }
1011 
1012 static int
1013 printins(Map *map, uvlong pc, char *buf, int n)
1014 {
1015 	Instr i;
1016 
1017 	i.curr = buf;
1018 	i.end = buf+n-1;
1019 	if(decode(map, pc, &i) < 0)
1020 		return -1;
1021 
1022 	(*opcodes[i.op].fmt)(&opcodes[i.op], &i);
1023 	return 4;
1024 }
1025 
1026 static int
1027 arminst(Map *map, uvlong pc, char modifier, char *buf, int n)
1028 {
1029 	USED(modifier);
1030 	return printins(map, pc, buf, n);
1031 }
1032 
1033 static int
1034 armdas(Map *map, uvlong pc, char *buf, int n)
1035 {
1036 	Instr i;
1037 
1038 	i.curr = buf;
1039 	i.end = buf+n;
1040 	if(decode(map, pc, &i) < 0)
1041 		return -1;
1042 	if(i.end-i.curr > 8)
1043 		i.curr = _hexify(buf, i.w, 7);
1044 	*i.curr = 0;
1045 	return 4;
1046 }
1047 
1048 static int
1049 arminstlen(Map *map, uvlong pc)
1050 {
1051 	Instr i;
1052 
1053 	if(decode(map, pc, &i) < 0)
1054 		return -1;
1055 	return 4;
1056 }
1057 
1058 static int
1059 armfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
1060 {
1061 	uvlong d;
1062 	Instr i;
1063 
1064 	if(decode(map, pc, &i) < 0)
1065 		return -1;
1066 
1067 	if(opcodes[i.op].foll) {
1068 		d = (*opcodes[i.op].foll)(map, rget, &i, pc);
1069 		if(d == -1)
1070 			return -1;
1071 	} else
1072 		d = pc+4;
1073 
1074 	foll[0] = d;
1075 	return 1;
1076 }
1077