Update lightrec 20220910 (#686)
[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 ",
43};
d16005f8 44
ba3814c1 45static const char * const special_opcodes[] = {
98fa08a5
PC
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
ba3814c1 76static const char * const regimm_opcodes[] = {
98fa08a5
PC
77 [OP_REGIMM_BLTZ] = "bltz ",
78 [OP_REGIMM_BGEZ] = "bgez ",
79 [OP_REGIMM_BLTZAL] = "bltzal ",
80 [OP_REGIMM_BGEZAL] = "bgezal ",
81};
d16005f8 82
ba3814c1 83static const char * const cp0_opcodes[] = {
98fa08a5
PC
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
ba3814c1 91static const char * const cp2_basic_opcodes[] = {
98fa08a5
PC
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
ba3814c1
PC
98static const char * const cp2_opcodes[] = {
99 [OP_CP2_RTPS] = "rtps ",
100 [OP_CP2_NCLIP] = "nclip ",
101 [OP_CP2_OP] = "op ",
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 ",
109 [OP_CP2_CC] = "cc ",
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 ",
121};
122
123static const char * const mult2_opcodes[] = {
124 "mult2 ", "multu2 ",
125};
126
127static const char * const opcode_flags[] = {
98fa08a5 128 "switched branch/DS",
98fa08a5
PC
129 "sync point",
130};
131
ba3814c1 132static const char * const opcode_io_flags[] = {
98fa08a5
PC
133 "self-modifying code",
134 "no invalidation",
22eee2ac
PC
135 "no mask",
136};
137
ba3814c1 138static const char * const opcode_io_modes[] = {
22eee2ac
PC
139 "Memory access",
140 "I/O access",
141 "RAM access",
142 "BIOS access",
143 "Scratchpad access",
ba3814c1 144 "Mapped I/O access"
98fa08a5 145};
d16005f8 146
ba3814c1 147static const char * const opcode_branch_flags[] = {
98fa08a5
PC
148 "emulate branch",
149 "local branch",
150};
151
ba3814c1 152static const char * const opcode_multdiv_flags[] = {
98fa08a5
PC
153 "No LO",
154 "No HI",
155 "No div check",
156};
157
03535202
PC
158static size_t do_snprintf(char *buf, size_t len, bool *first,
159 const char *arg1, const char *arg2)
160{
161 size_t bytes;
162
163 if (*first)
164 bytes = snprintf(buf, len, "(%s%s", arg1, arg2);
165 else
166 bytes = snprintf(buf, len, ", %s%s", arg1, arg2);
167
168 *first = false;
169
170 return bytes;
171}
172
173static const char * const reg_op_token[3] = {
174 "-", "*", "~",
175};
176
177static int print_flags(char *buf, size_t len, const struct opcode *op,
ba3814c1 178 const char * const *array, size_t array_size,
22eee2ac 179 bool is_io)
d16005f8 180{
22eee2ac
PC
181 const char *flag_name, *io_mode_name;
182 unsigned int i, io_mode;
98fa08a5
PC
183 size_t count = 0, bytes;
184 bool first = true;
03535202
PC
185 u32 flags = op->flags;
186 unsigned int reg_op;
d16005f8 187
98fa08a5
PC
188 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
189 if (!(flags & BIT(i)))
190 continue;
d16005f8 191
98fa08a5
PC
192 if (i < ARRAY_SIZE(opcode_flags))
193 flag_name = opcode_flags[i];
d16005f8 194 else
98fa08a5
PC
195 flag_name = array[i - ARRAY_SIZE(opcode_flags)];
196
03535202 197 bytes = do_snprintf(buf, len, &first, "", flag_name);
98fa08a5
PC
198 buf += bytes;
199 len -= bytes;
200 count += bytes;
d16005f8
PC
201 }
202
22eee2ac
PC
203 if (is_io) {
204 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
205 if (io_mode > 0) {
206 io_mode_name = opcode_io_modes[io_mode - 1];
207
03535202
PC
208 bytes = do_snprintf(buf, len, &first, "", io_mode_name);
209 buf += bytes;
210 len -= bytes;
211 count += bytes;
212 }
213 }
214
215 if (OPT_EARLY_UNLOAD) {
216 reg_op = LIGHTREC_FLAGS_GET_RS(flags);
217 if (reg_op) {
218 bytes = do_snprintf(buf, len, &first,
219 reg_op_token[reg_op - 1],
220 lightrec_reg_name(op->i.rs));
221 buf += bytes;
222 len -= bytes;
223 count += bytes;
224 }
225
226 reg_op = LIGHTREC_FLAGS_GET_RT(flags);
227 if (reg_op) {
228 bytes = do_snprintf(buf, len, &first,
229 reg_op_token[reg_op - 1],
230 lightrec_reg_name(op->i.rt));
231 buf += bytes;
232 len -= bytes;
233 count += bytes;
234 }
22eee2ac 235
03535202
PC
236 reg_op = LIGHTREC_FLAGS_GET_RD(flags);
237 if (reg_op) {
238 bytes = do_snprintf(buf, len, &first,
239 reg_op_token[reg_op - 1],
240 lightrec_reg_name(op->r.rd));
22eee2ac
PC
241 buf += bytes;
242 len -= bytes;
243 count += bytes;
244 }
245 }
246
98fa08a5
PC
247 if (!first)
248 count += snprintf(buf, len, ")");
249 else
250 *buf = '\0';
d16005f8 251
98fa08a5 252 return count;
d16005f8
PC
253}
254
98fa08a5 255static int print_op_special(union code c, char *buf, size_t len,
ba3814c1 256 const char * const **flags_ptr, size_t *nb_flags)
d16005f8 257{
98fa08a5
PC
258 switch (c.r.op) {
259 case OP_SPECIAL_SLL:
260 case OP_SPECIAL_SRL:
261 case OP_SPECIAL_SRA:
262 return snprintf(buf, len, "%s%s,%s,%u",
263 special_opcodes[c.r.op],
264 lightrec_reg_name(c.r.rd),
265 lightrec_reg_name(c.r.rt),
266 c.r.imm);
267 case OP_SPECIAL_SLLV:
268 case OP_SPECIAL_SRLV:
269 case OP_SPECIAL_SRAV:
270 case OP_SPECIAL_ADD:
271 case OP_SPECIAL_ADDU:
272 case OP_SPECIAL_SUB:
273 case OP_SPECIAL_SUBU:
274 case OP_SPECIAL_AND:
275 case OP_SPECIAL_OR:
276 case OP_SPECIAL_XOR:
277 case OP_SPECIAL_NOR:
278 case OP_SPECIAL_SLT:
279 case OP_SPECIAL_SLTU:
280 return snprintf(buf, len, "%s%s,%s,%s",
281 special_opcodes[c.r.op],
282 lightrec_reg_name(c.r.rd),
283 lightrec_reg_name(c.r.rt),
284 lightrec_reg_name(c.r.rs));
285 case OP_SPECIAL_JR:
d8b04acd
PC
286 *flags_ptr = opcode_branch_flags;
287 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
288 fallthrough;
98fa08a5
PC
289 case OP_SPECIAL_MTHI:
290 case OP_SPECIAL_MTLO:
291 return snprintf(buf, len, "%s%s",
292 special_opcodes[c.r.op],
293 lightrec_reg_name(c.r.rs));
294 case OP_SPECIAL_JALR:
295 return snprintf(buf, len, "%s%s,%s",
296 special_opcodes[c.r.op],
297 lightrec_reg_name(c.r.rd),
298 lightrec_reg_name(c.r.rt));
299 case OP_SPECIAL_SYSCALL:
300 case OP_SPECIAL_BREAK:
301 return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
302 case OP_SPECIAL_MFHI:
303 case OP_SPECIAL_MFLO:
304 return snprintf(buf, len, "%s%s",
305 special_opcodes[c.r.op],
306 lightrec_reg_name(c.r.rd));
307 case OP_SPECIAL_MULT:
308 case OP_SPECIAL_MULTU:
309 case OP_SPECIAL_DIV:
310 case OP_SPECIAL_DIVU:
311 *flags_ptr = opcode_multdiv_flags;
312 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
313 return snprintf(buf, len, "%s%s,%s,%s,%s",
314 special_opcodes[c.r.op],
315 lightrec_reg_name(get_mult_div_hi(c)),
316 lightrec_reg_name(get_mult_div_lo(c)),
317 lightrec_reg_name(c.r.rs),
318 lightrec_reg_name(c.r.rt));
d16005f8 319 default:
98fa08a5 320 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
d16005f8
PC
321 }
322}
323
98fa08a5 324static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
d16005f8 325{
98fa08a5 326 if (cp == 2) {
ba3814c1
PC
327 switch (c.r.op) {
328 case OP_CP2_BASIC:
98fa08a5 329 return snprintf(buf, len, "%s%s,%u",
ba3814c1 330 cp2_basic_opcodes[c.i.rs],
98fa08a5
PC
331 lightrec_reg_name(c.i.rt),
332 c.r.rd);
333 default:
ba3814c1 334 return snprintf(buf, len, "%s", cp2_opcodes[c.r.op]);
98fa08a5
PC
335 }
336 } else {
337 switch (c.i.rs) {
338 case OP_CP0_MFC0:
339 case OP_CP0_CFC0:
340 case OP_CP0_MTC0:
341 case OP_CP0_CTC0:
342 return snprintf(buf, len, "%s%s,%u",
343 cp0_opcodes[c.i.rs],
344 lightrec_reg_name(c.i.rt),
345 c.r.rd);
346 case OP_CP0_RFE:
347 return snprintf(buf, len, "rfe ");
348 default:
349 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
350 }
351 }
352}
353
354static int print_op(union code c, u32 pc, char *buf, size_t len,
ba3814c1 355 const char * const **flags_ptr, size_t *nb_flags,
22eee2ac 356 bool *is_io)
98fa08a5
PC
357{
358 if (c.opcode == 0)
359 return snprintf(buf, len, "nop ");
360
361 switch (c.i.op) {
362 case OP_SPECIAL:
363 return print_op_special(c, buf, len, flags_ptr, nb_flags);
364 case OP_REGIMM:
365 *flags_ptr = opcode_branch_flags;
366 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
367 return snprintf(buf, len, "%s%s,0x%x",
368 regimm_opcodes[c.i.rt],
369 lightrec_reg_name(c.i.rs),
370 pc + 4 + ((s16)c.i.imm << 2));
371 case OP_J:
372 case OP_JAL:
d8b04acd
PC
373 *flags_ptr = opcode_branch_flags;
374 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
98fa08a5
PC
375 return snprintf(buf, len, "%s0x%x",
376 std_opcodes[c.i.op],
377 (pc & 0xf0000000) | (c.j.imm << 2));
378 case OP_BEQ:
03535202
PC
379 if (c.i.rs == c.i.rt) {
380 *flags_ptr = opcode_branch_flags;
381 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
382 return snprintf(buf, len, "b 0x%x",
383 pc + 4 + ((s16)c.i.imm << 2));
384 }
385 fallthrough;
98fa08a5
PC
386 case OP_BNE:
387 case OP_BLEZ:
388 case OP_BGTZ:
389 *flags_ptr = opcode_branch_flags;
390 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
391 return snprintf(buf, len, "%s%s,%s,0x%x",
392 std_opcodes[c.i.op],
393 lightrec_reg_name(c.i.rs),
394 lightrec_reg_name(c.i.rt),
395 pc + 4 + ((s16)c.i.imm << 2));
396 case OP_ADDI:
397 case OP_ADDIU:
398 case OP_SLTI:
399 case OP_SLTIU:
400 case OP_ANDI:
401 case OP_ORI:
402 case OP_XORI:
403 return snprintf(buf, len, "%s%s,%s,0x%04hx",
404 std_opcodes[c.i.op],
405 lightrec_reg_name(c.i.rt),
406 lightrec_reg_name(c.i.rs),
407 (u16)c.i.imm);
408
409 case OP_LUI:
410 return snprintf(buf, len, "%s%s,0x%04hx",
411 std_opcodes[c.i.op],
412 lightrec_reg_name(c.i.rt),
413 (u16)c.i.imm);
414 case OP_CP0:
415 return print_op_cp(c, buf, len, 0);
416 case OP_CP2:
417 return print_op_cp(c, buf, len, 2);
418 case OP_LB:
419 case OP_LH:
420 case OP_LWL:
421 case OP_LW:
422 case OP_LBU:
423 case OP_LHU:
424 case OP_LWR:
425 case OP_SB:
426 case OP_SH:
427 case OP_SWL:
428 case OP_SW:
429 case OP_SWR:
430 *flags_ptr = opcode_io_flags;
431 *nb_flags = ARRAY_SIZE(opcode_io_flags);
22eee2ac 432 *is_io = true;
98fa08a5
PC
433 return snprintf(buf, len, "%s%s,%hd(%s)",
434 std_opcodes[c.i.op],
435 lightrec_reg_name(c.i.rt),
436 (s16)c.i.imm,
437 lightrec_reg_name(c.i.rs));
438 case OP_LWC2:
439 case OP_SWC2:
440 *flags_ptr = opcode_io_flags;
441 *nb_flags = ARRAY_SIZE(opcode_io_flags);
442 return snprintf(buf, len, "%s%s,%hd(%s)",
443 std_opcodes[c.i.op],
444 lightrec_reg_name(c.i.rt),
445 (s16)c.i.imm,
446 lightrec_reg_name(c.i.rs));
447 case OP_META_MOV:
448 return snprintf(buf, len, "move %s,%s",
449 lightrec_reg_name(c.r.rd),
450 lightrec_reg_name(c.r.rs));
451 case OP_META_EXTC:
452 return snprintf(buf, len, "extc %s,%s",
453 lightrec_reg_name(c.i.rt),
454 lightrec_reg_name(c.i.rs));
455 case OP_META_EXTS:
456 return snprintf(buf, len, "exts %s,%s",
457 lightrec_reg_name(c.i.rt),
458 lightrec_reg_name(c.i.rs));
ba3814c1
PC
459 case OP_META_MULT2:
460 case OP_META_MULTU2:
461 *flags_ptr = opcode_multdiv_flags;
462 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
463 return snprintf(buf, len, "%s%s,%s,%s,%u",
464 mult2_opcodes[c.i.op == OP_META_MULTU2],
465 lightrec_reg_name(get_mult_div_hi(c)),
466 lightrec_reg_name(get_mult_div_lo(c)),
467 lightrec_reg_name(c.r.rs), c.r.op);
98fa08a5
PC
468 default:
469 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
470 }
471}
472
02487de7 473void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
98fa08a5
PC
474{
475 const struct opcode *op;
ba3814c1 476 const char * const *flags_ptr;
98fa08a5
PC
477 size_t nb_flags, count, count2;
478 char buf[256], buf2[256], buf3[256];
d16005f8 479 unsigned int i;
02487de7 480 u32 pc, branch_pc, code;
22eee2ac 481 bool is_io;
98fa08a5
PC
482
483 for (i = 0; i < block->nb_ops; i++) {
484 op = &block->opcode_list[i];
485 branch_pc = get_branch_pc(block, i, 0);
486 pc = block->pc + (i << 2);
02487de7 487 code = LE32TOH(code_ptr[i]);
98fa08a5 488
02487de7 489 count = print_op((union code)code, pc, buf, sizeof(buf),
22eee2ac 490 &flags_ptr, &nb_flags, &is_io);
98fa08a5
PC
491
492 flags_ptr = NULL;
493 nb_flags = 0;
22eee2ac 494 is_io = false;
98fa08a5 495 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
22eee2ac 496 &flags_ptr, &nb_flags, &is_io);
98fa08a5 497
02487de7 498 if (code == op->c.opcode) {
98fa08a5
PC
499 *buf2 = '\0';
500 count2 = 0;
501 }
502
03535202 503 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
d16005f8 504
98fa08a5
PC
505 printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
506 buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);
d16005f8
PC
507 }
508}