update vibration handling
[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 * const 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         [OP_META_MULT2]         = "mult2   ",
44         [OP_META_MULTU2]        = "multu2  ",
45         [OP_META_LWU]           = "lwu     ",
46         [OP_META_SWU]           = "swu     ",
47 };
48
49 static const char * const special_opcodes[] = {
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 };
79
80 static const char * const regimm_opcodes[] = {
81         [OP_REGIMM_BLTZ]        = "bltz    ",
82         [OP_REGIMM_BGEZ]        = "bgez    ",
83         [OP_REGIMM_BLTZAL]      = "bltzal  ",
84         [OP_REGIMM_BGEZAL]      = "bgezal  ",
85 };
86
87 static const char * const cp0_opcodes[] = {
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
95 static const char * const cp2_basic_opcodes[] = {
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
102 static 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
127 static 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
134 static const char * const mult2_opcodes[] = {
135         "mult2   ", "multu2  ",
136 };
137
138 static const char * const opcode_flags[] = {
139         "switched branch/DS",
140         "sync point",
141 };
142
143 static const char * const opcode_io_flags[] = {
144         "self-modifying code",
145         "no invalidation",
146         "no mask",
147         "load delay",
148 };
149
150 static const char * const opcode_io_modes[] = {
151         "Memory access",
152         "I/O access",
153         "RAM access",
154         "BIOS access",
155         "Scratchpad access",
156         "Mapped I/O access"
157 };
158
159 static const char * const opcode_branch_flags[] = {
160         "emulate branch",
161         "local branch",
162 };
163
164 static const char * const opcode_movi_flags[] = {
165         "movi",
166 };
167
168 static const char * const opcode_multdiv_flags[] = {
169         "No LO",
170         "No HI",
171         "No div check",
172 };
173
174 static 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
189 static const char * const reg_op_token[3] = {
190         "-", "*", "~",
191 };
192
193 static int print_flags(char *buf, size_t len, const struct opcode *op,
194                        const char * const *array, size_t array_size,
195                        bool is_io)
196 {
197         const char *flag_name, *io_mode_name;
198         unsigned int i, io_mode;
199         size_t count = 0, bytes;
200         bool first = true;
201         u32 flags = op->flags;
202         unsigned int reg_op;
203
204         for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
205                 if (!(flags & BIT(i)))
206                         continue;
207
208                 if (i < ARRAY_SIZE(opcode_flags))
209                         flag_name = opcode_flags[i];
210                 else
211                         flag_name = array[i - ARRAY_SIZE(opcode_flags)];
212
213                 bytes = do_snprintf(buf, len, &first, "", flag_name);
214                 buf += bytes;
215                 len -= bytes;
216                 count += bytes;
217         }
218
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
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                 }
251
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));
257                         buf += bytes;
258                         len -= bytes;
259                         count += bytes;
260                 }
261         }
262
263         if (!first)
264                 count += snprintf(buf, len, ")");
265         else
266                 *buf = '\0';
267
268         return count;
269 }
270
271 static int print_op_special(union code c, char *buf, size_t len,
272                             const char * const **flags_ptr, size_t *nb_flags)
273 {
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:
302                 *flags_ptr = opcode_branch_flags;
303                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
304                 fallthrough;
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),
314                                 lightrec_reg_name(c.r.rs));
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));
335         default:
336                 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
337         }
338 }
339
340 static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
341 {
342         if (cp == 2) {
343                 switch (c.r.op) {
344                 case OP_CP2_BASIC:
345                         return snprintf(buf, len, "%s%s,%u",
346                                         cp2_basic_opcodes[c.i.rs],
347                                         lightrec_reg_name(c.i.rt),
348                                         c.r.rd);
349                 default:
350                         return snprintf(buf, len, "%s", cp2_opcodes[c.r.op]);
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
370 static int print_op(union code c, u32 pc, char *buf, size_t len,
371                     const char * const **flags_ptr, size_t *nb_flags,
372                     bool *is_io)
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:
389                 *flags_ptr = opcode_branch_flags;
390                 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
391                 return snprintf(buf, len, "%s0x%x",
392                                 std_opcodes[c.i.op],
393                                 (pc & 0xf0000000) | (c.j.imm << 2));
394         case OP_BEQ:
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;
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:
414         case OP_ORI:
415                 *flags_ptr = opcode_movi_flags;
416                 *nb_flags = ARRAY_SIZE(opcode_movi_flags);
417                 fallthrough;
418         case OP_SLTI:
419         case OP_SLTIU:
420         case OP_ANDI:
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:
429                 *flags_ptr = opcode_movi_flags;
430                 *nb_flags = ARRAY_SIZE(opcode_movi_flags);
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:
451         case OP_META_LWU:
452         case OP_META_SWU:
453                 *flags_ptr = opcode_io_flags;
454                 *nb_flags = ARRAY_SIZE(opcode_io_flags);
455                 *is_io = true;
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));
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));
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);
484         default:
485                 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
486         }
487 }
488
489 void lightrec_print_disassembly(const struct block *block, const u32 *code_ptr)
490 {
491         const struct opcode *op;
492         const char * const *flags_ptr;
493         size_t nb_flags, count, count2;
494         char buf[256], buf2[256], buf3[256];
495         unsigned int i;
496         u32 pc, branch_pc, code;
497         bool is_io;
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);
503                 code = LE32TOH(code_ptr[i]);
504
505                 count = print_op((union code)code, pc, buf, sizeof(buf),
506                                  &flags_ptr, &nb_flags, &is_io);
507
508                 flags_ptr = NULL;
509                 nb_flags = 0;
510                 is_io = false;
511                 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
512                                   &flags_ptr, &nb_flags, &is_io);
513
514                 if (code == op->c.opcode) {
515                         *buf2 = '\0';
516                         count2 = 0;
517                 }
518
519                 print_flags(buf3, sizeof(buf3), op, flags_ptr, nb_flags, is_io);
520
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);
523         }
524 }