xref: /plan9/sys/src/libmach/vcodas.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 	long 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, ulong pc)
286 {
287 	long 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 static void
310 bprint(Instr *i, char *fmt, ...)
311 {
312 	va_list arg;
313 
314 	va_start(arg, fmt);
315 	i->curr = vseprint(i->curr, i->end, fmt, arg);
316 	va_end(arg);
317 }
318 
319 static void
320 format(char *mnemonic, Instr *i, char *f)
321 {
322 	if (mnemonic)
323 		format(0, i, mnemonic);
324 	if (f == 0)
325 		return;
326 	if (i->curr < i->end)
327 		*i->curr++ = '\t';
328 	for ( ; *f && i->curr < i->end; f++) {
329 		if (*f != '%') {
330 			*i->curr++ = *f;
331 			continue;
332 		}
333 		switch (*++f) {
334 
335 		case 's':
336 			bprint(i, "%d", i->rs);
337 			break;
338 
339 		case 't':
340 			bprint(i, "%d", i->rt);
341 			break;
342 
343 		case 'd':
344 			bprint(i, "%d", i->rd);
345 			break;
346 
347 		case 'a':
348 			bprint(i, "%d", i->sa);
349 			break;
350 
351 		case 'l':
352 			if (i->rs == 30) {
353 				i->curr += symoff(i->curr, i->end-i->curr, i->immediate+mach->sb, CANY);
354 				bprint(i, "(SB)");
355 			} else
356 				bprint(i, "%lx(r%d)", i->immediate, i->rs);
357 			break;
358 
359 		case 'i':
360 			bprint(i, "$%lx", i->immediate);
361 			break;
362 
363 		case 'u':
364 			*i->curr++ = '$';
365 			i->curr += symoff(i->curr, i->end-i->curr, i->immediate, CANY);
366 			bprint(i, "(SB)");
367 			break;
368 
369 		case 'j':
370 			i->curr += symoff(i->curr, i->end-i->curr,
371 				(i->target<<2)|(i->addr & 0xF0000000), CANY);
372 			bprint(i, "(SB)");
373 			break;
374 
375 		case 'b':
376 			i->curr += symoff(i->curr, i->end-i->curr,
377 				(i->immediate<<2)+i->addr+4, CANY);
378 			break;
379 
380 		case 'c':
381 			bprint(i, "%lux", i->cofun);
382 			break;
383 
384 		case 'w':
385 			bprint(i, "[%lux]", i->w0);
386 			break;
387 
388 		case 'f':
389 			*i->curr++ = fsub[i->rs & 0x0F];
390 			break;
391 
392 		case '\0':
393 			*i->curr++ = '%';
394 			return;
395 
396 		default:
397 			bprint(i, "%%%c", *f);
398 			break;
399 		}
400 	}
401 }
402 
403 static void
404 copz(int cop, Instr *i)
405 {
406 	char *f, *m, buf[16];
407 
408 	m = buf;
409 	f = "%t,%d";
410 	switch (i->rs) {
411 
412 	case 0:
413 		sprint(buf, "mfc%d", cop);
414 		break;
415 
416 	case 2:
417 		sprint(buf, "cfc%d", cop);
418 		break;
419 
420 	case 4:
421 		sprint(buf, "mtc%d", cop);
422 		break;
423 
424 	case 6:
425 		sprint(buf, "ctc%d", cop);
426 		break;
427 
428 	case 8:
429 		f = "%b";
430 		switch (i->rt) {
431 
432 		case 0:
433 			sprint(buf, "bc%df", cop);
434 			break;
435 
436 		case 1:
437 			sprint(buf, "bc%dt", cop);
438 			break;
439 
440 		case 2:
441 			sprint(buf, "bc%dfl", cop);
442 			break;
443 
444 		case 3:
445 			sprint(buf, "bc%dtl", cop);
446 			break;
447 
448 		default:
449 			sprint(buf, "cop%d", cop);
450 			f = mipscoxxx;
451 			break;
452 		}
453 		break;
454 
455 	default:
456 		sprint(buf, "cop%d", cop);
457 		if (i->rs & 0x10)
458 			f = "function %c";
459 		else
460 			f = mipscoxxx;
461 		break;
462 	}
463 	format(m, i, f);
464 }
465 
466 static void
467 cop0(Instr *i)
468 {
469 	char *m = 0;
470 
471 	if (i->rs >= 0x10) {
472 		switch (i->cofun) {
473 
474 		case 1:
475 			m = "tlbr";
476 			break;
477 
478 		case 2:
479 			m = "tlbwi";
480 			break;
481 
482 		case 6:
483 			m = "tlbwr";
484 			break;
485 
486 		case 8:
487 			m = "tlbp";
488 			break;
489 
490 		case 16:
491 			m = "rfe";
492 			break;
493 
494 		case 32:
495 			m = "eret";
496 			break;
497 		}
498 		if (m) {
499 			format(m, i, 0);
500 			if (i->curr < i->end)
501 				*i->curr++ = 0;
502 			return;
503 		}
504 	}
505 	copz(0, i);
506 }
507 
508 int
509 _mipscoinst(Map *map, ulong pc, char *buf, int n)
510 {
511 	Instr i;
512 	Opcode *o;
513 	uchar op;
514 
515 	i.curr = buf;
516 	i.end = buf+n-1;
517 	if (mkinstr(&i, map, pc) < 0)
518 		return -1;
519 	switch (i.op) {
520 
521 	case 0x00:					/* SPECIAL */
522 		o = sopcodes;
523 		op = i.function;
524 		break;
525 
526 	case 0x01:					/* REGIMM */
527 		o = ropcodes;
528 		op = i.rt;
529 		break;
530 
531 	case 0x10:					/* COP0 */
532 		cop0(&i);
533 		return 4;
534 
535 	case 0x11:					/* COP1 */
536 		if (i.rs & 0x10) {
537 			o = fopcodes;
538 			op = i.function;
539 			break;
540 		}
541 		/*FALLTHROUGH*/
542 	case 0x12:					/* COP2 */
543 	case 0x13:					/* COP3 */
544 		copz(i.op-0x10, &i);
545 		return 4;
546 
547 	default:
548 		o = opcodes;
549 		op = i.op;
550 		break;
551 	}
552 	format(o[op].mnemonic, &i, o[op].mipsco);
553 	return 4;
554 }
555