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 ",
43 [OP_META_MULT2] = "mult2 ",
44 [OP_META_MULTU2] = "multu2 ",
45 [OP_META_LWU] = "lwu ",
46 [OP_META_SWU] = "swu ",
49 static const char * const special_opcodes[] = {
50 [OP_SPECIAL_SLL] = "sll ",
51 [OP_SPECIAL_SRL] = "srl ",
52 [OP_SPECIAL_SRA] = "sra ",
53 [OP_SPECIAL_SLLV] = "sllv ",
54 [OP_SPECIAL_SRLV] = "srlv ",
55 [OP_SPECIAL_SRAV] = "srav ",
56 [OP_SPECIAL_JR] = "jr ",
57 [OP_SPECIAL_JALR] = "jalr ",
58 [OP_SPECIAL_SYSCALL] = "syscall ",
59 [OP_SPECIAL_BREAK] = "break ",
60 [OP_SPECIAL_MFHI] = "mfhi ",
61 [OP_SPECIAL_MTHI] = "mthi ",
62 [OP_SPECIAL_MFLO] = "mflo ",
63 [OP_SPECIAL_MTLO] = "mtlo ",
64 [OP_SPECIAL_MULT] = "mult ",
65 [OP_SPECIAL_MULTU] = "multu ",
66 [OP_SPECIAL_DIV] = "div ",
67 [OP_SPECIAL_DIVU] = "divu ",
68 [OP_SPECIAL_ADD] = "add ",
69 [OP_SPECIAL_ADDU] = "addu ",
70 [OP_SPECIAL_SUB] = "sub ",
71 [OP_SPECIAL_SUBU] = "subu ",
72 [OP_SPECIAL_AND] = "and ",
73 [OP_SPECIAL_OR] = "or ",
74 [OP_SPECIAL_XOR] = "xor ",
75 [OP_SPECIAL_NOR] = "nor ",
76 [OP_SPECIAL_SLT] = "slt ",
77 [OP_SPECIAL_SLTU] = "sltu ",
80 static const char * const regimm_opcodes[] = {
81 [OP_REGIMM_BLTZ] = "bltz ",
82 [OP_REGIMM_BGEZ] = "bgez ",
83 [OP_REGIMM_BLTZAL] = "bltzal ",
84 [OP_REGIMM_BGEZAL] = "bgezal ",
87 static const char * const cp0_opcodes[] = {
88 [OP_CP0_MFC0] = "mfc0 ",
89 [OP_CP0_CFC0] = "cfc0 ",
90 [OP_CP0_MTC0] = "mtc0 ",
91 [OP_CP0_CTC0] = "ctc0 ",
95 static const char * const cp2_basic_opcodes[] = {
96 [OP_CP2_BASIC_MFC2] = "mfc2 ",
97 [OP_CP2_BASIC_CFC2] = "cfc2 ",
98 [OP_CP2_BASIC_MTC2] = "mtc2 ",
99 [OP_CP2_BASIC_CTC2] = "ctc2 ",
102 static const char * const cp2_opcodes[] = {
103 [OP_CP2_RTPS] = "rtps ",
104 [OP_CP2_NCLIP] = "nclip ",
106 [OP_CP2_DPCS] = "dpcs ",
107 [OP_CP2_INTPL] = "intpl ",
108 [OP_CP2_MVMVA] = "mvmva ",
109 [OP_CP2_NCDS] = "ncds ",
110 [OP_CP2_CDP] = "cdp ",
111 [OP_CP2_NCDT] = "ncdt ",
112 [OP_CP2_NCCS] = "nccs ",
114 [OP_CP2_NCS] = "ncs ",
115 [OP_CP2_NCT] = "nct ",
116 [OP_CP2_SQR] = "sqr ",
117 [OP_CP2_DCPL] = "dcpl ",
118 [OP_CP2_DPCT] = "dpct ",
119 [OP_CP2_AVSZ3] = "avsz3 ",
120 [OP_CP2_AVSZ4] = "avsz4 ",
121 [OP_CP2_RTPT] = "rtpt ",
122 [OP_CP2_GPF] = "gpf ",
123 [OP_CP2_GPL] = "gpl ",
124 [OP_CP2_NCCT] = "ncct ",
127 static const char * const meta_opcodes[] = {
128 [OP_META_MOV] = "move ",
129 [OP_META_EXTC] = "extc ",
130 [OP_META_EXTS] = "exts ",
131 [OP_META_COM] = "com ",
134 static const char * const mult2_opcodes[] = {
138 static const char * const opcode_flags[] = {
139 "switched branch/DS",
143 static const char * const opcode_io_flags[] = {
144 "self-modifying code",
150 static const char * const opcode_io_modes[] = {
159 static const char * const opcode_branch_flags[] = {
164 static const char * const opcode_movi_flags[] = {
168 static const char * const opcode_multdiv_flags[] = {
174 static size_t do_snprintf(char *buf, size_t len, bool *first,
175 const char *arg1, const char *arg2)
180 bytes = snprintf(buf, len, "(%s%s", arg1, arg2);
182 bytes = snprintf(buf, len, ", %s%s", arg1, arg2);
189 static const char * const reg_op_token[3] = {
193 static int print_flags(char *buf, size_t len, const struct opcode *op,
194 const char * const *array, size_t array_size,
197 const char *flag_name, *io_mode_name;
198 unsigned int i, io_mode;
199 size_t count = 0, bytes;
201 u32 flags = op->flags;
204 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
205 if (!(flags & BIT(i)))
208 if (i < ARRAY_SIZE(opcode_flags))
209 flag_name = opcode_flags[i];
211 flag_name = array[i - ARRAY_SIZE(opcode_flags)];
213 bytes = do_snprintf(buf, len, &first, "", flag_name);
220 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
222 io_mode_name = opcode_io_modes[io_mode - 1];
224 bytes = do_snprintf(buf, len, &first, "", io_mode_name);
231 if (OPT_EARLY_UNLOAD) {
232 reg_op = LIGHTREC_FLAGS_GET_RS(flags);
234 bytes = do_snprintf(buf, len, &first,
235 reg_op_token[reg_op - 1],
236 lightrec_reg_name(op->i.rs));
242 reg_op = LIGHTREC_FLAGS_GET_RT(flags);
244 bytes = do_snprintf(buf, len, &first,
245 reg_op_token[reg_op - 1],
246 lightrec_reg_name(op->i.rt));
252 reg_op = LIGHTREC_FLAGS_GET_RD(flags);
254 bytes = do_snprintf(buf, len, &first,
255 reg_op_token[reg_op - 1],
256 lightrec_reg_name(op->r.rd));
264 count += snprintf(buf, len, ")");
271 static int print_op_special(union code c, char *buf, size_t len,
272 const char * const **flags_ptr, size_t *nb_flags)
278 return snprintf(buf, len, "%s%s,%s,%u",
279 special_opcodes[c.r.op],
280 lightrec_reg_name(c.r.rd),
281 lightrec_reg_name(c.r.rt),
283 case OP_SPECIAL_SLLV:
284 case OP_SPECIAL_SRLV:
285 case OP_SPECIAL_SRAV:
287 case OP_SPECIAL_ADDU:
289 case OP_SPECIAL_SUBU:
295 case OP_SPECIAL_SLTU:
296 return snprintf(buf, len, "%s%s,%s,%s",
297 special_opcodes[c.r.op],
298 lightrec_reg_name(c.r.rd),
299 lightrec_reg_name(c.r.rt),
300 lightrec_reg_name(c.r.rs));
302 *flags_ptr = opcode_branch_flags;
303 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
305 case OP_SPECIAL_MTHI:
306 case OP_SPECIAL_MTLO:
307 return snprintf(buf, len, "%s%s",
308 special_opcodes[c.r.op],
309 lightrec_reg_name(c.r.rs));
310 case OP_SPECIAL_JALR:
311 return snprintf(buf, len, "%s%s,%s",
312 special_opcodes[c.r.op],
313 lightrec_reg_name(c.r.rd),
314 lightrec_reg_name(c.r.rs));
315 case OP_SPECIAL_SYSCALL:
316 case OP_SPECIAL_BREAK:
317 return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
318 case OP_SPECIAL_MFHI:
319 case OP_SPECIAL_MFLO:
320 return snprintf(buf, len, "%s%s",
321 special_opcodes[c.r.op],
322 lightrec_reg_name(c.r.rd));
323 case OP_SPECIAL_MULT:
324 case OP_SPECIAL_MULTU:
326 case OP_SPECIAL_DIVU:
327 *flags_ptr = opcode_multdiv_flags;
328 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
329 return snprintf(buf, len, "%s%s,%s,%s,%s",
330 special_opcodes[c.r.op],
331 lightrec_reg_name(get_mult_div_hi(c)),
332 lightrec_reg_name(get_mult_div_lo(c)),
333 lightrec_reg_name(c.r.rs),
334 lightrec_reg_name(c.r.rt));
336 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
340 static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
345 return snprintf(buf, len, "%s%s,%u",
346 cp2_basic_opcodes[c.i.rs],
347 lightrec_reg_name(c.i.rt),
350 return snprintf(buf, len, "%s", cp2_opcodes[c.r.op]);
358 return snprintf(buf, len, "%s%s,%u",
360 lightrec_reg_name(c.i.rt),
363 return snprintf(buf, len, "rfe ");
365 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
370 static int print_op(union code c, u32 pc, char *buf, size_t len,
371 const char * const **flags_ptr, size_t *nb_flags,
375 return snprintf(buf, len, "nop ");
379 return print_op_special(c, buf, len, flags_ptr, nb_flags);
381 *flags_ptr = opcode_branch_flags;
382 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
383 return snprintf(buf, len, "%s%s,0x%x",
384 regimm_opcodes[c.i.rt],
385 lightrec_reg_name(c.i.rs),
386 pc + 4 + ((s16)c.i.imm << 2));
389 *flags_ptr = opcode_branch_flags;
390 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
391 return snprintf(buf, len, "%s0x%x",
393 (pc & 0xf0000000) | (c.j.imm << 2));
395 if (c.i.rs == c.i.rt) {
396 *flags_ptr = opcode_branch_flags;
397 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
398 return snprintf(buf, len, "b 0x%x",
399 pc + 4 + ((s16)c.i.imm << 2));
405 *flags_ptr = opcode_branch_flags;
406 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
407 return snprintf(buf, len, "%s%s,%s,0x%x",
409 lightrec_reg_name(c.i.rs),
410 lightrec_reg_name(c.i.rt),
411 pc + 4 + ((s16)c.i.imm << 2));
415 *flags_ptr = opcode_movi_flags;
416 *nb_flags = ARRAY_SIZE(opcode_movi_flags);
422 return snprintf(buf, len, "%s%s,%s,0x%04hx",
424 lightrec_reg_name(c.i.rt),
425 lightrec_reg_name(c.i.rs),
429 *flags_ptr = opcode_movi_flags;
430 *nb_flags = ARRAY_SIZE(opcode_movi_flags);
431 return snprintf(buf, len, "%s%s,0x%04hx",
433 lightrec_reg_name(c.i.rt),
436 return print_op_cp(c, buf, len, 0);
438 return print_op_cp(c, buf, len, 2);
453 *flags_ptr = opcode_io_flags;
454 *nb_flags = ARRAY_SIZE(opcode_io_flags);
456 return snprintf(buf, len, "%s%s,%hd(%s)",
458 lightrec_reg_name(c.i.rt),
460 lightrec_reg_name(c.i.rs));
463 *flags_ptr = opcode_io_flags;
464 *nb_flags = ARRAY_SIZE(opcode_io_flags);
465 return snprintf(buf, len, "%s%s,%hd(%s)",
467 lightrec_reg_name(c.i.rt),
469 lightrec_reg_name(c.i.rs));
471 return snprintf(buf, len, "%s%s,%s",
472 meta_opcodes[c.m.op],
473 lightrec_reg_name(c.m.rd),
474 lightrec_reg_name(c.m.rs));
477 *flags_ptr = opcode_multdiv_flags;
478 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
479 return snprintf(buf, len, "%s%s,%s,%s,%u",
480 mult2_opcodes[c.i.op == OP_META_MULTU2],
481 lightrec_reg_name(get_mult_div_hi(c)),
482 lightrec_reg_name(get_mult_div_lo(c)),
483 lightrec_reg_name(c.r.rs), c.r.op);
485 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
489 void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
491 const struct opcode *op;
492 const char * const *flags_ptr;
493 size_t nb_flags, count, count2;
494 char buf[256], buf2[256], buf3[256];
496 u32 pc, branch_pc, code;
499 for (i = 0; i < block->nb_ops; i++) {
500 op = &block->opcode_list[i];
501 branch_pc = get_branch_pc(block, i, 0);
502 pc = block->pc + (i << 2);
503 code = LE32TOH(code_ptr[i]);
505 count = print_op((union code)code, pc, buf, sizeof(buf),
506 &flags_ptr, &nb_flags, &is_io);
511 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
512 &flags_ptr, &nb_flags, &is_io);
514 if (code == op->c.opcode) {
519 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
521 printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
522 buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);