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