Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/cpu_disasm.cpp
4214 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<GTEInstructionTable, 64> s_gte_instructions = {{
207
{"UNKNOWN", false, false, false}, // 0x00
208
{"rtps", true, true, false}, // 0x01
209
{"UNKNOWN", false, false, false}, // 0x02
210
{"UNKNOWN", false, false, false}, // 0x03
211
{"UNKNOWN", false, false, false}, // 0x04
212
{"UNKNOWN", false, false, false}, // 0x05
213
{"nclip", false, false, false}, // 0x06
214
{"UNKNOWN", false, false, false}, // 0x07
215
{"UNKNOWN", false, false, false}, // 0x08
216
{"UNKNOWN", false, false, false}, // 0x09
217
{"UNKNOWN", false, false, false}, // 0x0A
218
{"UNKNOWN", false, false, false}, // 0x0B
219
{"op", true, true, false}, // 0x0C
220
{"UNKNOWN", false, false, false}, // 0x0D
221
{"UNKNOWN", false, false, false}, // 0x0E
222
{"UNKNOWN", false, false, false}, // 0x0F
223
{"dpcs", true, true, false}, // 0x10
224
{"intpl", true, true, false}, // 0x11
225
{"mvmva", true, true, true}, // 0x12
226
{"ncds", true, true, false}, // 0x13
227
{"cdp", true, true, false}, // 0x14
228
{"UNKNOWN", false, false, false}, // 0x15
229
{"ncdt", true, true, false}, // 0x16
230
{"UNKNOWN", false, false, false}, // 0x17
231
{"UNKNOWN", false, false, false}, // 0x18
232
{"UNKNOWN", false, false, false}, // 0x19
233
{"UNKNOWN", false, false, false}, // 0x1A
234
{"nccs", true, true, false}, // 0x1B
235
{"cc", true, true, false}, // 0x1C
236
{"UNKNOWN", false, false, false}, // 0x1D
237
{"ncs", true, true, false}, // 0x1E
238
{"UNKNOWN", false, false, false}, // 0x1F
239
{"nct", true, true, false}, // 0x20
240
{"UNKNOWN", false, false, false}, // 0x21
241
{"UNKNOWN", false, false, false}, // 0x22
242
{"UNKNOWN", false, false, false}, // 0x23
243
{"UNKNOWN", false, false, false}, // 0x24
244
{"UNKNOWN", false, false, false}, // 0x25
245
{"UNKNOWN", false, false, false}, // 0x26
246
{"UNKNOWN", false, false, false}, // 0x27
247
{"sqr", true, true, false}, // 0x28
248
{"dcpl", true, true, false}, // 0x29
249
{"dpct", true, true, false}, // 0x2A
250
{"UNKNOWN", false, false, false}, // 0x2B
251
{"UNKNOWN", false, false, false}, // 0x2C
252
{"avsz3", false, false, false}, // 0x2D
253
{"avsz4", false, false, false}, // 0x2E
254
{"UNKNOWN", false, false, false}, // 0x2F
255
{"rtpt", true, true, false}, // 0x30
256
{"UNKNOWN", false, false, false}, // 0x31
257
{"UNKNOWN", false, false, false}, // 0x32
258
{"UNKNOWN", false, false, false}, // 0x33
259
{"UNKNOWN", false, false, false}, // 0x34
260
{"UNKNOWN", false, false, false}, // 0x35
261
{"UNKNOWN", false, false, false}, // 0x36
262
{"UNKNOWN", false, false, false}, // 0x37
263
{"UNKNOWN", false, false, false}, // 0x38
264
{"UNKNOWN", false, false, false}, // 0x39
265
{"UNKNOWN", false, false, false}, // 0x3A
266
{"UNKNOWN", false, false, false}, // 0x3B
267
{"UNKNOWN", false, false, false}, // 0x3C
268
{"gpf", true, true, false}, // 0x3D
269
{"gpl", true, true, false}, // 0x3E
270
{"ncct", true, true, false}, // 0x3F
271
}};
272
273
} // namespace CPU
274
275
void CPU::FormatInstruction(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format)
276
{
277
dest->clear();
278
279
const char* str = format;
280
while (*str != '\0')
281
{
282
const char ch = *(str++);
283
if (ch != '$')
284
{
285
dest->append(ch);
286
continue;
287
}
288
289
if (std::strncmp(str, "rs", 2) == 0)
290
{
291
dest->append(GetRegName(inst.r.rs));
292
str += 2;
293
}
294
else if (std::strncmp(str, "rt_", 3) == 0)
295
{
296
dest->append(GetRegName(inst.r.rt));
297
str += 3;
298
}
299
else if (std::strncmp(str, "rt", 2) == 0)
300
{
301
dest->append(GetRegName(inst.r.rt));
302
str += 2;
303
}
304
else if (std::strncmp(str, "rd", 2) == 0)
305
{
306
dest->append(GetRegName(inst.r.rd));
307
str += 2;
308
}
309
else if (std::strncmp(str, "shamt", 5) == 0)
310
{
311
dest->append_format("{}", ZeroExtend32(inst.r.shamt.GetValue()));
312
str += 5;
313
}
314
else if (std::strncmp(str, "immu", 4) == 0)
315
{
316
dest->append_format("{}", inst.i.imm_zext32());
317
str += 4;
318
}
319
else if (std::strncmp(str, "immx", 4) == 0)
320
{
321
dest->append_format("0x{:04x}", inst.i.imm_zext32());
322
str += 4;
323
}
324
else if (std::strncmp(str, "imm", 3) == 0)
325
{
326
dest->append_format("{}", static_cast<s32>(inst.i.imm_sext32()));
327
str += 3;
328
}
329
else if (std::strncmp(str, "rel", 3) == 0)
330
{
331
const u32 target = (pc + UINT32_C(4)) + (inst.i.imm_sext32() << 2);
332
dest->append_format("0x{:08x}", target);
333
str += 3;
334
}
335
else if (std::strncmp(str, "offsetrs", 8) == 0)
336
{
337
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
338
dest->append_format("{}0x{:x}({})", (offset < 0) ? "-" : "", (offset < 0) ? -offset : offset,
339
GetRegName(inst.i.rs));
340
str += 8;
341
}
342
else if (std::strncmp(str, "jt", 2) == 0)
343
{
344
const u32 target = ((pc + UINT32_C(4)) & UINT32_C(0xF0000000)) | (inst.j.target << 2);
345
dest->append_format("0x{:08x}", target);
346
str += 2;
347
}
348
else if (std::strncmp(str, "copcc", 5) == 0)
349
{
350
dest->append(((inst.bits & (UINT32_C(1) << 24)) != 0) ? 't' : 'f');
351
str += 5;
352
}
353
else if (std::strncmp(str, "coprdc", 6) == 0)
354
{
355
if (inst.IsCop2Instruction())
356
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()) + 32));
357
else
358
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
359
str += 6;
360
}
361
else if (std::strncmp(str, "coprd", 5) == 0)
362
{
363
if (inst.IsCop2Instruction())
364
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue())));
365
else
366
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
367
str += 5;
368
}
369
else if (std::strncmp(str, "coprt", 5) == 0)
370
{
371
if (inst.IsCop2Instruction())
372
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue())));
373
else
374
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rt.GetValue())));
375
str += 5;
376
}
377
else if (std::strncmp(str, "cop", 3) == 0)
378
{
379
dest->append_format("{}", static_cast<u8>(inst.op.GetValue()) & INSTRUCTION_COP_N_MASK);
380
str += 3;
381
}
382
else
383
{
384
Panic("Unknown operand");
385
}
386
}
387
}
388
389
void CPU::FormatComment(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format)
390
{
391
const CPU::Registers* regs = &CPU::g_state.regs;
392
393
const char* str = format;
394
while (*str != '\0')
395
{
396
const char ch = *(str++);
397
if (ch != '$')
398
continue;
399
400
if (std::strncmp(str, "rs", 2) == 0)
401
{
402
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ", GetRegName(inst.r.rs),
403
regs->r[static_cast<u8>(inst.r.rs.GetValue())]);
404
405
str += 2;
406
}
407
else if (std::strncmp(str, "rt_", 3) == 0)
408
{
409
str += 3;
410
}
411
else if (std::strncmp(str, "rt", 2) == 0)
412
{
413
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ", GetRegName(inst.r.rt),
414
regs->r[static_cast<u8>(inst.r.rt.GetValue())]);
415
str += 2;
416
}
417
else if (std::strncmp(str, "rd", 2) == 0)
418
{
419
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ", GetRegName(inst.r.rd),
420
regs->r[static_cast<u8>(inst.r.rd.GetValue())]);
421
str += 2;
422
}
423
else if (std::strncmp(str, "shamt", 5) == 0)
424
{
425
str += 5;
426
}
427
else if (std::strncmp(str, "immu", 4) == 0)
428
{
429
str += 4;
430
}
431
else if (std::strncmp(str, "imm", 3) == 0)
432
{
433
str += 3;
434
}
435
else if (std::strncmp(str, "rel", 3) == 0)
436
{
437
str += 3;
438
}
439
else if (std::strncmp(str, "offsetrs", 8) == 0)
440
{
441
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
442
const VirtualMemoryAddress address = (regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
443
444
if (!dest->empty())
445
dest->append_format(", ");
446
447
if (inst.op == InstructionOp::lb || inst.op == InstructionOp::lbu)
448
{
449
u8 data = 0;
450
CPU::SafeReadMemoryByte(address, &data);
451
dest->append_format("addr=0x{:08x}[0x{:02x}]", address, data);
452
}
453
else if (inst.op == InstructionOp::lh || inst.op == InstructionOp::lhu)
454
{
455
u16 data = 0;
456
CPU::SafeReadMemoryHalfWord(address, &data);
457
dest->append_format("addr=0x{:08x}[0x{:04x}]", address, data);
458
}
459
else if (inst.op == InstructionOp::lw || (inst.op >= InstructionOp::lwc0 && inst.op <= InstructionOp::lwc3) ||
460
inst.op == InstructionOp::lwl || inst.op == InstructionOp::lwr)
461
{
462
u32 data = 0;
463
CPU::SafeReadMemoryWord(address, &data);
464
dest->append_format("addr=0x{:08x}[0x{:08x}]", address, data);
465
}
466
else
467
{
468
dest->append_format("addr=0x{:08x}", address);
469
}
470
471
str += 8;
472
}
473
else if (std::strncmp(str, "jt", 2) == 0)
474
{
475
str += 2;
476
}
477
else if (std::strncmp(str, "copcc", 5) == 0)
478
{
479
str += 5;
480
}
481
else if (std::strncmp(str, "coprdc", 6) == 0)
482
{
483
if (inst.IsCop2Instruction())
484
{
485
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ",
486
GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()) + 32),
487
g_state.gte_regs.cr32[static_cast<u8>(inst.r.rd.GetValue())]);
488
}
489
str += 6;
490
}
491
else if (std::strncmp(str, "coprd", 5) == 0)
492
{
493
if (inst.IsCop2Instruction())
494
{
495
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ",
496
GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue())),
497
g_state.gte_regs.dr32[static_cast<u8>(inst.r.rd.GetValue())]);
498
}
499
500
str += 5;
501
}
502
else if (std::strncmp(str, "coprt", 5) == 0)
503
{
504
if (inst.IsCop2Instruction())
505
{
506
dest->append_format("{}{}=0x{:08x}", dest->empty() ? "" : ", ",
507
GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue())),
508
g_state.gte_regs.dr32[static_cast<u8>(inst.r.rt.GetValue())]);
509
}
510
511
str += 5;
512
}
513
else if (std::strncmp(str, "cop", 3) == 0)
514
{
515
str += 3;
516
}
517
else
518
{
519
Panic("Unknown operand");
520
}
521
}
522
}
523
524
template<typename T>
525
void CPU::FormatCopInstruction(SmallStringBase* dest, u32 pc, const Instruction inst,
526
const std::pair<T, const char*>* table, size_t table_size, T table_key)
527
{
528
for (size_t i = 0; i < table_size; i++)
529
{
530
if (table[i].first == table_key)
531
{
532
FormatInstruction(dest, inst, pc, table[i].second);
533
return;
534
}
535
}
536
537
dest->format("<cop{} 0x{:08x}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
538
}
539
540
template<typename T>
541
void CPU::FormatCopComment(SmallStringBase* dest, u32 pc, const Instruction inst,
542
const std::pair<T, const char*>* table, size_t table_size, T table_key)
543
{
544
for (size_t i = 0; i < table_size; i++)
545
{
546
if (table[i].first == table_key)
547
{
548
FormatComment(dest, inst, pc, table[i].second);
549
return;
550
}
551
}
552
}
553
554
void CPU::FormatGTEInstruction(SmallStringBase* dest, u32 pc, const Instruction inst)
555
{
556
const GTE::Instruction gi{inst.bits};
557
const GTEInstructionTable& t = s_gte_instructions[gi.command];
558
dest->assign(t.name);
559
560
if (t.sf && gi.sf)
561
dest->append(" sf");
562
563
if (t.lm && gi.lm)
564
dest->append(" lm");
565
566
if (t.mvmva)
567
{
568
dest->append_format(" m={} v={} t={}", static_cast<u8>(gi.mvmva_multiply_matrix),
569
static_cast<u8>(gi.mvmva_multiply_vector), static_cast<u8>(gi.mvmva_translation_vector));
570
}
571
}
572
573
void CPU::DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits)
574
{
575
const Instruction inst{bits};
576
switch (inst.op)
577
{
578
case InstructionOp::funct:
579
FormatInstruction(dest, inst, pc, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
580
return;
581
582
case InstructionOp::cop0:
583
case InstructionOp::cop1:
584
case InstructionOp::cop2:
585
case InstructionOp::cop3:
586
{
587
if (inst.cop.IsCommonInstruction())
588
{
589
FormatCopInstruction(dest, pc, inst, s_cop_common_table.data(), s_cop_common_table.size(), inst.cop.CommonOp());
590
}
591
else
592
{
593
switch (inst.op)
594
{
595
case InstructionOp::cop0:
596
{
597
FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
598
}
599
break;
600
601
case InstructionOp::cop2:
602
{
603
FormatGTEInstruction(dest, pc, inst);
604
}
605
break;
606
607
case InstructionOp::cop1:
608
case InstructionOp::cop3:
609
default:
610
{
611
dest->format("<cop{} 0x{:08x}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
612
}
613
break;
614
}
615
}
616
}
617
break;
618
619
// special case for bltz/bgez{al}
620
case InstructionOp::b:
621
{
622
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
623
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
624
const bool link = (rt & u8(0x1E)) == u8(0x10);
625
if (link)
626
FormatInstruction(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
627
else
628
FormatInstruction(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
629
}
630
break;
631
632
default:
633
FormatInstruction(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]);
634
break;
635
}
636
}
637
638
void CPU::DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits)
639
{
640
const Instruction inst{bits};
641
switch (inst.op)
642
{
643
case InstructionOp::funct:
644
FormatComment(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
FormatCopComment(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
FormatCopComment(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
663
}
664
break;
665
666
case InstructionOp::cop2:
667
// TODO: Show GTE regs?
668
break;
669
670
case InstructionOp::cop1:
671
case InstructionOp::cop3:
672
default:
673
{
674
dest->format("<cop{} 0x{:08x}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
675
}
676
break;
677
}
678
}
679
}
680
break;
681
682
// special case for bltz/bgez{al}
683
case InstructionOp::b:
684
{
685
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
686
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
687
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
688
if (link)
689
FormatComment(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
690
else
691
FormatComment(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
692
}
693
break;
694
695
default:
696
FormatComment(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]);
697
break;
698
}
699
}
700
701
const char* CPU::GetGTERegisterName(u32 index)
702
{
703
return (index < s_gte_register_names.size()) ? s_gte_register_names[index] : "";
704
}
705
706