Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/cpu_disasm.cpp
7328 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "cpu_disasm.h"
5
#include "cpu_core.h"
6
#include "cpu_types.h"
7
8
#include "common/assert.h"
9
#include "common/small_string.h"
10
11
#include <array>
12
13
namespace CPU {
14
namespace {
15
16
enum Operand : u8
17
{
18
Operand_None,
19
i_rs,
20
i_rt,
21
i_imm,
22
j_target,
23
r_rs,
24
r_rt,
25
r_rd,
26
r_shamt,
27
r_funct
28
};
29
30
struct TableEntry
31
{
32
const char* format;
33
};
34
35
struct GTEInstructionTable
36
{
37
const char* name;
38
bool sf;
39
bool lm;
40
bool mvmva;
41
};
42
} // namespace
43
44
static void FormatInstruction(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format);
45
static void FormatComment(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format);
46
47
template<typename T>
48
static void FormatCopInstruction(SmallStringBase* dest, u32 pc, const Instruction inst,
49
const std::pair<T, const char*>* table, size_t table_size, T table_key);
50
51
template<typename T>
52
static void FormatCopComment(SmallStringBase* dest, u32 pc, const Instruction inst,
53
const std::pair<T, const char*>* table, size_t table_size, T table_key);
54
55
static void FormatGTEInstruction(SmallStringBase* dest, u32 pc, const Instruction inst);
56
57
static const std::array<const char*, 64> s_base_table = {{
58
"", // 0
59
"UNKNOWN", // 1
60
"j $jt", // 2
61
"jal $jt", // 3
62
"beq $rs, $rt, $rel", // 4
63
"bne $rs, $rt, $rel", // 5
64
"blez $rs, $rel", // 6
65
"bgtz $rs, $rel", // 7
66
"addi $rt, $rs, $imm", // 8
67
"addiu $rt, $rs, $imm", // 9
68
"slti $rt, $rs, $imm", // 10
69
"sltiu $rt, $rs, $immu", // 11
70
"andi $rt, $rs, $immx", // 12
71
"ori $rt, $rs, $immx", // 13
72
"xori $rt, $rs, $immx", // 14
73
"lui $rt, $immx", // 15
74
"UNKNOWN", // 16
75
"UNKNOWN", // 17
76
"UNKNOWN", // 18
77
"UNKNOWN", // 19
78
"UNKNOWN", // 20
79
"UNKNOWN", // 21
80
"UNKNOWN", // 22
81
"UNKNOWN", // 23
82
"UNKNOWN", // 24
83
"UNKNOWN", // 25
84
"UNKNOWN", // 26
85
"UNKNOWN", // 27
86
"UNKNOWN", // 28
87
"UNKNOWN", // 29
88
"UNKNOWN", // 30
89
"UNKNOWN", // 31
90
"lb $rt, $offsetrs", // 32
91
"lh $rt, $offsetrs", // 33
92
"lwl $rt, $offsetrs", // 34
93
"lw $rt, $offsetrs", // 35
94
"lbu $rt, $offsetrs", // 36
95
"lhu $rt, $offsetrs", // 37
96
"lwr $rt, $offsetrs", // 38
97
"UNKNOWN", // 39
98
"sb $rt, $offsetrs", // 40
99
"sh $rt, $offsetrs", // 41
100
"swl $rt, $offsetrs", // 42
101
"sw $rt, $offsetrs", // 43
102
"UNKNOWN", // 44
103
"UNKNOWN", // 45
104
"swr $rt, $offsetrs", // 46
105
"UNKNOWN", // 47
106
"lwc0 $coprt, $offsetrs", // 48
107
"lwc1 $coprt, $offsetrs", // 49
108
"lwc2 $coprt, $offsetrs", // 50
109
"lwc3 $coprt, $offsetrs", // 51
110
"UNKNOWN", // 52
111
"UNKNOWN", // 53
112
"UNKNOWN", // 54
113
"UNKNOWN", // 55
114
"swc0 $coprt, $offsetrs", // 56
115
"swc1 $coprt, $offsetrs", // 57
116
"swc2 $coprt, $offsetrs", // 58
117
"swc3 $coprt, $offsetrs", // 59
118
"UNKNOWN", // 60
119
"UNKNOWN", // 61
120
"UNKNOWN", // 62
121
"UNKNOWN" // 63
122
}};
123
124
static const std::array<const char*, 64> s_special_table = {{
125
"sll $rd, $rt, $shamt", // 0
126
"UNKNOWN", // 1
127
"srl $rd, $rt, $shamt", // 2
128
"sra $rd, $rt, $shamt", // 3
129
"sllv $rd, $rt, $rs", // 4
130
"UNKNOWN", // 5
131
"srlv $rd, $rt, $rs", // 6
132
"srav $rd, $rt, $rs", // 7
133
"jr $rs", // 8
134
"jalr $rd, $rs", // 9
135
"UNKNOWN", // 10
136
"UNKNOWN", // 11
137
"syscall", // 12
138
"break", // 13
139
"UNKNOWN", // 14
140
"UNKNOWN", // 15
141
"mfhi $rd", // 16
142
"mthi $rs", // 17
143
"mflo $rd", // 18
144
"mtlo $rs", // 19
145
"UNKNOWN", // 20
146
"UNKNOWN", // 21
147
"UNKNOWN", // 22
148
"UNKNOWN", // 23
149
"mult $rs, $rt", // 24
150
"multu $rs, $rt", // 25
151
"div $rs, $rt", // 26
152
"divu $rs, $rt", // 27
153
"UNKNOWN", // 28
154
"UNKNOWN", // 29
155
"UNKNOWN", // 30
156
"UNKNOWN", // 31
157
"add $rd, $rs, $rt", // 32
158
"addu $rd, $rs, $rt", // 33
159
"sub $rd, $rs, $rt", // 34
160
"subu $rd, $rs, $rt", // 35
161
"and $rd, $rs, $rt", // 36
162
"or $rd, $rs, $rt", // 37
163
"xor $rd, $rs, $rt", // 38
164
"nor $rd, $rs, $rt", // 39
165
"UNKNOWN", // 40
166
"UNKNOWN", // 41
167
"slt $rd, $rs, $rt", // 42
168
"sltu $rd, $rs, $rt", // 43
169
"UNKNOWN", // 44
170
"UNKNOWN", // 45
171
"UNKNOWN", // 46
172
"UNKNOWN", // 47
173
"UNKNOWN", // 48
174
"UNKNOWN", // 49
175
"UNKNOWN", // 50
176
"UNKNOWN", // 51
177
"UNKNOWN", // 52
178
"UNKNOWN", // 53
179
"UNKNOWN", // 54
180
"UNKNOWN", // 55
181
"UNKNOWN", // 56
182
"UNKNOWN", // 57
183
"UNKNOWN", // 58
184
"UNKNOWN", // 59
185
"UNKNOWN", // 60
186
"UNKNOWN", // 61
187
"UNKNOWN", // 62
188
"UNKNOWN" // 63
189
}};
190
191
static const std::array<std::pair<CopCommonInstruction, const char*>, 4> s_cop_common_table = {
192
{{CopCommonInstruction::mfcn, "mfc$cop $rt_, $coprd"},
193
{CopCommonInstruction::cfcn, "cfc$cop $rt_, $coprdc"},
194
{CopCommonInstruction::mtcn, "mtc$cop $rt, $coprd"},
195
{CopCommonInstruction::ctcn, "ctc$cop $rt, $coprdc"}}};
196
197
static const std::array<std::pair<Cop0Instruction, const char*>, 1> s_cop0_table = {{{Cop0Instruction::rfe, "rfe"}}};
198
199
static constexpr const std::array<const char*, 64> s_gte_register_names = {
200
{"v0_xy", "v0_z", "v1_xy", "v1_z", "v2_xy", "v2_z", "rgbc", "otz", "ir0", "ir1", "ir2", "ir3", "sxy0",
201
"sxy1", "sxy2", "sxyp", "sz0", "sz1", "sz2", "sz3", "rgb0", "rgb1", "rgb2", "res1", "mac0", "mac1",
202
"mac2", "mac3", "irgb", "orgb", "lzcs", "lzcr", "rt_0", "rt_1", "rt_2", "rt_3", "rt_4", "trx", "try",
203
"trz", "llm_0", "llm_1", "llm_2", "llm_3", "llm_4", "rbk", "gbk", "bbk", "lcm_0", "lcm_1", "lcm_2", "lcm_3",
204
"lcm_4", "rfc", "gfc", "bfc", "ofx", "ofy", "h", "dqa", "dqb", "zsf3", "zsf4", "flag"}};
205
206
static constexpr const std::array<const char*, 32> s_cop0_register_names = {
207
{"$0", "$1", "$2", "BPC", "$4", "BDA", "TAR", "DCIC", "BadA", "BDAM", "$10",
208
"BPCM", "SR", "CAUSE", "EPC", "PRID", "$16", "$17", "$18", "$19", "$20", "$21",
209
"$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"}};
210
211
static constexpr const std::array<GTEInstructionTable, 64> s_gte_instructions = {{
212
{"UNKNOWN", false, false, false}, // 0x00
213
{"rtps", true, true, false}, // 0x01
214
{"UNKNOWN", false, false, false}, // 0x02
215
{"UNKNOWN", false, false, false}, // 0x03
216
{"UNKNOWN", false, false, false}, // 0x04
217
{"UNKNOWN", false, false, false}, // 0x05
218
{"nclip", false, false, false}, // 0x06
219
{"UNKNOWN", false, false, false}, // 0x07
220
{"UNKNOWN", false, false, false}, // 0x08
221
{"UNKNOWN", false, false, false}, // 0x09
222
{"UNKNOWN", false, false, false}, // 0x0A
223
{"UNKNOWN", false, false, false}, // 0x0B
224
{"op", true, true, false}, // 0x0C
225
{"UNKNOWN", false, false, false}, // 0x0D
226
{"UNKNOWN", false, false, false}, // 0x0E
227
{"UNKNOWN", false, false, false}, // 0x0F
228
{"dpcs", true, true, false}, // 0x10
229
{"intpl", true, true, false}, // 0x11
230
{"mvmva", true, true, true}, // 0x12
231
{"ncds", true, true, false}, // 0x13
232
{"cdp", true, true, false}, // 0x14
233
{"UNKNOWN", false, false, false}, // 0x15
234
{"ncdt", true, true, false}, // 0x16
235
{"UNKNOWN", false, false, false}, // 0x17
236
{"UNKNOWN", false, false, false}, // 0x18
237
{"UNKNOWN", false, false, false}, // 0x19
238
{"UNKNOWN", false, false, false}, // 0x1A
239
{"nccs", true, true, false}, // 0x1B
240
{"cc", true, true, false}, // 0x1C
241
{"UNKNOWN", false, false, false}, // 0x1D
242
{"ncs", true, true, false}, // 0x1E
243
{"UNKNOWN", false, false, false}, // 0x1F
244
{"nct", true, true, false}, // 0x20
245
{"UNKNOWN", false, false, false}, // 0x21
246
{"UNKNOWN", false, false, false}, // 0x22
247
{"UNKNOWN", false, false, false}, // 0x23
248
{"UNKNOWN", false, false, false}, // 0x24
249
{"UNKNOWN", false, false, false}, // 0x25
250
{"UNKNOWN", false, false, false}, // 0x26
251
{"UNKNOWN", false, false, false}, // 0x27
252
{"sqr", true, true, false}, // 0x28
253
{"dcpl", true, true, false}, // 0x29
254
{"dpct", true, true, false}, // 0x2A
255
{"UNKNOWN", false, false, false}, // 0x2B
256
{"UNKNOWN", false, false, false}, // 0x2C
257
{"avsz3", false, false, false}, // 0x2D
258
{"avsz4", false, false, false}, // 0x2E
259
{"UNKNOWN", false, false, false}, // 0x2F
260
{"rtpt", true, true, false}, // 0x30
261
{"UNKNOWN", false, false, false}, // 0x31
262
{"UNKNOWN", false, false, false}, // 0x32
263
{"UNKNOWN", false, false, false}, // 0x33
264
{"UNKNOWN", false, false, false}, // 0x34
265
{"UNKNOWN", false, false, false}, // 0x35
266
{"UNKNOWN", false, false, false}, // 0x36
267
{"UNKNOWN", false, false, false}, // 0x37
268
{"UNKNOWN", false, false, false}, // 0x38
269
{"UNKNOWN", false, false, false}, // 0x39
270
{"UNKNOWN", false, false, false}, // 0x3A
271
{"UNKNOWN", false, false, false}, // 0x3B
272
{"UNKNOWN", false, false, false}, // 0x3C
273
{"gpf", true, true, false}, // 0x3D
274
{"gpl", true, true, false}, // 0x3E
275
{"ncct", true, true, false}, // 0x3F
276
}};
277
278
} // namespace CPU
279
280
void CPU::FormatInstruction(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format)
281
{
282
dest->clear();
283
284
const char* str = format;
285
while (*str != '\0')
286
{
287
const char ch = *(str++);
288
if (ch != '$')
289
{
290
dest->append(ch);
291
continue;
292
}
293
294
if (std::strncmp(str, "rs", 2) == 0)
295
{
296
dest->append(GetRegName(inst.r.rs));
297
str += 2;
298
}
299
else if (std::strncmp(str, "rt_", 3) == 0)
300
{
301
dest->append(GetRegName(inst.r.rt));
302
str += 3;
303
}
304
else if (std::strncmp(str, "rt", 2) == 0)
305
{
306
dest->append(GetRegName(inst.r.rt));
307
str += 2;
308
}
309
else if (std::strncmp(str, "rd", 2) == 0)
310
{
311
dest->append(GetRegName(inst.r.rd));
312
str += 2;
313
}
314
else if (std::strncmp(str, "shamt", 5) == 0)
315
{
316
dest->append_format("{}", ZeroExtend32(inst.r.shamt.GetValue()));
317
str += 5;
318
}
319
else if (std::strncmp(str, "immu", 4) == 0)
320
{
321
dest->append_format("{}", inst.i.imm_zext32());
322
str += 4;
323
}
324
else if (std::strncmp(str, "immx", 4) == 0)
325
{
326
dest->append_format("0x{:04x}", inst.i.imm_zext32());
327
str += 4;
328
}
329
else if (std::strncmp(str, "imm", 3) == 0)
330
{
331
dest->append_format("{}", static_cast<s32>(inst.i.imm_sext32()));
332
str += 3;
333
}
334
else if (std::strncmp(str, "rel", 3) == 0)
335
{
336
const u32 target = (pc + UINT32_C(4)) + (inst.i.imm_sext32() << 2);
337
dest->append_format("0x{:08x}", target);
338
str += 3;
339
}
340
else if (std::strncmp(str, "offsetrs", 8) == 0)
341
{
342
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
343
dest->append_format("{}0x{:x}({})", (offset < 0) ? "-" : "", (offset < 0) ? -offset : offset,
344
GetRegName(inst.i.rs));
345
str += 8;
346
}
347
else if (std::strncmp(str, "jt", 2) == 0)
348
{
349
const u32 target = ((pc + UINT32_C(4)) & UINT32_C(0xF0000000)) | (inst.j.target << 2);
350
dest->append_format("0x{:08x}", target);
351
str += 2;
352
}
353
else if (std::strncmp(str, "copcc", 5) == 0)
354
{
355
dest->append(((inst.bits & (UINT32_C(1) << 24)) != 0) ? 't' : 'f');
356
str += 5;
357
}
358
else if (std::strncmp(str, "coprdc", 6) == 0)
359
{
360
if (inst.cop.cop_n == 2)
361
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()) + 32));
362
else
363
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
364
str += 6;
365
}
366
else if (std::strncmp(str, "coprd", 5) == 0)
367
{
368
if (inst.cop.cop_n == 2)
369
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue())));
370
else if (inst.cop.cop_n == 0)
371
dest->append(GetCop0RegisterName(static_cast<u8>(inst.r.rd.GetValue())));
372
else
373
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
374
str += 5;
375
}
376
else if (std::strncmp(str, "coprt", 5) == 0)
377
{
378
if (inst.cop.cop_n == 2)
379
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue())));
380
else if (inst.cop.cop_n == 0)
381
dest->append(GetCop0RegisterName(static_cast<u8>(inst.r.rt.GetValue())));
382
else
383
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rt.GetValue())));
384
str += 5;
385
}
386
else if (std::strncmp(str, "cop", 3) == 0)
387
{
388
dest->append_format("{}", inst.cop.cop_n.GetValue());
389
str += 3;
390
}
391
else
392
{
393
Panic("Unknown operand");
394
}
395
}
396
}
397
398
void CPU::FormatComment(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format)
399
{
400
const CPU::Registers* regs = &CPU::g_state.regs;
401
402
const char* str = format;
403
while (*str != '\0')
404
{
405
const char ch = *(str++);
406
if (ch != '$')
407
continue;
408
409
if (std::strncmp(str, "rs", 2) == 0)
410
{
411
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ", GetRegName(inst.r.rs),
412
regs->r[static_cast<u8>(inst.r.rs.GetValue())]);
413
414
str += 2;
415
}
416
else if (std::strncmp(str, "rt_", 3) == 0)
417
{
418
str += 3;
419
}
420
else if (std::strncmp(str, "rt", 2) == 0)
421
{
422
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ", GetRegName(inst.r.rt),
423
regs->r[static_cast<u8>(inst.r.rt.GetValue())]);
424
str += 2;
425
}
426
else if (std::strncmp(str, "rd", 2) == 0)
427
{
428
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ", GetRegName(inst.r.rd),
429
regs->r[static_cast<u8>(inst.r.rd.GetValue())]);
430
str += 2;
431
}
432
else if (std::strncmp(str, "shamt", 5) == 0)
433
{
434
str += 5;
435
}
436
else if (std::strncmp(str, "immu", 4) == 0)
437
{
438
str += 4;
439
}
440
else if (std::strncmp(str, "imm", 3) == 0)
441
{
442
str += 3;
443
}
444
else if (std::strncmp(str, "rel", 3) == 0)
445
{
446
str += 3;
447
}
448
else if (std::strncmp(str, "offsetrs", 8) == 0)
449
{
450
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
451
const VirtualMemoryAddress address = (regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
452
453
if (!dest->empty())
454
dest->append_format(", ");
455
456
if (inst.op == InstructionOp::lb || inst.op == InstructionOp::lbu)
457
{
458
u8 data = 0;
459
CPU::SafeReadMemoryByte(address, &data);
460
dest->append_format("addr=0x{:08x}[0x{:02x}]", address, data);
461
}
462
else if (inst.op == InstructionOp::lh || inst.op == InstructionOp::lhu)
463
{
464
u16 data = 0;
465
CPU::SafeReadMemoryHalfWord(address, &data);
466
dest->append_format("addr=0x{:08x}[0x{:04x}]", address, data);
467
}
468
else if (inst.op == InstructionOp::lw || (inst.op >= InstructionOp::lwc0 && inst.op <= InstructionOp::lwc3) ||
469
inst.op == InstructionOp::lwl || inst.op == InstructionOp::lwr)
470
{
471
u32 data = 0;
472
CPU::SafeReadMemoryWord(address, &data);
473
dest->append_format("addr=0x{:08x}[0x{:08x}]", address, data);
474
}
475
else
476
{
477
dest->append_format("addr=0x{:08x}", address);
478
}
479
480
str += 8;
481
}
482
else if (std::strncmp(str, "jt", 2) == 0)
483
{
484
str += 2;
485
}
486
else if (std::strncmp(str, "copcc", 5) == 0)
487
{
488
str += 5;
489
}
490
else if (std::strncmp(str, "coprdc", 6) == 0)
491
{
492
if (inst.cop.cop_n == 2)
493
{
494
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ",
495
GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()) + 32),
496
g_state.gte_regs.cr32[static_cast<u8>(inst.r.rd.GetValue())]);
497
}
498
499
str += 6;
500
}
501
else if (std::strncmp(str, "coprd", 5) == 0)
502
{
503
if (inst.cop.cop_n == 2)
504
{
505
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ",
506
GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue())),
507
g_state.gte_regs.dr32[static_cast<u8>(inst.r.rd.GetValue())]);
508
}
509
else if (inst.cop.cop_n == 0)
510
{
511
dest->append_format("{}{}", dest->empty() ? "" : ", ",
512
GetCop0RegisterName(static_cast<u8>(inst.r.rd.GetValue())));
513
514
u32 value = 0;
515
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
516
{
517
case Cop0Reg::BPC:
518
value = g_state.cop0_regs.BPC;
519
break;
520
521
case Cop0Reg::BPCM:
522
value = g_state.cop0_regs.BPCM;
523
break;
524
525
case Cop0Reg::BDA:
526
value = g_state.cop0_regs.BDA;
527
break;
528
529
case Cop0Reg::BDAM:
530
value = g_state.cop0_regs.BDAM;
531
break;
532
533
case Cop0Reg::DCIC:
534
value = g_state.cop0_regs.dcic.bits;
535
break;
536
537
case Cop0Reg::JUMPDEST:
538
value = g_state.cop0_regs.TAR;
539
break;
540
541
case Cop0Reg::BadVaddr:
542
value = g_state.cop0_regs.BadVaddr;
543
break;
544
545
case Cop0Reg::SR:
546
value = g_state.cop0_regs.sr.bits;
547
break;
548
549
case Cop0Reg::CAUSE:
550
value = g_state.cop0_regs.cause.bits;
551
break;
552
553
case Cop0Reg::EPC:
554
value = g_state.cop0_regs.EPC;
555
break;
556
557
case Cop0Reg::PRID:
558
value = g_state.cop0_regs.PRID;
559
break;
560
}
561
562
dest->append_format("=0x{:08x}", value);
563
}
564
565
str += 5;
566
}
567
else if (std::strncmp(str, "coprt", 5) == 0)
568
{
569
if (inst.cop.cop_n == 2)
570
{
571
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ",
572
GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue())),
573
g_state.gte_regs.dr32[static_cast<u8>(inst.r.rt.GetValue())]);
574
}
575
576
str += 5;
577
}
578
else if (std::strncmp(str, "cop", 3) == 0)
579
{
580
str += 3;
581
}
582
else
583
{
584
Panic("Unknown operand");
585
}
586
}
587
}
588
589
template<typename T>
590
void CPU::FormatCopInstruction(SmallStringBase* dest, u32 pc, const Instruction inst,
591
const std::pair<T, const char*>* table, size_t table_size, T table_key)
592
{
593
for (size_t i = 0; i < table_size; i++)
594
{
595
if (table[i].first == table_key)
596
{
597
FormatInstruction(dest, inst, pc, table[i].second);
598
return;
599
}
600
}
601
602
dest->format("<cop{} 0x{:08x}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
603
}
604
605
template<typename T>
606
void CPU::FormatCopComment(SmallStringBase* dest, u32 pc, const Instruction inst,
607
const std::pair<T, const char*>* table, size_t table_size, T table_key)
608
{
609
for (size_t i = 0; i < table_size; i++)
610
{
611
if (table[i].first == table_key)
612
{
613
FormatComment(dest, inst, pc, table[i].second);
614
return;
615
}
616
}
617
}
618
619
void CPU::FormatGTEInstruction(SmallStringBase* dest, u32 pc, const Instruction inst)
620
{
621
const GTE::Instruction gi{inst.bits};
622
const GTEInstructionTable& t = s_gte_instructions[gi.command];
623
dest->assign(t.name);
624
625
if (t.sf && gi.sf)
626
dest->append(" sf");
627
628
if (t.lm && gi.lm)
629
dest->append(" lm");
630
631
if (t.mvmva)
632
{
633
dest->append_format(" m={} v={} t={}", static_cast<u8>(gi.mvmva_multiply_matrix),
634
static_cast<u8>(gi.mvmva_multiply_vector), static_cast<u8>(gi.mvmva_translation_vector));
635
}
636
}
637
638
void CPU::DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits)
639
{
640
const Instruction inst{bits};
641
switch (inst.op)
642
{
643
case InstructionOp::funct:
644
FormatInstruction(dest, inst, pc, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
645
return;
646
647
case InstructionOp::cop0:
648
case InstructionOp::cop1:
649
case InstructionOp::cop2:
650
case InstructionOp::cop3:
651
{
652
if (inst.cop.IsCommonInstruction())
653
{
654
FormatCopInstruction(dest, pc, inst, s_cop_common_table.data(), s_cop_common_table.size(), inst.cop.CommonOp());
655
}
656
else
657
{
658
switch (inst.op)
659
{
660
case InstructionOp::cop0:
661
{
662
FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
663
}
664
break;
665
666
case InstructionOp::cop2:
667
{
668
FormatGTEInstruction(dest, pc, inst);
669
}
670
break;
671
672
case InstructionOp::cop1:
673
case InstructionOp::cop3:
674
default:
675
{
676
dest->format("<cop{} 0x{:08x}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
677
}
678
break;
679
}
680
}
681
}
682
break;
683
684
// special case for bltz/bgez{al}
685
case InstructionOp::b:
686
{
687
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
688
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
689
const bool link = (rt & u8(0x1E)) == u8(0x10);
690
if (link)
691
FormatInstruction(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
692
else
693
FormatInstruction(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
694
}
695
break;
696
697
default:
698
FormatInstruction(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]);
699
break;
700
}
701
}
702
703
void CPU::DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits)
704
{
705
const Instruction inst{bits};
706
switch (inst.op)
707
{
708
case InstructionOp::funct:
709
FormatComment(dest, inst, pc, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
710
return;
711
712
case InstructionOp::cop0:
713
case InstructionOp::cop1:
714
case InstructionOp::cop2:
715
case InstructionOp::cop3:
716
{
717
if (inst.cop.IsCommonInstruction())
718
{
719
FormatCopComment(dest, pc, inst, s_cop_common_table.data(), s_cop_common_table.size(), inst.cop.CommonOp());
720
}
721
else
722
{
723
switch (inst.op)
724
{
725
case InstructionOp::cop0:
726
{
727
FormatCopComment(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
728
}
729
break;
730
731
case InstructionOp::cop2:
732
// TODO: Show GTE regs?
733
break;
734
735
case InstructionOp::cop1:
736
case InstructionOp::cop3:
737
default:
738
{
739
dest->format("<cop{} 0x{:08x}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
740
}
741
break;
742
}
743
}
744
}
745
break;
746
747
// special case for bltz/bgez{al}
748
case InstructionOp::b:
749
{
750
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
751
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
752
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
753
if (link)
754
FormatComment(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
755
else
756
FormatComment(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
757
}
758
break;
759
760
default:
761
FormatComment(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]);
762
break;
763
}
764
}
765
766
const char* CPU::GetGTERegisterName(u32 index)
767
{
768
return (index < s_gte_register_names.size()) ? s_gte_register_names[index] : "";
769
}
770
771
const char* CPU::GetCop0RegisterName(u32 index)
772
{
773
return (index < s_cop0_register_names.size()) ? s_cop0_register_names[index] : "";
774
}
775
776