9b2dbd53b8189a65dc41912d188939549fc48032
[pcsx_rearmed.git] / deps / lightrec / disassembler.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3  * Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
4  */
5
6 #include <stdbool.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "lightrec-private.h"
12 #include "regcache.h"
13
14 static 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 };
44
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    ",
74 };
75
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  ",
81 };
82
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    ",
88         [OP_CP0_RFE]            = "rfe",
89 };
90
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    ",
96 };
97
98 static const char *opcode_flags[] = {
99         "switched branch/DS",
100         "unload Rs",
101         "unload Rt",
102         "unload Rd",
103         "sync point",
104 };
105
106 static const char *opcode_io_flags[] = {
107         "self-modifying code",
108         "no invalidation",
109         "no mask",
110 };
111
112 static const char *opcode_io_modes[] = {
113         "Memory access",
114         "I/O access",
115         "RAM access",
116         "BIOS access",
117         "Scratchpad access",
118 };
119
120 static const char *opcode_branch_flags[] = {
121         "emulate branch",
122         "local branch",
123 };
124
125 static const char *opcode_multdiv_flags[] = {
126         "No LO",
127         "No HI",
128         "No div check",
129 };
130
131 static int print_flags(char *buf, size_t len, u16 flags,
132                        const char **array, size_t array_size,
133                        bool is_io)
134 {
135         const char *flag_name, *io_mode_name;
136         unsigned int i, io_mode;
137         size_t count = 0, bytes;
138         bool first = true;
139
140         for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
141                 if (!(flags & BIT(i)))
142                         continue;
143
144                 if (i < ARRAY_SIZE(opcode_flags))
145                         flag_name = opcode_flags[i];
146                 else
147                         flag_name = array[i - ARRAY_SIZE(opcode_flags)];
148
149                 if (first)
150                         bytes = snprintf(buf, len, "(%s", flag_name);
151                 else
152                         bytes = snprintf(buf, len, ", %s", flag_name);
153
154                 first = false;
155                 buf += bytes;
156                 len -= bytes;
157                 count += bytes;
158         }
159
160         if (is_io) {
161                 io_mode = LIGHTREC_FLAGS_GET_IO_MODE(flags);
162                 if (io_mode > 0) {
163                         io_mode_name = opcode_io_modes[io_mode - 1];
164
165                         if (first)
166                                 bytes = snprintf(buf, len, "(%s", io_mode_name);
167                         else
168                                 bytes = snprintf(buf, len, ", %s", io_mode_name);
169
170                         first = false;
171                         buf += bytes;
172                         len -= bytes;
173                         count += bytes;
174                 }
175         }
176
177         if (!first)
178                 count += snprintf(buf, len, ")");
179         else
180                 *buf = '\0';
181
182         return count;
183 }
184
185 static int print_op_special(union code c, char *buf, size_t len,
186                             const char ***flags_ptr, size_t *nb_flags)
187 {
188         switch (c.r.op) {
189         case OP_SPECIAL_SLL:
190         case OP_SPECIAL_SRL:
191         case OP_SPECIAL_SRA:
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),
196                                 c.r.imm);
197         case OP_SPECIAL_SLLV:
198         case OP_SPECIAL_SRLV:
199         case OP_SPECIAL_SRAV:
200         case OP_SPECIAL_ADD:
201         case OP_SPECIAL_ADDU:
202         case OP_SPECIAL_SUB:
203         case OP_SPECIAL_SUBU:
204         case OP_SPECIAL_AND:
205         case OP_SPECIAL_OR:
206         case OP_SPECIAL_XOR:
207         case OP_SPECIAL_NOR:
208         case OP_SPECIAL_SLT:
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));
215         case OP_SPECIAL_JR:
216                 *flags_ptr = opcode_branch_flags;
217                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
218                 fallthrough;
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:
239         case OP_SPECIAL_DIV:
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));
249         default:
250                 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
251         }
252 }
253
254 static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
255 {
256         if (cp == 2) {
257                 switch (c.i.rs) {
258                 case OP_CP0_MFC0:
259                 case OP_CP0_CFC0:
260                 case OP_CP0_MTC0:
261                 case OP_CP0_CTC0:
262                         return snprintf(buf, len, "%s%s,%u",
263                                         cp2_opcodes[c.i.rs],
264                                         lightrec_reg_name(c.i.rt),
265                                         c.r.rd);
266                 default:
267                         return snprintf(buf, len, "cp2     (0x%08x)", c.opcode);
268                 }
269         } else {
270                 switch (c.i.rs) {
271                 case OP_CP0_MFC0:
272                 case OP_CP0_CFC0:
273                 case OP_CP0_MTC0:
274                 case OP_CP0_CTC0:
275                         return snprintf(buf, len, "%s%s,%u",
276                                         cp0_opcodes[c.i.rs],
277                                         lightrec_reg_name(c.i.rt),
278                                         c.r.rd);
279                 case OP_CP0_RFE:
280                         return snprintf(buf, len, "rfe     ");
281                 default:
282                         return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
283                 }
284         }
285 }
286
287 static int print_op(union code c, u32 pc, char *buf, size_t len,
288                     const char ***flags_ptr, size_t *nb_flags,
289                     bool *is_io)
290 {
291         if (c.opcode == 0)
292                 return snprintf(buf, len, "nop     ");
293
294         switch (c.i.op) {
295         case OP_SPECIAL:
296                 return print_op_special(c, buf, len, flags_ptr, nb_flags);
297         case OP_REGIMM:
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));
304         case OP_J:
305         case OP_JAL:
306                 *flags_ptr = opcode_branch_flags;
307                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
308                 return snprintf(buf, len, "%s0x%x",
309                                 std_opcodes[c.i.op],
310                                 (pc & 0xf0000000) | (c.j.imm << 2));
311         case OP_BEQ:
312         case OP_BNE:
313         case OP_BLEZ:
314         case OP_BGTZ:
315                 *flags_ptr = opcode_branch_flags;
316                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
317                 return snprintf(buf, len, "%s%s,%s,0x%x",
318                                 std_opcodes[c.i.op],
319                                 lightrec_reg_name(c.i.rs),
320                                 lightrec_reg_name(c.i.rt),
321                                 pc + 4 + ((s16)c.i.imm << 2));
322         case OP_ADDI:
323         case OP_ADDIU:
324         case OP_SLTI:
325         case OP_SLTIU:
326         case OP_ANDI:
327         case OP_ORI:
328         case OP_XORI:
329                 return snprintf(buf, len, "%s%s,%s,0x%04hx",
330                                 std_opcodes[c.i.op],
331                                 lightrec_reg_name(c.i.rt),
332                                 lightrec_reg_name(c.i.rs),
333                                 (u16)c.i.imm);
334
335         case OP_LUI:
336                 return snprintf(buf, len, "%s%s,0x%04hx",
337                                 std_opcodes[c.i.op],
338                                 lightrec_reg_name(c.i.rt),
339                                 (u16)c.i.imm);
340         case OP_CP0:
341                 return print_op_cp(c, buf, len, 0);
342         case OP_CP2:
343                 return print_op_cp(c, buf, len, 2);
344         case OP_LB:
345         case OP_LH:
346         case OP_LWL:
347         case OP_LW:
348         case OP_LBU:
349         case OP_LHU:
350         case OP_LWR:
351         case OP_SB:
352         case OP_SH:
353         case OP_SWL:
354         case OP_SW:
355         case OP_SWR:
356                 *flags_ptr = opcode_io_flags;
357                 *nb_flags = ARRAY_SIZE(opcode_io_flags);
358                 *is_io = true;
359                 return snprintf(buf, len, "%s%s,%hd(%s)",
360                                 std_opcodes[c.i.op],
361                                 lightrec_reg_name(c.i.rt),
362                                 (s16)c.i.imm,
363                                 lightrec_reg_name(c.i.rs));
364         case OP_LWC2:
365         case OP_SWC2:
366                 *flags_ptr = opcode_io_flags;
367                 *nb_flags = ARRAY_SIZE(opcode_io_flags);
368                 return snprintf(buf, len, "%s%s,%hd(%s)",
369                                 std_opcodes[c.i.op],
370                                 lightrec_reg_name(c.i.rt),
371                                 (s16)c.i.imm,
372                                 lightrec_reg_name(c.i.rs));
373         case OP_META_MOV:
374                 return snprintf(buf, len, "move    %s,%s",
375                                 lightrec_reg_name(c.r.rd),
376                                 lightrec_reg_name(c.r.rs));
377         case OP_META_EXTC:
378                 return snprintf(buf, len, "extc    %s,%s",
379                                 lightrec_reg_name(c.i.rt),
380                                 lightrec_reg_name(c.i.rs));
381         case OP_META_EXTS:
382                 return snprintf(buf, len, "exts    %s,%s",
383                                 lightrec_reg_name(c.i.rt),
384                                 lightrec_reg_name(c.i.rs));
385         default:
386                 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
387         }
388 }
389
390 void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
391 {
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];
396         unsigned int i;
397         u32 pc, branch_pc, code;
398         bool is_io;
399
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]);
405
406                 count = print_op((union code)code, pc, buf, sizeof(buf),
407                                  &flags_ptr, &nb_flags, &is_io);
408
409                 flags_ptr = NULL;
410                 nb_flags = 0;
411                 is_io = false;
412                 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
413                                   &flags_ptr, &nb_flags, &is_io);
414
415                 if (code == op->c.opcode) {
416                         *buf2 = '\0';
417                         count2 = 0;
418                 }
419
420                 print_flags(buf3, sizeof(buf3), op->flags, flags_ptr, nb_flags,
421                             is_io);
422
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);
425         }
426 }