#include "stdafx.h" #include "PPUDisAsm.h" #include "PPUFunction.h" #include "PPUAnalyser.h" #include "Emu/IdManager.h" #include "util/asm.hpp" const ppu_decoder s_ppu_disasm; const ppu_decoder s_ppu_itype; extern const std::unordered_map& get_exported_function_names_as_addr_indexed_map(); enum class ppu_syscall_code : u64; extern std::shared_ptr make_basic_ppu_disasm() { return std::make_shared(cpu_disasm_mode::normal, vm::g_sudo_addr); } u32 PPUDisAsm::disasm(u32 pc) { last_opcode.clear(); if (pc < m_start_pc) { return 0; } if (m_offset == vm::g_sudo_addr && !vm::check_addr(pc, vm::page_executable)) { return 0; } dump_pc = pc; be_t op{}; std::memcpy(&op, m_offset + pc, 4); m_op = op; (this->*(s_ppu_disasm.decode(m_op)))({ m_op }); if (m_offset != vm::g_sudo_addr) { // Exported functions lookup is not allowed in this case format_by_mode(); return 4; } const auto& map = get_exported_function_names_as_addr_indexed_map(); if (auto it = map.find(pc); it != map.end()) { last_opcode += " #"; last_opcode += it->second; } format_by_mode(); return 4; } std::pair PPUDisAsm::get_memory_span() const { return {m_offset + m_start_pc, (1ull << 32) - m_start_pc}; } std::unique_ptr PPUDisAsm::copy_type_erased() const { return std::make_unique(*this); } std::pair PPUDisAsm::try_get_const_op_gpr_value(u32 reg, u32 pc, u32 TTL) const { if (!TTL) { // Recursion limit (Time To Live) return {}; } if (pc == umax) { // Default arg: choose pc of previous instruction if (dump_pc == 0) { // Do not underflow return {}; } pc = dump_pc - 4; } #if __cpp_using_enum >= 201907 using enum const_op; #else constexpr const_op none = const_op::none, form = const_op::form, xor_mask = const_op::xor_mask; #endif // Scan PPU executable memory backwards until unmapped or non-executable memory block is encountered for (u32 i = pc; i >= m_start_pc && (m_offset != vm::g_sudo_addr || vm::check_addr(i, vm::page_executable));) { const u32 opcode = *reinterpret_cast*>(m_offset + i); const ppu_opcode_t op{ opcode }; const auto type = s_ppu_itype.decode(opcode); auto is_branch = [](enum ppu_itype::type itype) { return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR; }; if (is_branch(type) || type == ppu_itype::UNK) { // TODO: Detect calls, ignore them if reg is a non-volatile register return {}; } // Get constant register value #define GET_CONST_OP_REG(var, reg, op) \ {\ /* Search for the constant value of the register*/\ const auto [const_op, value] = try_get_const_op_gpr_value(reg, i - 4, TTL - 1);\ \ if (const_op != const_op::op)\ {\ /* Cannot compute constant value if register/operation is not constant*/\ return {};\ }\ \ var = value;\ } void() /*<- Require a semicolon*/ #define GET_CONST_REG(var, reg) GET_CONST_OP_REG(var, reg, form) switch (type) { case ppu_itype::ADDI: { if (op.rd != reg) { // Destination register is not relevant to us break; } u64 reg_ra = 0; if (op.ra) { GET_CONST_REG(reg_ra, op.ra); } return { form, reg_ra + op.simm16 }; } case ppu_itype::ADDIS: { if (op.rd != reg) { break; } u64 reg_ra = 0; if (op.ra) { GET_CONST_REG(reg_ra, op.ra); } return { form, reg_ra + op.simm16 * 65536 }; } case ppu_itype::ORI: { if (op.rs == op.ra && !op.uimm16) { // NO-OP break; } if (op.ra != reg) { // Destination register is not relevant to us break; } u64 reg_rs = 0; GET_CONST_REG(reg_rs, op.rs); return { form, reg_rs | op.uimm16 }; } case ppu_itype::ORIS: { if (op.rs == op.ra && !op.uimm16) { // NO-OP break; } if (op.ra != reg) { break; } u64 reg_rs = 0; GET_CONST_REG(reg_rs, op.rs); return { form, reg_rs | (u64{op.uimm16} << 16)}; } case ppu_itype::XORIS: { if (op.ra != reg) { break; } const auto [const_op, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1); if (const_op == none) { return { xor_mask, (u64{op.uimm16} << 16) }; } if (const_op != form) { // Unexpected return {}; } return { form, reg_rs ^ (u64{op.uimm16} << 16)}; } case ppu_itype::RLDICR: { if (op.ra != reg) { break; } u64 reg_rs = 0; GET_CONST_REG(reg_rs, op.rs); return { form, utils::rol64(reg_rs, op.sh64) & (~0ull << (op.mbe64 ^ 63)) }; } case ppu_itype::OR: { if (op.rs == op.rb && op.rs == op.ra) { // NO-OP break; } if (op.ra != reg) { break; } u64 reg_rs = 0, reg_rb = 0; GET_CONST_REG(reg_rs, op.rs); // Try to optimize if it's a register move operation if (op.rs != op.rb) { GET_CONST_REG(reg_rb, op.rb); } return { form, reg_rs | reg_rb }; } case ppu_itype::XOR: { if (op.ra != reg) { break; } if (op.rs == op.rb) { return { form, 0 }; } const auto [const_op_rs, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1); const auto [const_op_rb, reg_rb] = try_get_const_op_gpr_value(op.rb, i - 4, TTL - 1); if (const_op_rs == form && const_op_rb == form) { // Normally it is not the case return { form, reg_rs ^ reg_rb }; } if (const_op_rs == form && const_op_rb == none) { return { xor_mask, reg_rs }; } else if (const_op_rb == form && const_op_rs == none) { return { xor_mask, reg_rb }; } return {}; } default: { // Ordinary test // TODO: Proper detection of destination register(s) modification (if there are any) if (op.ra == reg || op.rd == reg) { return {}; } break; } } if (i == 0) { return {}; } i -= 4; } return {}; } enum CellError : u32; void comment_constant(std::string& last_opcode, u64 value) { // Test if potentially a CELL error if ((value >> 28) == 0xf'ffff'fff8u || (value >> 28) == 0x8u) { const usz old_size = last_opcode.size(); // Comment as CELL error fmt::append(last_opcode, " #%s (0x%x)", CellError{static_cast(value)}, value); // Test if failed to format (appended " #0x8".. in such case) if (last_opcode[old_size + 2] != '0') { // Success return; } // Revert and fallback last_opcode.resize(old_size); } // Comment constant formation fmt::append(last_opcode, " #0x%x", value); } constexpr std::pair get_BC_info(u32 bo, u32 bi) { std::pair info{}; switch (bo) { case 0b00000: case 0b00001: { info = {"bdnzf", 'f'}; break; } case 0b00010: case 0b00011: { info = {"bdzf", 'f'}; break; } case 0b01000: case 0b01001: { info = {"bdnzt", 't'}; break; } case 0b01010: case 0b01011: { info = {"bdzt", 't'}; break; } case 0b10010: { info.first = "bdz"; break; } case 0b11010: { info = {"bdz", '-'}; break; } case 0b11011: { info = {"bdz", '+'}; break; } case 0b10000: { info.first = "bdnz"; break; } case 0b11000: { info = {"bdnz", '-'}; break; } case 0b11001: { info = {"bdnz", '+'}; break; } case 0b00100: { switch (bi % 4) { case 0x0: info.first = "bge"; break; case 0x1: info.first = "ble"; break; case 0x2: info.first = "bne"; break; case 0x3: info.first = "bns"; break; default: fmt::throw_exception("Unreachable"); } break; } case 0b00110: { info.second = '-'; switch (bi % 4) { case 0x0: info.first = "bge"; break; case 0x1: info.first = "ble"; break; case 0x2: info.first = "bne"; break; case 0x3: info.first = "bns"; break; default: fmt::throw_exception("Unreachable"); } break; } case 0b00111: { info.second = '+'; switch (bi % 4) { case 0x0: info.first = "bge"; break; case 0x1: info.first = "ble"; break; case 0x2: info.first = "bne"; break; case 0x3: info.first = "bns"; break; default: fmt::throw_exception("Unreachable"); } break; } case 0b01100: { switch (bi % 4) { case 0x0: info.first = "blt"; break; case 0x1: info.first = "bgt"; break; case 0x2: info.first = "beq"; break; case 0x3: info.first = "bso"; break; default: fmt::throw_exception("Unreachable"); } break; } case 0b01110: { info.second = '-'; switch (bi % 4) { case 0x0: info.first = "blt"; break; case 0x1: info.first = "bgt"; break; case 0x2: info.first = "beq"; break; case 0x3: info.first = "bso"; break; default: fmt::throw_exception("Unreachable"); } break; } case 0b01111: { info.second = '+'; switch (bi % 4) { case 0x0: info.first = "blt"; break; case 0x1: info.first = "bgt"; break; case 0x2: info.first = "beq"; break; case 0x3: info.first = "bso"; break; default: fmt::throw_exception("Unreachable"); } break; } //case 0b10100: //{ // info.first = "b"; break; //} default: break; } return info; } void PPUDisAsm::MFVSCR(ppu_opcode_t op) { DisAsm_V1("mfvscr", op.vd); } void PPUDisAsm::MTVSCR(ppu_opcode_t op) { DisAsm_V1("mtvscr", op.vb); } void PPUDisAsm::VADDCUW(ppu_opcode_t op) { DisAsm_V3("vaddcuw", op.vd, op.va, op.vb); } void PPUDisAsm::VADDFP(ppu_opcode_t op) { DisAsm_V3("vaddfp", op.vd, op.va, op.vb); } void PPUDisAsm::VADDSBS(ppu_opcode_t op) { DisAsm_V3("vaddsbs", op.vd, op.va, op.vb); } void PPUDisAsm::VADDSHS(ppu_opcode_t op) { DisAsm_V3("vaddshs", op.vd, op.va, op.vb); } void PPUDisAsm::VADDSWS(ppu_opcode_t op) { DisAsm_V3("vaddsws", op.vd, op.va, op.vb); } void PPUDisAsm::VADDUBM(ppu_opcode_t op) { DisAsm_V3("vaddubm", op.vd, op.va, op.vb); } void PPUDisAsm::VADDUBS(ppu_opcode_t op) { DisAsm_V3("vaddubs", op.vd, op.va, op.vb); } void PPUDisAsm::VADDUHM(ppu_opcode_t op) { DisAsm_V3("vadduhm", op.vd, op.va, op.vb); } void PPUDisAsm::VADDUHS(ppu_opcode_t op) { DisAsm_V3("vadduhs", op.vd, op.va, op.vb); } void PPUDisAsm::VADDUWM(ppu_opcode_t op) { DisAsm_V3("vadduwm", op.vd, op.va, op.vb); } void PPUDisAsm::VADDUWS(ppu_opcode_t op) { DisAsm_V3("vadduws", op.vd, op.va, op.vb); } void PPUDisAsm::VAND(ppu_opcode_t op) { DisAsm_V3("vand", op.vd, op.va, op.vb); } void PPUDisAsm::VANDC(ppu_opcode_t op) { DisAsm_V3("vandc", op.vd, op.va, op.vb); } void PPUDisAsm::VAVGSB(ppu_opcode_t op) { DisAsm_V3("vavgsb", op.vd, op.va, op.vb); } void PPUDisAsm::VAVGSH(ppu_opcode_t op) { DisAsm_V3("vavgsh", op.vd, op.va, op.vb); } void PPUDisAsm::VAVGSW(ppu_opcode_t op) { DisAsm_V3("vavgsw", op.vd, op.va, op.vb); } void PPUDisAsm::VAVGUB(ppu_opcode_t op) { DisAsm_V3("vavgub", op.vd, op.va, op.vb); } void PPUDisAsm::VAVGUH(ppu_opcode_t op) { DisAsm_V3("vavguh", op.vd, op.va, op.vb); } void PPUDisAsm::VAVGUW(ppu_opcode_t op) { DisAsm_V3("vavguw", op.vd, op.va, op.vb); } void PPUDisAsm::VCFSX(ppu_opcode_t op) { DisAsm_V2_UIMM("vcfsx", op.vd, op.vb, op.vuimm); } void PPUDisAsm::VCFUX(ppu_opcode_t op) { DisAsm_V2_UIMM("vcfux", op.vd, op.vb, op.vuimm); } void PPUDisAsm::VCMPBFP(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpbfp." : "vcmpbfp", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPEQFP(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpeqfp." : "vcmpeqfp", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPEQUB(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpequb." : "vcmpequb", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPEQUH(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpequh." : "vcmpequh", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPEQUW(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpequw." : "vcmpequw", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGEFP(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgefp." : "vcmpgefp", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTFP(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtfp." : "vcmpgtfp", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTSB(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtsb." : "vcmpgtsb", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTSH(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtsh." : "vcmpgtsh", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTSW(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtsw." : "vcmpgtsw", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTUB(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtub." : "vcmpgtub", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTUH(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtuh." : "vcmpgtuh", op.vd, op.va, op.vb); } void PPUDisAsm::VCMPGTUW(ppu_opcode_t op) { DisAsm_V3(op.oe ? "vcmpgtuw." : "vcmpgtuw", op.vd, op.va, op.vb); } void PPUDisAsm::VCTSXS(ppu_opcode_t op) { DisAsm_V2_UIMM("vctsxs", op.vd, op.vb, op.vuimm); } void PPUDisAsm::VCTUXS(ppu_opcode_t op) { DisAsm_V2_UIMM("vctuxs", op.vd, op.vb, op.vuimm); } void PPUDisAsm::VEXPTEFP(ppu_opcode_t op) { DisAsm_V2("vexptefp", op.vd, op.vb); } void PPUDisAsm::VLOGEFP(ppu_opcode_t op) { DisAsm_V2("vlogefp", op.vd, op.vb); } void PPUDisAsm::VMADDFP(ppu_opcode_t op) { DisAsm_V4("vmaddfp", op.vd, op.va, op.vc, op.vb); } void PPUDisAsm::VMAXFP(ppu_opcode_t op) { DisAsm_V3("vmaxfp", op.vd, op.va, op.vb); } void PPUDisAsm::VMAXSB(ppu_opcode_t op) { DisAsm_V3("vmaxsb", op.vd, op.va, op.vb); } void PPUDisAsm::VMAXSH(ppu_opcode_t op) { DisAsm_V3("vmaxsh", op.vd, op.va, op.vb); } void PPUDisAsm::VMAXSW(ppu_opcode_t op) { DisAsm_V3("vmaxsw", op.vd, op.va, op.vb); } void PPUDisAsm::VMAXUB(ppu_opcode_t op) { DisAsm_V3("vmaxub", op.vd, op.va, op.vb); } void PPUDisAsm::VMAXUH(ppu_opcode_t op) { DisAsm_V3("vmaxuh", op.vd, op.va, op.vb); } void PPUDisAsm::VMAXUW(ppu_opcode_t op) { DisAsm_V3("vmaxuw", op.vd, op.va, op.vb); } void PPUDisAsm::VMHADDSHS(ppu_opcode_t op) { DisAsm_V4("vmhaddshs", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMHRADDSHS(ppu_opcode_t op) { DisAsm_V4("vmhraddshs", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMINFP(ppu_opcode_t op) { DisAsm_V3("vminfp", op.vd, op.va, op.vb); } void PPUDisAsm::VMINSB(ppu_opcode_t op) { DisAsm_V3("vminsb", op.vd, op.va, op.vb); } void PPUDisAsm::VMINSH(ppu_opcode_t op) { DisAsm_V3("vminsh", op.vd, op.va, op.vb); } void PPUDisAsm::VMINSW(ppu_opcode_t op) { DisAsm_V3("vminsw", op.vd, op.va, op.vb); } void PPUDisAsm::VMINUB(ppu_opcode_t op) { DisAsm_V3("vminub", op.vd, op.va, op.vb); } void PPUDisAsm::VMINUH(ppu_opcode_t op) { DisAsm_V3("vminuh", op.vd, op.va, op.vb); } void PPUDisAsm::VMINUW(ppu_opcode_t op) { DisAsm_V3("vminuw", op.vd, op.va, op.vb); } void PPUDisAsm::VMLADDUHM(ppu_opcode_t op) { DisAsm_V4("vmladduhm", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMRGHB(ppu_opcode_t op) { DisAsm_V3("vmrghb", op.vd, op.va, op.vb); } void PPUDisAsm::VMRGHH(ppu_opcode_t op) { DisAsm_V3("vmrghh", op.vd, op.va, op.vb); } void PPUDisAsm::VMRGHW(ppu_opcode_t op) { DisAsm_V3("vmrghw", op.vd, op.va, op.vb); } void PPUDisAsm::VMRGLB(ppu_opcode_t op) { DisAsm_V3("vmrglb", op.vd, op.va, op.vb); } void PPUDisAsm::VMRGLH(ppu_opcode_t op) { DisAsm_V3("vmrglh", op.vd, op.va, op.vb); } void PPUDisAsm::VMRGLW(ppu_opcode_t op) { DisAsm_V3("vmrglw", op.vd, op.va, op.vb); } void PPUDisAsm::VMSUMMBM(ppu_opcode_t op) { DisAsm_V4("vmsummbm", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMSUMSHM(ppu_opcode_t op) { DisAsm_V4("vmsumshm", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMSUMSHS(ppu_opcode_t op) { DisAsm_V4("vmsumshs", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMSUMUBM(ppu_opcode_t op) { DisAsm_V4("vmsumubm", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMSUMUHM(ppu_opcode_t op) { DisAsm_V4("vmsumuhm", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMSUMUHS(ppu_opcode_t op) { DisAsm_V4("vmsumuhs", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VMULESB(ppu_opcode_t op) { DisAsm_V3("vmulesb", op.vd, op.va, op.vb); } void PPUDisAsm::VMULESH(ppu_opcode_t op) { DisAsm_V3("vmulesh", op.vd, op.va, op.vb); } void PPUDisAsm::VMULEUB(ppu_opcode_t op) { DisAsm_V3("vmuleub", op.vd, op.va, op.vb); } void PPUDisAsm::VMULEUH(ppu_opcode_t op) { DisAsm_V3("vmuleuh", op.vd, op.va, op.vb); } void PPUDisAsm::VMULOSB(ppu_opcode_t op) { DisAsm_V3("vmulosb", op.vd, op.va, op.vb); } void PPUDisAsm::VMULOSH(ppu_opcode_t op) { DisAsm_V3("vmulosh", op.vd, op.va, op.vb); } void PPUDisAsm::VMULOUB(ppu_opcode_t op) { DisAsm_V3("vmuloub", op.vd, op.va, op.vb); } void PPUDisAsm::VMULOUH(ppu_opcode_t op) { DisAsm_V3("vmulouh", op.vd, op.va, op.vb); } void PPUDisAsm::VNMSUBFP(ppu_opcode_t op) { DisAsm_V4("vnmsubfp", op.vd, op.va, op.vc, op.vb); } void PPUDisAsm::VNOR(ppu_opcode_t op) { DisAsm_V3("vnor", op.vd, op.va, op.vb); } void PPUDisAsm::VOR(ppu_opcode_t op) { DisAsm_V3("vor", op.vd, op.va, op.vb); } void PPUDisAsm::VPERM(ppu_opcode_t op) { DisAsm_V4("vperm", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VPKPX(ppu_opcode_t op) { DisAsm_V3("vpkpx", op.vd, op.va, op.vb); } void PPUDisAsm::VPKSHSS(ppu_opcode_t op) { DisAsm_V3("vpkshss", op.vd, op.va, op.vb); } void PPUDisAsm::VPKSHUS(ppu_opcode_t op) { DisAsm_V3("vpkshus", op.vd, op.va, op.vb); } void PPUDisAsm::VPKSWSS(ppu_opcode_t op) { DisAsm_V3("vpkswss", op.vd, op.va, op.vb); } void PPUDisAsm::VPKSWUS(ppu_opcode_t op) { DisAsm_V3("vpkswus", op.vd, op.va, op.vb); } void PPUDisAsm::VPKUHUM(ppu_opcode_t op) { DisAsm_V3("vpkuhum", op.vd, op.va, op.vb); } void PPUDisAsm::VPKUHUS(ppu_opcode_t op) { DisAsm_V3("vpkuhus", op.vd, op.va, op.vb); } void PPUDisAsm::VPKUWUM(ppu_opcode_t op) { DisAsm_V3("vpkuwum", op.vd, op.va, op.vb); } void PPUDisAsm::VPKUWUS(ppu_opcode_t op) { DisAsm_V3("vpkuwus", op.vd, op.va, op.vb); } void PPUDisAsm::VREFP(ppu_opcode_t op) { DisAsm_V2("vrefp", op.vd, op.vb); } void PPUDisAsm::VRFIM(ppu_opcode_t op) { DisAsm_V2("vrfim", op.vd, op.vb); } void PPUDisAsm::VRFIN(ppu_opcode_t op) { DisAsm_V2("vrfin", op.vd, op.vb); } void PPUDisAsm::VRFIP(ppu_opcode_t op) { DisAsm_V2("vrfip", op.vd, op.vb); } void PPUDisAsm::VRFIZ(ppu_opcode_t op) { DisAsm_V2("vrfiz", op.vd, op.vb); } void PPUDisAsm::VRLB(ppu_opcode_t op) { DisAsm_V3("vrlb", op.vd, op.va, op.vb); } void PPUDisAsm::VRLH(ppu_opcode_t op) { DisAsm_V3("vrlh", op.vd, op.va, op.vb); } void PPUDisAsm::VRLW(ppu_opcode_t op) { DisAsm_V3("vrlw", op.vd, op.va, op.vb); } void PPUDisAsm::VRSQRTEFP(ppu_opcode_t op) { DisAsm_V2("vrsqrtefp", op.vd, op.vb); } void PPUDisAsm::VSEL(ppu_opcode_t op) { DisAsm_V4("vsel", op.vd, op.va, op.vb, op.vc); } void PPUDisAsm::VSL(ppu_opcode_t op) { DisAsm_V3("vsl", op.vd, op.va, op.vb); } void PPUDisAsm::VSLB(ppu_opcode_t op) { DisAsm_V3("vslb", op.vd, op.va, op.vb); } void PPUDisAsm::VSLDOI(ppu_opcode_t op) { DisAsm_V3_UIMM("vsldoi", op.vd, op.va, op.vb, op.vsh); } void PPUDisAsm::VSLH(ppu_opcode_t op) { DisAsm_V3("vslh", op.vd, op.va, op.vb); } void PPUDisAsm::VSLO(ppu_opcode_t op) { DisAsm_V3("vslo", op.vd, op.va, op.vb); } void PPUDisAsm::VSLW(ppu_opcode_t op) { DisAsm_V3("vslw", op.vd, op.va, op.vb); } void PPUDisAsm::VSPLTB(ppu_opcode_t op) { DisAsm_V2_UIMM("vspltb", op.vd, op.vb, op.vuimm & 0xf); } void PPUDisAsm::VSPLTH(ppu_opcode_t op) { DisAsm_V2_UIMM("vsplth", op.vd, op.vb, op.vuimm & 0x7); } void PPUDisAsm::VSPLTISB(ppu_opcode_t op) { DisAsm_V1_SIMM("vspltisb", op.vd, op.vsimm); } void PPUDisAsm::VSPLTISH(ppu_opcode_t op) { DisAsm_V1_SIMM("vspltish", op.vd, op.vsimm); } void PPUDisAsm::VSPLTISW(ppu_opcode_t op) { DisAsm_V1_SIMM("vspltisw", op.vd, op.vsimm); } void PPUDisAsm::VSPLTW(ppu_opcode_t op) { DisAsm_V2_UIMM("vspltw", op.vd, op.vb, op.vuimm & 0x3); } void PPUDisAsm::VSR(ppu_opcode_t op) { DisAsm_V3("vsr", op.vd, op.va, op.vb); } void PPUDisAsm::VSRAB(ppu_opcode_t op) { DisAsm_V3("vsrab", op.vd, op.va, op.vb); } void PPUDisAsm::VSRAH(ppu_opcode_t op) { DisAsm_V3("vsrah", op.vd, op.va, op.vb); } void PPUDisAsm::VSRAW(ppu_opcode_t op) { DisAsm_V3("vsraw", op.vd, op.va, op.vb); } void PPUDisAsm::VSRB(ppu_opcode_t op) { DisAsm_V3("vsrb", op.vd, op.va, op.vb); } void PPUDisAsm::VSRH(ppu_opcode_t op) { DisAsm_V3("vsrh", op.vd, op.va, op.vb); } void PPUDisAsm::VSRO(ppu_opcode_t op) { DisAsm_V3("vsro", op.vd, op.va, op.vb); } void PPUDisAsm::VSRW(ppu_opcode_t op) { DisAsm_V3("vsrw", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBCUW(ppu_opcode_t op) { DisAsm_V3("vsubcuw", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBFP(ppu_opcode_t op) { DisAsm_V3("vsubfp", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBSBS(ppu_opcode_t op) { DisAsm_V3("vsubsbs", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBSHS(ppu_opcode_t op) { DisAsm_V3("vsubshs", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBSWS(ppu_opcode_t op) { DisAsm_V3("vsubsws", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBUBM(ppu_opcode_t op) { DisAsm_V3("vsububm", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBUBS(ppu_opcode_t op) { DisAsm_V3("vsububs", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBUHM(ppu_opcode_t op) { DisAsm_V3("vsubuhm", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBUHS(ppu_opcode_t op) { DisAsm_V3("vsubuhs", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBUWM(ppu_opcode_t op) { DisAsm_V3("vsubuwm", op.vd, op.va, op.vb); } void PPUDisAsm::VSUBUWS(ppu_opcode_t op) { DisAsm_V3("vsubuws", op.vd, op.va, op.vb); } void PPUDisAsm::VSUMSWS(ppu_opcode_t op) { DisAsm_V3("vsumsws", op.vd, op.va, op.vb); } void PPUDisAsm::VSUM2SWS(ppu_opcode_t op) { DisAsm_V3("vsum2sws", op.vd, op.va, op.vb); } void PPUDisAsm::VSUM4SBS(ppu_opcode_t op) { DisAsm_V3("vsum4sbs", op.vd, op.va, op.vb); } void PPUDisAsm::VSUM4SHS(ppu_opcode_t op) { DisAsm_V3("vsum4shs", op.vd, op.va, op.vb); } void PPUDisAsm::VSUM4UBS(ppu_opcode_t op) { DisAsm_V3("vsum4ubs", op.vd, op.va, op.vb); } void PPUDisAsm::VUPKHPX(ppu_opcode_t op) { DisAsm_V2("vupkhpx", op.vd, op.vb); } void PPUDisAsm::VUPKHSB(ppu_opcode_t op) { DisAsm_V2("vupkhsb", op.vd, op.vb); } void PPUDisAsm::VUPKHSH(ppu_opcode_t op) { DisAsm_V2("vupkhsh", op.vd, op.vb); } void PPUDisAsm::VUPKLPX(ppu_opcode_t op) { DisAsm_V2("vupklpx", op.vd, op.vb); } void PPUDisAsm::VUPKLSB(ppu_opcode_t op) { DisAsm_V2("vupklsb", op.vd, op.vb); } void PPUDisAsm::VUPKLSH(ppu_opcode_t op) { DisAsm_V2("vupklsh", op.vd, op.vb); } void PPUDisAsm::VXOR(ppu_opcode_t op) { DisAsm_V3("vxor", op.vd, op.va, op.vb); } void PPUDisAsm::TDI(ppu_opcode_t op) { DisAsm_INT1_R1_IMM("tdi", op.bo, op.ra, op.simm16); } void PPUDisAsm::TWI(ppu_opcode_t op) { DisAsm_INT1_R1_IMM("twi", op.bo, op.ra, op.simm16); } void PPUDisAsm::MULLI(ppu_opcode_t op) { DisAsm_R2_IMM("mulli", op.rd, op.ra, op.simm16); } void PPUDisAsm::SUBFIC(ppu_opcode_t op) { DisAsm_R2_IMM("subfic", op.rd, op.ra, op.simm16); } void PPUDisAsm::CMPLI(ppu_opcode_t op) { DisAsm_CR1_R1_IMM(op.l10 ? "cmpldi" : "cmplwi", op.crfd, op.ra, op.uimm16); // Try to obtain the true constant value we are comparing against, comment on success // Upper 16/48 bits of it if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF)) { // Fixup value (merge the lower 16-bits of that value) value |= op.uimm16; if (!op.l10) { value = static_cast(value); } comment_constant(last_opcode, value); } } void PPUDisAsm::CMPI(ppu_opcode_t op) { DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.simm16); // See CMPLI if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF)) { // Signed fixup value ^= s64{op.simm16}; if (!op.l10) { value = static_cast(value); } comment_constant(last_opcode, value); } } void PPUDisAsm::ADDIC(ppu_opcode_t op) { DisAsm_R2_IMM(op.main & 1 ? "addic." : "addic", op.rd, op.ra, op.simm16); } void PPUDisAsm::ADDI(ppu_opcode_t op) { if (op.ra == 0) { DisAsm_R1_IMM("li", op.rd, op.simm16); } else { DisAsm_R2_IMM("addi", op.rd, op.ra, op.simm16); } } void PPUDisAsm::ADDIS(ppu_opcode_t op) { if (op.ra == 0) { DisAsm_R1_IMM("lis", op.rd, op.simm16); } else { DisAsm_R2_IMM("addis", op.rd, op.ra, op.simm16); } } void PPUDisAsm::BC(ppu_opcode_t op) { const u32 bo = op.bo; const u32 bi = op.bi; const s32 bd = op.ds * 4; const u32 aa = op.aa; const u32 lk = op.lk; if (m_mode == cpu_disasm_mode::compiler_elf) { fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk); return; } const auto [inst, sign] = get_BC_info(bo, bi); if (!inst) { fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk); return; } std::string final = inst; if (lk) final += 'l'; if (aa) final += 'a'; if (sign) final += sign; // Check if need to display full BI value if (sign == 't' || sign == 'f') { if (aa) { DisAsm_BI_BRANCH_A(final, bi, bd); } else { DisAsm_BI_BRANCH(final, bi, bd); } return; } if (aa) { DisAsm_CR_BRANCH_A(final, bi / 4, bd); } else { DisAsm_CR_BRANCH(final, bi / 4, bd); } } void PPUDisAsm::SC(ppu_opcode_t op) { if (op.opcode != ppu_instructions::SC(0) && op.opcode != ppu_instructions::SC(1)) { return UNK(op); } fmt::append(last_opcode, "%-*s ", PadOp(), "sc"); if (op.lev) { fmt::append(last_opcode, "%u ", op.lev); } // Try to get constant syscall index auto [is_const, index] = try_get_const_gpr_value(11); if (!is_const) { return; } switch (op.lev) { case 0: { // Lv2 syscall if (index < 1024u) { fmt::append(last_opcode, "#%s", ppu_syscall_code{index}); } return; } case 1: { // Lv1 syscall fmt::append(last_opcode, "#lv1_syscall_%u", index); return; } default: return; } } void PPUDisAsm::B(ppu_opcode_t op) { const u32 li = op.bt24; const u32 aa = op.aa; const u32 lk = op.lk; if (m_mode == cpu_disasm_mode::compiler_elf) { fmt::append(last_opcode, "b 0x%x, %d, %d", li, aa, lk); return; } switch (lk) { case 0: switch (aa) { case 0: DisAsm_BRANCH("b", li); break; case 1: DisAsm_BRANCH_A("ba", li); break; } break; case 1: switch (aa) { case 0: DisAsm_BRANCH("bl", li); break; case 1: DisAsm_BRANCH_A("bla", li); break; } break; } } void PPUDisAsm::MCRF(ppu_opcode_t op) { DisAsm_CR2("mcrf", op.crfd, op.crfs); } void PPUDisAsm::BCLR(ppu_opcode_t op) { const u32 bo = op.bo; const u32 bi = op.bi; const u32 bh = op.bh; const u32 lk = op.lk; if (bo == 0b10100) { last_opcode += (lk ? "blrl" : "blr"); return; } const auto [inst, sign] = get_BC_info(bo, bi); if (!inst) { fmt::append(last_opcode, "bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk); return; } std::string final = std::string(inst) + (lk ? "lrl" : "lr"); if (sign) final += sign; // Check if need to display full BI value if (sign == 't' || sign == 'f') { DisAsm_BI_BRANCH(final, bi, bh); return; } DisAsm_CR_BRANCH(final, bi / 4, bh); } void PPUDisAsm::CRNOR(ppu_opcode_t op) { if (op.crba == op.crbb) { DisAsm_BI2("crnot", op.crbd, op.crba); return; } DisAsm_BI3("crnor", op.crbd, op.crba, op.crbb); } void PPUDisAsm::CRANDC(ppu_opcode_t op) { DisAsm_BI3("crandc", op.crbd, op.crba, op.crbb); } void PPUDisAsm::ISYNC(ppu_opcode_t) { last_opcode += "isync"; } void PPUDisAsm::CRXOR(ppu_opcode_t op) { if (op.crba == op.crbb && op.crba == op.crbd) { DisAsm_BI1("crclr", op.crbd); return; } DisAsm_BI3("crxor", op.crbd, op.crba, op.crbb); } void PPUDisAsm::CRNAND(ppu_opcode_t op) { DisAsm_BI3("crnand", op.crbd, op.crba, op.crbb); } void PPUDisAsm::CRAND(ppu_opcode_t op) { DisAsm_BI3("crand", op.crbd, op.crba, op.crbb); } void PPUDisAsm::CREQV(ppu_opcode_t op) { if (op.crba == op.crbb && op.crba == op.crbd) { DisAsm_BI1("crset", op.crbd); return; } DisAsm_BI3("creqv", op.crbd, op.crba, op.crbb); } void PPUDisAsm::CRORC(ppu_opcode_t op) { DisAsm_BI3("crorc", op.crbd, op.crba, op.crbb); } void PPUDisAsm::CROR(ppu_opcode_t op) { if (op.crba == op.crbb) { DisAsm_BI2("crmove", op.crbd, op.crba); return; } DisAsm_BI3("cror", op.crbd, op.crba, op.crbb); } void PPUDisAsm::BCCTR(ppu_opcode_t op) { const u32 bo = op.bo; const u32 bi = op.bi; const u32 bh = op.bh; const u32 lk = op.lk; if (bo == 0b10100) { last_opcode += (lk ? "bctrl" : "bctr"); return; } const auto [inst, sign] = get_BC_info(bo, bi); if (!inst || inst[1] == 'd') { // Invalid or unknown bcctr form fmt::append(last_opcode, "bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk); return; } std::string final = inst; final += lk ? "ctrl"sv : "ctr"sv; if (sign) final += sign; DisAsm_CR_BRANCH(final, bi / 4, bh); } void PPUDisAsm::RLWIMI(ppu_opcode_t op) { DisAsm_R2_INT3_RC("rlwimi", op.ra, op.rs, op.sh32, op.mb32, op.me32, op.rc); } void PPUDisAsm::RLWINM(ppu_opcode_t op) { if (op.mb32 == 0 && op.sh32 == 31 - op.me32) { DisAsm_R2_INT1_RC("slwi", op.ra, op.rs, op.sh32, op.rc); } else if (op.me32 == 31 && op.sh32 == 32 - op.mb32) { DisAsm_R2_INT1_RC("srwi", op.ra, op.rs, 32 - op.sh32, op.rc); } else { DisAsm_R2_INT3_RC("rlwinm", op.ra, op.rs, op.sh32, op.mb32, op.me32, op.rc); } } void PPUDisAsm::RLWNM(ppu_opcode_t op) { DisAsm_R3_INT2_RC("rlwnm", op.ra, op.rs, op.rb, op.mb32, op.me32, op.rc); } void PPUDisAsm::ORI(ppu_opcode_t op) { if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; } if (op.uimm16 == 0) return DisAsm_R2("mr", op.ra, op.rs); DisAsm_R2_IMM("ori", op.ra, op.rs, op.uimm16); if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const) { // Comment constant formation comment_constant(last_opcode, value | op.uimm16); } } void PPUDisAsm::ORIS(ppu_opcode_t op) { if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; } DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16); } void PPUDisAsm::XORI(ppu_opcode_t op) { DisAsm_R2_IMM("xori", op.ra, op.rs, op.uimm16); } void PPUDisAsm::XORIS(ppu_opcode_t op) { DisAsm_R2_IMM("xoris", op.ra, op.rs, op.uimm16); } void PPUDisAsm::ANDI(ppu_opcode_t op) { DisAsm_R2_IMM("andi.", op.ra, op.rs, op.uimm16); } void PPUDisAsm::ANDIS(ppu_opcode_t op) { DisAsm_R2_IMM("andis.", op.ra, op.rs, op.uimm16); } void PPUDisAsm::RLDICL(ppu_opcode_t op) { const u32 sh = op.sh64; const u32 mb = op.mbe64; if (sh == 0) { DisAsm_R2_INT1_RC("clrldi", op.ra, op.rs, mb, op.rc); } else if (mb == 0) { DisAsm_R2_INT1_RC("rotldi", op.ra, op.rs, sh, op.rc); } else if (mb == 64 - sh) { DisAsm_R2_INT1_RC("srdi", op.ra, op.rs, mb, op.rc); } else { DisAsm_R2_INT2_RC("rldicl", op.ra, op.rs, sh, mb, op.rc); } } void PPUDisAsm::RLDICR(ppu_opcode_t op) { const u32 sh = op.sh64; const u32 me = op.mbe64; if (sh == 63 - me) { DisAsm_R2_INT1_RC("sldi", op.ra, op.rs, sh, op.rc); } else { DisAsm_R2_INT2_RC("rldicr", op.ra, op.rs, sh, me, op.rc); } } void PPUDisAsm::RLDIC(ppu_opcode_t op) { const u32 sh = op.sh64; const u32 mb = op.mbe64; DisAsm_R2_INT2_RC("rldic", op.ra, op.rs, sh, mb, op.rc); } void PPUDisAsm::RLDIMI(ppu_opcode_t op) { const u32 sh = op.sh64; const u32 mb = op.mbe64; DisAsm_R2_INT2_RC("rldimi", op.ra, op.rs, sh, mb, op.rc); } void PPUDisAsm::RLDCL(ppu_opcode_t op) { const u32 mb = op.mbe64; DisAsm_R3_INT2_RC("rldcl", op.ra, op.rs, op.rb, mb, 0, op.rc); } void PPUDisAsm::RLDCR(ppu_opcode_t op) { const u32 me = op.mbe64; DisAsm_R3_INT2_RC("rldcr", op.ra, op.rs, op.rb, me, 0, op.rc); } void PPUDisAsm::CMP(ppu_opcode_t op) { DisAsm_CR1_R2(op.l10 ? "cmpd" : "cmpw", op.crfd, op.ra, op.rb); } void PPUDisAsm::TW(ppu_opcode_t op) { DisAsm_INT1_R2("tw", op.bo, op.ra, op.rb); } void PPUDisAsm::LVSL(ppu_opcode_t op) { DisAsm_V1_R2("lvsl", op.vd, op.ra, op.rb); } void PPUDisAsm::LVEBX(ppu_opcode_t op) { DisAsm_V1_R2("lvebx", op.vd, op.ra, op.rb); } void PPUDisAsm::SUBFC(ppu_opcode_t op) { DisAsm_R3_OE_RC("subfc", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::ADDC(ppu_opcode_t op) { DisAsm_R3_OE_RC("addc", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::MULHDU(ppu_opcode_t op) { DisAsm_R3_RC("mulhdu", op.rd, op.ra, op.rb, op.rc); } void PPUDisAsm::MULHWU(ppu_opcode_t op) { DisAsm_R3_RC("mulhwu", op.rd, op.ra, op.rb, op.rc); } void PPUDisAsm::MFOCRF(ppu_opcode_t op) { if (op.l11) { const u8 crm = static_cast(op.crm); const int cr = std::countl_zero(crm); if (cr >= 8 || crm & (crm - 1)) { // Note: invalid form DisAsm_R1_IMM("mfocrf", op.rd, crm); } else { DisAsm_R1_CR1("mfocrf", op.rd, cr); } } else { DisAsm_R1("mfcr", op.rd); } } void PPUDisAsm::LWARX(ppu_opcode_t op) { DisAsm_R3("lwarx", op.rd, op.ra, op.rb); } void PPUDisAsm::LDX(ppu_opcode_t op) { DisAsm_R3("ldx", op.rd, op.ra, op.rb); } void PPUDisAsm::LWZX(ppu_opcode_t op) { DisAsm_R3("lwzx", op.rd, op.ra, op.rb); } void PPUDisAsm::SLW(ppu_opcode_t op) { DisAsm_R3_RC("slw", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::CNTLZW(ppu_opcode_t op) { DisAsm_R2_RC("cntlzw", op.ra, op.rs, op.rc); } void PPUDisAsm::SLD(ppu_opcode_t op) { DisAsm_R3_RC("sld", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::AND(ppu_opcode_t op) { DisAsm_R3_RC("and", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::CMPL(ppu_opcode_t op) { DisAsm_CR1_R2(op.l10 ? "cmpld" : "cmplw", op.crfd, op.ra, op.rb); } void PPUDisAsm::LVSR(ppu_opcode_t op) { DisAsm_V1_R2("lvsr", op.vd, op.ra, op.rb); } void PPUDisAsm::LVEHX(ppu_opcode_t op) { DisAsm_V1_R2("lvehx", op.vd, op.ra, op.rb); } void PPUDisAsm::SUBF(ppu_opcode_t op) { DisAsm_R3_OE_RC("subf", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::LDUX(ppu_opcode_t op) { DisAsm_R3("ldux", op.rd, op.ra, op.rb); } void PPUDisAsm::DCBST(ppu_opcode_t op) { DisAsm_R2("dcbst", op.ra, op.rb); } void PPUDisAsm::LWZUX(ppu_opcode_t op) { DisAsm_R3("lwzux", op.rd, op.ra, op.rb); } void PPUDisAsm::CNTLZD(ppu_opcode_t op) { DisAsm_R2_RC("cntlzd", op.ra, op.rs, op.rc); } void PPUDisAsm::ANDC(ppu_opcode_t op) { DisAsm_R3_RC("andc", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::TD(ppu_opcode_t op) { DisAsm_INT1_R2("td", op.bo, op.ra, op.rb); } void PPUDisAsm::LVEWX(ppu_opcode_t op) { DisAsm_V1_R2("lvewx", op.vd, op.ra, op.rb); } void PPUDisAsm::MULHD(ppu_opcode_t op) { DisAsm_R3_RC("mulhd", op.rd, op.ra, op.rb, op.rc); } void PPUDisAsm::MULHW(ppu_opcode_t op) { DisAsm_R3_RC("mulhw", op.rd, op.ra, op.rb, op.rc); } void PPUDisAsm::LDARX(ppu_opcode_t op) { DisAsm_R3("ldarx", op.rd, op.ra, op.rb); } void PPUDisAsm::DCBF(ppu_opcode_t op) { DisAsm_R2("dcbf", op.ra, op.rb); } void PPUDisAsm::LBZX(ppu_opcode_t op) { DisAsm_R3("lbzx", op.rd, op.ra, op.rb); } void PPUDisAsm::LVX(ppu_opcode_t op) { DisAsm_V1_R2("lvx", op.vd, op.ra, op.rb); } void PPUDisAsm::NEG(ppu_opcode_t op) { DisAsm_R2_OE_RC("neg", op.rd, op.ra, op.oe, op.rc); } void PPUDisAsm::LBZUX(ppu_opcode_t op) { DisAsm_R3("lbzux", op.rd, op.ra, op.rb); } void PPUDisAsm::NOR(ppu_opcode_t op) { if (op.rs == op.rb) { DisAsm_R2_RC("not", op.ra, op.rs, op.rc); } else { DisAsm_R3_RC("nor", op.ra, op.rs, op.rb, op.rc); } } void PPUDisAsm::STVEBX(ppu_opcode_t op) { DisAsm_V1_R2("stvebx", op.vs, op.ra, op.rb); } void PPUDisAsm::SUBFE(ppu_opcode_t op) { DisAsm_R3_OE_RC("subfe", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::ADDE(ppu_opcode_t op) { DisAsm_R3_OE_RC("adde", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::MTOCRF(ppu_opcode_t op) { const u8 crm = static_cast(op.crm); if (!op.l11 && crm == 0xff) { DisAsm_R1("mtcr", op.rs); return; } const int cr = std::countl_zero(crm); const auto name = op.l11 ? "mtocrf" : "mtcrf"; if (cr >= 8 || crm & (crm - 1)) { DisAsm_INT1_R1(name, crm, op.rs); } else { DisAsm_CR1_R1(name, cr, op.rs); } } void PPUDisAsm::STDX(ppu_opcode_t op) { DisAsm_R3("stdx", op.rs, op.ra, op.rb); } void PPUDisAsm::STWCX(ppu_opcode_t op) { DisAsm_R3("stwcx.", op.rs, op.ra, op.rb); } void PPUDisAsm::STWX(ppu_opcode_t op) { DisAsm_R3("stwx", op.rs, op.ra, op.rb); } void PPUDisAsm::STVEHX(ppu_opcode_t op) { DisAsm_V1_R2("stvehx", op.vs, op.ra, op.rb); } void PPUDisAsm::STDUX(ppu_opcode_t op) { DisAsm_R3("stdux", op.rs, op.ra, op.rb); } void PPUDisAsm::STWUX(ppu_opcode_t op) { DisAsm_R3("stwux", op.rs, op.ra, op.rb); } void PPUDisAsm::STVEWX(ppu_opcode_t op) { DisAsm_V1_R2("stvewx", op.vs, op.ra, op.rb); } void PPUDisAsm::SUBFZE(ppu_opcode_t op) { DisAsm_R2_OE_RC("subfze", op.rd, op.ra, op.oe, op.rc); } void PPUDisAsm::ADDZE(ppu_opcode_t op) { DisAsm_R2_OE_RC("addze", op.rd, op.ra, op.oe, op.rc); } void PPUDisAsm::STDCX(ppu_opcode_t op) { DisAsm_R3("stdcx.", op.rs, op.ra, op.rb); } void PPUDisAsm::STBX(ppu_opcode_t op) { DisAsm_R3("stbx", op.rs, op.ra, op.rb); } void PPUDisAsm::STVX(ppu_opcode_t op) { DisAsm_V1_R2("stvx", op.vd, op.ra, op.rb); } void PPUDisAsm::SUBFME(ppu_opcode_t op) { DisAsm_R2_OE_RC("subfme", op.rd, op.ra, op.oe, op.rc); } void PPUDisAsm::MULLD(ppu_opcode_t op) { DisAsm_R3_OE_RC("mulld", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::ADDME(ppu_opcode_t op) { DisAsm_R2_OE_RC("addme", op.rd, op.ra, op.oe, op.rc); } void PPUDisAsm::MULLW(ppu_opcode_t op) { DisAsm_R3_OE_RC("mullw", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::DCBTST(ppu_opcode_t op) { DisAsm_R3("dcbtst", op.ra, op.rb, op.bo); } void PPUDisAsm::STBUX(ppu_opcode_t op) { DisAsm_R3("stbux", op.rs, op.ra, op.rb); } void PPUDisAsm::ADD(ppu_opcode_t op) { DisAsm_R3_OE_RC("add", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::DCBT(ppu_opcode_t op) { DisAsm_R2("dcbt", op.ra, op.rb); } void PPUDisAsm::LHZX(ppu_opcode_t op) { DisAsm_R3("lhzx", op.rd, op.ra, op.rb); } void PPUDisAsm::EQV(ppu_opcode_t op) { DisAsm_R3_RC("eqv", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::ECIWX(ppu_opcode_t op) { DisAsm_R3("eciwx", op.rd, op.ra, op.rb); } void PPUDisAsm::LHZUX(ppu_opcode_t op) { DisAsm_R3("lhzux", op.rd, op.ra, op.rb); } void PPUDisAsm::XOR(ppu_opcode_t op) { DisAsm_R3_RC("xor", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::MFSPR(ppu_opcode_t op) { const u32 n = (op.spr >> 5) | ((op.spr & 0x1f) << 5); switch (n) { case 0x001: DisAsm_R1("mfxer", op.rd); break; case 0x008: DisAsm_R1("mflr", op.rd); break; case 0x009: DisAsm_R1("mfctr", op.rd); break; default: DisAsm_R1_IMM("mfspr", op.rd, n); break; } } void PPUDisAsm::LWAX(ppu_opcode_t op) { DisAsm_R3("lwax", op.rd, op.ra, op.rb); } void PPUDisAsm::DST(ppu_opcode_t op) { DisAsm_R2("dst(t)", op.ra, op.rb); } void PPUDisAsm::LHAX(ppu_opcode_t op) { DisAsm_R3("lhax", op.rd, op.ra, op.rb); } void PPUDisAsm::LVXL(ppu_opcode_t op) { DisAsm_V1_R2("lvxl", op.vd, op.ra, op.rb); } void PPUDisAsm::MFTB(ppu_opcode_t op) { const u32 n = (op.spr >> 5) | ((op.spr & 0x1f) << 5); switch (n) { case 268: DisAsm_R1("mftb", op.rd); break; case 269: DisAsm_R1("mftbu", op.rd); break; default: DisAsm_R1_IMM("mftb", op.rd, op.spr); break; } } void PPUDisAsm::LWAUX(ppu_opcode_t op) { DisAsm_R3("lwaux", op.rd, op.ra, op.rb); } void PPUDisAsm::DSTST(ppu_opcode_t op) { DisAsm_R2("dstst(t)", op.ra, op.rb); } void PPUDisAsm::LHAUX(ppu_opcode_t op) { DisAsm_R3("lhaux", op.rd, op.ra, op.rb); } void PPUDisAsm::STHX(ppu_opcode_t op) { DisAsm_R3("sthx", op.rs, op.ra, op.rb); } void PPUDisAsm::ORC(ppu_opcode_t op) { DisAsm_R3_RC("orc", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::ECOWX(ppu_opcode_t op) { DisAsm_R3("ecowx", op.rs, op.ra, op.rb); } void PPUDisAsm::STHUX(ppu_opcode_t op) { DisAsm_R3("sthux", op.rs, op.ra, op.rb); } void PPUDisAsm::OR(ppu_opcode_t op) { if (op.rs == op.rb) { switch (op.opcode) { case 0x7f9ce378: last_opcode += "db8cyc"; return; case 0x7fbdeb78: last_opcode += "db10cyc"; return; case 0x7fdef378: last_opcode += "db12cyc"; return; case 0x7ffffb78: last_opcode += "db16cyc"; return; default: DisAsm_R2_RC("mr", op.ra, op.rb, op.rc); } } else { DisAsm_R3_RC("or", op.ra, op.rs, op.rb, op.rc); } } void PPUDisAsm::DIVDU(ppu_opcode_t op) { DisAsm_R3_OE_RC("divdu", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::DIVWU(ppu_opcode_t op) { DisAsm_R3_OE_RC("divwu", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::MTSPR(ppu_opcode_t op) { const u32 n = (op.spr & 0x1f) + ((op.spr >> 5) & 0x1f); switch (n) { case 0x001: DisAsm_R1("mtxer", op.rs); break; case 0x008: DisAsm_R1("mtlr", op.rs); break; case 0x009: DisAsm_R1("mtctr", op.rs); break; default: DisAsm_R1_IMM("mtspr", n, op.rs); break; } } void PPUDisAsm::DCBI(ppu_opcode_t op) { DisAsm_R2("dcbi", op.ra, op.rb); } void PPUDisAsm::NAND(ppu_opcode_t op) { DisAsm_R3_RC("nand", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::STVXL(ppu_opcode_t op) { DisAsm_V1_R2("stvxl", op.vs, op.ra, op.rb); } void PPUDisAsm::DIVD(ppu_opcode_t op) { DisAsm_R3_OE_RC("divd", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::DIVW(ppu_opcode_t op) { DisAsm_R3_OE_RC("divw", op.rd, op.ra, op.rb, op.oe, op.rc); } void PPUDisAsm::LVLX(ppu_opcode_t op) { DisAsm_V1_R2("lvlx", op.vd, op.ra, op.rb); } void PPUDisAsm::LDBRX(ppu_opcode_t op) { DisAsm_R3("ldbrx", op.rd, op.ra, op.rb); } void PPUDisAsm::LSWX(ppu_opcode_t op) { DisAsm_R3("lswx", op.rd, op.ra, op.rb); } void PPUDisAsm::LWBRX(ppu_opcode_t op) { DisAsm_R3("lwbrx", op.rd, op.ra, op.rb); } void PPUDisAsm::LFSX(ppu_opcode_t op) { DisAsm_F1_R2("lfsx", op.frd, op.ra, op.rb); } void PPUDisAsm::SRW(ppu_opcode_t op) { DisAsm_R3_RC("srw", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::SRD(ppu_opcode_t op) { DisAsm_R3_RC("srd", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::LVRX(ppu_opcode_t op) { DisAsm_V1_R2("lvrx", op.vd, op.ra, op.rb); } void PPUDisAsm::LSWI(ppu_opcode_t op) { DisAsm_R2_INT1("lswi", op.rd, op.ra, op.rb); } void PPUDisAsm::LFSUX(ppu_opcode_t op) { DisAsm_F1_R2("lfsux", op.frd, op.ra, op.rb); } void PPUDisAsm::SYNC(ppu_opcode_t op) { last_opcode += (op.l10 ? "lwsync" : "sync"); } void PPUDisAsm::LFDX(ppu_opcode_t op) { DisAsm_F1_R2("lfdx", op.frd, op.ra, op.rb); } void PPUDisAsm::LFDUX(ppu_opcode_t op) { DisAsm_F1_R2("lfdux", op.frd, op.ra, op.rb); } void PPUDisAsm::STVLX(ppu_opcode_t op) { DisAsm_V1_R2("stvlx", op.vs, op.ra, op.rb); } void PPUDisAsm::STDBRX(ppu_opcode_t op) { DisAsm_R3("stdbrx", op.rs, op.ra, op.rb); } void PPUDisAsm::STSWX(ppu_opcode_t op) { DisAsm_R3("swswx", op.rs, op.ra, op.rb); } void PPUDisAsm::STWBRX(ppu_opcode_t op) { DisAsm_R3("stwbrx", op.rs, op.ra, op.rb); } void PPUDisAsm::STFSX(ppu_opcode_t op) { DisAsm_F1_R2("stfsx", op.frs, op.ra, op.rb); } void PPUDisAsm::STVRX(ppu_opcode_t op) { DisAsm_V1_R2("stvrx", op.vs, op.ra, op.rb); } void PPUDisAsm::STFSUX(ppu_opcode_t op) { DisAsm_F1_R2("stfsux", op.frs, op.ra, op.rb); } void PPUDisAsm::STSWI(ppu_opcode_t op) { DisAsm_R2_INT1("stswi", op.rd, op.ra, op.rb); } void PPUDisAsm::STFDX(ppu_opcode_t op) { DisAsm_F1_R2("stfdx", op.frs, op.ra, op.rb); } void PPUDisAsm::STFDUX(ppu_opcode_t op) { DisAsm_F1_R2("stfdux", op.frs, op.ra, op.rb); } void PPUDisAsm::LVLXL(ppu_opcode_t op) { DisAsm_V1_R2("lvlxl", op.vd, op.ra, op.rb); } void PPUDisAsm::LHBRX(ppu_opcode_t op) { DisAsm_R3("lhbrx", op.rd, op.ra, op.rb); } void PPUDisAsm::SRAW(ppu_opcode_t op) { DisAsm_R3_RC("sraw", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::SRAD(ppu_opcode_t op) { DisAsm_R3_RC("srad", op.ra, op.rs, op.rb, op.rc); } void PPUDisAsm::LVRXL(ppu_opcode_t op) { DisAsm_V1_R2("lvrxl", op.vd, op.ra, op.rb); } void PPUDisAsm::DSS(ppu_opcode_t) { last_opcode += "dss"; } void PPUDisAsm::SRAWI(ppu_opcode_t op) { DisAsm_R2_INT1_RC("srawi", op.ra, op.rs, op.sh32, op.rc); } void PPUDisAsm::SRADI(ppu_opcode_t op) { DisAsm_R2_INT1_RC("sradi", op.ra, op.rs, op.sh64, op.rc); } void PPUDisAsm::EIEIO(ppu_opcode_t) { last_opcode += "eieio"; } void PPUDisAsm::STVLXL(ppu_opcode_t op) { DisAsm_V1_R2("stvlxl", op.vs, op.ra, op.rb); } void PPUDisAsm::STHBRX(ppu_opcode_t op) { DisAsm_R3("sthbrx", op.rs, op.ra, op.rb); } void PPUDisAsm::EXTSH(ppu_opcode_t op) { DisAsm_R2_RC("extsh", op.ra, op.rs, op.rc); } void PPUDisAsm::STVRXL(ppu_opcode_t op) { DisAsm_V1_R2("stvrxl", op.vs, op.ra, op.rb); } void PPUDisAsm::EXTSB(ppu_opcode_t op) { DisAsm_R2_RC("extsb", op.ra, op.rs, op.rc); } void PPUDisAsm::STFIWX(ppu_opcode_t op) { DisAsm_F1_R2("stfiwx", op.frs, op.ra, op.rb); } void PPUDisAsm::EXTSW(ppu_opcode_t op) { DisAsm_R2_RC("extsw", op.ra, op.rs, op.rc); } void PPUDisAsm::ICBI(ppu_opcode_t op) { DisAsm_R2("icbi", op.ra, op.rb); } void PPUDisAsm::DCBZ(ppu_opcode_t op) { DisAsm_R2("dcbz", op.ra, op.rb); } void PPUDisAsm::LWZ(ppu_opcode_t op) { DisAsm_R2_IMM("lwz", op.rd, op.ra, op.simm16); } void PPUDisAsm::LWZU(ppu_opcode_t op) { DisAsm_R2_IMM("lwzu", op.rd, op.ra, op.simm16); } void PPUDisAsm::LBZ(ppu_opcode_t op) { DisAsm_R2_IMM("lbz", op.rd, op.ra, op.simm16); } void PPUDisAsm::LBZU(ppu_opcode_t op) { DisAsm_R2_IMM("lbzu", op.rd, op.ra, op.simm16); } void PPUDisAsm::STW(ppu_opcode_t op) { DisAsm_R2_IMM("stw", op.rs, op.ra, op.simm16); } void PPUDisAsm::STWU(ppu_opcode_t op) { DisAsm_R2_IMM("stwu", op.rs, op.ra, op.simm16); } void PPUDisAsm::STB(ppu_opcode_t op) { DisAsm_R2_IMM("stb", op.rs, op.ra, op.simm16); } void PPUDisAsm::STBU(ppu_opcode_t op) { DisAsm_R2_IMM("stbu", op.rs, op.ra, op.simm16); } void PPUDisAsm::LHZ(ppu_opcode_t op) { DisAsm_R2_IMM("lhz", op.rs, op.ra, op.simm16); } void PPUDisAsm::LHZU(ppu_opcode_t op) { DisAsm_R2_IMM("lhzu", op.rs, op.ra, op.simm16); } void PPUDisAsm::LHA(ppu_opcode_t op) { DisAsm_R2_IMM("lha", op.rs, op.ra, op.simm16); } void PPUDisAsm::LHAU(ppu_opcode_t op) { DisAsm_R2_IMM("lhau", op.rs, op.ra, op.simm16); } void PPUDisAsm::STH(ppu_opcode_t op) { DisAsm_R2_IMM("sth", op.rs, op.ra, op.simm16); } void PPUDisAsm::STHU(ppu_opcode_t op) { DisAsm_R2_IMM("sthu", op.rs, op.ra, op.simm16); } void PPUDisAsm::LMW(ppu_opcode_t op) { DisAsm_R2_IMM("lmw", op.rd, op.ra, op.simm16); } void PPUDisAsm::STMW(ppu_opcode_t op) { DisAsm_R2_IMM("stmw", op.rs, op.ra, op.simm16); } void PPUDisAsm::LFS(ppu_opcode_t op) { DisAsm_F1_IMM_R1("lfs", op.frd, op.simm16, op.ra); } void PPUDisAsm::LFSU(ppu_opcode_t op) { DisAsm_F1_IMM_R1("lfsu", op.frd, op.simm16, op.ra); } void PPUDisAsm::LFD(ppu_opcode_t op) { DisAsm_F1_IMM_R1("lfd", op.frd, op.simm16, op.ra); } void PPUDisAsm::LFDU(ppu_opcode_t op) { DisAsm_F1_IMM_R1("lfdu", op.frd, op.simm16, op.ra); } void PPUDisAsm::STFS(ppu_opcode_t op) { DisAsm_F1_IMM_R1("stfs", op.frs, op.simm16, op.ra); } void PPUDisAsm::STFSU(ppu_opcode_t op) { DisAsm_F1_IMM_R1("stfsu", op.frs, op.simm16, op.ra); } void PPUDisAsm::STFD(ppu_opcode_t op) { DisAsm_F1_IMM_R1("stfd", op.frs, op.simm16, op.ra); } void PPUDisAsm::STFDU(ppu_opcode_t op) { DisAsm_F1_IMM_R1("stfdu", op.frs, op.simm16, op.ra); } void PPUDisAsm::LD(ppu_opcode_t op) { DisAsm_R2_IMM("ld", op.rd, op.ra, op.ds * 4); } void PPUDisAsm::LDU(ppu_opcode_t op) { DisAsm_R2_IMM("ldu", op.rd, op.ra, op.ds * 4); } void PPUDisAsm::LWA(ppu_opcode_t op) { DisAsm_R2_IMM("lwa", op.rd, op.ra, op.ds * 4); } void PPUDisAsm::STD(ppu_opcode_t op) { DisAsm_R2_IMM("std", op.rs, op.ra, op.ds * 4); } void PPUDisAsm::STDU(ppu_opcode_t op) { DisAsm_R2_IMM("stdu", op.rs, op.ra, op.ds * 4); } void PPUDisAsm::FDIVS(ppu_opcode_t op) { DisAsm_F3_RC("fdivs", op.frd, op.fra, op.frb, op.rc); } void PPUDisAsm::FSUBS(ppu_opcode_t op) { DisAsm_F3_RC("fsubs", op.frd, op.fra, op.frb, op.rc); } void PPUDisAsm::FADDS(ppu_opcode_t op) { DisAsm_F3_RC("fadds", op.frd, op.fra, op.frb, op.rc); } void PPUDisAsm::FSQRTS(ppu_opcode_t op) { DisAsm_F2_RC("fsqrts", op.frd, op.frb, op.rc); } void PPUDisAsm::FRES(ppu_opcode_t op) { DisAsm_F2_RC("fres", op.frd, op.frb, op.rc); } void PPUDisAsm::FMULS(ppu_opcode_t op) { DisAsm_F3_RC("fmuls", op.frd, op.fra, op.frc, op.rc); } void PPUDisAsm::FMADDS(ppu_opcode_t op) { DisAsm_F4_RC("fmadds", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FMSUBS(ppu_opcode_t op) { DisAsm_F4_RC("fmsubs", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FNMSUBS(ppu_opcode_t op) { DisAsm_F4_RC("fnmsubs", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FNMADDS(ppu_opcode_t op) { DisAsm_F4_RC("fnmadds", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::MTFSB1(ppu_opcode_t op) { fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb1." : "mtfsb1", op.crbd); } void PPUDisAsm::MCRFS(ppu_opcode_t op) { DisAsm_CR2("mcrfs", op.crfd, op.crfs); } void PPUDisAsm::MTFSB0(ppu_opcode_t op) { fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb0." : "mtfsb0", op.crbd); } void PPUDisAsm::MTFSFI(ppu_opcode_t op) { fmt::append(last_opcode, "%-*s cr%d,%d,%d", PadOp(), op.rc ? "mtfsfi." : "mtfsfi", op.crfd, op.i, op.l15); } void PPUDisAsm::MFFS(ppu_opcode_t op) { DisAsm_F1_RC("mffs", op.frd, op.rc); } void PPUDisAsm::MTFSF(ppu_opcode_t op) { fmt::append(last_opcode, "%-*s %d,f%d,%d,%d", PadOp(), op.rc ? "mtfsf." : "mtfsf", op.rc, op.flm, op.frb, op.l6, op.l15); } void PPUDisAsm::FCMPU(ppu_opcode_t op) { DisAsm_CR1_F2("fcmpu", op.crfd, op.fra, op.frb); } void PPUDisAsm::FRSP(ppu_opcode_t op) { DisAsm_F2_RC("frsp", op.frd, op.frb, op.rc); } void PPUDisAsm::FCTIW(ppu_opcode_t op) { DisAsm_F2_RC("fctiw", op.frd, op.frb, op.rc); } void PPUDisAsm::FCTIWZ(ppu_opcode_t op) { DisAsm_F2_RC("fctiwz", op.frd, op.frb, op.rc); } void PPUDisAsm::FDIV(ppu_opcode_t op) { DisAsm_F3_RC("fdiv", op.frd, op.fra, op.frb, op.rc); } void PPUDisAsm::FSUB(ppu_opcode_t op) { DisAsm_F3_RC("fsub", op.frd, op.fra, op.frb, op.rc); } void PPUDisAsm::FADD(ppu_opcode_t op) { DisAsm_F3_RC("fadd", op.frd, op.fra, op.frb, op.rc); } void PPUDisAsm::FSQRT(ppu_opcode_t op) { DisAsm_F2_RC("fsqrt", op.frd, op.frb, op.rc); } void PPUDisAsm::FSEL(ppu_opcode_t op) { DisAsm_F4_RC("fsel", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FMUL(ppu_opcode_t op) { DisAsm_F3_RC("fmul", op.frd, op.fra, op.frc, op.rc); } void PPUDisAsm::FRSQRTE(ppu_opcode_t op) { DisAsm_F2_RC("frsqrte", op.frd, op.frb, op.rc); } void PPUDisAsm::FMSUB(ppu_opcode_t op) { DisAsm_F4_RC("fmsub", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FMADD(ppu_opcode_t op) { DisAsm_F4_RC("fmadd", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FNMSUB(ppu_opcode_t op) { DisAsm_F4_RC("fnmsub", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FNMADD(ppu_opcode_t op) { DisAsm_F4_RC("fnmadd", op.frd, op.fra, op.frc, op.frb, op.rc); } void PPUDisAsm::FCMPO(ppu_opcode_t op) { DisAsm_F3("fcmpo", op.crfd, op.fra, op.frb); } void PPUDisAsm::FNEG(ppu_opcode_t op) { DisAsm_F2_RC("fneg", op.frd, op.frb, op.rc); } void PPUDisAsm::FMR(ppu_opcode_t op) { DisAsm_F2_RC("fmr", op.frd, op.frb, op.rc); } void PPUDisAsm::FNABS(ppu_opcode_t op) { DisAsm_F2_RC("fnabs", op.frd, op.frb, op.rc); } void PPUDisAsm::FABS(ppu_opcode_t op) { DisAsm_F2_RC("fabs", op.frd, op.frb, op.rc); } void PPUDisAsm::FCTID(ppu_opcode_t op) { DisAsm_F2_RC("fctid", op.frd, op.frb, op.rc); } void PPUDisAsm::FCTIDZ(ppu_opcode_t op) { DisAsm_F2_RC("fctidz", op.frd, op.frb, op.rc); } void PPUDisAsm::FCFID(ppu_opcode_t op) { DisAsm_F2_RC("fcfid", op.frd, op.frb, op.rc); } extern std::vector g_ppu_function_names; void PPUDisAsm::UNK(ppu_opcode_t) { if (u32 addr{}; g_fxo->is_init() && (addr = g_fxo->get().addr)) { // HLE function index const u32 index = (dump_pc - addr) / 8; if (dump_pc % 8 == 4 && index < ppu_function_manager::get().size()) { fmt::append(last_opcode, "Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index); return; } } last_opcode += "?? ??"; }