cpu_disasm.cpp (21957B)
1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR 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, $immu", // 12 71 "ori $rt, $rs, $immu", // 13 72 "xori $rt, $rs, $immu", // 14 73 "lui $rt, $imm", // 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, "imm", 3) == 0) 320 { 321 // dest->AppendFormattedString("%d", static_cast<int>(inst.i.imm_sext32())); 322 dest->append_format("{:04x}", inst.i.imm_zext32()); 323 str += 3; 324 } 325 else if (std::strncmp(str, "rel", 3) == 0) 326 { 327 const u32 target = (pc + UINT32_C(4)) + (inst.i.imm_sext32() << 2); 328 dest->append_format("{:08x}", target); 329 str += 3; 330 } 331 else if (std::strncmp(str, "offsetrs", 8) == 0) 332 { 333 const s32 offset = static_cast<s32>(inst.i.imm_sext32()); 334 dest->append_format("{}({})", offset, GetRegName(inst.i.rs)); 335 str += 8; 336 } 337 else if (std::strncmp(str, "jt", 2) == 0) 338 { 339 const u32 target = ((pc + UINT32_C(4)) & UINT32_C(0xF0000000)) | (inst.j.target << 2); 340 dest->append_format("{:08x}", target); 341 str += 2; 342 } 343 else if (std::strncmp(str, "copcc", 5) == 0) 344 { 345 dest->append(((inst.bits & (UINT32_C(1) << 24)) != 0) ? 't' : 'f'); 346 str += 5; 347 } 348 else if (std::strncmp(str, "coprdc", 6) == 0) 349 { 350 if (inst.IsCop2Instruction()) 351 dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()) + 32)); 352 else 353 dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue()))); 354 str += 6; 355 } 356 else if (std::strncmp(str, "coprd", 5) == 0) 357 { 358 if (inst.IsCop2Instruction()) 359 dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()))); 360 else 361 dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue()))); 362 str += 5; 363 } 364 else if (std::strncmp(str, "coprt", 5) == 0) 365 { 366 if (inst.IsCop2Instruction()) 367 dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue()))); 368 else 369 dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rt.GetValue()))); 370 str += 5; 371 } 372 else if (std::strncmp(str, "cop", 3) == 0) 373 { 374 dest->append_format("{}", static_cast<u8>(inst.op.GetValue()) & INSTRUCTION_COP_N_MASK); 375 str += 3; 376 } 377 else 378 { 379 Panic("Unknown operand"); 380 } 381 } 382 } 383 384 void CPU::FormatComment(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format) 385 { 386 const CPU::Registers* regs = &CPU::g_state.regs; 387 388 const char* str = format; 389 while (*str != '\0') 390 { 391 const char ch = *(str++); 392 if (ch != '$') 393 continue; 394 395 if (std::strncmp(str, "rs", 2) == 0) 396 { 397 dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rs), 398 regs->r[static_cast<u8>(inst.r.rs.GetValue())]); 399 400 str += 2; 401 } 402 else if (std::strncmp(str, "rt_", 3) == 0) 403 { 404 str += 3; 405 } 406 else if (std::strncmp(str, "rt", 2) == 0) 407 { 408 dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rt), 409 regs->r[static_cast<u8>(inst.r.rt.GetValue())]); 410 str += 2; 411 } 412 else if (std::strncmp(str, "rd", 2) == 0) 413 { 414 dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rd), 415 regs->r[static_cast<u8>(inst.r.rd.GetValue())]); 416 str += 2; 417 } 418 else if (std::strncmp(str, "shamt", 5) == 0) 419 { 420 str += 5; 421 } 422 else if (std::strncmp(str, "immu", 4) == 0) 423 { 424 str += 4; 425 } 426 else if (std::strncmp(str, "imm", 3) == 0) 427 { 428 str += 3; 429 } 430 else if (std::strncmp(str, "rel", 3) == 0) 431 { 432 str += 3; 433 } 434 else if (std::strncmp(str, "offsetrs", 8) == 0) 435 { 436 const s32 offset = static_cast<s32>(inst.i.imm_sext32()); 437 const VirtualMemoryAddress address = (regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset); 438 439 if (!dest->empty()) 440 dest->append_format(", "); 441 442 if (inst.op == InstructionOp::lb || inst.op == InstructionOp::lbu) 443 { 444 u8 data = 0; 445 CPU::SafeReadMemoryByte(address, &data); 446 dest->append_format("addr={:08X}[{:02X}]", address, data); 447 } 448 else if (inst.op == InstructionOp::lh || inst.op == InstructionOp::lhu) 449 { 450 u16 data = 0; 451 CPU::SafeReadMemoryHalfWord(address, &data); 452 dest->append_format("addr={:08X}[{:04X}]", address, data); 453 } 454 else if (inst.op == InstructionOp::lw || (inst.op >= InstructionOp::lwc0 && inst.op <= InstructionOp::lwc3) || 455 inst.op == InstructionOp::lwl || inst.op == InstructionOp::lwr) 456 { 457 u32 data = 0; 458 CPU::SafeReadMemoryWord(address, &data); 459 dest->append_format("addr={:08X}[{:08X}]", address, data); 460 } 461 else 462 { 463 dest->append_format("addr={:08X}", address); 464 } 465 466 str += 8; 467 } 468 else if (std::strncmp(str, "jt", 2) == 0) 469 { 470 str += 2; 471 } 472 else if (std::strncmp(str, "copcc", 5) == 0) 473 { 474 str += 5; 475 } 476 else if (std::strncmp(str, "coprdc", 6) == 0) 477 { 478 if (inst.IsCop2Instruction()) 479 { 480 dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", 481 GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue()) + 32), 482 g_state.gte_regs.cr32[static_cast<u8>(inst.r.rd.GetValue())]); 483 } 484 str += 6; 485 } 486 else if (std::strncmp(str, "coprd", 5) == 0) 487 { 488 if (inst.IsCop2Instruction()) 489 { 490 dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", 491 GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue())), 492 g_state.gte_regs.dr32[static_cast<u8>(inst.r.rd.GetValue())]); 493 } 494 495 str += 5; 496 } 497 else if (std::strncmp(str, "coprt", 5) == 0) 498 { 499 if (inst.IsCop2Instruction()) 500 { 501 dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", 502 GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue())), 503 g_state.gte_regs.dr32[static_cast<u8>(inst.r.rt.GetValue())]); 504 } 505 506 str += 5; 507 } 508 else if (std::strncmp(str, "cop", 3) == 0) 509 { 510 str += 3; 511 } 512 else 513 { 514 Panic("Unknown operand"); 515 } 516 } 517 } 518 519 template<typename T> 520 void CPU::FormatCopInstruction(SmallStringBase* dest, u32 pc, const Instruction inst, 521 const std::pair<T, const char*>* table, size_t table_size, T table_key) 522 { 523 for (size_t i = 0; i < table_size; i++) 524 { 525 if (table[i].first == table_key) 526 { 527 FormatInstruction(dest, inst, pc, table[i].second); 528 return; 529 } 530 } 531 532 dest->format("<cop{} 0x{:08X}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); 533 } 534 535 template<typename T> 536 void CPU::FormatCopComment(SmallStringBase* dest, u32 pc, const Instruction inst, 537 const std::pair<T, const char*>* table, size_t table_size, T table_key) 538 { 539 for (size_t i = 0; i < table_size; i++) 540 { 541 if (table[i].first == table_key) 542 { 543 FormatComment(dest, inst, pc, table[i].second); 544 return; 545 } 546 } 547 } 548 549 void CPU::FormatGTEInstruction(SmallStringBase* dest, u32 pc, const Instruction inst) 550 { 551 const GTE::Instruction gi{inst.bits}; 552 const GTEInstructionTable& t = s_gte_instructions[gi.command]; 553 dest->append(t.name); 554 555 if (t.sf && gi.sf) 556 dest->append(" sf"); 557 558 if (t.lm && gi.lm) 559 dest->append(" lm"); 560 561 if (t.mvmva) 562 { 563 dest->append_format(" m={} v={} t={}", static_cast<u8>(gi.mvmva_multiply_matrix), 564 static_cast<u8>(gi.mvmva_multiply_vector), static_cast<u8>(gi.mvmva_translation_vector)); 565 } 566 } 567 568 void CPU::DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits) 569 { 570 const Instruction inst{bits}; 571 switch (inst.op) 572 { 573 case InstructionOp::funct: 574 FormatInstruction(dest, inst, pc, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]); 575 return; 576 577 case InstructionOp::cop0: 578 case InstructionOp::cop1: 579 case InstructionOp::cop2: 580 case InstructionOp::cop3: 581 { 582 if (inst.cop.IsCommonInstruction()) 583 { 584 FormatCopInstruction(dest, pc, inst, s_cop_common_table.data(), s_cop_common_table.size(), inst.cop.CommonOp()); 585 } 586 else 587 { 588 switch (inst.op) 589 { 590 case InstructionOp::cop0: 591 { 592 FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op()); 593 } 594 break; 595 596 case InstructionOp::cop2: 597 { 598 FormatGTEInstruction(dest, pc, inst); 599 } 600 break; 601 602 case InstructionOp::cop1: 603 case InstructionOp::cop3: 604 default: 605 { 606 dest->format("<cop{} 0x{:08X}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); 607 } 608 break; 609 } 610 } 611 } 612 break; 613 614 // special case for bltz/bgez{al} 615 case InstructionOp::b: 616 { 617 const u8 rt = static_cast<u8>(inst.i.rt.GetValue()); 618 const bool bgez = ConvertToBoolUnchecked(rt & u8(1)); 619 const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1)); 620 if (link) 621 FormatInstruction(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel"); 622 else 623 FormatInstruction(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel"); 624 } 625 break; 626 627 default: 628 FormatInstruction(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]); 629 break; 630 } 631 } 632 633 void CPU::DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits) 634 { 635 const Instruction inst{bits}; 636 switch (inst.op) 637 { 638 case InstructionOp::funct: 639 FormatComment(dest, inst, pc, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]); 640 return; 641 642 case InstructionOp::cop0: 643 case InstructionOp::cop1: 644 case InstructionOp::cop2: 645 case InstructionOp::cop3: 646 { 647 if (inst.cop.IsCommonInstruction()) 648 { 649 FormatCopComment(dest, pc, inst, s_cop_common_table.data(), s_cop_common_table.size(), inst.cop.CommonOp()); 650 } 651 else 652 { 653 switch (inst.op) 654 { 655 case InstructionOp::cop0: 656 { 657 FormatCopComment(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op()); 658 } 659 break; 660 661 case InstructionOp::cop2: 662 // TODO: Show GTE regs? 663 break; 664 665 case InstructionOp::cop1: 666 case InstructionOp::cop3: 667 default: 668 { 669 dest->format("<cop{} 0x{:08X}>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); 670 } 671 break; 672 } 673 } 674 } 675 break; 676 677 // special case for bltz/bgez{al} 678 case InstructionOp::b: 679 { 680 const u8 rt = static_cast<u8>(inst.i.rt.GetValue()); 681 const bool bgez = ConvertToBoolUnchecked(rt & u8(1)); 682 const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1)); 683 if (link) 684 FormatComment(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel"); 685 else 686 FormatComment(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel"); 687 } 688 break; 689 690 default: 691 FormatComment(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]); 692 break; 693 } 694 } 695 696 const char* CPU::GetGTERegisterName(u32 index) 697 { 698 return (index < s_gte_register_names.size()) ? s_gte_register_names[index] : ""; 699 }