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 *std_opcodes[] = {
22 [OP_ADDIU] = "addiu ",
24 [OP_SLTIU] = "sltiu ",
45 static const char *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 *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 *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 *cp2_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 *opcode_flags[] = {
106 static const char *opcode_io_flags[] = {
107 "self-modifying code",
112 static const char *opcode_io_modes[] = {
120 static const char *opcode_branch_flags[] = {
125 static const char *opcode_multdiv_flags[] = {
131 static int print_flags(char *buf, size_t len, u16 flags,
132 const char **array, size_t array_size,
135 const char *flag_name, *io_mode_name;
136 unsigned int i, io_mode;
137 size_t count = 0, bytes;
140 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
141 if (!(flags & BIT(i)))
144 if (i < ARRAY_SIZE(opcode_flags))
145 flag_name = opcode_flags[i];
147 flag_name = array[i - ARRAY_SIZE(opcode_flags)];
150 bytes = snprintf(buf, len, "(%s", flag_name);
152 bytes = snprintf(buf, len, ", %s", flag_name);
161 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
163 io_mode_name = opcode_io_modes[io_mode - 1];
166 bytes = snprintf(buf, len, "(%s", io_mode_name);
168 bytes = snprintf(buf, len, ", %s", io_mode_name);
178 count += snprintf(buf, len, ")");
185 static int print_op_special(union code c, char *buf, size_t len,
186 const char ***flags_ptr, size_t *nb_flags)
192 return snprintf(buf, len, "%s%s,%s,%u",
193 special_opcodes[c.r.op],
194 lightrec_reg_name(c.r.rd),
195 lightrec_reg_name(c.r.rt),
197 case OP_SPECIAL_SLLV:
198 case OP_SPECIAL_SRLV:
199 case OP_SPECIAL_SRAV:
201 case OP_SPECIAL_ADDU:
203 case OP_SPECIAL_SUBU:
209 case OP_SPECIAL_SLTU:
210 return snprintf(buf, len, "%s%s,%s,%s",
211 special_opcodes[c.r.op],
212 lightrec_reg_name(c.r.rd),
213 lightrec_reg_name(c.r.rt),
214 lightrec_reg_name(c.r.rs));
216 *flags_ptr = opcode_branch_flags;
217 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
219 case OP_SPECIAL_MTHI:
220 case OP_SPECIAL_MTLO:
221 return snprintf(buf, len, "%s%s",
222 special_opcodes[c.r.op],
223 lightrec_reg_name(c.r.rs));
224 case OP_SPECIAL_JALR:
225 return snprintf(buf, len, "%s%s,%s",
226 special_opcodes[c.r.op],
227 lightrec_reg_name(c.r.rd),
228 lightrec_reg_name(c.r.rt));
229 case OP_SPECIAL_SYSCALL:
230 case OP_SPECIAL_BREAK:
231 return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
232 case OP_SPECIAL_MFHI:
233 case OP_SPECIAL_MFLO:
234 return snprintf(buf, len, "%s%s",
235 special_opcodes[c.r.op],
236 lightrec_reg_name(c.r.rd));
237 case OP_SPECIAL_MULT:
238 case OP_SPECIAL_MULTU:
240 case OP_SPECIAL_DIVU:
241 *flags_ptr = opcode_multdiv_flags;
242 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
243 return snprintf(buf, len, "%s%s,%s,%s,%s",
244 special_opcodes[c.r.op],
245 lightrec_reg_name(get_mult_div_hi(c)),
246 lightrec_reg_name(get_mult_div_lo(c)),
247 lightrec_reg_name(c.r.rs),
248 lightrec_reg_name(c.r.rt));
250 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
254 static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
262 return snprintf(buf, len, "%s%s,%u",
264 lightrec_reg_name(c.i.rt),
267 return snprintf(buf, len, "cp2 (0x%08x)", c.opcode);
275 return snprintf(buf, len, "%s%s,%u",
277 lightrec_reg_name(c.i.rt),
280 return snprintf(buf, len, "rfe ");
282 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
287 static int print_op(union code c, u32 pc, char *buf, size_t len,
288 const char ***flags_ptr, size_t *nb_flags,
292 return snprintf(buf, len, "nop ");
296 return print_op_special(c, buf, len, flags_ptr, nb_flags);
298 *flags_ptr = opcode_branch_flags;
299 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
300 return snprintf(buf, len, "%s%s,0x%x",
301 regimm_opcodes[c.i.rt],
302 lightrec_reg_name(c.i.rs),
303 pc + 4 + ((s16)c.i.imm << 2));
306 *flags_ptr = opcode_branch_flags;
307 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
308 return snprintf(buf, len, "%s0x%x",
310 (pc & 0xf0000000) | (c.j.imm << 2));
315 *flags_ptr = opcode_branch_flags;
316 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
317 return snprintf(buf, len, "%s%s,%s,0x%x",
319 lightrec_reg_name(c.i.rs),
320 lightrec_reg_name(c.i.rt),
321 pc + 4 + ((s16)c.i.imm << 2));
329 return snprintf(buf, len, "%s%s,%s,0x%04hx",
331 lightrec_reg_name(c.i.rt),
332 lightrec_reg_name(c.i.rs),
336 return snprintf(buf, len, "%s%s,0x%04hx",
338 lightrec_reg_name(c.i.rt),
341 return print_op_cp(c, buf, len, 0);
343 return print_op_cp(c, buf, len, 2);
356 *flags_ptr = opcode_io_flags;
357 *nb_flags = ARRAY_SIZE(opcode_io_flags);
359 return snprintf(buf, len, "%s%s,%hd(%s)",
361 lightrec_reg_name(c.i.rt),
363 lightrec_reg_name(c.i.rs));
366 *flags_ptr = opcode_io_flags;
367 *nb_flags = ARRAY_SIZE(opcode_io_flags);
368 return snprintf(buf, len, "%s%s,%hd(%s)",
370 lightrec_reg_name(c.i.rt),
372 lightrec_reg_name(c.i.rs));
374 return snprintf(buf, len, "move %s,%s",
375 lightrec_reg_name(c.r.rd),
376 lightrec_reg_name(c.r.rs));
378 return snprintf(buf, len, "extc %s,%s",
379 lightrec_reg_name(c.i.rt),
380 lightrec_reg_name(c.i.rs));
382 return snprintf(buf, len, "exts %s,%s",
383 lightrec_reg_name(c.i.rt),
384 lightrec_reg_name(c.i.rs));
386 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
390 void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
392 const struct opcode *op;
393 const char **flags_ptr;
394 size_t nb_flags, count, count2;
395 char buf[256], buf2[256], buf3[256];
397 u32 pc, branch_pc, code;
400 for (i = 0; i < block->nb_ops; i++) {
401 op = &block->opcode_list[i];
402 branch_pc = get_branch_pc(block, i, 0);
403 pc = block->pc + (i << 2);
404 code = LE32TOH(code_ptr[i]);
406 count = print_op((union code)code, pc, buf, sizeof(buf),
407 &flags_ptr, &nb_flags, &is_io);
412 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
413 &flags_ptr, &nb_flags, &is_io);
415 if (code == op->c.opcode) {
420 print_flags(buf3, sizeof(buf3), op->flags, flags_ptr, nb_flags,
423 printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
424 buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);