5c94324fe89db1b04875c084a64d33e55f9b3cdc
[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         "sync point",
101 };
102
103 static const char *opcode_io_flags[] = {
104         "self-modifying code",
105         "no invalidation",
106         "no mask",
107 };
108
109 static const char *opcode_io_modes[] = {
110         "Memory access",
111         "I/O access",
112         "RAM access",
113         "BIOS access",
114         "Scratchpad access",
115 };
116
117 static const char *opcode_branch_flags[] = {
118         "emulate branch",
119         "local branch",
120 };
121
122 static const char *opcode_multdiv_flags[] = {
123         "No LO",
124         "No HI",
125         "No div check",
126 };
127
128 static 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
143 static const char * const reg_op_token[3] = {
144         "-", "*", "~",
145 };
146
147 static int print_flags(char *buf, size_t len, const struct opcode *op,
148                        const char **array, size_t array_size,
149                        bool is_io)
150 {
151         const char *flag_name, *io_mode_name;
152         unsigned int i, io_mode;
153         size_t count = 0, bytes;
154         bool first = true;
155         u32 flags = op->flags;
156         unsigned int reg_op;
157
158         for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
159                 if (!(flags & BIT(i)))
160                         continue;
161
162                 if (i < ARRAY_SIZE(opcode_flags))
163                         flag_name = opcode_flags[i];
164                 else
165                         flag_name = array[i - ARRAY_SIZE(opcode_flags)];
166
167                 bytes = do_snprintf(buf, len, &first, "", flag_name);
168                 buf += bytes;
169                 len -= bytes;
170                 count += bytes;
171         }
172
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
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                 }
205
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));
211                         buf += bytes;
212                         len -= bytes;
213                         count += bytes;
214                 }
215         }
216
217         if (!first)
218                 count += snprintf(buf, len, ")");
219         else
220                 *buf = '\0';
221
222         return count;
223 }
224
225 static int print_op_special(union code c, char *buf, size_t len,
226                             const char ***flags_ptr, size_t *nb_flags)
227 {
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:
256                 *flags_ptr = opcode_branch_flags;
257                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
258                 fallthrough;
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));
289         default:
290                 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
291         }
292 }
293
294 static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
295 {
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
327 static int print_op(union code c, u32 pc, char *buf, size_t len,
328                     const char ***flags_ptr, size_t *nb_flags,
329                     bool *is_io)
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:
346                 *flags_ptr = opcode_branch_flags;
347                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
348                 return snprintf(buf, len, "%s0x%x",
349                                 std_opcodes[c.i.op],
350                                 (pc & 0xf0000000) | (c.j.imm << 2));
351         case OP_BEQ:
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;
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);
405                 *is_io = true;
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
437 void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
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];
443         unsigned int i;
444         u32 pc, branch_pc, code;
445         bool is_io;
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);
451                 code = LE32TOH(code_ptr[i]);
452
453                 count = print_op((union code)code, pc, buf, sizeof(buf),
454                                  &flags_ptr, &nb_flags, &is_io);
455
456                 flags_ptr = NULL;
457                 nb_flags = 0;
458                 is_io = false;
459                 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
460                                   &flags_ptr, &nb_flags, &is_io);
461
462                 if (code == op->c.opcode) {
463                         *buf2 = '\0';
464                         count2 = 0;
465                 }
466
467                 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
468
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);
471         }
472 }