libretro: direct fb access requires duping support
[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
ba3814c1 14static const char * const std_opcodes[] = {
98fa08a5
PC
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 ",
5459088b
PC
43 [OP_META_MULT2] = "mult2 ",
44 [OP_META_MULTU2] = "multu2 ",
45 [OP_META_LWU] = "lwu ",
46 [OP_META_SWU] = "swu ",
98fa08a5 47};
d16005f8 48
ba3814c1 49static const char * const special_opcodes[] = {
98fa08a5
PC
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 ",
78};
d16005f8 79
ba3814c1 80static const char * const regimm_opcodes[] = {
98fa08a5
PC
81 [OP_REGIMM_BLTZ] = "bltz ",
82 [OP_REGIMM_BGEZ] = "bgez ",
83 [OP_REGIMM_BLTZAL] = "bltzal ",
84 [OP_REGIMM_BGEZAL] = "bgezal ",
85};
d16005f8 86
ba3814c1 87static const char * const cp0_opcodes[] = {
98fa08a5
PC
88 [OP_CP0_MFC0] = "mfc0 ",
89 [OP_CP0_CFC0] = "cfc0 ",
90 [OP_CP0_MTC0] = "mtc0 ",
91 [OP_CP0_CTC0] = "ctc0 ",
92 [OP_CP0_RFE] = "rfe",
93};
94
ba3814c1 95static const char * const cp2_basic_opcodes[] = {
98fa08a5
PC
96 [OP_CP2_BASIC_MFC2] = "mfc2 ",
97 [OP_CP2_BASIC_CFC2] = "cfc2 ",
98 [OP_CP2_BASIC_MTC2] = "mtc2 ",
99 [OP_CP2_BASIC_CTC2] = "ctc2 ",
100};
101
ba3814c1
PC
102static const char * const cp2_opcodes[] = {
103 [OP_CP2_RTPS] = "rtps ",
104 [OP_CP2_NCLIP] = "nclip ",
105 [OP_CP2_OP] = "op ",
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 ",
113 [OP_CP2_CC] = "cc ",
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 ",
125};
126
cb72ea13
PC
127static 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 ",
132};
133
ba3814c1
PC
134static const char * const mult2_opcodes[] = {
135 "mult2 ", "multu2 ",
136};
137
138static const char * const opcode_flags[] = {
98fa08a5 139 "switched branch/DS",
98fa08a5
PC
140 "sync point",
141};
142
ba3814c1 143static const char * const opcode_io_flags[] = {
98fa08a5
PC
144 "self-modifying code",
145 "no invalidation",
22eee2ac 146 "no mask",
cb72ea13 147 "load delay",
22eee2ac
PC
148};
149
ba3814c1 150static const char * const opcode_io_modes[] = {
22eee2ac
PC
151 "Memory access",
152 "I/O access",
153 "RAM access",
154 "BIOS access",
155 "Scratchpad access",
ba3814c1 156 "Mapped I/O access"
98fa08a5 157};
d16005f8 158
ba3814c1 159static const char * const opcode_branch_flags[] = {
98fa08a5
PC
160 "emulate branch",
161 "local branch",
162};
163
684432ad
PC
164static const char * const opcode_movi_flags[] = {
165 "movi",
166};
167
ba3814c1 168static const char * const opcode_multdiv_flags[] = {
98fa08a5
PC
169 "No LO",
170 "No HI",
171 "No div check",
172};
173
03535202
PC
174static size_t do_snprintf(char *buf, size_t len, bool *first,
175 const char *arg1, const char *arg2)
176{
177 size_t bytes;
178
179 if (*first)
180 bytes = snprintf(buf, len, "(%s%s", arg1, arg2);
181 else
182 bytes = snprintf(buf, len, ", %s%s", arg1, arg2);
183
184 *first = false;
185
186 return bytes;
187}
188
189static const char * const reg_op_token[3] = {
190 "-", "*", "~",
191};
192
193static int print_flags(char *buf, size_t len, const struct opcode *op,
ba3814c1 194 const char * const *array, size_t array_size,
22eee2ac 195 bool is_io)
d16005f8 196{
22eee2ac
PC
197 const char *flag_name, *io_mode_name;
198 unsigned int i, io_mode;
98fa08a5
PC
199 size_t count = 0, bytes;
200 bool first = true;
03535202
PC
201 u32 flags = op->flags;
202 unsigned int reg_op;
d16005f8 203
98fa08a5
PC
204 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
205 if (!(flags & BIT(i)))
206 continue;
d16005f8 207
98fa08a5
PC
208 if (i < ARRAY_SIZE(opcode_flags))
209 flag_name = opcode_flags[i];
d16005f8 210 else
98fa08a5
PC
211 flag_name = array[i - ARRAY_SIZE(opcode_flags)];
212
03535202 213 bytes = do_snprintf(buf, len, &first, "", flag_name);
98fa08a5
PC
214 buf += bytes;
215 len -= bytes;
216 count += bytes;
d16005f8
PC
217 }
218
22eee2ac
PC
219 if (is_io) {
220 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
221 if (io_mode > 0) {
222 io_mode_name = opcode_io_modes[io_mode - 1];
223
03535202
PC
224 bytes = do_snprintf(buf, len, &first, "", io_mode_name);
225 buf += bytes;
226 len -= bytes;
227 count += bytes;
228 }
229 }
230
231 if (OPT_EARLY_UNLOAD) {
232 reg_op = LIGHTREC_FLAGS_GET_RS(flags);
233 if (reg_op) {
234 bytes = do_snprintf(buf, len, &first,
235 reg_op_token[reg_op - 1],
236 lightrec_reg_name(op->i.rs));
237 buf += bytes;
238 len -= bytes;
239 count += bytes;
240 }
241
242 reg_op = LIGHTREC_FLAGS_GET_RT(flags);
243 if (reg_op) {
244 bytes = do_snprintf(buf, len, &first,
245 reg_op_token[reg_op - 1],
246 lightrec_reg_name(op->i.rt));
247 buf += bytes;
248 len -= bytes;
249 count += bytes;
250 }
22eee2ac 251
03535202
PC
252 reg_op = LIGHTREC_FLAGS_GET_RD(flags);
253 if (reg_op) {
254 bytes = do_snprintf(buf, len, &first,
255 reg_op_token[reg_op - 1],
256 lightrec_reg_name(op->r.rd));
22eee2ac
PC
257 buf += bytes;
258 len -= bytes;
259 count += bytes;
260 }
261 }
262
98fa08a5
PC
263 if (!first)
264 count += snprintf(buf, len, ")");
265 else
266 *buf = '\0';
d16005f8 267
98fa08a5 268 return count;
d16005f8
PC
269}
270
98fa08a5 271static int print_op_special(union code c, char *buf, size_t len,
ba3814c1 272 const char * const **flags_ptr, size_t *nb_flags)
d16005f8 273{
98fa08a5
PC
274 switch (c.r.op) {
275 case OP_SPECIAL_SLL:
276 case OP_SPECIAL_SRL:
277 case OP_SPECIAL_SRA:
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),
282 c.r.imm);
283 case OP_SPECIAL_SLLV:
284 case OP_SPECIAL_SRLV:
285 case OP_SPECIAL_SRAV:
286 case OP_SPECIAL_ADD:
287 case OP_SPECIAL_ADDU:
288 case OP_SPECIAL_SUB:
289 case OP_SPECIAL_SUBU:
290 case OP_SPECIAL_AND:
291 case OP_SPECIAL_OR:
292 case OP_SPECIAL_XOR:
293 case OP_SPECIAL_NOR:
294 case OP_SPECIAL_SLT:
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));
301 case OP_SPECIAL_JR:
d8b04acd
PC
302 *flags_ptr = opcode_branch_flags;
303 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
304 fallthrough;
98fa08a5
PC
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),
9259d748 314 lightrec_reg_name(c.r.rs));
98fa08a5
PC
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:
325 case OP_SPECIAL_DIV:
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));
d16005f8 335 default:
98fa08a5 336 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
d16005f8
PC
337 }
338}
339
98fa08a5 340static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
d16005f8 341{
98fa08a5 342 if (cp == 2) {
ba3814c1
PC
343 switch (c.r.op) {
344 case OP_CP2_BASIC:
98fa08a5 345 return snprintf(buf, len, "%s%s,%u",
ba3814c1 346 cp2_basic_opcodes[c.i.rs],
98fa08a5
PC
347 lightrec_reg_name(c.i.rt),
348 c.r.rd);
349 default:
ba3814c1 350 return snprintf(buf, len, "%s", cp2_opcodes[c.r.op]);
98fa08a5
PC
351 }
352 } else {
353 switch (c.i.rs) {
354 case OP_CP0_MFC0:
355 case OP_CP0_CFC0:
356 case OP_CP0_MTC0:
357 case OP_CP0_CTC0:
358 return snprintf(buf, len, "%s%s,%u",
359 cp0_opcodes[c.i.rs],
360 lightrec_reg_name(c.i.rt),
361 c.r.rd);
362 case OP_CP0_RFE:
363 return snprintf(buf, len, "rfe ");
364 default:
365 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
366 }
367 }
368}
369
370static int print_op(union code c, u32 pc, char *buf, size_t len,
ba3814c1 371 const char * const **flags_ptr, size_t *nb_flags,
22eee2ac 372 bool *is_io)
98fa08a5
PC
373{
374 if (c.opcode == 0)
375 return snprintf(buf, len, "nop ");
376
377 switch (c.i.op) {
378 case OP_SPECIAL:
379 return print_op_special(c, buf, len, flags_ptr, nb_flags);
380 case OP_REGIMM:
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));
387 case OP_J:
388 case OP_JAL:
d8b04acd
PC
389 *flags_ptr = opcode_branch_flags;
390 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
98fa08a5
PC
391 return snprintf(buf, len, "%s0x%x",
392 std_opcodes[c.i.op],
393 (pc & 0xf0000000) | (c.j.imm << 2));
394 case OP_BEQ:
03535202
PC
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));
400 }
401 fallthrough;
98fa08a5
PC
402 case OP_BNE:
403 case OP_BLEZ:
404 case OP_BGTZ:
405 *flags_ptr = opcode_branch_flags;
406 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
407 return snprintf(buf, len, "%s%s,%s,0x%x",
408 std_opcodes[c.i.op],
409 lightrec_reg_name(c.i.rs),
410 lightrec_reg_name(c.i.rt),
411 pc + 4 + ((s16)c.i.imm << 2));
412 case OP_ADDI:
413 case OP_ADDIU:
684432ad
PC
414 case OP_ORI:
415 *flags_ptr = opcode_movi_flags;
416 *nb_flags = ARRAY_SIZE(opcode_movi_flags);
417 fallthrough;
98fa08a5
PC
418 case OP_SLTI:
419 case OP_SLTIU:
420 case OP_ANDI:
98fa08a5
PC
421 case OP_XORI:
422 return snprintf(buf, len, "%s%s,%s,0x%04hx",
423 std_opcodes[c.i.op],
424 lightrec_reg_name(c.i.rt),
425 lightrec_reg_name(c.i.rs),
426 (u16)c.i.imm);
427
428 case OP_LUI:
684432ad
PC
429 *flags_ptr = opcode_movi_flags;
430 *nb_flags = ARRAY_SIZE(opcode_movi_flags);
98fa08a5
PC
431 return snprintf(buf, len, "%s%s,0x%04hx",
432 std_opcodes[c.i.op],
433 lightrec_reg_name(c.i.rt),
434 (u16)c.i.imm);
435 case OP_CP0:
436 return print_op_cp(c, buf, len, 0);
437 case OP_CP2:
438 return print_op_cp(c, buf, len, 2);
439 case OP_LB:
440 case OP_LH:
441 case OP_LWL:
442 case OP_LW:
443 case OP_LBU:
444 case OP_LHU:
445 case OP_LWR:
446 case OP_SB:
447 case OP_SH:
448 case OP_SWL:
449 case OP_SW:
450 case OP_SWR:
5459088b
PC
451 case OP_META_LWU:
452 case OP_META_SWU:
98fa08a5
PC
453 *flags_ptr = opcode_io_flags;
454 *nb_flags = ARRAY_SIZE(opcode_io_flags);
22eee2ac 455 *is_io = true;
98fa08a5
PC
456 return snprintf(buf, len, "%s%s,%hd(%s)",
457 std_opcodes[c.i.op],
458 lightrec_reg_name(c.i.rt),
459 (s16)c.i.imm,
460 lightrec_reg_name(c.i.rs));
461 case OP_LWC2:
462 case OP_SWC2:
463 *flags_ptr = opcode_io_flags;
464 *nb_flags = ARRAY_SIZE(opcode_io_flags);
465 return snprintf(buf, len, "%s%s,%hd(%s)",
466 std_opcodes[c.i.op],
467 lightrec_reg_name(c.i.rt),
468 (s16)c.i.imm,
469 lightrec_reg_name(c.i.rs));
cb72ea13
PC
470 case OP_META:
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));
ba3814c1
PC
475 case OP_META_MULT2:
476 case OP_META_MULTU2:
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);
98fa08a5
PC
484 default:
485 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
486 }
487}
488
02487de7 489void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
98fa08a5
PC
490{
491 const struct opcode *op;
ba3814c1 492 const char * const *flags_ptr;
98fa08a5
PC
493 size_t nb_flags, count, count2;
494 char buf[256], buf2[256], buf3[256];
d16005f8 495 unsigned int i;
02487de7 496 u32 pc, branch_pc, code;
22eee2ac 497 bool is_io;
98fa08a5
PC
498
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);
02487de7 503 code = LE32TOH(code_ptr[i]);
98fa08a5 504
02487de7 505 count = print_op((union code)code, pc, buf, sizeof(buf),
22eee2ac 506 &flags_ptr, &nb_flags, &is_io);
98fa08a5
PC
507
508 flags_ptr = NULL;
509 nb_flags = 0;
22eee2ac 510 is_io = false;
98fa08a5 511 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
22eee2ac 512 &flags_ptr, &nb_flags, &is_io);
98fa08a5 513
02487de7 514 if (code == op->c.opcode) {
98fa08a5
PC
515 *buf2 = '\0';
516 count2 = 0;
517 }
518
03535202 519 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
d16005f8 520
98fa08a5
PC
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);
d16005f8
PC
523 }
524}