xref: /plan9/sys/src/libmach/vcodas.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <mach.h>
5 
6 	/* mips native disassembler */
7 
8 typedef struct {
9 	uvlong addr;			/* pc of instr */
10 	uchar op;			/* bits 31-26 */
11 	uchar rs;			/* bits 25-21 */
12 	uchar rt;			/* bits 20-16 */
13 	uchar rd;			/* bits 15-11 */
14 	uchar sa;			/* bits 10-6 */
15 	uchar function;			/* bits 5-0 */
16 	long immediate;			/* bits 15-0 */
17 	ulong cofun;			/* bits 24-0 */
18 	ulong target;			/* bits 25-0 */
19 	long w0;
20 	char *curr;			/* current fill point */
21 	char *end;			/* end of buffer */
22 	char *err;
23 } Instr;
24 
25 typedef struct {
26 	char *mnemonic;
27 	char *mipsco;
28 } Opcode;
29 
30 static char mipscoload[] = "r%t,%l";
31 static char mipscoalui[] = "r%t,r%s,%i";
32 static char mipscoalu3op[] = "r%d,r%s,r%t";
33 static char mipscoboc[] = "r%s,r%t,%b";
34 static char mipscoboc0[] = "r%s,%b";
35 static char mipscorsrt[] = "r%s,r%t";
36 static char mipscorsi[] = "r%s,%i";
37 static char mipscoxxx[] = "%w";
38 static char mipscofp3[] = "f%a,f%d,f%t";	/* fd,fs,ft */
39 static char mipscofp2[] = "f%a,f%d";		/* fd,fs */
40 static char mipscofpc[] = "f%d,f%t";		/* fs,ft */
41 
42 static Opcode opcodes[64] = {
43 	0,		0,
44 	0,		0,
45 	"j",		"%j",
46 	"jal",		"%j",
47 	"beq",		mipscoboc,
48 	"bne",		mipscoboc,
49 	"blez",		mipscoboc0,
50 	"bgtz",		mipscoboc0,
51 	"addi",		mipscoalui,
52 	"addiu",	mipscoalui,
53 	"slti",		mipscoalui,
54 	"sltiu",	mipscoalui,
55 	"andi",		mipscoalui,
56 	"ori",		mipscoalui,
57 	"xori",		mipscoalui,
58 	"lui",		"r%t,%u",
59 	"cop0",		0,
60 	"cop1",		0,
61 	"cop2",		0,
62 	"cop3",		0,
63 	"beql",		mipscoboc,
64 	"bnel",		mipscoboc,
65 	"blezl",	mipscoboc0,
66 	"bgtzl",	mipscoboc0,
67 	"instr18",	mipscoxxx,
68 	"instr19",	mipscoxxx,
69 	"instr1A",	mipscoxxx,
70 	"instr1B",	mipscoxxx,
71 	"instr1C",	mipscoxxx,
72 	"instr1D",	mipscoxxx,
73 	"instr1E",	mipscoxxx,
74 	"instr1F",	mipscoxxx,
75 	"lb",		mipscoload,
76 	"lh",		mipscoload,
77 	"lwl",		mipscoload,
78 	"lw",		mipscoload,
79 	"lbu",		mipscoload,
80 	"lhu",		mipscoload,
81 	"lwr",		mipscoload,
82 	"instr27",	mipscoxxx,
83 	"sb",		mipscoload,
84 	"sh",		mipscoload,
85 	"swl",		mipscoload,
86 	"sw",		mipscoload,
87 	"instr2C",	mipscoxxx,
88 	"instr2D",	mipscoxxx,
89 	"swr",		mipscoload,
90 	"cache",	"",
91 	"ll",		mipscoload,
92 	"lwc1",		mipscoload,
93 	"lwc2",		mipscoload,
94 	"lwc3",		mipscoload,
95 	"instr34",	mipscoxxx,
96 	"ld",		mipscoload,
97 	"ld",		mipscoload,
98 	"ld",		mipscoload,
99 	"sc",		mipscoload,
100 	"swc1",		mipscoload,
101 	"swc2",		mipscoload,
102 	"swc3",		mipscoload,
103 	"instr3C",	mipscoxxx,
104 	"sd",		mipscoload,
105 	"sd",		mipscoload,
106 	"sd",		mipscoload,
107 };
108 
109 static Opcode sopcodes[64] = {
110 	"sll",		"r%d,r%t,$%a",
111 	"special01",	mipscoxxx,
112 	"srl",		"r%d,r%t,$%a",
113 	"sra",		"r%d,r%t,$%a",
114 	"sllv",		"r%d,r%t,R%s",
115 	"special05",	mipscoxxx,
116 	"srlv",		"r%d,r%t,r%s",
117 	"srav",		"r%d,r%t,r%s",
118 	"jr",		"r%s",
119 	"jalr",		"r%d,r%s",
120 	"special0A",	mipscoxxx,
121 	"special0B",	mipscoxxx,
122 	"syscall",	"",
123 	"break",	"",
124 	"special0E",	mipscoxxx,
125 	"sync",		"",
126 	"mfhi",		"r%d",
127 	"mthi",		"r%s",
128 	"mflo",		"r%d",
129 	"mtlo",		"r%s",
130 	"special14",	mipscoxxx,
131 	"special15",	mipscoxxx,
132 	"special16",	mipscoxxx,
133 	"special17",	mipscoxxx,
134 	"mult",		mipscorsrt,
135 	"multu",	mipscorsrt,
136 	"div",		mipscorsrt,
137 	"divu",		mipscorsrt,
138 	"special1C",	mipscoxxx,
139 	"special1D",	mipscoxxx,
140 	"special1E",	mipscoxxx,
141 	"special1F",	mipscoxxx,
142 	"add",		mipscoalu3op,
143 	"addu",		mipscoalu3op,
144 	"sub",		mipscoalu3op,
145 	"subu",		mipscoalu3op,
146 	"and",		mipscoalu3op,
147 	"or",		mipscoalu3op,
148 	"xor",		mipscoalu3op,
149 	"nor",		mipscoalu3op,
150 	"special28",	mipscoxxx,
151 	"special29",	mipscoxxx,
152 	"slt",		mipscoalu3op,
153 	"sltu",		mipscoalu3op,
154 	"special2C",	mipscoxxx,
155 	"special2D",	mipscoxxx,
156 	"special2E",	mipscoxxx,
157 	"special2F",	mipscoxxx,
158 	"tge",		mipscorsrt,
159 	"tgeu",		mipscorsrt,
160 	"tlt",		mipscorsrt,
161 	"tltu",		mipscorsrt,
162 	"teq",		mipscorsrt,
163 	"special35",	mipscoxxx,
164 	"tne",		mipscorsrt,
165 	"special37",	mipscoxxx,
166 	"special38",	mipscoxxx,
167 	"special39",	mipscoxxx,
168 	"special3A",	mipscoxxx,
169 	"special3B",	mipscoxxx,
170 	"special3C",	mipscoxxx,
171 	"special3D",	mipscoxxx,
172 	"special3E",	mipscoxxx,
173 	"special3F",	mipscoxxx,
174 };
175 
176 static Opcode ropcodes[32] = {
177 	"bltz",		mipscoboc0,
178 	"bgez",		mipscoboc0,
179 	"bltzl",	mipscoboc0,
180 	"bgezl",	mipscoboc0,
181 	"regimm04",	mipscoxxx,
182 	"regimm05",	mipscoxxx,
183 	"regimm06",	mipscoxxx,
184 	"regimm07",	mipscoxxx,
185 	"tgei",		mipscorsi,
186 	"tgeiu",	mipscorsi,
187 	"tlti",		mipscorsi,
188 	"tltiu",	mipscorsi,
189 	"teqi",		mipscorsi,
190 	"regimm0D",	mipscoxxx,
191 	"tnei",		mipscorsi,
192 	"regimm0F",	mipscoxxx,
193 	"bltzal",	mipscoboc0,
194 	"bgezal",	mipscoboc0,
195 	"bltzall",	mipscoboc0,
196 	"bgezall",	mipscoboc0,
197 	"regimm14",	mipscoxxx,
198 	"regimm15",	mipscoxxx,
199 	"regimm16",	mipscoxxx,
200 	"regimm17",	mipscoxxx,
201 	"regimm18",	mipscoxxx,
202 	"regimm19",	mipscoxxx,
203 	"regimm1A",	mipscoxxx,
204 	"regimm1B",	mipscoxxx,
205 	"regimm1C",	mipscoxxx,
206 	"regimm1D",	mipscoxxx,
207 	"regimm1E",	mipscoxxx,
208 	"regimm1F",	mipscoxxx,
209 };
210 
211 static Opcode fopcodes[64] = {
212 	"add.%f",	mipscofp3,
213 	"sub.%f",	mipscofp3,
214 	"mul.%f",	mipscofp3,
215 	"div.%f",	mipscofp3,
216 	"sqrt.%f",	mipscofp2,
217 	"abs.%f",	mipscofp2,
218 	"mov.%f",	mipscofp2,
219 	"neg.%f",	mipscofp2,
220 	"finstr08",	mipscoxxx,
221 	"finstr09",	mipscoxxx,
222 	"finstr0A",	mipscoxxx,
223 	"finstr0B",	mipscoxxx,
224 	"round.w.%f",	mipscofp2,
225 	"trunc.w%f",	mipscofp2,
226 	"ceil.w%f",	mipscofp2,
227 	"floor.w%f",	mipscofp2,
228 	"finstr10",	mipscoxxx,
229 	"finstr11",	mipscoxxx,
230 	"finstr12",	mipscoxxx,
231 	"finstr13",	mipscoxxx,
232 	"finstr14",	mipscoxxx,
233 	"finstr15",	mipscoxxx,
234 	"finstr16",	mipscoxxx,
235 	"finstr17",	mipscoxxx,
236 	"finstr18",	mipscoxxx,
237 	"finstr19",	mipscoxxx,
238 	"finstr1A",	mipscoxxx,
239 	"finstr1B",	mipscoxxx,
240 	"finstr1C",	mipscoxxx,
241 	"finstr1D",	mipscoxxx,
242 	"finstr1E",	mipscoxxx,
243 	"finstr1F",	mipscoxxx,
244 	"cvt.s.%f",	mipscofp2,
245 	"cvt.d.%f",	mipscofp2,
246 	"cvt.e.%f",	mipscofp2,
247 	"cvt.q.%f",	mipscofp2,
248 	"cvt.w.%f",	mipscofp2,
249 	"finstr25",	mipscoxxx,
250 	"finstr26",	mipscoxxx,
251 	"finstr27",	mipscoxxx,
252 	"finstr28",	mipscoxxx,
253 	"finstr29",	mipscoxxx,
254 	"finstr2A",	mipscoxxx,
255 	"finstr2B",	mipscoxxx,
256 	"finstr2C",	mipscoxxx,
257 	"finstr2D",	mipscoxxx,
258 	"finstr2E",	mipscoxxx,
259 	"finstr2F",	mipscoxxx,
260 	"c.f.%f",	mipscofpc,
261 	"c.un.%f",	mipscofpc,
262 	"c.eq.%f",	mipscofpc,
263 	"c.ueq.%f",	mipscofpc,
264 	"c.olt.%f",	mipscofpc,
265 	"c.ult.%f",	mipscofpc,
266 	"c.ole.%f",	mipscofpc,
267 	"c.ule.%f",	mipscofpc,
268 	"c.sf.%f",	mipscofpc,
269 	"c.ngle.%f",	mipscofpc,
270 	"c.seq.%f",	mipscofpc,
271 	"c.ngl.%f",	mipscofpc,
272 	"c.lt.%f",	mipscofpc,
273 	"c.nge.%f",	mipscofpc,
274 	"c.le.%f",	mipscofpc,
275 	"c.ngt.%f",	mipscofpc,
276 };
277 
278 static char fsub[16] = {
279 	's', 'd', 'e', 'q', 'w', '?', '?', '?',
280 	'?', '?', '?', '?', '?', '?', '?', '?'
281 };
282 
283 
284 static int
285 mkinstr(Instr *i, Map *map, uvlong pc)
286 {
287 	ulong w;
288 
289 	if (get4(map, pc, &w) < 0) {
290 		werrstr("can't read instruction: %r");
291 		return -1;
292 	}
293 	i->addr = pc;
294 	i->op = (w >> 26) & 0x3F;
295 	i->rs = (w >> 21) & 0x1F;
296 	i->rt = (w >> 16) & 0x1F;
297 	i->rd = (w >> 11) & 0x1F;
298 	i->sa = (w >> 6) & 0x1F;
299 	i->function = w & 0x3F;
300 	i->immediate = w & 0x0000FFFF;
301 	if (i->immediate & 0x8000)
302 		i->immediate |= ~0x0000FFFF;
303 	i->cofun = w & 0x01FFFFFF;
304 	i->target = w & 0x03FFFFFF;
305 	i->w0 = w;
306 	return 1;
307 }
308 
309 #pragma	varargck	argpos	bprint		2
310 
311 static void
312 bprint(Instr *i, char *fmt, ...)
313 {
314 	va_list arg;
315 
316 	va_start(arg, fmt);
317 	i->curr = vseprint(i->curr, i->end, fmt, arg);
318 	va_end(arg);
319 }
320 
321 static void
322 format(char *mnemonic, Instr *i, char *f)
323 {
324 	if (mnemonic)
325 		format(0, i, mnemonic);
326 	if (f == 0)
327 		return;
328 	if (i->curr < i->end)
329 		*i->curr++ = '\t';
330 	for ( ; *f && i->curr < i->end; f++) {
331 		if (*f != '%') {
332 			*i->curr++ = *f;
333 			continue;
334 		}
335 		switch (*++f) {
336 
337 		case 's':
338 			bprint(i, "%d", i->rs);
339 			break;
340 
341 		case 't':
342 			bprint(i, "%d", i->rt);
343 			break;
344 
345 		case 'd':
346 			bprint(i, "%d", i->rd);
347 			break;
348 
349 		case 'a':
350 			bprint(i, "%d", i->sa);
351 			break;
352 
353 		case 'l':
354 			if (i->rs == 30) {
355 				i->curr += symoff(i->curr, i->end-i->curr, i->immediate+mach->sb, CANY);
356 				bprint(i, "(SB)");
357 			} else
358 				bprint(i, "%lx(r%d)", i->immediate, i->rs);
359 			break;
360 
361 		case 'i':
362 			bprint(i, "$%lx", i->immediate);
363 			break;
364 
365 		case 'u':
366 			*i->curr++ = '$';
367 			i->curr += symoff(i->curr, i->end-i->curr, i->immediate, CANY);
368 			bprint(i, "(SB)");
369 			break;
370 
371 		case 'j':
372 			i->curr += symoff(i->curr, i->end-i->curr,
373 				(i->target<<2)|(i->addr & 0xF0000000), CANY);
374 			bprint(i, "(SB)");
375 			break;
376 
377 		case 'b':
378 			i->curr += symoff(i->curr, i->end-i->curr,
379 				(i->immediate<<2)+i->addr+4, CANY);
380 			break;
381 
382 		case 'c':
383 			bprint(i, "%lux", i->cofun);
384 			break;
385 
386 		case 'w':
387 			bprint(i, "[%lux]", i->w0);
388 			break;
389 
390 		case 'f':
391 			*i->curr++ = fsub[i->rs & 0x0F];
392 			break;
393 
394 		case '\0':
395 			*i->curr++ = '%';
396 			return;
397 
398 		default:
399 			bprint(i, "%%%c", *f);
400 			break;
401 		}
402 	}
403 }
404 
405 static void
406 copz(int cop, Instr *i)
407 {
408 	char *f, *m, buf[16];
409 
410 	m = buf;
411 	f = "%t,%d";
412 	switch (i->rs) {
413 
414 	case 0:
415 		sprint(buf, "mfc%d", cop);
416 		break;
417 
418 	case 2:
419 		sprint(buf, "cfc%d", cop);
420 		break;
421 
422 	case 4:
423 		sprint(buf, "mtc%d", cop);
424 		break;
425 
426 	case 6:
427 		sprint(buf, "ctc%d", cop);
428 		break;
429 
430 	case 8:
431 		f = "%b";
432 		switch (i->rt) {
433 
434 		case 0:
435 			sprint(buf, "bc%df", cop);
436 			break;
437 
438 		case 1:
439 			sprint(buf, "bc%dt", cop);
440 			break;
441 
442 		case 2:
443 			sprint(buf, "bc%dfl", cop);
444 			break;
445 
446 		case 3:
447 			sprint(buf, "bc%dtl", cop);
448 			break;
449 
450 		default:
451 			sprint(buf, "cop%d", cop);
452 			f = mipscoxxx;
453 			break;
454 		}
455 		break;
456 
457 	default:
458 		sprint(buf, "cop%d", cop);
459 		if (i->rs & 0x10)
460 			f = "function %c";
461 		else
462 			f = mipscoxxx;
463 		break;
464 	}
465 	format(m, i, f);
466 }
467 
468 static void
469 cop0(Instr *i)
470 {
471 	char *m = 0;
472 
473 	if (i->rs >= 0x10) {
474 		switch (i->cofun) {
475 
476 		case 1:
477 			m = "tlbr";
478 			break;
479 
480 		case 2:
481 			m = "tlbwi";
482 			break;
483 
484 		case 6:
485 			m = "tlbwr";
486 			break;
487 
488 		case 8:
489 			m = "tlbp";
490 			break;
491 
492 		case 16:
493 			m = "rfe";
494 			break;
495 
496 		case 32:
497 			m = "eret";
498 			break;
499 		}
500 		if (m) {
501 			format(m, i, 0);
502 			if (i->curr < i->end)
503 				*i->curr++ = 0;
504 			return;
505 		}
506 	}
507 	copz(0, i);
508 }
509 
510 int
511 _mipscoinst(Map *map, uvlong pc, char *buf, int n)
512 {
513 	Instr i;
514 	Opcode *o;
515 	uchar op;
516 
517 	i.curr = buf;
518 	i.end = buf+n-1;
519 	if (mkinstr(&i, map, pc) < 0)
520 		return -1;
521 	switch (i.op) {
522 
523 	case 0x00:					/* SPECIAL */
524 		o = sopcodes;
525 		op = i.function;
526 		break;
527 
528 	case 0x01:					/* REGIMM */
529 		o = ropcodes;
530 		op = i.rt;
531 		break;
532 
533 	case 0x10:					/* COP0 */
534 		cop0(&i);
535 		return 4;
536 
537 	case 0x11:					/* COP1 */
538 		if (i.rs & 0x10) {
539 			o = fopcodes;
540 			op = i.function;
541 			break;
542 		}
543 		/*FALLTHROUGH*/
544 	case 0x12:					/* COP2 */
545 	case 0x13:					/* COP3 */
546 		copz(i.op-0x10, &i);
547 		return 4;
548 
549 	default:
550 		o = opcodes;
551 		op = i.op;
552 		break;
553 	}
554 	format(o[op].mnemonic, &i, o[op].mipsco);
555 	return 4;
556 }
557