1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
11 #include "lightrec-private.h"
14 static const char * const std_opcodes[] = {
22 [OP_ADDIU] = "addiu ",
24 [OP_SLTIU] = "sltiu ",
45 static const char * const special_opcodes[] = {
46 [OP_SPECIAL_SLL] = "sll ",
47 [OP_SPECIAL_SRL] = "srl ",
48 [OP_SPECIAL_SRA] = "sra ",
49 [OP_SPECIAL_SLLV] = "sllv ",
50 [OP_SPECIAL_SRLV] = "srlv ",
51 [OP_SPECIAL_SRAV] = "srav ",
52 [OP_SPECIAL_JR] = "jr ",
53 [OP_SPECIAL_JALR] = "jalr ",
54 [OP_SPECIAL_SYSCALL] = "syscall ",
55 [OP_SPECIAL_BREAK] = "break ",
56 [OP_SPECIAL_MFHI] = "mfhi ",
57 [OP_SPECIAL_MTHI] = "mthi ",
58 [OP_SPECIAL_MFLO] = "mflo ",
59 [OP_SPECIAL_MTLO] = "mtlo ",
60 [OP_SPECIAL_MULT] = "mult ",
61 [OP_SPECIAL_MULTU] = "multu ",
62 [OP_SPECIAL_DIV] = "div ",
63 [OP_SPECIAL_DIVU] = "divu ",
64 [OP_SPECIAL_ADD] = "add ",
65 [OP_SPECIAL_ADDU] = "addu ",
66 [OP_SPECIAL_SUB] = "sub ",
67 [OP_SPECIAL_SUBU] = "subu ",
68 [OP_SPECIAL_AND] = "and ",
69 [OP_SPECIAL_OR] = "or ",
70 [OP_SPECIAL_XOR] = "xor ",
71 [OP_SPECIAL_NOR] = "nor ",
72 [OP_SPECIAL_SLT] = "slt ",
73 [OP_SPECIAL_SLTU] = "sltu ",
76 static const char * const regimm_opcodes[] = {
77 [OP_REGIMM_BLTZ] = "bltz ",
78 [OP_REGIMM_BGEZ] = "bgez ",
79 [OP_REGIMM_BLTZAL] = "bltzal ",
80 [OP_REGIMM_BGEZAL] = "bgezal ",
83 static const char * const cp0_opcodes[] = {
84 [OP_CP0_MFC0] = "mfc0 ",
85 [OP_CP0_CFC0] = "cfc0 ",
86 [OP_CP0_MTC0] = "mtc0 ",
87 [OP_CP0_CTC0] = "ctc0 ",
91 static const char * const cp2_basic_opcodes[] = {
92 [OP_CP2_BASIC_MFC2] = "mfc2 ",
93 [OP_CP2_BASIC_CFC2] = "cfc2 ",
94 [OP_CP2_BASIC_MTC2] = "mtc2 ",
95 [OP_CP2_BASIC_CTC2] = "ctc2 ",
98 static const char * const cp2_opcodes[] = {
99 [OP_CP2_RTPS] = "rtps ",
100 [OP_CP2_NCLIP] = "nclip ",
102 [OP_CP2_DPCS] = "dpcs ",
103 [OP_CP2_INTPL] = "intpl ",
104 [OP_CP2_MVMVA] = "mvmva ",
105 [OP_CP2_NCDS] = "ncds ",
106 [OP_CP2_CDP] = "cdp ",
107 [OP_CP2_NCDT] = "ncdt ",
108 [OP_CP2_NCCS] = "nccs ",
110 [OP_CP2_NCS] = "ncs ",
111 [OP_CP2_NCT] = "nct ",
112 [OP_CP2_SQR] = "sqr ",
113 [OP_CP2_DCPL] = "dcpl ",
114 [OP_CP2_DPCT] = "dpct ",
115 [OP_CP2_AVSZ3] = "avsz3 ",
116 [OP_CP2_AVSZ4] = "avsz4 ",
117 [OP_CP2_RTPT] = "rtpt ",
118 [OP_CP2_GPF] = "gpf ",
119 [OP_CP2_GPL] = "gpl ",
120 [OP_CP2_NCCT] = "ncct ",
123 static const char * const meta_opcodes[] = {
124 [OP_META_MOV] = "move ",
125 [OP_META_EXTC] = "extc ",
126 [OP_META_EXTS] = "exts ",
127 [OP_META_COM] = "com ",
130 static const char * const mult2_opcodes[] = {
134 static const char * const opcode_flags[] = {
135 "switched branch/DS",
139 static const char * const opcode_io_flags[] = {
140 "self-modifying code",
146 static const char * const opcode_io_modes[] = {
155 static const char * const opcode_branch_flags[] = {
160 static const char * const opcode_multdiv_flags[] = {
166 static size_t do_snprintf(char *buf, size_t len, bool *first,
167 const char *arg1, const char *arg2)
172 bytes = snprintf(buf, len, "(%s%s", arg1, arg2);
174 bytes = snprintf(buf, len, ", %s%s", arg1, arg2);
181 static const char * const reg_op_token[3] = {
185 static int print_flags(char *buf, size_t len, const struct opcode *op,
186 const char * const *array, size_t array_size,
189 const char *flag_name, *io_mode_name;
190 unsigned int i, io_mode;
191 size_t count = 0, bytes;
193 u32 flags = op->flags;
196 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
197 if (!(flags & BIT(i)))
200 if (i < ARRAY_SIZE(opcode_flags))
201 flag_name = opcode_flags[i];
203 flag_name = array[i - ARRAY_SIZE(opcode_flags)];
205 bytes = do_snprintf(buf, len, &first, "", flag_name);
212 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
214 io_mode_name = opcode_io_modes[io_mode - 1];
216 bytes = do_snprintf(buf, len, &first, "", io_mode_name);
223 if (OPT_EARLY_UNLOAD) {
224 reg_op = LIGHTREC_FLAGS_GET_RS(flags);
226 bytes = do_snprintf(buf, len, &first,
227 reg_op_token[reg_op - 1],
228 lightrec_reg_name(op->i.rs));
234 reg_op = LIGHTREC_FLAGS_GET_RT(flags);
236 bytes = do_snprintf(buf, len, &first,
237 reg_op_token[reg_op - 1],
238 lightrec_reg_name(op->i.rt));
244 reg_op = LIGHTREC_FLAGS_GET_RD(flags);
246 bytes = do_snprintf(buf, len, &first,
247 reg_op_token[reg_op - 1],
248 lightrec_reg_name(op->r.rd));
256 count += snprintf(buf, len, ")");
263 static int print_op_special(union code c, char *buf, size_t len,
264 const char * const **flags_ptr, size_t *nb_flags)
270 return snprintf(buf, len, "%s%s,%s,%u",
271 special_opcodes[c.r.op],
272 lightrec_reg_name(c.r.rd),
273 lightrec_reg_name(c.r.rt),
275 case OP_SPECIAL_SLLV:
276 case OP_SPECIAL_SRLV:
277 case OP_SPECIAL_SRAV:
279 case OP_SPECIAL_ADDU:
281 case OP_SPECIAL_SUBU:
287 case OP_SPECIAL_SLTU:
288 return snprintf(buf, len, "%s%s,%s,%s",
289 special_opcodes[c.r.op],
290 lightrec_reg_name(c.r.rd),
291 lightrec_reg_name(c.r.rt),
292 lightrec_reg_name(c.r.rs));
294 *flags_ptr = opcode_branch_flags;
295 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
297 case OP_SPECIAL_MTHI:
298 case OP_SPECIAL_MTLO:
299 return snprintf(buf, len, "%s%s",
300 special_opcodes[c.r.op],
301 lightrec_reg_name(c.r.rs));
302 case OP_SPECIAL_JALR:
303 return snprintf(buf, len, "%s%s,%s",
304 special_opcodes[c.r.op],
305 lightrec_reg_name(c.r.rd),
306 lightrec_reg_name(c.r.rs));
307 case OP_SPECIAL_SYSCALL:
308 case OP_SPECIAL_BREAK:
309 return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
310 case OP_SPECIAL_MFHI:
311 case OP_SPECIAL_MFLO:
312 return snprintf(buf, len, "%s%s",
313 special_opcodes[c.r.op],
314 lightrec_reg_name(c.r.rd));
315 case OP_SPECIAL_MULT:
316 case OP_SPECIAL_MULTU:
318 case OP_SPECIAL_DIVU:
319 *flags_ptr = opcode_multdiv_flags;
320 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
321 return snprintf(buf, len, "%s%s,%s,%s,%s",
322 special_opcodes[c.r.op],
323 lightrec_reg_name(get_mult_div_hi(c)),
324 lightrec_reg_name(get_mult_div_lo(c)),
325 lightrec_reg_name(c.r.rs),
326 lightrec_reg_name(c.r.rt));
328 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
332 static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
337 return snprintf(buf, len, "%s%s,%u",
338 cp2_basic_opcodes[c.i.rs],
339 lightrec_reg_name(c.i.rt),
342 return snprintf(buf, len, "%s", cp2_opcodes[c.r.op]);
350 return snprintf(buf, len, "%s%s,%u",
352 lightrec_reg_name(c.i.rt),
355 return snprintf(buf, len, "rfe ");
357 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
362 static int print_op(union code c, u32 pc, char *buf, size_t len,
363 const char * const **flags_ptr, size_t *nb_flags,
367 return snprintf(buf, len, "nop ");
371 return print_op_special(c, buf, len, flags_ptr, nb_flags);
373 *flags_ptr = opcode_branch_flags;
374 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
375 return snprintf(buf, len, "%s%s,0x%x",
376 regimm_opcodes[c.i.rt],
377 lightrec_reg_name(c.i.rs),
378 pc + 4 + ((s16)c.i.imm << 2));
381 *flags_ptr = opcode_branch_flags;
382 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
383 return snprintf(buf, len, "%s0x%x",
385 (pc & 0xf0000000) | (c.j.imm << 2));
387 if (c.i.rs == c.i.rt) {
388 *flags_ptr = opcode_branch_flags;
389 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
390 return snprintf(buf, len, "b 0x%x",
391 pc + 4 + ((s16)c.i.imm << 2));
397 *flags_ptr = opcode_branch_flags;
398 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
399 return snprintf(buf, len, "%s%s,%s,0x%x",
401 lightrec_reg_name(c.i.rs),
402 lightrec_reg_name(c.i.rt),
403 pc + 4 + ((s16)c.i.imm << 2));
411 return snprintf(buf, len, "%s%s,%s,0x%04hx",
413 lightrec_reg_name(c.i.rt),
414 lightrec_reg_name(c.i.rs),
418 return snprintf(buf, len, "%s%s,0x%04hx",
420 lightrec_reg_name(c.i.rt),
423 return print_op_cp(c, buf, len, 0);
425 return print_op_cp(c, buf, len, 2);
438 *flags_ptr = opcode_io_flags;
439 *nb_flags = ARRAY_SIZE(opcode_io_flags);
441 return snprintf(buf, len, "%s%s,%hd(%s)",
443 lightrec_reg_name(c.i.rt),
445 lightrec_reg_name(c.i.rs));
448 *flags_ptr = opcode_io_flags;
449 *nb_flags = ARRAY_SIZE(opcode_io_flags);
450 return snprintf(buf, len, "%s%s,%hd(%s)",
452 lightrec_reg_name(c.i.rt),
454 lightrec_reg_name(c.i.rs));
456 return snprintf(buf, len, "%s%s,%s",
457 meta_opcodes[c.m.op],
458 lightrec_reg_name(c.m.rd),
459 lightrec_reg_name(c.m.rs));
462 *flags_ptr = opcode_multdiv_flags;
463 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
464 return snprintf(buf, len, "%s%s,%s,%s,%u",
465 mult2_opcodes[c.i.op == OP_META_MULTU2],
466 lightrec_reg_name(get_mult_div_hi(c)),
467 lightrec_reg_name(get_mult_div_lo(c)),
468 lightrec_reg_name(c.r.rs), c.r.op);
470 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
474 void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
476 const struct opcode *op;
477 const char * const *flags_ptr;
478 size_t nb_flags, count, count2;
479 char buf[256], buf2[256], buf3[256];
481 u32 pc, branch_pc, code;
484 for (i = 0; i < block->nb_ops; i++) {
485 op = &block->opcode_list[i];
486 branch_pc = get_branch_pc(block, i, 0);
487 pc = block->pc + (i << 2);
488 code = LE32TOH(code_ptr[i]);
490 count = print_op((union code)code, pc, buf, sizeof(buf),
491 &flags_ptr, &nb_flags, &is_io);
496 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
497 &flags_ptr, &nb_flags, &is_io);
499 if (code == op->c.opcode) {
504 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
506 printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
507 buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);