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
98fa08a5
PC
14static 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};
d16005f8 44
98fa08a5
PC
45static 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};
d16005f8 75
98fa08a5
PC
76static 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};
d16005f8 82
98fa08a5
PC
83static 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
91static 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
98static const char *opcode_flags[] = {
99 "switched branch/DS",
100 "unload Rs",
101 "unload Rt",
102 "unload Rd",
103 "sync point",
104};
105
106static const char *opcode_io_flags[] = {
98fa08a5
PC
107 "self-modifying code",
108 "no invalidation",
22eee2ac
PC
109 "no mask",
110};
111
112static const char *opcode_io_modes[] = {
113 "Memory access",
114 "I/O access",
115 "RAM access",
116 "BIOS access",
117 "Scratchpad access",
98fa08a5 118};
d16005f8 119
98fa08a5
PC
120static const char *opcode_branch_flags[] = {
121 "emulate branch",
122 "local branch",
123};
124
125static const char *opcode_multdiv_flags[] = {
126 "No LO",
127 "No HI",
128 "No div check",
129};
130
131static int print_flags(char *buf, size_t len, u16 flags,
22eee2ac
PC
132 const char **array, size_t array_size,
133 bool is_io)
d16005f8 134{
22eee2ac
PC
135 const char *flag_name, *io_mode_name;
136 unsigned int i, io_mode;
98fa08a5
PC
137 size_t count = 0, bytes;
138 bool first = true;
d16005f8 139
98fa08a5
PC
140 for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
141 if (!(flags & BIT(i)))
142 continue;
d16005f8 143
98fa08a5
PC
144 if (i < ARRAY_SIZE(opcode_flags))
145 flag_name = opcode_flags[i];
d16005f8 146 else
98fa08a5
PC
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;
d16005f8
PC
158 }
159
22eee2ac
PC
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
98fa08a5
PC
177 if (!first)
178 count += snprintf(buf, len, ")");
179 else
180 *buf = '\0';
d16005f8 181
98fa08a5 182 return count;
d16005f8
PC
183}
184
98fa08a5
PC
185static int print_op_special(union code c, char *buf, size_t len,
186 const char ***flags_ptr, size_t *nb_flags)
d16005f8 187{
98fa08a5
PC
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 case OP_SPECIAL_MTHI:
217 case OP_SPECIAL_MTLO:
218 return snprintf(buf, len, "%s%s",
219 special_opcodes[c.r.op],
220 lightrec_reg_name(c.r.rs));
221 case OP_SPECIAL_JALR:
222 return snprintf(buf, len, "%s%s,%s",
223 special_opcodes[c.r.op],
224 lightrec_reg_name(c.r.rd),
225 lightrec_reg_name(c.r.rt));
226 case OP_SPECIAL_SYSCALL:
227 case OP_SPECIAL_BREAK:
228 return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
229 case OP_SPECIAL_MFHI:
230 case OP_SPECIAL_MFLO:
231 return snprintf(buf, len, "%s%s",
232 special_opcodes[c.r.op],
233 lightrec_reg_name(c.r.rd));
234 case OP_SPECIAL_MULT:
235 case OP_SPECIAL_MULTU:
236 case OP_SPECIAL_DIV:
237 case OP_SPECIAL_DIVU:
238 *flags_ptr = opcode_multdiv_flags;
239 *nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
240 return snprintf(buf, len, "%s%s,%s,%s,%s",
241 special_opcodes[c.r.op],
242 lightrec_reg_name(get_mult_div_hi(c)),
243 lightrec_reg_name(get_mult_div_lo(c)),
244 lightrec_reg_name(c.r.rs),
245 lightrec_reg_name(c.r.rt));
d16005f8 246 default:
98fa08a5 247 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
d16005f8
PC
248 }
249}
250
98fa08a5 251static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
d16005f8 252{
98fa08a5
PC
253 if (cp == 2) {
254 switch (c.i.rs) {
255 case OP_CP0_MFC0:
256 case OP_CP0_CFC0:
257 case OP_CP0_MTC0:
258 case OP_CP0_CTC0:
259 return snprintf(buf, len, "%s%s,%u",
260 cp2_opcodes[c.i.rs],
261 lightrec_reg_name(c.i.rt),
262 c.r.rd);
263 default:
264 return snprintf(buf, len, "cp2 (0x%08x)", c.opcode);
265 }
266 } else {
267 switch (c.i.rs) {
268 case OP_CP0_MFC0:
269 case OP_CP0_CFC0:
270 case OP_CP0_MTC0:
271 case OP_CP0_CTC0:
272 return snprintf(buf, len, "%s%s,%u",
273 cp0_opcodes[c.i.rs],
274 lightrec_reg_name(c.i.rt),
275 c.r.rd);
276 case OP_CP0_RFE:
277 return snprintf(buf, len, "rfe ");
278 default:
279 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
280 }
281 }
282}
283
284static int print_op(union code c, u32 pc, char *buf, size_t len,
22eee2ac
PC
285 const char ***flags_ptr, size_t *nb_flags,
286 bool *is_io)
98fa08a5
PC
287{
288 if (c.opcode == 0)
289 return snprintf(buf, len, "nop ");
290
291 switch (c.i.op) {
292 case OP_SPECIAL:
293 return print_op_special(c, buf, len, flags_ptr, nb_flags);
294 case OP_REGIMM:
295 *flags_ptr = opcode_branch_flags;
296 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
297 return snprintf(buf, len, "%s%s,0x%x",
298 regimm_opcodes[c.i.rt],
299 lightrec_reg_name(c.i.rs),
300 pc + 4 + ((s16)c.i.imm << 2));
301 case OP_J:
302 case OP_JAL:
303 return snprintf(buf, len, "%s0x%x",
304 std_opcodes[c.i.op],
305 (pc & 0xf0000000) | (c.j.imm << 2));
306 case OP_BEQ:
307 case OP_BNE:
308 case OP_BLEZ:
309 case OP_BGTZ:
310 *flags_ptr = opcode_branch_flags;
311 *nb_flags = ARRAY_SIZE(opcode_branch_flags);
312 return snprintf(buf, len, "%s%s,%s,0x%x",
313 std_opcodes[c.i.op],
314 lightrec_reg_name(c.i.rs),
315 lightrec_reg_name(c.i.rt),
316 pc + 4 + ((s16)c.i.imm << 2));
317 case OP_ADDI:
318 case OP_ADDIU:
319 case OP_SLTI:
320 case OP_SLTIU:
321 case OP_ANDI:
322 case OP_ORI:
323 case OP_XORI:
324 return snprintf(buf, len, "%s%s,%s,0x%04hx",
325 std_opcodes[c.i.op],
326 lightrec_reg_name(c.i.rt),
327 lightrec_reg_name(c.i.rs),
328 (u16)c.i.imm);
329
330 case OP_LUI:
331 return snprintf(buf, len, "%s%s,0x%04hx",
332 std_opcodes[c.i.op],
333 lightrec_reg_name(c.i.rt),
334 (u16)c.i.imm);
335 case OP_CP0:
336 return print_op_cp(c, buf, len, 0);
337 case OP_CP2:
338 return print_op_cp(c, buf, len, 2);
339 case OP_LB:
340 case OP_LH:
341 case OP_LWL:
342 case OP_LW:
343 case OP_LBU:
344 case OP_LHU:
345 case OP_LWR:
346 case OP_SB:
347 case OP_SH:
348 case OP_SWL:
349 case OP_SW:
350 case OP_SWR:
351 *flags_ptr = opcode_io_flags;
352 *nb_flags = ARRAY_SIZE(opcode_io_flags);
22eee2ac 353 *is_io = true;
98fa08a5
PC
354 return snprintf(buf, len, "%s%s,%hd(%s)",
355 std_opcodes[c.i.op],
356 lightrec_reg_name(c.i.rt),
357 (s16)c.i.imm,
358 lightrec_reg_name(c.i.rs));
359 case OP_LWC2:
360 case OP_SWC2:
361 *flags_ptr = opcode_io_flags;
362 *nb_flags = ARRAY_SIZE(opcode_io_flags);
363 return snprintf(buf, len, "%s%s,%hd(%s)",
364 std_opcodes[c.i.op],
365 lightrec_reg_name(c.i.rt),
366 (s16)c.i.imm,
367 lightrec_reg_name(c.i.rs));
368 case OP_META_MOV:
369 return snprintf(buf, len, "move %s,%s",
370 lightrec_reg_name(c.r.rd),
371 lightrec_reg_name(c.r.rs));
372 case OP_META_EXTC:
373 return snprintf(buf, len, "extc %s,%s",
374 lightrec_reg_name(c.i.rt),
375 lightrec_reg_name(c.i.rs));
376 case OP_META_EXTS:
377 return snprintf(buf, len, "exts %s,%s",
378 lightrec_reg_name(c.i.rt),
379 lightrec_reg_name(c.i.rs));
380 default:
381 return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
382 }
383}
384
385void lightrec_print_disassembly(const struct block *block, const u32 *code)
386{
387 const struct opcode *op;
388 const char **flags_ptr;
389 size_t nb_flags, count, count2;
390 char buf[256], buf2[256], buf3[256];
d16005f8 391 unsigned int i;
98fa08a5 392 u32 pc, branch_pc;
22eee2ac 393 bool is_io;
98fa08a5
PC
394
395 for (i = 0; i < block->nb_ops; i++) {
396 op = &block->opcode_list[i];
397 branch_pc = get_branch_pc(block, i, 0);
398 pc = block->pc + (i << 2);
399
400 count = print_op((union code)code[i], pc, buf, sizeof(buf),
22eee2ac 401 &flags_ptr, &nb_flags, &is_io);
98fa08a5
PC
402
403 flags_ptr = NULL;
404 nb_flags = 0;
22eee2ac 405 is_io = false;
98fa08a5 406 count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
22eee2ac 407 &flags_ptr, &nb_flags, &is_io);
98fa08a5
PC
408
409 if (code[i] == op->c.opcode) {
410 *buf2 = '\0';
411 count2 = 0;
412 }
413
22eee2ac
PC
414 print_flags(buf3, sizeof(buf3), op->flags, flags_ptr, nb_flags,
415 is_io);
d16005f8 416
98fa08a5
PC
417 printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
418 buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);
d16005f8
PC
419 }
420}