From ab50e5483ed428d79bccf0a37b58415f9c8456fd Mon Sep 17 00:00:00 2001 From: Eladash Date: Tue, 12 Oct 2021 23:12:30 +0300 Subject: [PATCH] GUI Utilities: Implement instruction search, PPU/SPU disasm improvements (#10968) * GUI Utilities: Implement instruction search in PS3 memory * String Searcher: Case insensitive search * PPU DisAsm: Comment constants with ORI * PPU DisAsm: Add 64-bit constant support * SPU/PPU DisAsm: Print CELL errors in disasm * PPU DisAsm: Constant comparison support --- rpcs3/Emu/CPU/CPUDisAsm.h | 44 ++-- rpcs3/Emu/Cell/Modules/cellAudioIn.h | 2 +- rpcs3/Emu/Cell/Modules/cellAudioOut.h | 2 +- rpcs3/Emu/Cell/PPCDisAsm.h | 216 +++++++++------- rpcs3/Emu/Cell/PPUDisAsm.cpp | 302 +++++++++++++++++++---- rpcs3/Emu/Cell/PPUDisAsm.h | 256 +++++++++++-------- rpcs3/Emu/Cell/PPUModule.cpp | 18 +- rpcs3/Emu/Cell/PPUThread.cpp | 3 +- rpcs3/Emu/Cell/SPUDisAsm.cpp | 149 +++++++---- rpcs3/Emu/Cell/SPUDisAsm.h | 107 ++++---- rpcs3/Emu/Cell/SPURecompiler.cpp | 2 +- rpcs3/Emu/Cell/SPUThread.cpp | 3 +- rpcs3/Emu/Cell/lv2/lv2.cpp | 72 ++++++ rpcs3/Emu/RSX/RSXDisAsm.cpp | 59 +++-- rpcs3/Emu/RSX/RSXDisAsm.h | 4 +- rpcs3/rpcs3qt/debugger_frame.cpp | 18 +- rpcs3/rpcs3qt/debugger_list.cpp | 3 +- rpcs3/rpcs3qt/main_window.cpp | 7 +- rpcs3/rpcs3qt/memory_string_searcher.cpp | 153 ++++++++++-- rpcs3/rpcs3qt/memory_string_searcher.h | 30 ++- rpcs3/rpcs3qt/memory_viewer_panel.h | 2 +- 21 files changed, 1043 insertions(+), 409 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUDisAsm.h b/rpcs3/Emu/CPU/CPUDisAsm.h index c7f8a5b7e9..c24a1cab4d 100644 --- a/rpcs3/Emu/CPU/CPUDisAsm.h +++ b/rpcs3/Emu/CPU/CPUDisAsm.h @@ -17,12 +17,13 @@ class cpu_thread; class CPUDisAsm { protected: - const cpu_disasm_mode m_mode{}; + cpu_disasm_mode m_mode{}; const std::add_pointer_t m_offset{}; + const u32 m_start_pc; const std::add_pointer_t m_cpu{}; u32 m_op = 0; - void Write(const std::string& value) + void format_by_mode() { switch (m_mode) { @@ -32,28 +33,27 @@ protected: static_cast(m_op >> 24), static_cast(m_op >> 16), static_cast(m_op >> 8), - static_cast(m_op >> 0), value); + static_cast(m_op >> 0), last_opcode); break; } case cpu_disasm_mode::interpreter: { - last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc, + last_opcode.insert(0, fmt::format("[%08x] %02x %02x %02x %02x: ", dump_pc, static_cast(m_op >> 24), static_cast(m_op >> 16), static_cast(m_op >> 8), - static_cast(m_op >> 0), value); + static_cast(m_op >> 0))); break; } case cpu_disasm_mode::compiler_elf: { - last_opcode = value + '\n'; + last_opcode += '\n'; break; } case cpu_disasm_mode::normal: { - last_opcode = value; break; } default: fmt::throw_exception("Unreachable"); @@ -64,26 +64,23 @@ public: std::string last_opcode{}; u32 dump_pc{}; - template , int> = 0> - static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode) + CPUDisAsm& change_mode(cpu_disasm_mode mode) { - return T{mode, dis.m_offset, dis.m_cpu}; + m_mode = mode; + return *this; } protected: - CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr) + CPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0, const cpu_thread* cpu = nullptr) : m_mode(mode) - , m_offset(offset) + , m_offset(offset - start_pc) + , m_start_pc(start_pc) , m_cpu(cpu) { } - CPUDisAsm(const CPUDisAsm&) = delete; - CPUDisAsm& operator=(const CPUDisAsm&) = delete; - virtual ~CPUDisAsm() = default; - virtual u32 DisAsmBranchTarget(s32 /*imm*/); // TODO: Add builtin fmt helpper for best performance @@ -109,16 +106,17 @@ protected: return fmt::format("%s%s", v < 0 ? "-" : "", av); } - std::string FixOp(std::string op) const + // Signify the formatting function the minimum required amount of characters to print for an instruction + // Padding with spaces + int PadOp(std::string_view op = {}, int min_spaces = 0) const { - if (m_mode != cpu_disasm_mode::normal) - { - op.resize(std::max(op.length(), 10), ' '); - } - - return op; + return m_mode == cpu_disasm_mode::normal ? (static_cast(op.size()) + min_spaces) : 10; } public: + virtual ~CPUDisAsm() = default; + virtual u32 disasm(u32 pc) = 0; + virtual std::pair get_memory_span() const = 0; + virtual std::unique_ptr copy_type_erased() const = 0; }; diff --git a/rpcs3/Emu/Cell/Modules/cellAudioIn.h b/rpcs3/Emu/Cell/Modules/cellAudioIn.h index 2dfc77bf16..a630ce47cd 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudioIn.h +++ b/rpcs3/Emu/Cell/Modules/cellAudioIn.h @@ -4,7 +4,7 @@ #include "util/endian.hpp" // Error codes -enum CellAudioInError +enum CellAudioInError : u32 { CELL_AUDIO_IN_ERROR_NOT_IMPLEMENTED = 0x8002b260, CELL_AUDIO_IN_ERROR_ILLEGAL_CONFIGURATION = 0x8002b261, diff --git a/rpcs3/Emu/Cell/Modules/cellAudioOut.h b/rpcs3/Emu/Cell/Modules/cellAudioOut.h index 22ef3e09e3..7c52ec1147 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudioOut.h +++ b/rpcs3/Emu/Cell/Modules/cellAudioOut.h @@ -1,7 +1,7 @@ #pragma once // Error codes -enum CellAudioOutError +enum CellAudioOutError : u32 { CELL_AUDIO_OUT_ERROR_NOT_IMPLEMENTED = 0x8002b240, CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION = 0x8002b241, diff --git a/rpcs3/Emu/Cell/PPCDisAsm.h b/rpcs3/Emu/Cell/PPCDisAsm.h index 52a45a7ae5..2035837735 100644 --- a/rpcs3/Emu/Cell/PPCDisAsm.h +++ b/rpcs3/Emu/Cell/PPCDisAsm.h @@ -5,232 +5,264 @@ class PPCDisAsm : public CPUDisAsm { protected: - PPCDisAsm(cpu_disasm_mode mode, const u8* offset) : CPUDisAsm(mode, offset) + PPCDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : CPUDisAsm(mode, offset, start_pc) { } virtual u32 DisAsmBranchTarget(const s32 imm) override = 0; - void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3) + usz insert_char_if(usz pos, bool insert, char c) { - Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3)); + if (!insert) + { + return pos; + } + + ensure(std::exchange(last_opcode[pos], c) == ' ' && last_opcode[pos + 1] == ' '); + return pos + 1; } - void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm) + + usz insert_char_if(std::string_view op, bool insert, char c = '.') { - Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm)); + return insert_char_if(op.size(), insert, c); } - void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2) + + void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3) { - Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2)); + fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3); } - void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm) + void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm) { - Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm)); + fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm); } - void DisAsm_V2(const std::string& op, u32 v0, u32 v1) + void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2) { - Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1)); + fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2); } - void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm) + void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm) { - Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm))); + fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm); } - void DisAsm_V1(const std::string& op, u32 v0) + void DisAsm_V2(std::string_view op, u32 v0, u32 v1) { - Write(fmt::format("%s v%d", FixOp(op), v0)); + fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1); } - void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2) + void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm) { - Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2)); + fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm)); } - void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc) + void DisAsm_V1(std::string_view op, u32 v0) { - Write(fmt::format("%s%s cr%d,f%d,f%d", FixOp(op), (rc ? "." : ""), cr0, f0, f1)); + fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0); } - void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1) + void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2) + { + fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2); + } + void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc) + { + fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1); + insert_char_if(op, !!rc); + } + void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1) { DisAsm_CR1_F2_RC(op, cr0, f0, f1, false); } - void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1) + void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1) { - Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1)); + fmt::append(last_opcode, "%-*s %d,r%d,r%d", PadOp(), op, i0, r0, r1); } - void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0) + void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0) { - Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s %d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0)); } - void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc) + void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc) { - Write(fmt::format("%s%s %d,r%d", FixOp(op), (rc ? "." : ""), i0, r0)); + fmt::append(last_opcode, "%-*s %d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0); + insert_char_if(op, !!rc); } - void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0) + void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0) { DisAsm_INT1_R1_RC(op, i0, r0, false); } - void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc) + void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc) { - Write(fmt::format("%s%s f%d,f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2, f3)); + fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3); + insert_char_if(op, !!rc); } - void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc) + void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc) { - Write(fmt::format("%s%s f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2)); + fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2); + insert_char_if(op, !!rc); } - void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2) + void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2) { DisAsm_F3_RC(op, f0, f1, f2, false); } - void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc) + void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc) { - Write(fmt::format("%s%s f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1)); + fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1); + insert_char_if(op, !!rc); } - void DisAsm_F2(const std::string& op, u32 f0, u32 f1) + void DisAsm_F2(std::string_view op, u32 f0, u32 f1) { DisAsm_F2_RC(op, f0, f1, false); } - void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1) + void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1) { if(m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1)); + fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1); return; } - Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1)); + fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1); } - void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc) + void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc) { if(m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("%s%s f%d,r%d,%s", FixOp(op), (rc ? "." : ""), f0, r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0)); + insert_char_if(op, !!rc); return; } - Write(fmt::format("%s%s f%d,%s(r%d)", FixOp(op), (rc ? "." : ""), f0, SignedHex(imm0), r0)); + fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0); + insert_char_if(op, !!rc); } - void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0) + void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0) { DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false); } - void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc) + void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc) { - Write(fmt::format("%s%s f%d", FixOp(op), (rc ? "." : ""), f0)); + fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0); + insert_char_if(op, !!rc); } - void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc) + void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc) { - Write(fmt::format("%s%s r%d", FixOp(op), (rc ? "." : ""), r0)); + fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0); + insert_char_if(op, !!rc); } - void DisAsm_R1(const std::string& op, u32 r0) + void DisAsm_R1(std::string_view op, u32 r0) { DisAsm_R1_RC(op, r0, false); } - void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 oe, u32 rc) + void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 oe, u32 rc) { - Write(fmt::format("%s%s%s r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1)); + fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1); + insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.'); } - void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc) + void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc) { DisAsm_R2_OE_RC(op, r0, r1, false, rc); } - void DisAsm_R2(const std::string& op, u32 r0, u32 r1) + void DisAsm_R2(std::string_view op, u32 r0, u32 r1) { DisAsm_R2_RC(op, r0, r1, false); } - void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc) + void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc) { - Write(fmt::format("%s%s%s r%d,r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1, r2)); + fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1, r2); + insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.'); } - void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc) + void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc) { - Write(fmt::format("%s%s r%d,r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, r2, i0, i1)); + fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1); + insert_char_if(op, !!rc); } - void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc) + void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc) { DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc); } - void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2) + void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2) { DisAsm_R3_RC(op, r0, r1, r2, false); } - void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc) + void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc) { - Write(fmt::format("%s%s r%d,r%d,%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1, i2)); + fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2); + insert_char_if(op, !!rc); } - void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2) + void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2) { DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false); } - void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc) + void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc) { - Write(fmt::format("%s%s r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1)); + fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1); + insert_char_if(op, !!rc); } - void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1) + void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1) { DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false); } - void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc) + void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc) { - Write(fmt::format("%s%s r%d,r%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0)); + fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0); + insert_char_if(op, !!rc); } - void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0) + void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0) { DisAsm_R2_INT1_RC(op, r0, r1, i0, false); } - void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0) + void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0) { if(m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0)); return; } - Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1)); + fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1); } - void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0) + void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0) { - Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0)); } - void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0) + void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0) { - Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0)); + fmt::append(last_opcode, "%-*s %d,r%d #%x", PadOp(), op, imm0, r0, imm0); } - void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0) + void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0) { - Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0)); } - void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc) + void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc) { - Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1)); + fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1); + insert_char_if(op, !!rc); } - void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1) + void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1) { DisAsm_CR1_R2_RC(op, cr0, r0, r1, false); } - void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1) + void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1) { - Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1)); + fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1); } - void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2) + void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2) { - Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2)); + fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2); } - void DisAsm_INT1(const std::string& op, const int i0) + void DisAsm_INT1(std::string_view op, const int i0) { - Write(fmt::format("%s %d", FixOp(op), i0)); + fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0); } - void DisAsm_BRANCH(const std::string& op, const int pc) + void DisAsm_BRANCH(std::string_view op, const int pc) { - Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc)); } - void DisAsm_BRANCH_A(const std::string& op, const int pc) + void DisAsm_BRANCH_A(std::string_view op, const int pc) { - Write(fmt::format("%s 0x%x", FixOp(op), pc)); + fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc); } - void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc) + void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc) { - Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc)); } - void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc) + void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc) { - Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc)); } }; diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index f58d3928c5..21398ee57a 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -4,6 +4,8 @@ #include "PPUAnalyser.h" #include "Emu/IdManager.h" +#include "util/asm.hpp" + const ppu_decoder s_ppu_disasm; const ppu_decoder s_ppu_itype; @@ -11,16 +13,36 @@ extern const std::unordered_map& get_exported_function_na 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_mode != cpu_disasm_mode::interpreter && m_mode != cpu_disasm_mode::normal) + if (m_offset != vm::g_sudo_addr) { + // Exported functions lookup is not allowed in this case + format_by_mode(); return 4; } @@ -32,29 +54,50 @@ u32 PPUDisAsm::disasm(u32 pc) last_opcode += it->second; } + format_by_mode(); return 4; } -std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const +std::pair PPUDisAsm::get_memory_span() const { - if (m_mode != cpu_disasm_mode::interpreter && m_mode != cpu_disasm_mode::normal) + 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) { - pc = dump_pc; + // Default arg: choose pc of previous instruction + + if (dump_pc == 0) + { + // Do not underflow + return {}; + } + + pc = dump_pc - 4; } - if (!vm::check_addr(pc, vm::page_executable)) - { - return {}; - } +#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 - 4; vm::check_addr(i, vm::page_executable); i -= 4) + 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 }; @@ -73,20 +116,22 @@ std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const } // Get constant register value - #define GET_CONST_REG(var, reg) \ + #define GET_CONST_OP_REG(var, reg, op) \ {\ /* Search for the constant value of the register*/\ - const auto [is_const, value] = try_get_const_gpr_value(reg, i);\ + const auto [const_op, value] = try_get_const_op_gpr_value(reg, i - 4, TTL - 1);\ \ - if (!is_const)\ + if (const_op != const_op::op)\ {\ - /* Cannot compute constant value if register is not constant*/\ + /* 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: @@ -104,7 +149,7 @@ std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const GET_CONST_REG(reg_ra, op.ra); } - return { true, reg_ra + op.simm16 }; + return { form, reg_ra + op.simm16 }; } case ppu_itype::ADDIS: { @@ -120,10 +165,16 @@ std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const GET_CONST_REG(reg_ra, op.ra); } - return { true, reg_ra + op.simm16 * 65536 }; + 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 @@ -134,9 +185,50 @@ std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const GET_CONST_REG(reg_rs, op.rs); - return { true, reg_rs | op.uimm16 }; + 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) { @@ -147,7 +239,64 @@ std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const GET_CONST_REG(reg_rs, op.rs); - return { true, reg_rs | (u64{op.uimm16} << 16)}; + 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: { @@ -161,11 +310,45 @@ std::pair PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const 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{}; @@ -1045,11 +1228,40 @@ void PPUDisAsm::SUBFIC(ppu_opcode_t op) 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) @@ -1091,7 +1303,7 @@ void PPUDisAsm::BC(ppu_opcode_t op) if (m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk)); + fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk); return; } @@ -1099,7 +1311,7 @@ void PPUDisAsm::BC(ppu_opcode_t op) if (!inst) { - Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk)); + fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk); return; } @@ -1143,11 +1355,11 @@ void PPUDisAsm::SC(ppu_opcode_t op) // Try to get constant syscall index if (auto [is_const, index] = try_get_const_gpr_value(11); is_const && index < 1024u) { - Write(fmt::format("sc #%s", ppu_syscall_code{index})); + fmt::append(last_opcode, "%-*s #%s", PadOp(), ppu_syscall_code{index}); return; } - Write("sc"); + last_opcode += "sc"; } void PPUDisAsm::B(ppu_opcode_t op) @@ -1158,7 +1370,7 @@ void PPUDisAsm::B(ppu_opcode_t op) if (m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("b 0x%x, %d, %d", li, aa, lk)); + fmt::append(last_opcode, "b 0x%x, %d, %d", li, aa, lk); return; } @@ -1196,7 +1408,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op) if (bo == 0b10100) { - Write(lk ? "blrl" : "blr"); + last_opcode += (lk ? "blrl" : "blr"); return; } @@ -1204,7 +1416,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op) if (!inst) { - Write(fmt::format("bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk)); + fmt::append(last_opcode, "bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk); return; } @@ -1239,7 +1451,7 @@ void PPUDisAsm::CRANDC(ppu_opcode_t op) void PPUDisAsm::ISYNC(ppu_opcode_t) { - Write("isync"); + last_opcode += "isync"; } void PPUDisAsm::CRXOR(ppu_opcode_t op) @@ -1299,7 +1511,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op) if (bo == 0b10100) { - Write(lk ? "bctrl" : "bctr"); + last_opcode += (lk ? "bctrl" : "bctr"); return; } @@ -1308,7 +1520,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op) if (!inst || inst[1] == 'd') { // Invalid or unknown bcctr form - Write(fmt::format("bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk)); + fmt::append(last_opcode, "bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk); return; } @@ -1347,14 +1559,20 @@ void PPUDisAsm::RLWNM(ppu_opcode_t op) void PPUDisAsm::ORI(ppu_opcode_t op) { - if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop"); + 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) return Write("nop"); + if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; } DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16); } @@ -1898,10 +2116,10 @@ void PPUDisAsm::OR(ppu_opcode_t op) { switch (op.opcode) { - case 0x7f9ce378: return Write("db8cyc"); - case 0x7fbdeb78: return Write("db10cyc"); - case 0x7fdef378: return Write("db12cyc"); - case 0x7ffffb78: return Write("db16cyc"); + 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); } } @@ -2011,7 +2229,7 @@ void PPUDisAsm::LFSUX(ppu_opcode_t op) void PPUDisAsm::SYNC(ppu_opcode_t op) { - Write(op.l10 ? "lwsync" : "sync"); + last_opcode += (op.l10 ? "lwsync" : "sync"); } void PPUDisAsm::LFDX(ppu_opcode_t op) @@ -2101,7 +2319,7 @@ void PPUDisAsm::LVRXL(ppu_opcode_t op) void PPUDisAsm::DSS(ppu_opcode_t) { - Write("dss()"); + last_opcode += "dss"; } void PPUDisAsm::SRAWI(ppu_opcode_t op) @@ -2116,7 +2334,7 @@ void PPUDisAsm::SRADI(ppu_opcode_t op) void PPUDisAsm::EIEIO(ppu_opcode_t) { - Write("eieio"); + last_opcode += "eieio"; } void PPUDisAsm::STVLXL(ppu_opcode_t op) @@ -2361,7 +2579,7 @@ void PPUDisAsm::FNMADDS(ppu_opcode_t op) void PPUDisAsm::MTFSB1(ppu_opcode_t op) { - Write(fmt::format("mtfsb1%s %d", op.rc ? "." : "", op.crbd)); + fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb1." : "mtfsb1", op.crbd); } void PPUDisAsm::MCRFS(ppu_opcode_t op) @@ -2371,12 +2589,12 @@ void PPUDisAsm::MCRFS(ppu_opcode_t op) void PPUDisAsm::MTFSB0(ppu_opcode_t op) { - Write(fmt::format("mtfsb0%s %d", op.rc ? "." : "", op.crbd)); + fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb0." : "mtfsb0", op.crbd); } void PPUDisAsm::MTFSFI(ppu_opcode_t op) { - Write(fmt::format("mtfsfi%s cr%d,%d,%d", op.rc ? "." : "", op.crfd, op.i, op.l15)); + 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) @@ -2386,7 +2604,7 @@ void PPUDisAsm::MFFS(ppu_opcode_t op) void PPUDisAsm::MTFSF(ppu_opcode_t op) { - Write(fmt::format("mtfsf%s %d,f%d,%d,%d", op.rc ? "." : "", op.rc, op.flm, op.frb, op.l6, op.l15)); + 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) @@ -2515,10 +2733,10 @@ void PPUDisAsm::UNK(ppu_opcode_t) if (dump_pc % 8 == 4 && index < ppu_function_manager::get().size()) { - Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index)); + fmt::append(last_opcode, "Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index); return; } } - Write("?? ??"); + last_opcode += "?? ??"; } diff --git a/rpcs3/Emu/Cell/PPUDisAsm.h b/rpcs3/Emu/Cell/PPUDisAsm.h index 6d8c5c6d80..5da64c00bc 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.h +++ b/rpcs3/Emu/Cell/PPUDisAsm.h @@ -29,265 +29,303 @@ private: } private: - void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3) + void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3) { - Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3)); + fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3); } - void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm) + void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm) { - Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm)); + fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm); } - void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2) + void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2) { - Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2)); + fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2); } - void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm) + void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm) { - Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm)); + fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm); } - void DisAsm_V2(const std::string& op, u32 v0, u32 v1) + void DisAsm_V2(std::string_view op, u32 v0, u32 v1) { - Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1)); + fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1); } - void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm) + void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm) { - Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm))); + fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm)); } - void DisAsm_V1(const std::string& op, u32 v0) + void DisAsm_V1(std::string_view op, u32 v0) { - Write(fmt::format("%s v%d", FixOp(op), v0)); + fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0); } - void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2) + void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2) { - Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2)); + fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2); } - void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc) + void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc) { - Write(fmt::format("%s cr%d,f%d,f%d", FixOp(op + (rc ? "." : "")), cr0, f0, f1)); + fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1); + insert_char_if(op, !!rc); } - void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1) + void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1) { DisAsm_CR1_F2_RC(op, cr0, f0, f1, false); } - void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1) + void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1) { - Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1)); + fmt::append(last_opcode, "%-*s%d,r%d,r%d", PadOp(), op, i0, r0, r1); } - void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0) + void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0) { - Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s%d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0)); } - void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc) + void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc) { - Write(fmt::format("%s %d,r%d", FixOp(op + (rc ? "." : "")), i0, r0)); + fmt::append(last_opcode, "%-*s%d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0); + insert_char_if(op, !!rc); } - void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0) + void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0) { DisAsm_INT1_R1_RC(op, i0, r0, false); } - void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc) + void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc) { - Write(fmt::format("%s f%d,f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2, f3)); + fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3); + insert_char_if(op, !!rc); } - void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc) + void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc) { - Write(fmt::format("%s f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2)); + fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2); + insert_char_if(op, !!rc); } - void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2) + void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2) { DisAsm_F3_RC(op, f0, f1, f2, false); } - void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc) + void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc) { - Write(fmt::format("%s f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1)); + fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1); + insert_char_if(op, !!rc); } - void DisAsm_F2(const std::string& op, u32 f0, u32 f1) + void DisAsm_F2(std::string_view op, u32 f0, u32 f1) { DisAsm_F2_RC(op, f0, f1, false); } - void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1) + void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1) { if (m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1)); + fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1); return; } - Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1)); + fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1); } - void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc) + void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc) { if (m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("%s f%d,r%d,%s", FixOp(op + (rc ? "." : "")), f0, r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0)); + insert_char_if(op, !!rc); return; } - Write(fmt::format("%s f%d,%s(r%d)", FixOp(op + (rc ? "." : "")), f0, SignedHex(imm0), r0)); + fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0); + insert_char_if(op, !!rc); } - void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0) + void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0) { DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false); } - void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc) + void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc) { - Write(fmt::format("%s f%d", FixOp(op + (rc ? "." : "")), f0)); + fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0); + insert_char_if(op, !!rc); } - void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc) + void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc) { - Write(fmt::format("%s r%d", FixOp(op + (rc ? "." : "")), r0)); + fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0); + insert_char_if(op, !!rc); } - void DisAsm_R1(const std::string& op, u32 r0) + void DisAsm_R1(std::string_view op, u32 r0) { DisAsm_R1_RC(op, r0, false); } - void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 _oe, u32 rc) + void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 _oe, u32 rc) { - Write(fmt::format("%s r%d,r%d", FixOp(op + (_oe ? "o" : "") + (rc ? "." : "")), r0, r1)); + fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1); + insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.'); } - void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc) + void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc) { DisAsm_R2_OE_RC(op, r0, r1, false, rc); } - void DisAsm_R2(const std::string& op, u32 r0, u32 r1) + void DisAsm_R2(std::string_view op, u32 r0, u32 r1) { DisAsm_R2_RC(op, r0, r1, false); } - void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc) + void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc) { - Write(fmt::format("%s r%d,r%d,r%d", FixOp(op + (rc ? "." : "") + (_oe ? "o" : "")), r0, r1, r2)); + fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1, r2); + insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.'); } - void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc) + void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc) { - Write(fmt::format("%s r%d,r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, r2, i0, i1)); + fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1); + insert_char_if(op, !!rc); } - void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc) + void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc) { DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc); } - void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2) + void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2) { DisAsm_R3_RC(op, r0, r1, r2, false); } - void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc) + void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc) { - Write(fmt::format("%s r%d,r%d,%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1, i2)); + fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2); + insert_char_if(op, !!rc); } - void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2) + void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2) { DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false); } - void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc) + void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc) { - Write(fmt::format("%s r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1)); + fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1); + insert_char_if(op, !!rc); } - void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1) + void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1) { DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false); } - void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc) + void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc) { - Write(fmt::format("%s r%d,r%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0)); + fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0); + insert_char_if(op, !!rc); } - void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0) + void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0) { DisAsm_R2_INT1_RC(op, r0, r1, i0, false); } - void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0) + void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0) { if (m_mode == cpu_disasm_mode::compiler_elf) { - Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0)); return; } - Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1)); + fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1); } - void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0) + void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0) { - Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0)); } - void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0) + void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0) { - Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0)); + fmt::append(last_opcode, "%-*s%d,r%d #%x", PadOp(), op, imm0, r0, imm0); } - void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0) + void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0) { - Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0))); + fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0)); } - void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc) + void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc) { - Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1)); + fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1); + insert_char_if(op, !!rc); } - void DisAsm_CR1_R1(const std::string& op, u32 cr0, u32 r0) + void DisAsm_CR1_R1(std::string_view op, u32 cr0, u32 r0) { - Write(fmt::format("%s cr%d,r%d", FixOp(op), cr0, r0)); + fmt::append(last_opcode, "%-*s cr%d,r%d", PadOp(), op, cr0, r0); } - void DisAsm_R1_CR1(const std::string& op, u32 r0, u32 cr0) + void DisAsm_R1_CR1(std::string_view op, u32 r0, u32 cr0) { - Write(fmt::format("%s r%d,cr%d", FixOp(op), r0, cr0)); + fmt::append(last_opcode, "%-*s r%d,cr%d", PadOp(), op, r0, cr0); } - void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1) + void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1) { DisAsm_CR1_R2_RC(op, cr0, r0, r1, false); } - void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1) + void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1) { - Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1)); + fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1); } - void DisAsm_BI1(const std::string& op, const int i0) + void DisAsm_BI1(std::string_view op, const int i0) { - Write(fmt::format("%s cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0))); + fmt::append(last_opcode, "%-*s cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0)); } - void DisAsm_BI2(const std::string& op, const int i0, const int i1) + void DisAsm_BI2(std::string_view op, const int i0, const int i1) { - Write(fmt::format("%s cr%d[%s],cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1))); + fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1)); } - void DisAsm_BI3(const std::string& op, const int i0, const int i1, const int i2) + void DisAsm_BI3(std::string_view op, const int i0, const int i1, const int i2) { - Write(fmt::format("%s cr%d[%s],cr%d[%s],cr%d[%s]", FixOp(op), - i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2))); + fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s],cr%d[%s]", PadOp(), op, + i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2)); } - void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2) + void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2) { - Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2)); + fmt::append(last_opcode, "%-*s%d,%d,%d", PadOp(), op, i0, i1, i2); } - void DisAsm_INT1(const std::string& op, const int i0) + void DisAsm_INT1(std::string_view op, const int i0) { - Write(fmt::format("%s %d", FixOp(op), i0)); + fmt::append(last_opcode, "%-*s%d", PadOp(), op, i0); } - void DisAsm_BRANCH(const std::string& op, const int pc) + void DisAsm_BRANCH(std::string_view op, const int pc) { - Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc)); } - void DisAsm_BRANCH_A(const std::string& op, const int pc) + void DisAsm_BRANCH_A(std::string_view op, const int pc) { - Write(fmt::format("%s 0x%x", FixOp(op), pc)); + fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc); } - void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc) + void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc) { - Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s%d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc)); } - void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc) + void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc) { - Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc)); } - void DisAsm_CR_BRANCH_A(const std::string& op, u32 cr, const int pc) + void DisAsm_CR_BRANCH_A(std::string_view op, u32 cr, const int pc) { - Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, pc)); + fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, pc); } - void DisAsm_BI_BRANCH(const std::string& op, u32 bi, const int pc) + void DisAsm_BI_BRANCH(std::string_view op, u32 bi, const int pc) { - Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc))); + fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc)); } - void DisAsm_BI_BRANCH_A(const std::string& op, u32 bi, const int pc) + void DisAsm_BI_BRANCH_A(std::string_view op, u32 bi, const int pc) { - Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), pc)); + fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), pc); } public: u32 disasm(u32 pc) override; - std::pair try_get_const_gpr_value(u32 reg, u32 pc = -1) const; + std::pair get_memory_span() const override; + std::unique_ptr copy_type_erased() const override; + + enum class const_op + { + none, + form, // Cosntant formation + xor_mask, // Constant XOR mask applied (used with CMPI/CMPLI instructions, covers their limit of 16-bit immediates) + }; + + std::pair try_get_const_op_gpr_value(u32 reg, u32 pc = -1, u32 TTL = 10) const; + + std::pair try_get_const_gpr_value(u32 reg, u32 pc = -1) const + { + auto [op, res] = try_get_const_op_gpr_value(reg, pc); + return {op == const_op::form, res}; + } + + std::pair try_get_const_xor_gpr_value(u32 reg, u32 pc = -1) const + { + auto [op, res] = try_get_const_op_gpr_value(reg, pc); + return {op == const_op::xor_mask, res}; + } void MFVSCR(ppu_opcode_t op); void MTVSCR(ppu_opcode_t op); diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 7347643ec6..6605daa38f 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -391,8 +391,21 @@ static void ppu_initialize_modules(ppu_linkage_info* link) // For the debugger (g_ppu_function_names shouldn't change, string_view should suffice) extern const std::unordered_map& get_exported_function_names_as_addr_indexed_map() { - static std::unordered_map res; - static u64 update_time = 0; + struct info_t + { + std::unordered_map res; + u64 update_time = 0; + }; + + static thread_local std::unique_ptr info; + + if (!info) + { + info = std::make_unique(); + info->res.reserve(ppu_module_manager::get().size()); + } + + auto& [res, update_time] = *info; const auto link = g_fxo->try_get(); const auto hle_funcs = g_fxo->try_get(); @@ -414,7 +427,6 @@ extern const std::unordered_map& get_exported_function_na update_time = current_time; res.clear(); - res.reserve(ppu_module_manager::get().size()); for (auto& pair : ppu_module_manager::get()) { diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index d472e7d84b..379de9d3bc 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -864,6 +864,8 @@ std::string ppu_thread::dump_regs() const { std::string ret; + PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr); + for (uint i = 0; i < 32; ++i) { auto reg = gpr[i]; @@ -914,7 +916,6 @@ std::string ppu_thread::dump_regs() const } else { - PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr); dis_asm.disasm(reg); fmt::append(ret, " -> %s", dis_asm.last_opcode); } diff --git a/rpcs3/Emu/Cell/SPUDisAsm.cpp b/rpcs3/Emu/Cell/SPUDisAsm.cpp index 41518857a1..21a8bc16f2 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/SPUDisAsm.cpp @@ -12,30 +12,58 @@ const spu_decoder s_spu_iflag; u32 SPUDisAsm::disasm(u32 pc) { + last_opcode.clear(); + + if (pc < m_start_pc || pc >= SPU_LS_SIZE) + { + return 0; + } + dump_pc = pc; be_t op; std::memcpy(&op, m_offset + pc, 4); m_op = op; (this->*(s_spu_disasm.decode(m_op)))({ m_op }); + + format_by_mode(); return 4; } -std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const +std::pair SPUDisAsm::get_memory_span() const { - if (m_mode != cpu_disasm_mode::interpreter && m_mode != cpu_disasm_mode::normal) + return {m_offset + m_start_pc, SPU_LS_SIZE - m_start_pc}; +} + +std::unique_ptr SPUDisAsm::copy_type_erased() const +{ + return std::make_unique(*this); +} + +std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc, u32 TTL) const +{ + if (!TTL) { + // Recursion limit (Time To Live) return {}; } if (pc == umax) { - pc = dump_pc; + // Default arg: choose pc of previous instruction + + if (dump_pc == 0) + { + // Do not underflow + return {}; + } + + pc = dump_pc - 4; } // Scan LS backwards from this instruction (until PC=0) // Search for the first register modification or branch instruction - for (s32 i = pc - 4; i >= 0; i -= 4) + for (s32 i = static_cast(pc); i >= static_cast(m_start_pc); i -= 4) { const u32 opcode = *reinterpret_cast*>(m_offset + i); const spu_opcode_t op0{ opcode }; @@ -58,6 +86,21 @@ std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const continue; } + // Get constant register value + #define GET_CONST_REG(var, reg) \ + {\ + /* Search for the constant value of the register*/\ + const auto [is_const, value] = try_get_const_value(reg, i - 4, TTL - 1);\ + \ + if (!is_const)\ + {\ + /* Cannot compute constant value if register is not constant*/\ + return {};\ + }\ + \ + var = value;\ + } void() /*<- Require a semicolon*/ + //const auto flag = s_spu_iflag.decode(opcode); // TODO: It detects spurious register modifications @@ -127,25 +170,13 @@ std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const } case spu_itype::IOHL: { - // Avoid multi-recursion for now - if (dump_pc != pc) - { - return {}; - } + v128 reg_val{}; - if (i >= 4) - { - // Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation) - // But don't limit to it - const auto [is_const, value] = try_get_const_value(reg, i); + // Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation) + // But don't limit to it + GET_CONST_REG(reg_val, op0.rt); - if (is_const) - { - return { true, value | v128::from32p(op0.i16) }; - } - } - - return {}; + return { true, reg_val | v128::from32p(op0.i16) }; } case spu_itype::STQA: case spu_itype::STQD: @@ -156,6 +187,27 @@ std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const // Do not modify RT break; } + case spu_itype::SHLQBYI: + { + if (op0.si7) + { + // Unimplemented, doubt needed + return {}; + } + + // Move register value operation + v128 reg_val{}; + GET_CONST_REG(reg_val, op0.ra); + + return { true, reg_val }; + } + case spu_itype::ORI: + { + v128 reg_val{}; + GET_CONST_REG(reg_val, op0.ra); + + return { true, reg_val | v128::from32p(op0.si10) }; + } default: return {}; } } @@ -236,6 +288,8 @@ SPUDisAsm::insert_mask_info SPUDisAsm::try_get_insert_mask_info(const v128& mask void SPUDisAsm::WRCH(spu_opcode_t op) { + DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]); + const auto [is_const, value] = try_get_const_value(op.rt); if (is_const) @@ -244,7 +298,7 @@ void SPUDisAsm::WRCH(spu_opcode_t op) { case MFC_Cmd: { - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], MFC(value._u8[12])).c_str()); + fmt::append(last_opcode, " #%s", MFC(value._u8[12])); return; } case MFC_WrListStallAck: @@ -252,62 +306,75 @@ void SPUDisAsm::WRCH(spu_opcode_t op) { const u32 v = value._u32[3]; if (v && !(v & (v - 1))) - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s (tag=%u)", spu_reg_name[op.rt], SignedHex(v), std::countr_zero(v)).c_str()); // Single-tag mask + fmt::append(last_opcode, " #%s (tag=%u)", SignedHex(v), std::countr_zero(v)); // Single-tag mask else - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(v)).c_str()); // Multi-tag mask (or zero) + fmt::append(last_opcode, " #%s", SignedHex(v)); // Multi-tag mask (or zero) return; } case MFC_EAH: { - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str()); + fmt::append(last_opcode, " #%s", SignedHex(value._u32[3])); return; } case MFC_Size: { - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u16[6])).c_str()); + fmt::append(last_opcode, " #%s", SignedHex(value._u16[6])); return; } case MFC_TagID: { - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%u", spu_reg_name[op.rt], value._u8[12]).c_str()); + fmt::append(last_opcode, " #%u", value._u8[12]); return; } case MFC_WrTagUpdate: { const auto upd = fmt::format("%s", mfc_tag_update(value._u32[3])); - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], upd == "empty" ? "IMMEDIATE" : upd).c_str()); + fmt::append(last_opcode, " #%s", upd == "empty" ? "IMMEDIATE" : upd); return; } default: { - DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str()); + fmt::append(last_opcode, " #%s", SignedHex(value._u32[3])); return; } } } - - DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]); } +enum CellError : u32; + void SPUDisAsm::IOHL(spu_opcode_t op) { + DisAsm("iohl", spu_reg_name[op.rt], op.i16); + const auto [is_const, value] = try_get_const_value(op.rt); - if (is_const) + u32 val0 = value._u32[0]; + + // Only print constant for a 4 equal 32-bit constants array + if (is_const && value == v128::from32p(val0)) { - // Only print constant for a 4 equal 32-bit constants array - if (value == v128::from32p(value._u32[0])) + // Fixup value + val0 |= op.i16; + + // Test if potentially a CELL error + if ((val0 >> 28) == 0x8u) { - DisAsm("iohl", spu_reg_name[op.rt], fmt::format("%s #%s", SignedHex(+op.i16), (value._u32[0] | op.i16)).c_str()); - return; + // Comment as CELL error + fmt::append(last_opcode, " #%s (0x%x)", CellError{val0}, val0); + } + else + { + // Comment constant formation + fmt::append(last_opcode, " #0x%x", val0); } } - - DisAsm("iohl", spu_reg_name[op.rt], op.i16); } void SPUDisAsm::SHUFB(spu_opcode_t op) { + DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]); + const auto [is_const, value] = try_get_const_value(op.rc); if (is_const) @@ -319,15 +386,13 @@ void SPUDisAsm::SHUFB(spu_opcode_t op) if ((size >= 4u && !src) || (size == 2u && src == 1u) || (size == 1u && src == 3u)) { // Comment insertion pattern for CWD-alike instruction - DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u]", spu_reg_name[op.rc], size * 8, dst).c_str()); + fmt::append(last_opcode, " #i%u[%u]", size * 8, dst); return; } // Comment insertion pattern for unknown instruction formations - DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u] = [%u]", spu_reg_name[op.rc], size * 8, dst, src).c_str()); + fmt::append(last_opcode, " #i%u[%u] = [%u]", size * 8, dst, src); return; } } - - DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]); } diff --git a/rpcs3/Emu/Cell/SPUDisAsm.h b/rpcs3/Emu/Cell/SPUDisAsm.h index b2ff5bac3e..59db41ac5d 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.h +++ b/rpcs3/Emu/Cell/SPUDisAsm.h @@ -69,15 +69,24 @@ static constexpr const char* spu_ch_name[128] = "ch121", "ch122", "ch123", "ch124", "ch125", "ch126", "ch127", }; +namespace utils +{ + class shm; +} + class SPUDisAsm final : public PPCDisAsm { + std::shared_ptr m_shm; public: - SPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset) + SPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : PPCDisAsm(mode, offset, start_pc) { } - ~SPUDisAsm() + ~SPUDisAsm() = default; + + void set_shm(std::shared_ptr shm) { + m_shm = std::move(shm); } private: @@ -86,84 +95,96 @@ private: return spu_branch_target(dump_pc, imm); } - static const char* BrIndirectSuffix(u32 de) + static char BrIndirectSuffix(u32 de) { switch (de) { - case 0b01: return "e"; - case 0b10: return "d"; - //case 0b11: return "(undef)"; - default: return ""; + case 0b01: return 'e'; + case 0b10: return 'd'; + case 0b11: return '!'; + default: return '\0'; } } - std::string& FixOp(std::string& op) const - { - if (m_mode != cpu_disasm_mode::normal) - { - op.append(std::max(10 - ::narrow(op.size()), 0), ' '); - } - - return op; - } void DisAsm(const char* op) { - Write(op); + last_opcode += op; } - void DisAsm(std::string op, u32 a1) + void DisAsm(std::string_view op, u32 a1) { - Write(fmt::format("%s 0x%x", FixOp(op), a1)); + fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, a1); } - void DisAsm(std::string op, const char* a1) + void DisAsm(std::string_view op, const char* a1) { - Write(fmt::format("%s %s", FixOp(op), a1)); + fmt::append(last_opcode, "%-*s %s", PadOp(), op, a1); } - void DisAsm(std::string op, const char* a1, const char* a2) + void DisAsm(std::string_view op, const char* a1, const char* a2) { - Write(fmt::format("%s %s,%s", FixOp(op), a1, a2)); + fmt::append(last_opcode, "%-*s %s,%s", PadOp(), op, a1, a2); } - void DisAsm(std::string op, int a1, const char* a2) + void DisAsm(std::string_view op, int a1, const char* a2) { - Write(fmt::format("%s 0x%x,%s", FixOp(op), a1, a2)); + fmt::append(last_opcode, "%-*s 0x%x,%s", PadOp(), op, a1, a2); } - void DisAsm(std::string op, const char* a1, int a2) + void DisAsm(std::string_view op, const char* a1, int a2) { - Write(fmt::format("%s %s,%s", FixOp(op), a1, SignedHex(a2))); + fmt::append(last_opcode, "%-*s %s,%s", PadOp(), op, a1, SignedHex(a2)); } - void DisAsm(std::string op, int a1, int a2) + void DisAsm(std::string_view op, int a1, int a2) { - Write(fmt::format("%s 0x%x,0x%x", FixOp(op), a1, a2)); + fmt::append(last_opcode, "%-*s 0x%x,0x%x", PadOp(), op, a1, a2); } - void DisAsm(std::string op, const char* a1, const char* a2, const char* a3) + void DisAsm(std::string_view op, const char* a1, const char* a2, const char* a3) { - Write(fmt::format("%s %s,%s,%s", FixOp(op), a1, a2, a3)); + fmt::append(last_opcode, "%-*s %s,%s,%s", PadOp(), op, a1, a2, a3); } - void DisAsm(std::string op, const char* a1, int a2, const char* a3) + void DisAsm(std::string_view op, const char* a1, int a2, const char* a3) { - Write(fmt::format("%s %s,%s(%s)", FixOp(op), a1, SignedHex(a2), a3)); + fmt::append(last_opcode, "%-*s %s,%s(%s)", PadOp(), op, a1, SignedHex(a2), a3); } - void DisAsm(std::string op, const char* a1, const char* a2, int a3) + void DisAsm(std::string_view op, const char* a1, const char* a2, int a3) { - Write(fmt::format("%s %s,%s,%s", FixOp(op), a1, a2, SignedHex(a3))); + fmt::append(last_opcode, "%-*s %s,%s,%s", PadOp(), op, a1, a2, SignedHex(a3)); } - void DisAsm(std::string op, const char* a1, const char* a2, const char* a3, const char* a4) + void DisAsm(std::string_view op, const char* a1, const char* a2, const char* a3, const char* a4) { - Write(fmt::format("%s %s,%s,%s,%s", FixOp(op), a1, a2, a3, a4)); + fmt::append(last_opcode, "%-*s %s,%s,%s,%s", PadOp(), op, a1, a2, a3, a4); } using field_de_t = decltype(spu_opcode_t::de); - void DisAsm(std::string op, field_de_t de, const char* a1) + void DisAsm(std::string_view op, field_de_t de, const char* a1) { - Write(fmt::format("%s %s", FixOp(op.append(BrIndirectSuffix(de))), a1)); + const char c = BrIndirectSuffix(de); + + if (c == '!') + { + // Invalid + fmt::append(last_opcode, "?? ?? (%s)", op); + return; + } + + fmt::append(last_opcode, "%-*s %s", PadOp(op, c ? 1 : 0), op, a1); + insert_char_if(op, !!c, c); } - void DisAsm(std::string op, field_de_t de, const char* a1, const char* a2) + void DisAsm(std::string_view op, field_de_t de, const char* a1, const char* a2) { - Write(fmt::format("%s %s,%s", FixOp(op.append(BrIndirectSuffix(de))), a1, a2)); + const char c = BrIndirectSuffix(de); + + if (c == '!') + { + fmt::append(last_opcode, "?? ?? (%s)", op); + return; + } + + fmt::append(last_opcode, "%-*s %s,%s", PadOp(op, c ? 1 : 0), op, a1, a2); + insert_char_if(op, !!c, c); } public: u32 disasm(u32 pc) override; - std::pair try_get_const_value(u32 reg, u32 pc = -1) const; + std::pair get_memory_span() const override; + std::unique_ptr copy_type_erased() const override; + std::pair try_get_const_value(u32 reg, u32 pc = -1, u32 TTL = 10) const; struct insert_mask_info { @@ -992,6 +1013,6 @@ public: void UNK(spu_opcode_t /*op*/) { - Write("?? ??"); + DisAsm("?? ??"); } }; diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index df08606adb..5b56d01e11 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -3249,7 +3249,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) void spu_recompiler_base::dump(const spu_program& result, std::string& out) { - SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast(result.data.data()) - result.lower_bound); + SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast(result.data.data()), result.lower_bound); std::string hash; { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 63cbf1845f..6eb538281f 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1216,6 +1216,8 @@ std::string spu_thread::dump_regs() const const bool floats_only = debugger_float_mode.load(); + SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls); + for (u32 i = 0; i < 128; i++, ret += '\n') { fmt::append(ret, "%s: ", spu_reg_name[i]); @@ -1269,7 +1271,6 @@ std::string spu_thread::dump_regs() const if (i3 >= 0x80 && is_exec_code(i3)) { - SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls); dis_asm.disasm(i3); fmt::append(ret, " -> %s", dis_asm.last_opcode); } diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index fef4509ea4..9a60bf6d55 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -936,9 +936,81 @@ const std::array, 1024> g_ppu_syscal #undef BIND_SYSC #undef NULL_FUNC +// TODO: more enums +enum CellAdecError : u32; +enum CellAtracError : u32; +enum CellAtracMultiError : u32; +enum CellAudioError : u32; +enum CellAudioOutError : u32; +enum CellAudioInError : u32; + +enum CellVideoOutError : u32; + +enum CellSpursCoreError : u32; +enum CellSpursPolicyModuleError : u32; +enum CellSpursTaskError : u32; +enum CellSpursJobError : u32; + +enum CellGameError : u32; +enum CellGameDataError : u32; +enum CellDiscGameError : u32; +enum CellHddGameError : u32; + +enum SceNpTrophyError : u32; +enum SceNpError : u32; + +template +constexpr auto formatter_of = std::make_pair(EnumMin, &fmt_class_string::format); + +const std::map s_error_codes_formatting_by_type +{ + formatter_of<0x80610000, CellAdecError>, + formatter_of<0x80612100, CellAdecError>, + formatter_of<0x80610300, CellAtracError>, + formatter_of<0x80610b00, CellAtracMultiError>, + formatter_of<0x80310700, CellAudioError>, + formatter_of<0x8002b240, CellAudioOutError>, + formatter_of<0x8002b260, CellAudioInError>, + formatter_of<0x8002b220, CellVideoOutError>, + + formatter_of<0x80410700, CellSpursCoreError>, + formatter_of<0x80410800, CellSpursPolicyModuleError>, + formatter_of<0x80410900, CellSpursTaskError>, + formatter_of<0x80410A00, CellSpursJobError>, + + formatter_of<0x8002cb00, CellGameError>, + formatter_of<0x8002b600, CellGameDataError>, + formatter_of<0x8002bd00, CellDiscGameError>, + formatter_of<0x8002ba00, CellHddGameError>, + + formatter_of<0x80022900, SceNpTrophyError>, + formatter_of<0x80029500, SceNpError>, +}; + template<> void fmt_class_string::format(std::string& out, u64 arg) { + // Test if can be formatted by this formatter + const bool lv2_cell_error = (arg >> 8) == 0x800100u; + + if (!lv2_cell_error) + { + // Format by external enum formatters + auto upper = s_error_codes_formatting_by_type.upper_bound(arg); + + if (upper == s_error_codes_formatting_by_type.begin()) + { + // Format as unknown by another enum formatter + upper->second(out, arg); + return; + } + + // Find the formatter whose base is the highest that is not more than arg + const auto found = std::prev(upper); + found->second(out, arg); + return; + } + format_enum(out, arg, [](auto error) { switch (error) diff --git a/rpcs3/Emu/RSX/RSXDisAsm.cpp b/rpcs3/Emu/RSX/RSXDisAsm.cpp index 3e88e5cce1..be9f2e841c 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.cpp +++ b/rpcs3/Emu/RSX/RSXDisAsm.cpp @@ -15,20 +15,34 @@ u32 RSXDisAsm::disasm(u32 pc) { last_opcode.clear(); - u32 addr = static_cast(m_cpu)->iomap_table.get_addr(pc); + auto try_read_op = [this](u32 pc) -> bool + { + if (pc < m_start_pc) + { + return false; + } - if (addr == umax) return 0; + if (m_offset == vm::g_sudo_addr) + { + // Translation needed + pc = static_cast(m_cpu)->iomap_table.get_addr(pc); + + if (pc == umax) return false; + } + + m_op = *reinterpret_cast*>(m_offset + pc); + return true; + }; + + if (!try_read_op(pc)) + { + return 0; + } - m_op = *reinterpret_cast*>(m_offset + addr); dump_pc = pc; if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK) { - if (m_mode == cpu_disasm_mode::list) - { - return 0; - } - if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD) { u32 jumpAddr = m_op & RSX_METHOD_OLD_JUMP_OFFSET_MASK; @@ -57,24 +71,15 @@ u32 RSXDisAsm::disasm(u32 pc) } else if ((m_op & RSX_METHOD_NOP_MASK) == RSX_METHOD_NOP_CMD) { - if (m_mode == cpu_disasm_mode::list) - { - return 0; - } - u32 i = 1; - for (pc += 4; i < 4096; i++, pc += 4) + for (pc += 4; m_mode != cpu_disasm_mode::list && pc && i < 4096; i++, pc += 4) { - addr = static_cast(m_cpu)->iomap_table.get_addr(pc); - - if (addr == umax) + if (!try_read_op(pc)) { break; } - m_op = *reinterpret_cast*>(m_offset + addr); - if ((m_op & RSX_METHOD_NOP_MASK) != RSX_METHOD_NOP_CMD) { break; @@ -106,17 +111,13 @@ u32 RSXDisAsm::disasm(u32 pc) for (u32 i = 0; i < (m_mode == cpu_disasm_mode::list ? count : 1); i++, pc += 4) { - addr = static_cast(m_cpu)->iomap_table.get_addr(pc); - - if (addr == umax) + if (!try_read_op(pc)) { last_opcode.clear(); Write("?? ??", -1); return 4; } - m_op = *reinterpret_cast*>(m_offset + addr); - const u32 id = id_start + (non_inc ? 0 : i); if (rsx::methods[id] == &rsx::invalid_method) @@ -134,6 +135,16 @@ u32 RSXDisAsm::disasm(u32 pc) } } +std::pair RSXDisAsm::get_memory_span() const +{ + return {m_offset + m_start_pc, (1ull << 32) - m_start_pc}; +} + +std::unique_ptr RSXDisAsm::copy_type_erased() const +{ + return std::make_unique(*this); +} + void RSXDisAsm::Write(const std::string& str, s32 count, bool is_non_inc, u32 id) { switch (m_mode) diff --git a/rpcs3/Emu/RSX/RSXDisAsm.h b/rpcs3/Emu/RSX/RSXDisAsm.h index 46eb401c93..13648062da 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.h +++ b/rpcs3/Emu/RSX/RSXDisAsm.h @@ -5,7 +5,7 @@ class RSXDisAsm final : public CPUDisAsm { public: - RSXDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu) : CPUDisAsm(mode, offset, cpu) + RSXDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc, const cpu_thread* cpu) : CPUDisAsm(mode, offset, start_pc, cpu) { } @@ -14,4 +14,6 @@ private: public: u32 disasm(u32 pc) override; + std::pair get_memory_span() const override; + std::unique_ptr copy_type_erased() const override; }; diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index a17bd2d3d7..0a5e21c308 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -2,6 +2,7 @@ #include "register_editor_dialog.h" #include "instruction_editor_dialog.h" #include "memory_viewer_panel.h" +#include "memory_string_searcher.h" #include "gui_settings.h" #include "debugger_list.h" #include "breakpoint_list.h" @@ -306,6 +307,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) QLabel* l = new QLabel(tr( "Keys Ctrl+G: Go to typed address." "\nKeys Ctrl+B: Open breakpoints settings." + "\nKeys Ctrl+S: Search memory string utility." "\nKeys Alt+S: Capture SPU images of selected SPU." "\nKey D: SPU MFC commands logger, MFC debug setting must be enabled." "\nKey D: Also PPU calling history logger, interpreter and non-zero call history size must be used." @@ -364,6 +366,20 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) open_breakpoints_settings(); return; } + case Qt::Key_S: + { + if (m_disasm && (cpu->id_type() == 2 || cpu->id_type() == 1)) + { + if (cpu->id_type() == 2) + { + // Save shared pointer to shared memory handle, ensure the destructor will not be called until the SPUDisAsm is destroyed + static_cast(m_disasm.get())->set_shm(static_cast(cpu)->shm); + } + + idm::make(this, m_disasm, cpu->id_type() == 2 ? cpu->get_name() : ""); + } + return; + } default: break; } } @@ -911,7 +927,7 @@ void debugger_frame::OnSelectUnit() if (get_cpu()) { - m_disasm = std::make_shared(cpu_disasm_mode::interpreter, vm::g_sudo_addr, m_rsx); + m_disasm = std::make_shared(cpu_disasm_mode::interpreter, vm::g_sudo_addr, 0, m_rsx); } break; diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp index 30677170ee..292fd89984 100644 --- a/rpcs3/rpcs3qt/debugger_list.cpp +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -223,7 +223,8 @@ void debugger_list::create_rsx_command_detail(u32 pc, int row) pc += std::max(m_disasm->disasm(pc), 4); } - RSXDisAsm rsx_dis = CPUDisAsm::copy_and_change_mode(*static_cast(m_disasm), cpu_disasm_mode::list); + RSXDisAsm rsx_dis = *static_cast(m_disasm); + rsx_dis.change_mode(cpu_disasm_mode::list); // Either invalid or not a method if (rsx_dis.disasm(pc) <= 4) return; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 5958dc5105..823186bc61 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -63,6 +63,9 @@ LOG_CHANNEL(gui_log, "GUI"); extern atomic_t g_user_asked_for_frame_capture; +class CPUDisAsm; +std::shared_ptr make_basic_ppu_disasm(); + inline std::string sstr(const QString& _in) { return _in.toStdString(); } main_window::main_window(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget *parent) @@ -2221,8 +2224,8 @@ void main_window::CreateConnects() connect(ui->toolsStringSearchAct, &QAction::triggered, this, [this] { - memory_string_searcher* mss = new memory_string_searcher(this); - mss->show(); + if (!Emu.IsStopped()) + idm::make(this, make_basic_ppu_disasm()); }); connect(ui->toolsDecryptSprxLibsAct, &QAction::triggered, this, &main_window::DecryptSPRXLibraries); diff --git a/rpcs3/rpcs3qt/memory_string_searcher.cpp b/rpcs3/rpcs3qt/memory_string_searcher.cpp index 6e5deeb77f..e36bb624aa 100644 --- a/rpcs3/rpcs3qt/memory_string_searcher.cpp +++ b/rpcs3/rpcs3qt/memory_string_searcher.cpp @@ -1,6 +1,8 @@ #include "memory_string_searcher.h" #include "Emu/Memory/vm.h" #include "Emu/Memory/vm_reservation.h" +#include "Emu/CPU/CPUDisAsm.h" +#include "Emu/IdManager.h" #include "Utilities/Thread.h" #include "Utilities/StrUtil.h" @@ -13,6 +15,7 @@ #include #include +#include #include "util/logs.hpp" #include "util/sysinfo.hpp" @@ -26,15 +29,28 @@ enum : int as_hex, as_f64, as_f32, + as_inst, }; -memory_string_searcher::memory_string_searcher(QWidget* parent) +memory_string_searcher::memory_string_searcher(QWidget* parent, std::shared_ptr disasm, std::string_view title) : QDialog(parent) + , m_disasm(std::move(disasm)) { - setWindowTitle(tr("String Searcher")); + if (title.empty()) + { + setWindowTitle(tr("String Searcher")); + } + else + { + setWindowTitle(tr("String Searcher Of %1").arg(title.data())); + } + setObjectName("memory_string_searcher"); setAttribute(Qt::WA_DeleteOnClose); + // Extract memory view from the disassembler + std::tie(m_ptr, m_size) = m_disasm->get_memory_span(); + m_addr_line = new QLineEdit(this); m_addr_line->setFixedWidth(QLabel("This is the very length of the lineedit due to hidpi reasons.").sizeHint().width()); m_addr_line->setPlaceholderText(tr("Search...")); @@ -52,10 +68,12 @@ memory_string_searcher::memory_string_searcher(QWidget* parent) m_cbox_input_mode->addItem("HEX bytes/integer", QVariant::fromValue(+as_hex)); m_cbox_input_mode->addItem("Double", QVariant::fromValue(+as_f64)); m_cbox_input_mode->addItem("Float", QVariant::fromValue(+as_f32)); + m_cbox_input_mode->addItem("Instruction", QVariant::fromValue(+as_inst)); m_cbox_input_mode->setToolTip(tr("String: search the memory for the specified string." "\nHEX bytes/integer: search the memory for hexadecimal values. Spaces, commas, \"0x\", \"0X\", \"\\x\" ensure separation of bytes but they are not mandatory." "\nDouble: reinterpret the string as 64-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0." - "\nFloat: reinterpret the string as 32-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0.")); + "\nFloat: reinterpret the string as 32-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0." + "\nInstruction: search an instruction contains the text of the string.")); QHBoxLayout* hbox_panel = new QHBoxLayout(); hbox_panel->addWidget(m_addr_line); @@ -68,6 +86,15 @@ memory_string_searcher::memory_string_searcher(QWidget* parent) connect(button_search, &QAbstractButton::clicked, this, &memory_string_searcher::OnSearch); layout()->setSizeConstraint(QLayout::SetFixedSize); + + // Show by default + show(); + + // Expected to be created by IDM, emulation stop will close it + connect(this, &memory_string_searcher::finished, [id = idm::last_id()](int) + { + idm::remove(id); + }); } void memory_string_searcher::OnSearch() @@ -97,6 +124,7 @@ void memory_string_searcher::OnSearch() switch (mode) { + case as_inst: case as_string: { case_insensitive = m_chkbox_case_insensitive->isChecked(); @@ -134,8 +162,8 @@ void memory_string_searcher::OnSearch() if (const usz pos = wstr.find_first_not_of(hex_chars); pos != umax) { - gui_log.error("String '%s' cannot be interpreted as hexadecimal byte string due to unknown character '%s'.", - m_addr_line->text().toStdString(), std::string_view{&wstr[pos], 1}); + gui_log.error("String '%s' cannot be interpreted as hexadecimal byte string due to unknown character '%c'.", + m_addr_line->text().toStdString(), wstr[pos]); return; } @@ -189,8 +217,8 @@ void memory_string_searcher::OnSearch() atomic_t found = 0; atomic_t avail_addr = 0; - // There's no need for so many threads - const u32 max_threads = utils::aligned_div(utils::get_thread_count(), 2); + // There's no need for so many threads (except for instructions searching) + const u32 max_threads = utils::aligned_div(utils::get_thread_count(), mode != as_inst ? 2 : 1); static constexpr u32 block_size = 0x2000000; @@ -198,18 +226,91 @@ void memory_string_searcher::OnSearch() const named_thread_group workers("String Searcher "sv, max_threads, [&]() { + if (mode == as_inst) + { + auto disasm = m_disasm->copy_type_erased(); + disasm->change_mode(cpu_disasm_mode::normal); + + const usz limit = std::min(m_size, m_ptr == vm::g_sudo_addr ? 0x4000'0000 : m_size); + + while (true) + { + u32 addr; + + const bool ok = avail_addr.fetch_op([&](u32& val) + { + if (val < limit && val != umax) + { + while (m_ptr == vm::g_sudo_addr && !vm::check_addr(val, vm::page_executable)) + { + // Skip unmapped memory + val = utils::align(val + 1, 0x10000); + + if (!val) + { + return false; + } + } + + addr = val; + + // Iterate 16k instructions at a time + val += 0x10000; + + if (!val) + { + // Overflow detection + val = -1; + } + + return true; + } + + return false; + }).second; + + if (!ok) + { + return; + } + + for (u32 i = 0; i < 0x10000; i += 4) + { + if (disasm->disasm(addr + i)) + { + auto& last = disasm->last_opcode; + + if (case_insensitive) + { + std::transform(last.begin(), last.end(), last.begin(), ::tolower); + } + + if (last.find(wstr) != umax) + { + gui_log.success("Found instruction at 0x%08x: '%s'", addr + i, disasm->last_opcode); + found++; + } + } + } + } + + return; + } + u32 local_found = 0; u32 addr = 0; bool ok = false; + const u64 addr_limit = (m_size >= block_size ? m_size - block_size : 0); + while (true) { if (!(addr % block_size)) { - std::tie(addr, ok) = avail_addr.fetch_op([](u32& val) + std::tie(addr, ok) = avail_addr.fetch_op([&](u32& val) { - if (val <= 0 - block_size) + if (val <= addr_limit) { // Iterate in 32MB blocks val += block_size; @@ -228,8 +329,14 @@ void memory_string_searcher::OnSearch() break; } - if (![&addr = addr]() + if (![&]() { + if (m_ptr != vm::g_sudo_addr) + { + // Always valid + return true; + } + // Skip unmapped memory for (const u32 end = utils::align(addr + 1, block_size) - 0x1000; !vm::check_addr(addr, 0); addr += 0x1000) { @@ -252,9 +359,9 @@ void memory_string_searcher::OnSearch() continue; } - u64 addr_max = addr; + const u64 end_mem = std::min(utils::align(addr + 1, block_size), m_size); - const u64 end_mem = std::min(utils::align(addr + 1, block_size) + 0x1000, u32{umax}); + u64 addr_max = m_ptr == vm::g_sudo_addr ? addr : end_mem; // Determine allocation size quickly while (addr_max < end_mem && vm::check_addr(static_cast(addr_max), vm::page_1m_size)) @@ -272,7 +379,12 @@ void memory_string_searcher::OnSearch() addr_max += 0x1000; } - std::string_view section{vm::get_super_ptr(addr), addr_max - addr}; + auto get_ptr = [&](u32 address) + { + return static_cast(m_ptr) + address; + }; + + std::string_view section{get_ptr(addr), addr_max - addr}; usz first_char = 0; @@ -282,12 +394,12 @@ void memory_string_searcher::OnSearch() { const u32 start = addr + first_char; - std::string_view test_sv{vm::get_super_ptr(start), addr_max - start}; + std::string_view test_sv{get_ptr(start), addr_max - start}; // Do not use allocating functions such as fmt::to_lower if (test_sv.size() >= wstr.size() && std::all_of(wstr.begin(), wstr.end(), [&](const char& c) { return c == ::tolower(test_sv[&c - wstr.data()]); })) { - gui_log.success("Found at 0x%08x", start); + gui_log.success("Found at 0x%08x: '%s'", start, test_sv); local_found++; } @@ -301,7 +413,7 @@ void memory_string_searcher::OnSearch() { const u32 start = addr + first_char; - if (std::string_view{vm::get_super_ptr(start), addr_max - start}.starts_with(wstr)) + if (std::string_view{get_ptr(start), addr_max - start}.starts_with(wstr)) { gui_log.success("Found at 0x%08x", start); local_found++; @@ -311,10 +423,13 @@ void memory_string_searcher::OnSearch() } } - addr = static_cast(std::min(end_mem - 0x1000, addr_max)); - // Check if at last page - if (addr_max >= 0u - 0x1000) break; + if (addr_max >= m_size - 0x1000) + { + break; + } + + addr = addr_max; } found += local_found; diff --git a/rpcs3/rpcs3qt/memory_string_searcher.h b/rpcs3/rpcs3qt/memory_string_searcher.h index 73c2876b49..34aa5464b5 100644 --- a/rpcs3/rpcs3qt/memory_string_searcher.h +++ b/rpcs3/rpcs3qt/memory_string_searcher.h @@ -1,11 +1,15 @@ #pragma once +#include "util/types.hpp" + #include class QLineEdit; class QCheckBox; class QComboBox; +class CPUDisAsm; + class memory_string_searcher : public QDialog { Q_OBJECT @@ -14,9 +18,33 @@ class memory_string_searcher : public QDialog QCheckBox* m_chkbox_case_insensitive = nullptr; QComboBox* m_cbox_input_mode = nullptr; + std::shared_ptr m_disasm; + + const void* m_ptr; + usz m_size; + public: - explicit memory_string_searcher(QWidget* parent); + explicit memory_string_searcher(QWidget* parent, std::shared_ptr disasm, std::string_view title = {}); private Q_SLOTS: void OnSearch(); }; + +// Lifetime management with IDM +struct memory_searcher_handle +{ + static constexpr u32 id_base = 1; + static constexpr u32 id_step = 1; + static constexpr u32 id_count = 2048; + + template requires (std::is_constructible_v) + memory_searcher_handle(Args&&... args) + : m_mss(new memory_string_searcher(std::forward(args)...)) + { + } + + ~memory_searcher_handle() { m_mss->close(); m_mss->deleteLater(); } + +private: + const std::add_pointer_t m_mss; +}; diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.h b/rpcs3/rpcs3qt/memory_viewer_panel.h index ca8fbf28ad..403ea463e2 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.h +++ b/rpcs3/rpcs3qt/memory_viewer_panel.h @@ -87,7 +87,7 @@ struct memory_viewer_handle static constexpr u32 id_step = 1; static constexpr u32 id_count = 2048; - template + template requires (std::is_constructible_v) memory_viewer_handle(Args&&... args) : m_mvp(new memory_viewer_panel(std::forward(args)...)) {