Update lightrec 20220716 (#672)
[pcsx_rearmed.git] / deps / lightrec / disassembler.c
CommitLineData
98fa08a5 1// SPDX-License-Identifier: LGPL-2.1-or-later
d16005f8 2/*
98fa08a5 3 * Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
d16005f8
PC
4 */
5
d16005f8 6#include <stdbool.h>
98fa08a5 7#include <stdio.h>
d16005f8
PC
8#include <stdlib.h>
9#include <string.h>
10
d16005f8 11#include "lightrec-private.h"
98fa08a5 12#include "regcache.h"
d16005f8 13
98fa08a5
PC
14static const char *std_opcodes[] = {
15 [OP_J] = "j ",
16 [OP_JAL] = "jal ",
17 [OP_BEQ] = "beq ",
18 [OP_BNE] = "bne ",
19 [OP_BLEZ] = "blez ",
20 [OP_BGTZ] = "bgtz ",
21 [OP_ADDI] = "addi ",
22 [OP_ADDIU] = "addiu ",
23 [OP_SLTI] = "slti ",
24 [OP_SLTIU] = "sltiu ",
25 [OP_ANDI] = "andi ",
26 [OP_ORI] = "ori ",
27 [OP_XORI] = "xori ",
28 [OP_LUI] = "lui ",
29 [OP_LB] = "lb ",
30 [OP_LH] = "lh ",
31 [OP_LWL] = "lwl ",
32 [OP_LW] = "lw ",
33 [OP_LBU] = "lbu ",
34 [OP_LHU] = "lhu ",
35 [OP_LWR] = "lwr ",
36 [OP_SB] = "sb ",
37 [OP_SH] = "sh ",
38 [OP_SWL] = "swl ",
39 [OP_SW] = "sw ",
40 [OP_SWR] = "swr ",
41 [OP_LWC2] = "lwc2 ",
42 [OP_SWC2] = "swc2 ",
43};
d16005f8 44
98fa08a5
PC
45static 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 ",
74};
d16005f8 75
98fa08a5
PC
76static const char *regimm_opcodes[] = {
77 [OP_REGIMM_BLTZ] = "bltz ",
78 [OP_REGIMM_BGEZ] = "bgez ",
79 [OP_REGIMM_BLTZAL] = "bltzal ",
80 [OP_REGIMM_BGEZAL] = "bgezal ",
81};
d16005f8 82
98fa08a5
PC
83static const char *cp0_opcodes[] = {
84 [OP_CP0_MFC0] = "mfc0 ",
85 [OP_CP0_CFC0] = "cfc0 ",
86 [OP_CP0_MTC0] = "mtc0 ",
87 [OP_CP0_CTC0] = "ctc0 ",
88 [OP_CP0_RFE] = "rfe",
89};
90
91static 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 ",
96};
97
98static const char *opcode_flags[] = {
99 "switched branch/DS",
98fa08a5
PC
100 "sync point",
101};
102
103static const char *opcode_io_flags[] = {
98fa08a5
PC
104 "self-modifying code",
105 "no invalidation",
22eee2ac
PC
106 "no mask",
107};
108
109static const char *opcode_io_modes[] = {
110 "Memory access",
111 "I/O access",
112 "RAM access",
113 "BIOS access",
114 "Scratchpad access",
98fa08a5 115};
d16005f8 116
98fa08a5
PC
117static const char *opcode_branch_flags[] = {
118 "emulate branch",
119 "local branch",
120};
121
122static const char *opcode_multdiv_flags[] = {
123 "No LO",
124 "No HI",
125 "No div check",
126};
127
03535202
PC
128static size_t do_snprintf(char *buf, size_t len, bool *first,
129 const char *arg1, const char *arg2)
130{
131 size_t bytes;
132
133 if (*first)
134 bytes = snprintf(buf, len, "(%s%s", arg1, arg2);
135 else
136 bytes = snprintf(buf, len, ", %s%s", arg1, arg2);
137
138 *first = false;
139
140 return bytes;
141}
142
143static const char * const reg_op_token[3] = {
144 "-", "*", "~",
145};
146
147static int print_flags(char *buf, size_t len, const struct opcode *op,
22eee2ac
PC
148 const char **array, size_t array_size,
149 bool is_io)
d16005f8 150{
22eee2ac
PC
151 const char *flag_name, *io_mode_name;
152 unsigned int i, io_mode;
98fa08a5
PC
153 size_t count = 0, bytes;
154 bool first = true;
03535202
PC
155 u32 flags = op->flags;
156 unsigned int reg_op;
d16005f8 157
98fa08a5
PC
158 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
159 if (!(flags & BIT(i)))
160 continue;
d16005f8 161
98fa08a5
PC
162 if (i < ARRAY_SIZE(opcode_flags))
163 flag_name = opcode_flags[i];
d16005f8 164 else
98fa08a5
PC
165 flag_name = array[i - ARRAY_SIZE(opcode_flags)];
166
03535202 167 bytes = do_snprintf(buf, len, &first, "", flag_name);
98fa08a5
PC
168 buf += bytes;
169 len -= bytes;
170 count += bytes;
d16005f8
PC
171 }
172
22eee2ac
PC
173 if (is_io) {
174 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
175 if (io_mode > 0) {
176 io_mode_name = opcode_io_modes[io_mode - 1];
177
03535202
PC
178 bytes = do_snprintf(buf, len, &first, "", io_mode_name);
179 buf += bytes;
180 len -= bytes;
181 count += bytes;
182 }
183 }
184
185 if (OPT_EARLY_UNLOAD) {
186 reg_op = LIGHTREC_FLAGS_GET_RS(flags);
187 if (reg_op) {
188 bytes = do_snprintf(buf, len, &first,
189 reg_op_token[reg_op - 1],
190 lightrec_reg_name(op->i.rs));
191 buf += bytes;
192 len -= bytes;
193 count += bytes;
194 }
195
196 reg_op = LIGHTREC_FLAGS_GET_RT(flags);
197 if (reg_op) {
198 bytes = do_snprintf(buf, len, &first,
199 reg_op_token[reg_op - 1],
200 lightrec_reg_name(op->i.rt));
201 buf += bytes;
202 len -= bytes;
203 count += bytes;
204 }
22eee2ac 205
03535202
PC
206 reg_op = LIGHTREC_FLAGS_GET_RD(flags);
207 if (reg_op) {
208 bytes = do_snprintf(buf, len, &first,
209 reg_op_token[reg_op - 1],
210 lightrec_reg_name(op->r.rd));
22eee2ac
PC
211 buf += bytes;
212 len -= bytes;
213 count += bytes;
214 }
215 }
216
98fa08a5
PC
217 if (!first)
218 count += snprintf(buf, len, ")");
219 else
220 *buf = '\0';
d16005f8 221
98fa08a5 222 return count;
d16005f8
PC
223}
224
98fa08a5
PC
225static int print_op_special(union code c, char *buf, size_t len,
226 const char ***flags_ptr, size_t *nb_flags)
d16005f8 227{
98fa08a5
PC
228 switch (c.r.op) {
229 case OP_SPECIAL_SLL:
230 case OP_SPECIAL_SRL:
231 case OP_SPECIAL_SRA:
232 return snprintf(buf, len, "%s%s,%s,%u",
233 special_opcodes[c.r.op],
234 lightrec_reg_name(c.r.rd),
235 lightrec_reg_name(c.r.rt),
236 c.r.imm);
237 case OP_SPECIAL_SLLV:
238 case OP_SPECIAL_SRLV:
239 case OP_SPECIAL_SRAV:
240 case OP_SPECIAL_ADD:
241 case OP_SPECIAL_ADDU:
242 case OP_SPECIAL_SUB:
243 case OP_SPECIAL_SUBU:
244 case OP_SPECIAL_AND:
245 case OP_SPECIAL_OR:
246 case OP_SPECIAL_XOR:
247 case OP_SPECIAL_NOR:
248 case OP_SPECIAL_SLT:
249 case OP_SPECIAL_SLTU:
250 return snprintf(buf, len, "%s%s,%s,%s",
251 special_opcodes[c.r.op],
252 lightrec_reg_name(c.r.rd),
253 lightrec_reg_name(c.r.rt),
254 lightrec_reg_name(c.r.rs));
255 case OP_SPECIAL_JR:
d8b04acd
PC
256 *flags_ptr = opcode_branch_flags;
257 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
258 fallthrough;
98fa08a5
PC
259 case OP_SPECIAL_MTHI:
260 case OP_SPECIAL_MTLO:
261 return snprintf(buf, len, "%s%s",
262 special_opcodes[c.r.op],
263 lightrec_reg_name(c.r.rs));
264 case OP_SPECIAL_JALR:
265 return snprintf(buf, len, "%s%s,%s",
266 special_opcodes[c.r.op],
267 lightrec_reg_name(c.r.rd),
268 lightrec_reg_name(c.r.rt));
269 case OP_SPECIAL_SYSCALL:
270 case OP_SPECIAL_BREAK:
271 return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
272 case OP_SPECIAL_MFHI:
273 case OP_SPECIAL_MFLO:
274 return snprintf(buf, len, "%s%s",
275 special_opcodes[c.r.op],
276 lightrec_reg_name(c.r.rd));
277 case OP_SPECIAL_MULT:
278 case OP_SPECIAL_MULTU:
279 case OP_SPECIAL_DIV:
280 case OP_SPECIAL_DIVU:
281 *flags_ptr = opcode_multdiv_flags;
282 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
283 return snprintf(buf, len, "%s%s,%s,%s,%s",
284 special_opcodes[c.r.op],
285 lightrec_reg_name(get_mult_div_hi(c)),
286 lightrec_reg_name(get_mult_div_lo(c)),
287 lightrec_reg_name(c.r.rs),
288 lightrec_reg_name(c.r.rt));
d16005f8 289 default:
98fa08a5 290 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
d16005f8
PC
291 }
292}
293
98fa08a5 294static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
d16005f8 295{
98fa08a5
PC
296 if (cp == 2) {
297 switch (c.i.rs) {
298 case OP_CP0_MFC0:
299 case OP_CP0_CFC0:
300 case OP_CP0_MTC0:
301 case OP_CP0_CTC0:
302 return snprintf(buf, len, "%s%s,%u",
303 cp2_opcodes[c.i.rs],
304 lightrec_reg_name(c.i.rt),
305 c.r.rd);
306 default:
307 return snprintf(buf, len, "cp2 (0x%08x)", c.opcode);
308 }
309 } else {
310 switch (c.i.rs) {
311 case OP_CP0_MFC0:
312 case OP_CP0_CFC0:
313 case OP_CP0_MTC0:
314 case OP_CP0_CTC0:
315 return snprintf(buf, len, "%s%s,%u",
316 cp0_opcodes[c.i.rs],
317 lightrec_reg_name(c.i.rt),
318 c.r.rd);
319 case OP_CP0_RFE:
320 return snprintf(buf, len, "rfe ");
321 default:
322 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
323 }
324 }
325}
326
327static int print_op(union code c, u32 pc, char *buf, size_t len,
22eee2ac
PC
328 const char ***flags_ptr, size_t *nb_flags,
329 bool *is_io)
98fa08a5
PC
330{
331 if (c.opcode == 0)
332 return snprintf(buf, len, "nop ");
333
334 switch (c.i.op) {
335 case OP_SPECIAL:
336 return print_op_special(c, buf, len, flags_ptr, nb_flags);
337 case OP_REGIMM:
338 *flags_ptr = opcode_branch_flags;
339 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
340 return snprintf(buf, len, "%s%s,0x%x",
341 regimm_opcodes[c.i.rt],
342 lightrec_reg_name(c.i.rs),
343 pc + 4 + ((s16)c.i.imm << 2));
344 case OP_J:
345 case OP_JAL:
d8b04acd
PC
346 *flags_ptr = opcode_branch_flags;
347 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
98fa08a5
PC
348 return snprintf(buf, len, "%s0x%x",
349 std_opcodes[c.i.op],
350 (pc & 0xf0000000) | (c.j.imm << 2));
351 case OP_BEQ:
03535202
PC
352 if (c.i.rs == c.i.rt) {
353 *flags_ptr = opcode_branch_flags;
354 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
355 return snprintf(buf, len, "b 0x%x",
356 pc + 4 + ((s16)c.i.imm << 2));
357 }
358 fallthrough;
98fa08a5
PC
359 case OP_BNE:
360 case OP_BLEZ:
361 case OP_BGTZ:
362 *flags_ptr = opcode_branch_flags;
363 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
364 return snprintf(buf, len, "%s%s,%s,0x%x",
365 std_opcodes[c.i.op],
366 lightrec_reg_name(c.i.rs),
367 lightrec_reg_name(c.i.rt),
368 pc + 4 + ((s16)c.i.imm << 2));
369 case OP_ADDI:
370 case OP_ADDIU:
371 case OP_SLTI:
372 case OP_SLTIU:
373 case OP_ANDI:
374 case OP_ORI:
375 case OP_XORI:
376 return snprintf(buf, len, "%s%s,%s,0x%04hx",
377 std_opcodes[c.i.op],
378 lightrec_reg_name(c.i.rt),
379 lightrec_reg_name(c.i.rs),
380 (u16)c.i.imm);
381
382 case OP_LUI:
383 return snprintf(buf, len, "%s%s,0x%04hx",
384 std_opcodes[c.i.op],
385 lightrec_reg_name(c.i.rt),
386 (u16)c.i.imm);
387 case OP_CP0:
388 return print_op_cp(c, buf, len, 0);
389 case OP_CP2:
390 return print_op_cp(c, buf, len, 2);
391 case OP_LB:
392 case OP_LH:
393 case OP_LWL:
394 case OP_LW:
395 case OP_LBU:
396 case OP_LHU:
397 case OP_LWR:
398 case OP_SB:
399 case OP_SH:
400 case OP_SWL:
401 case OP_SW:
402 case OP_SWR:
403 *flags_ptr = opcode_io_flags;
404 *nb_flags = ARRAY_SIZE(opcode_io_flags);
22eee2ac 405 *is_io = true;
98fa08a5
PC
406 return snprintf(buf, len, "%s%s,%hd(%s)",
407 std_opcodes[c.i.op],
408 lightrec_reg_name(c.i.rt),
409 (s16)c.i.imm,
410 lightrec_reg_name(c.i.rs));
411 case OP_LWC2:
412 case OP_SWC2:
413 *flags_ptr = opcode_io_flags;
414 *nb_flags = ARRAY_SIZE(opcode_io_flags);
415 return snprintf(buf, len, "%s%s,%hd(%s)",
416 std_opcodes[c.i.op],
417 lightrec_reg_name(c.i.rt),
418 (s16)c.i.imm,
419 lightrec_reg_name(c.i.rs));
420 case OP_META_MOV:
421 return snprintf(buf, len, "move %s,%s",
422 lightrec_reg_name(c.r.rd),
423 lightrec_reg_name(c.r.rs));
424 case OP_META_EXTC:
425 return snprintf(buf, len, "extc %s,%s",
426 lightrec_reg_name(c.i.rt),
427 lightrec_reg_name(c.i.rs));
428 case OP_META_EXTS:
429 return snprintf(buf, len, "exts %s,%s",
430 lightrec_reg_name(c.i.rt),
431 lightrec_reg_name(c.i.rs));
432 default:
433 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
434 }
435}
436
02487de7 437void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
98fa08a5
PC
438{
439 const struct opcode *op;
440 const char **flags_ptr;
441 size_t nb_flags, count, count2;
442 char buf[256], buf2[256], buf3[256];
d16005f8 443 unsigned int i;
02487de7 444 u32 pc, branch_pc, code;
22eee2ac 445 bool is_io;
98fa08a5
PC
446
447 for (i = 0; i < block->nb_ops; i++) {
448 op = &block->opcode_list[i];
449 branch_pc = get_branch_pc(block, i, 0);
450 pc = block->pc + (i << 2);
02487de7 451 code = LE32TOH(code_ptr[i]);
98fa08a5 452
02487de7 453 count = print_op((union code)code, pc, buf, sizeof(buf),
22eee2ac 454 &flags_ptr, &nb_flags, &is_io);
98fa08a5
PC
455
456 flags_ptr = NULL;
457 nb_flags = 0;
22eee2ac 458 is_io = false;
98fa08a5 459 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
22eee2ac 460 &flags_ptr, &nb_flags, &is_io);
98fa08a5 461
02487de7 462 if (code == op->c.opcode) {
98fa08a5
PC
463 *buf2 = '\0';
464 count2 = 0;
465 }
466
03535202 467 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
d16005f8 468
98fa08a5
PC
469 printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
470 buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);
d16005f8
PC
471 }
472}