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
This commit is contained in:
Eladash 2021-10-12 23:12:30 +03:00 committed by GitHub
parent 8a72bdb422
commit ab50e5483e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1043 additions and 409 deletions

View file

@ -17,12 +17,13 @@ class cpu_thread;
class CPUDisAsm class CPUDisAsm
{ {
protected: protected:
const cpu_disasm_mode m_mode{}; cpu_disasm_mode m_mode{};
const std::add_pointer_t<const u8> m_offset{}; const std::add_pointer_t<const u8> m_offset{};
const u32 m_start_pc;
const std::add_pointer_t<const cpu_thread> m_cpu{}; const std::add_pointer_t<const cpu_thread> m_cpu{};
u32 m_op = 0; u32 m_op = 0;
void Write(const std::string& value) void format_by_mode()
{ {
switch (m_mode) switch (m_mode)
{ {
@ -32,28 +33,27 @@ protected:
static_cast<u8>(m_op >> 24), static_cast<u8>(m_op >> 24),
static_cast<u8>(m_op >> 16), static_cast<u8>(m_op >> 16),
static_cast<u8>(m_op >> 8), static_cast<u8>(m_op >> 8),
static_cast<u8>(m_op >> 0), value); static_cast<u8>(m_op >> 0), last_opcode);
break; break;
} }
case cpu_disasm_mode::interpreter: 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<u8>(m_op >> 24), static_cast<u8>(m_op >> 24),
static_cast<u8>(m_op >> 16), static_cast<u8>(m_op >> 16),
static_cast<u8>(m_op >> 8), static_cast<u8>(m_op >> 8),
static_cast<u8>(m_op >> 0), value); static_cast<u8>(m_op >> 0)));
break; break;
} }
case cpu_disasm_mode::compiler_elf: case cpu_disasm_mode::compiler_elf:
{ {
last_opcode = value + '\n'; last_opcode += '\n';
break; break;
} }
case cpu_disasm_mode::normal: case cpu_disasm_mode::normal:
{ {
last_opcode = value;
break; break;
} }
default: fmt::throw_exception("Unreachable"); default: fmt::throw_exception("Unreachable");
@ -64,26 +64,23 @@ public:
std::string last_opcode{}; std::string last_opcode{};
u32 dump_pc{}; u32 dump_pc{};
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0> CPUDisAsm& change_mode(cpu_disasm_mode mode)
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
{ {
return T{mode, dis.m_offset, dis.m_cpu}; m_mode = mode;
return *this;
} }
protected: 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_mode(mode)
, m_offset(offset) , m_offset(offset - start_pc)
, m_start_pc(start_pc)
, m_cpu(cpu) , m_cpu(cpu)
{ {
} }
CPUDisAsm(const CPUDisAsm&) = delete;
CPUDisAsm& operator=(const CPUDisAsm&) = delete; CPUDisAsm& operator=(const CPUDisAsm&) = delete;
virtual ~CPUDisAsm() = default;
virtual u32 DisAsmBranchTarget(s32 /*imm*/); virtual u32 DisAsmBranchTarget(s32 /*imm*/);
// TODO: Add builtin fmt helpper for best performance // TODO: Add builtin fmt helpper for best performance
@ -109,16 +106,17 @@ protected:
return fmt::format("%s%s", v < 0 ? "-" : "", av); 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) return m_mode == cpu_disasm_mode::normal ? (static_cast<int>(op.size()) + min_spaces) : 10;
{
op.resize(std::max<usz>(op.length(), 10), ' ');
}
return op;
} }
public: public:
virtual ~CPUDisAsm() = default;
virtual u32 disasm(u32 pc) = 0; virtual u32 disasm(u32 pc) = 0;
virtual std::pair<const void*, usz> get_memory_span() const = 0;
virtual std::unique_ptr<CPUDisAsm> copy_type_erased() const = 0;
}; };

View file

@ -4,7 +4,7 @@
#include "util/endian.hpp" #include "util/endian.hpp"
// Error codes // Error codes
enum CellAudioInError enum CellAudioInError : u32
{ {
CELL_AUDIO_IN_ERROR_NOT_IMPLEMENTED = 0x8002b260, CELL_AUDIO_IN_ERROR_NOT_IMPLEMENTED = 0x8002b260,
CELL_AUDIO_IN_ERROR_ILLEGAL_CONFIGURATION = 0x8002b261, CELL_AUDIO_IN_ERROR_ILLEGAL_CONFIGURATION = 0x8002b261,

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// Error codes // Error codes
enum CellAudioOutError enum CellAudioOutError : u32
{ {
CELL_AUDIO_OUT_ERROR_NOT_IMPLEMENTED = 0x8002b240, CELL_AUDIO_OUT_ERROR_NOT_IMPLEMENTED = 0x8002b240,
CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION = 0x8002b241, CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION = 0x8002b241,

View file

@ -5,232 +5,264 @@
class PPCDisAsm : public CPUDisAsm class PPCDisAsm : public CPUDisAsm
{ {
protected: 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; 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)
}
void DisAsm_V3_UIMM(const std::string& 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)); return pos;
} }
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
ensure(std::exchange(last_opcode[pos], c) == ' ' && last_opcode[pos + 1] == ' ');
return pos + 1;
}
usz insert_char_if(std::string_view op, bool insert, char c = '.')
{ {
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2)); return insert_char_if(op.size(), insert, c);
} }
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
{ {
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,v%d", PadOp(), op, v0, v1, v2, v3);
} }
void DisAsm_V2(const std::string& op, u32 v0, u32 v1) void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
{ {
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1)); fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
} }
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm) void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
{ {
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm))); fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
} }
void DisAsm_V1(const std::string& op, u32 v0) void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
{ {
Write(fmt::format("%s v%d", FixOp(op), v0)); fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
} }
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2) void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
{ {
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2)); fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
} }
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc) void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
{ {
Write(fmt::format("%s%s cr%d,f%d,f%d", FixOp(op), (rc ? "." : ""), cr0, f0, f1)); fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
} }
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1) void DisAsm_V1(std::string_view op, u32 v0)
{
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
}
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); 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); 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); 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); 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) 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; 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) 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; 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); 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); 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); 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); 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); 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); 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); 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); 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); 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) 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; 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); 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));
} }
}; };

View file

@ -4,6 +4,8 @@
#include "PPUAnalyser.h" #include "PPUAnalyser.h"
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
#include "util/asm.hpp"
const ppu_decoder<PPUDisAsm> s_ppu_disasm; const ppu_decoder<PPUDisAsm> s_ppu_disasm;
const ppu_decoder<ppu_itype> s_ppu_itype; const ppu_decoder<ppu_itype> s_ppu_itype;
@ -11,16 +13,36 @@ extern const std::unordered_map<u32, std::string_view>& get_exported_function_na
enum class ppu_syscall_code : u64; enum class ppu_syscall_code : u64;
extern std::shared_ptr<CPUDisAsm> make_basic_ppu_disasm()
{
return std::make_shared<PPUDisAsm>(cpu_disasm_mode::normal, vm::g_sudo_addr);
}
u32 PPUDisAsm::disasm(u32 pc) 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; dump_pc = pc;
be_t<u32> op{}; be_t<u32> op{};
std::memcpy(&op, m_offset + pc, 4); std::memcpy(&op, m_offset + pc, 4);
m_op = op; m_op = op;
(this->*(s_ppu_disasm.decode(m_op)))({ m_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; return 4;
} }
@ -32,29 +54,50 @@ u32 PPUDisAsm::disasm(u32 pc)
last_opcode += it->second; last_opcode += it->second;
} }
format_by_mode();
return 4; return 4;
} }
std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const std::pair<const void*, usz> 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<CPUDisAsm> PPUDisAsm::copy_type_erased() const
{
return std::make_unique<PPUDisAsm>(*this);
}
std::pair<PPUDisAsm::const_op, u64> PPUDisAsm::try_get_const_op_gpr_value(u32 reg, u32 pc, u32 TTL) const
{
if (!TTL)
{ {
// Recursion limit (Time To Live)
return {}; return {};
} }
if (pc == umax) if (pc == umax)
{ {
pc = dump_pc; // Default arg: choose pc of previous instruction
}
if (!vm::check_addr(pc, vm::page_executable)) if (dump_pc == 0)
{ {
// Do not underflow
return {}; 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 // 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<const be_t<u32>*>(m_offset + i); const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
const ppu_opcode_t op{ opcode }; const ppu_opcode_t op{ opcode };
@ -73,20 +116,22 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
} }
// Get constant register value // 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*/\ /* 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 {};\ return {};\
}\ }\
\ \
var = value;\ var = value;\
} void() /*<- Require a semicolon*/ } void() /*<- Require a semicolon*/
#define GET_CONST_REG(var, reg) GET_CONST_OP_REG(var, reg, form)
switch (type) switch (type)
{ {
case ppu_itype::ADDI: case ppu_itype::ADDI:
@ -104,7 +149,7 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_ra, op.ra); GET_CONST_REG(reg_ra, op.ra);
} }
return { true, reg_ra + op.simm16 }; return { form, reg_ra + op.simm16 };
} }
case ppu_itype::ADDIS: case ppu_itype::ADDIS:
{ {
@ -120,10 +165,16 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_ra, op.ra); 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: case ppu_itype::ORI:
{ {
if (op.rs == op.ra && !op.uimm16)
{
// NO-OP
break;
}
if (op.ra != reg) if (op.ra != reg)
{ {
// Destination register is not relevant to us // Destination register is not relevant to us
@ -134,9 +185,50 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_rs, op.rs); GET_CONST_REG(reg_rs, op.rs);
return { true, reg_rs | op.uimm16 }; return { form, reg_rs | op.uimm16 };
} }
case ppu_itype::ORIS: 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) if (op.ra != reg)
{ {
@ -147,7 +239,64 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_rs, op.rs); 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: default:
{ {
@ -161,11 +310,45 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
break; break;
} }
} }
if (i == 0)
{
return {};
}
i -= 4;
} }
return {}; 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<u32>(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<const char*, char> get_BC_info(u32 bo, u32 bi) constexpr std::pair<const char*, char> get_BC_info(u32 bo, u32 bi)
{ {
std::pair<const char*, char> info{}; std::pair<const char*, char> info{};
@ -1045,11 +1228,40 @@ void PPUDisAsm::SUBFIC(ppu_opcode_t op)
void PPUDisAsm::CMPLI(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); 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<u32>(value);
}
comment_constant(last_opcode, value);
}
} }
void PPUDisAsm::CMPI(ppu_opcode_t op) void PPUDisAsm::CMPI(ppu_opcode_t op)
{ {
DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.simm16); 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<u32>(value);
}
comment_constant(last_opcode, value);
}
} }
void PPUDisAsm::ADDIC(ppu_opcode_t op) 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) 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; return;
} }
@ -1099,7 +1311,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
if (!inst) 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; return;
} }
@ -1143,11 +1355,11 @@ void PPUDisAsm::SC(ppu_opcode_t op)
// Try to get constant syscall index // Try to get constant syscall index
if (auto [is_const, index] = try_get_const_gpr_value(11); is_const && index < 1024u) 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; return;
} }
Write("sc"); last_opcode += "sc";
} }
void PPUDisAsm::B(ppu_opcode_t op) 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) 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; return;
} }
@ -1196,7 +1408,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
if (bo == 0b10100) if (bo == 0b10100)
{ {
Write(lk ? "blrl" : "blr"); last_opcode += (lk ? "blrl" : "blr");
return; return;
} }
@ -1204,7 +1416,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
if (!inst) 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; return;
} }
@ -1239,7 +1451,7 @@ void PPUDisAsm::CRANDC(ppu_opcode_t op)
void PPUDisAsm::ISYNC(ppu_opcode_t) void PPUDisAsm::ISYNC(ppu_opcode_t)
{ {
Write("isync"); last_opcode += "isync";
} }
void PPUDisAsm::CRXOR(ppu_opcode_t op) void PPUDisAsm::CRXOR(ppu_opcode_t op)
@ -1299,7 +1511,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
if (bo == 0b10100) if (bo == 0b10100)
{ {
Write(lk ? "bctrl" : "bctr"); last_opcode += (lk ? "bctrl" : "bctr");
return; return;
} }
@ -1308,7 +1520,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
if (!inst || inst[1] == 'd') if (!inst || inst[1] == 'd')
{ {
// Invalid or unknown bcctr form // 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; return;
} }
@ -1347,14 +1559,20 @@ void PPUDisAsm::RLWNM(ppu_opcode_t op)
void PPUDisAsm::ORI(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); if (op.uimm16 == 0) return DisAsm_R2("mr", op.ra, op.rs);
DisAsm_R2_IMM("ori", op.ra, op.rs, op.uimm16); 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) 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); DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16);
} }
@ -1898,10 +2116,10 @@ void PPUDisAsm::OR(ppu_opcode_t op)
{ {
switch (op.opcode) switch (op.opcode)
{ {
case 0x7f9ce378: return Write("db8cyc"); case 0x7f9ce378: last_opcode += "db8cyc"; return;
case 0x7fbdeb78: return Write("db10cyc"); case 0x7fbdeb78: last_opcode += "db10cyc"; return;
case 0x7fdef378: return Write("db12cyc"); case 0x7fdef378: last_opcode += "db12cyc"; return;
case 0x7ffffb78: return Write("db16cyc"); case 0x7ffffb78: last_opcode += "db16cyc"; return;
default: DisAsm_R2_RC("mr", op.ra, op.rb, op.rc); 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) 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) void PPUDisAsm::LFDX(ppu_opcode_t op)
@ -2101,7 +2319,7 @@ void PPUDisAsm::LVRXL(ppu_opcode_t op)
void PPUDisAsm::DSS(ppu_opcode_t) void PPUDisAsm::DSS(ppu_opcode_t)
{ {
Write("dss()"); last_opcode += "dss";
} }
void PPUDisAsm::SRAWI(ppu_opcode_t op) void PPUDisAsm::SRAWI(ppu_opcode_t op)
@ -2116,7 +2334,7 @@ void PPUDisAsm::SRADI(ppu_opcode_t op)
void PPUDisAsm::EIEIO(ppu_opcode_t) void PPUDisAsm::EIEIO(ppu_opcode_t)
{ {
Write("eieio"); last_opcode += "eieio";
} }
void PPUDisAsm::STVLXL(ppu_opcode_t op) 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) 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) 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) 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) 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) 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) 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) 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()) 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; return;
} }
} }
Write("?? ??"); last_opcode += "?? ??";
} }

View file

@ -29,265 +29,303 @@ private:
} }
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); 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); 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); 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); 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) 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; 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) 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; 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); 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); 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); 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); 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); 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); 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); 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); 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); 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) 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; 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); 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), 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))); 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: public:
u32 disasm(u32 pc) override; u32 disasm(u32 pc) override;
std::pair<bool, u64> try_get_const_gpr_value(u32 reg, u32 pc = -1) const; std::pair<const void*, usz> get_memory_span() const override;
std::unique_ptr<CPUDisAsm> 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<const_op, u64> try_get_const_op_gpr_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
std::pair<bool, u64> 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<bool, u64> 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 MFVSCR(ppu_opcode_t op);
void MTVSCR(ppu_opcode_t op); void MTVSCR(ppu_opcode_t op);

View file

@ -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) // For the debugger (g_ppu_function_names shouldn't change, string_view should suffice)
extern const std::unordered_map<u32, std::string_view>& get_exported_function_names_as_addr_indexed_map() extern const std::unordered_map<u32, std::string_view>& get_exported_function_names_as_addr_indexed_map()
{ {
static std::unordered_map<u32, std::string_view> res; struct info_t
static u64 update_time = 0; {
std::unordered_map<u32, std::string_view> res;
u64 update_time = 0;
};
static thread_local std::unique_ptr<info_t> info;
if (!info)
{
info = std::make_unique<info_t>();
info->res.reserve(ppu_module_manager::get().size());
}
auto& [res, update_time] = *info;
const auto link = g_fxo->try_get<ppu_linkage_info>(); const auto link = g_fxo->try_get<ppu_linkage_info>();
const auto hle_funcs = g_fxo->try_get<ppu_function_manager>(); const auto hle_funcs = g_fxo->try_get<ppu_function_manager>();
@ -414,7 +427,6 @@ extern const std::unordered_map<u32, std::string_view>& get_exported_function_na
update_time = current_time; update_time = current_time;
res.clear(); res.clear();
res.reserve(ppu_module_manager::get().size());
for (auto& pair : ppu_module_manager::get()) for (auto& pair : ppu_module_manager::get())
{ {

View file

@ -864,6 +864,8 @@ std::string ppu_thread::dump_regs() const
{ {
std::string ret; std::string ret;
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
for (uint i = 0; i < 32; ++i) for (uint i = 0; i < 32; ++i)
{ {
auto reg = gpr[i]; auto reg = gpr[i];
@ -914,7 +916,6 @@ std::string ppu_thread::dump_regs() const
} }
else else
{ {
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
dis_asm.disasm(reg); dis_asm.disasm(reg);
fmt::append(ret, " -> %s", dis_asm.last_opcode); fmt::append(ret, " -> %s", dis_asm.last_opcode);
} }

View file

@ -12,30 +12,58 @@ const spu_decoder<spu_iflag> s_spu_iflag;
u32 SPUDisAsm::disasm(u32 pc) u32 SPUDisAsm::disasm(u32 pc)
{ {
last_opcode.clear();
if (pc < m_start_pc || pc >= SPU_LS_SIZE)
{
return 0;
}
dump_pc = pc; dump_pc = pc;
be_t<u32> op; be_t<u32> op;
std::memcpy(&op, m_offset + pc, 4); std::memcpy(&op, m_offset + pc, 4);
m_op = op; m_op = op;
(this->*(s_spu_disasm.decode(m_op)))({ m_op }); (this->*(s_spu_disasm.decode(m_op)))({ m_op });
format_by_mode();
return 4; return 4;
} }
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const std::pair<const void*, usz> 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<CPUDisAsm> SPUDisAsm::copy_type_erased() const
{
return std::make_unique<SPUDisAsm>(*this);
}
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc, u32 TTL) const
{
if (!TTL)
{ {
// Recursion limit (Time To Live)
return {}; return {};
} }
if (pc == umax) 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) // Scan LS backwards from this instruction (until PC=0)
// Search for the first register modification or branch instruction // Search for the first register modification or branch instruction
for (s32 i = pc - 4; i >= 0; i -= 4) for (s32 i = static_cast<s32>(pc); i >= static_cast<s32>(m_start_pc); i -= 4)
{ {
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i); const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
const spu_opcode_t op0{ opcode }; const spu_opcode_t op0{ opcode };
@ -58,6 +86,21 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
continue; 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); //const auto flag = s_spu_iflag.decode(opcode);
// TODO: It detects spurious register modifications // TODO: It detects spurious register modifications
@ -127,25 +170,13 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
} }
case spu_itype::IOHL: case spu_itype::IOHL:
{ {
// Avoid multi-recursion for now v128 reg_val{};
if (dump_pc != pc)
{
return {};
}
if (i >= 4)
{
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation) // Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
// But don't limit to it // But don't limit to it
const auto [is_const, value] = try_get_const_value(reg, i); GET_CONST_REG(reg_val, op0.rt);
if (is_const) return { true, reg_val | v128::from32p(op0.i16) };
{
return { true, value | v128::from32p(op0.i16) };
}
}
return {};
} }
case spu_itype::STQA: case spu_itype::STQA:
case spu_itype::STQD: case spu_itype::STQD:
@ -156,6 +187,27 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
// Do not modify RT // Do not modify RT
break; 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 {}; 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) 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); const auto [is_const, value] = try_get_const_value(op.rt);
if (is_const) if (is_const)
@ -244,7 +298,7 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
{ {
case MFC_Cmd: 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; return;
} }
case MFC_WrListStallAck: case MFC_WrListStallAck:
@ -252,62 +306,75 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
{ {
const u32 v = value._u32[3]; const u32 v = value._u32[3];
if (v && !(v & (v - 1))) 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 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; return;
} }
case MFC_EAH: 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; return;
} }
case MFC_Size: 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; return;
} }
case MFC_TagID: 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; return;
} }
case MFC_WrTagUpdate: case MFC_WrTagUpdate:
{ {
const auto upd = fmt::format("%s", mfc_tag_update(value._u32[3])); 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; return;
} }
default: 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; return;
} }
} }
} }
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
} }
enum CellError : u32;
void SPUDisAsm::IOHL(spu_opcode_t op) 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); 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 (value == v128::from32p(value._u32[0]))
{
DisAsm("iohl", spu_reg_name[op.rt], fmt::format("%s #%s", SignedHex(+op.i16), (value._u32[0] | op.i16)).c_str());
return;
}
}
DisAsm("iohl", spu_reg_name[op.rt], op.i16); // Only print constant for a 4 equal 32-bit constants array
if (is_const && value == v128::from32p(val0))
{
// Fixup value
val0 |= op.i16;
// Test if potentially a CELL error
if ((val0 >> 28) == 0x8u)
{
// 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);
}
}
} }
void SPUDisAsm::SHUFB(spu_opcode_t op) 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); const auto [is_const, value] = try_get_const_value(op.rc);
if (is_const) 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)) if ((size >= 4u && !src) || (size == 2u && src == 1u) || (size == 1u && src == 3u))
{ {
// Comment insertion pattern for CWD-alike instruction // 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; return;
} }
// Comment insertion pattern for unknown instruction formations // 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; return;
} }
} }
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
} }

View file

@ -69,15 +69,24 @@ static constexpr const char* spu_ch_name[128] =
"ch121", "ch122", "ch123", "ch124", "ch125", "ch126", "ch127", "ch121", "ch122", "ch123", "ch124", "ch125", "ch126", "ch127",
}; };
namespace utils
{
class shm;
}
class SPUDisAsm final : public PPCDisAsm class SPUDisAsm final : public PPCDisAsm
{ {
std::shared_ptr<utils::shm> m_shm;
public: 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<utils::shm> shm)
{ {
m_shm = std::move(shm);
} }
private: private:
@ -86,84 +95,96 @@ private:
return spu_branch_target(dump_pc, imm); return spu_branch_target(dump_pc, imm);
} }
static const char* BrIndirectSuffix(u32 de) static char BrIndirectSuffix(u32 de)
{ {
switch (de) switch (de)
{ {
case 0b01: return "e"; case 0b01: return 'e';
case 0b10: return "d"; case 0b10: return 'd';
//case 0b11: return "(undef)"; case 0b11: return '!';
default: return ""; default: return '\0';
} }
} }
std::string& FixOp(std::string& op) const
{
if (m_mode != cpu_disasm_mode::normal)
{
op.append(std::max<int>(10 - ::narrow<int>(op.size()), 0), ' ');
}
return op;
}
void DisAsm(const char* 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); 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;
} }
void DisAsm(std::string op, field_de_t de, const char* a1, const char* a2)
fmt::append(last_opcode, "%-*s %s", PadOp(op, c ? 1 : 0), op, a1);
insert_char_if(op, !!c, c);
}
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: public:
u32 disasm(u32 pc) override; u32 disasm(u32 pc) override;
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1) const; std::pair<const void*, usz> get_memory_span() const override;
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
struct insert_mask_info struct insert_mask_info
{ {
@ -992,6 +1013,6 @@ public:
void UNK(spu_opcode_t /*op*/) void UNK(spu_opcode_t /*op*/)
{ {
Write("?? ??"); DisAsm("?? ??");
} }
}; };

View file

@ -3249,7 +3249,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
void spu_recompiler_base::dump(const spu_program& result, std::string& out) void spu_recompiler_base::dump(const spu_program& result, std::string& out)
{ {
SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast<const u8*>(result.data.data()) - result.lower_bound); SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast<const u8*>(result.data.data()), result.lower_bound);
std::string hash; std::string hash;
{ {

View file

@ -1216,6 +1216,8 @@ std::string spu_thread::dump_regs() const
const bool floats_only = debugger_float_mode.load(); 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') for (u32 i = 0; i < 128; i++, ret += '\n')
{ {
fmt::append(ret, "%s: ", spu_reg_name[i]); 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)) if (i3 >= 0x80 && is_exec_code(i3))
{ {
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
dis_asm.disasm(i3); dis_asm.disasm(i3);
fmt::append(ret, " -> %s", dis_asm.last_opcode); fmt::append(ret, " -> %s", dis_asm.last_opcode);
} }

View file

@ -936,9 +936,81 @@ const std::array<std::pair<ppu_function_t, std::string_view>, 1024> g_ppu_syscal
#undef BIND_SYSC #undef BIND_SYSC
#undef NULL_FUNC #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 <u64 EnumMin, typename E>
constexpr auto formatter_of = std::make_pair(EnumMin, &fmt_class_string<E>::format);
const std::map<u64, void(*)(std::string&, u64)> 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<> template<>
void fmt_class_string<CellError>::format(std::string& out, u64 arg) void fmt_class_string<CellError>::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) format_enum(out, arg, [](auto error)
{ {
switch (error) switch (error)

View file

@ -15,20 +15,34 @@ u32 RSXDisAsm::disasm(u32 pc)
{ {
last_opcode.clear(); last_opcode.clear();
u32 addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc); auto try_read_op = [this](u32 pc) -> bool
if (addr == umax) return 0;
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
dump_pc = pc;
if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK)
{ {
if (m_mode == cpu_disasm_mode::list) if (pc < m_start_pc)
{
return false;
}
if (m_offset == vm::g_sudo_addr)
{
// Translation needed
pc = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
if (pc == umax) return false;
}
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + pc);
return true;
};
if (!try_read_op(pc))
{ {
return 0; return 0;
} }
dump_pc = pc;
if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK)
{
if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD) if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)
{ {
u32 jumpAddr = m_op & RSX_METHOD_OLD_JUMP_OFFSET_MASK; 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) else if ((m_op & RSX_METHOD_NOP_MASK) == RSX_METHOD_NOP_CMD)
{ {
if (m_mode == cpu_disasm_mode::list)
{
return 0;
}
u32 i = 1; 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<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc); if (!try_read_op(pc))
if (addr == umax)
{ {
break; break;
} }
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
if ((m_op & RSX_METHOD_NOP_MASK) != RSX_METHOD_NOP_CMD) if ((m_op & RSX_METHOD_NOP_MASK) != RSX_METHOD_NOP_CMD)
{ {
break; 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) for (u32 i = 0; i < (m_mode == cpu_disasm_mode::list ? count : 1); i++, pc += 4)
{ {
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc); if (!try_read_op(pc))
if (addr == umax)
{ {
last_opcode.clear(); last_opcode.clear();
Write("?? ??", -1); Write("?? ??", -1);
return 4; return 4;
} }
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
const u32 id = id_start + (non_inc ? 0 : i); const u32 id = id_start + (non_inc ? 0 : i);
if (rsx::methods[id] == &rsx::invalid_method) if (rsx::methods[id] == &rsx::invalid_method)
@ -134,6 +135,16 @@ u32 RSXDisAsm::disasm(u32 pc)
} }
} }
std::pair<const void*, usz> RSXDisAsm::get_memory_span() const
{
return {m_offset + m_start_pc, (1ull << 32) - m_start_pc};
}
std::unique_ptr<CPUDisAsm> RSXDisAsm::copy_type_erased() const
{
return std::make_unique<RSXDisAsm>(*this);
}
void RSXDisAsm::Write(const std::string& str, s32 count, bool is_non_inc, u32 id) void RSXDisAsm::Write(const std::string& str, s32 count, bool is_non_inc, u32 id)
{ {
switch (m_mode) switch (m_mode)

View file

@ -5,7 +5,7 @@
class RSXDisAsm final : public CPUDisAsm class RSXDisAsm final : public CPUDisAsm
{ {
public: 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: public:
u32 disasm(u32 pc) override; u32 disasm(u32 pc) override;
std::pair<const void*, usz> get_memory_span() const override;
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
}; };

View file

@ -2,6 +2,7 @@
#include "register_editor_dialog.h" #include "register_editor_dialog.h"
#include "instruction_editor_dialog.h" #include "instruction_editor_dialog.h"
#include "memory_viewer_panel.h" #include "memory_viewer_panel.h"
#include "memory_string_searcher.h"
#include "gui_settings.h" #include "gui_settings.h"
#include "debugger_list.h" #include "debugger_list.h"
#include "breakpoint_list.h" #include "breakpoint_list.h"
@ -306,6 +307,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
QLabel* l = new QLabel(tr( QLabel* l = new QLabel(tr(
"Keys Ctrl+G: Go to typed address." "Keys Ctrl+G: Go to typed address."
"\nKeys Ctrl+B: Open breakpoints settings." "\nKeys Ctrl+B: Open breakpoints settings."
"\nKeys Ctrl+S: Search memory string utility."
"\nKeys Alt+S: Capture SPU images of selected SPU." "\nKeys Alt+S: Capture SPU images of selected SPU."
"\nKey D: SPU MFC commands logger, MFC debug setting must be enabled." "\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." "\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(); open_breakpoints_settings();
return; 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<SPUDisAsm*>(m_disasm.get())->set_shm(static_cast<const spu_thread*>(cpu)->shm);
}
idm::make<memory_searcher_handle>(this, m_disasm, cpu->id_type() == 2 ? cpu->get_name() : "");
}
return;
}
default: break; default: break;
} }
} }
@ -911,7 +927,7 @@ void debugger_frame::OnSelectUnit()
if (get_cpu()) if (get_cpu())
{ {
m_disasm = std::make_shared<RSXDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr, m_rsx); m_disasm = std::make_shared<RSXDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr, 0, m_rsx);
} }
break; break;

View file

@ -223,7 +223,8 @@ void debugger_list::create_rsx_command_detail(u32 pc, int row)
pc += std::max<u32>(m_disasm->disasm(pc), 4); pc += std::max<u32>(m_disasm->disasm(pc), 4);
} }
RSXDisAsm rsx_dis = CPUDisAsm::copy_and_change_mode(*static_cast<RSXDisAsm*>(m_disasm), cpu_disasm_mode::list); RSXDisAsm rsx_dis = *static_cast<RSXDisAsm*>(m_disasm);
rsx_dis.change_mode(cpu_disasm_mode::list);
// Either invalid or not a method // Either invalid or not a method
if (rsx_dis.disasm(pc) <= 4) return; if (rsx_dis.disasm(pc) <= 4) return;

View file

@ -63,6 +63,9 @@ LOG_CHANNEL(gui_log, "GUI");
extern atomic_t<bool> g_user_asked_for_frame_capture; extern atomic_t<bool> g_user_asked_for_frame_capture;
class CPUDisAsm;
std::shared_ptr<CPUDisAsm> make_basic_ppu_disasm();
inline std::string sstr(const QString& _in) { return _in.toStdString(); } inline std::string sstr(const QString& _in) { return _in.toStdString(); }
main_window::main_window(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget *parent) main_window::main_window(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget *parent)
@ -2221,8 +2224,8 @@ void main_window::CreateConnects()
connect(ui->toolsStringSearchAct, &QAction::triggered, this, [this] connect(ui->toolsStringSearchAct, &QAction::triggered, this, [this]
{ {
memory_string_searcher* mss = new memory_string_searcher(this); if (!Emu.IsStopped())
mss->show(); idm::make<memory_searcher_handle>(this, make_basic_ppu_disasm());
}); });
connect(ui->toolsDecryptSprxLibsAct, &QAction::triggered, this, &main_window::DecryptSPRXLibraries); connect(ui->toolsDecryptSprxLibsAct, &QAction::triggered, this, &main_window::DecryptSPRXLibraries);

View file

@ -1,6 +1,8 @@
#include "memory_string_searcher.h" #include "memory_string_searcher.h"
#include "Emu/Memory/vm.h" #include "Emu/Memory/vm.h"
#include "Emu/Memory/vm_reservation.h" #include "Emu/Memory/vm_reservation.h"
#include "Emu/CPU/CPUDisAsm.h"
#include "Emu/IdManager.h"
#include "Utilities/Thread.h" #include "Utilities/Thread.h"
#include "Utilities/StrUtil.h" #include "Utilities/StrUtil.h"
@ -13,6 +15,7 @@
#include <QCheckBox> #include <QCheckBox>
#include <charconv> #include <charconv>
#include <unordered_map>
#include "util/logs.hpp" #include "util/logs.hpp"
#include "util/sysinfo.hpp" #include "util/sysinfo.hpp"
@ -26,15 +29,28 @@ enum : int
as_hex, as_hex,
as_f64, as_f64,
as_f32, as_f32,
as_inst,
}; };
memory_string_searcher::memory_string_searcher(QWidget* parent) memory_string_searcher::memory_string_searcher(QWidget* parent, std::shared_ptr<CPUDisAsm> disasm, std::string_view title)
: QDialog(parent) : QDialog(parent)
, m_disasm(std::move(disasm))
{ {
if (title.empty())
{
setWindowTitle(tr("String Searcher")); setWindowTitle(tr("String Searcher"));
}
else
{
setWindowTitle(tr("String Searcher Of %1").arg(title.data()));
}
setObjectName("memory_string_searcher"); setObjectName("memory_string_searcher");
setAttribute(Qt::WA_DeleteOnClose); 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 = 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->setFixedWidth(QLabel("This is the very length of the lineedit due to hidpi reasons.").sizeHint().width());
m_addr_line->setPlaceholderText(tr("Search...")); 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("HEX bytes/integer", QVariant::fromValue(+as_hex));
m_cbox_input_mode->addItem("Double", QVariant::fromValue(+as_f64)); 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("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." 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." "\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." "\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(); QHBoxLayout* hbox_panel = new QHBoxLayout();
hbox_panel->addWidget(m_addr_line); 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); connect(button_search, &QAbstractButton::clicked, this, &memory_string_searcher::OnSearch);
layout()->setSizeConstraint(QLayout::SetFixedSize); 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<memory_searcher_handle>(id);
});
} }
void memory_string_searcher::OnSearch() void memory_string_searcher::OnSearch()
@ -97,6 +124,7 @@ void memory_string_searcher::OnSearch()
switch (mode) switch (mode)
{ {
case as_inst:
case as_string: case as_string:
{ {
case_insensitive = m_chkbox_case_insensitive->isChecked(); 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) 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'.", gui_log.error("String '%s' cannot be interpreted as hexadecimal byte string due to unknown character '%c'.",
m_addr_line->text().toStdString(), std::string_view{&wstr[pos], 1}); m_addr_line->text().toStdString(), wstr[pos]);
return; return;
} }
@ -189,8 +217,8 @@ void memory_string_searcher::OnSearch()
atomic_t<u32> found = 0; atomic_t<u32> found = 0;
atomic_t<u32> avail_addr = 0; atomic_t<u32> avail_addr = 0;
// There's no need for so many threads // There's no need for so many threads (except for instructions searching)
const u32 max_threads = utils::aligned_div(utils::get_thread_count(), 2); const u32 max_threads = utils::aligned_div(utils::get_thread_count(), mode != as_inst ? 2 : 1);
static constexpr u32 block_size = 0x2000000; 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, [&]() 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 local_found = 0;
u32 addr = 0; u32 addr = 0;
bool ok = false; bool ok = false;
const u64 addr_limit = (m_size >= block_size ? m_size - block_size : 0);
while (true) while (true)
{ {
if (!(addr % block_size)) 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 // Iterate in 32MB blocks
val += block_size; val += block_size;
@ -228,8 +329,14 @@ void memory_string_searcher::OnSearch()
break; break;
} }
if (![&addr = addr]() if (![&]()
{ {
if (m_ptr != vm::g_sudo_addr)
{
// Always valid
return true;
}
// Skip unmapped memory // Skip unmapped memory
for (const u32 end = utils::align(addr + 1, block_size) - 0x1000; !vm::check_addr(addr, 0); addr += 0x1000) 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; continue;
} }
u64 addr_max = addr; const u64 end_mem = std::min<u64>(utils::align<u64>(addr + 1, block_size), m_size);
const u64 end_mem = std::min<u64>(utils::align<u64>(addr + 1, block_size) + 0x1000, u32{umax}); u64 addr_max = m_ptr == vm::g_sudo_addr ? addr : end_mem;
// Determine allocation size quickly // Determine allocation size quickly
while (addr_max < end_mem && vm::check_addr(static_cast<u32>(addr_max), vm::page_1m_size)) while (addr_max < end_mem && vm::check_addr(static_cast<u32>(addr_max), vm::page_1m_size))
@ -272,7 +379,12 @@ void memory_string_searcher::OnSearch()
addr_max += 0x1000; addr_max += 0x1000;
} }
std::string_view section{vm::get_super_ptr<const char>(addr), addr_max - addr}; auto get_ptr = [&](u32 address)
{
return static_cast<const char*>(m_ptr) + address;
};
std::string_view section{get_ptr(addr), addr_max - addr};
usz first_char = 0; usz first_char = 0;
@ -282,12 +394,12 @@ void memory_string_searcher::OnSearch()
{ {
const u32 start = addr + first_char; const u32 start = addr + first_char;
std::string_view test_sv{vm::get_super_ptr<const char>(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 // 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()]); })) 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++; local_found++;
} }
@ -301,7 +413,7 @@ void memory_string_searcher::OnSearch()
{ {
const u32 start = addr + first_char; const u32 start = addr + first_char;
if (std::string_view{vm::get_super_ptr<const char>(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); gui_log.success("Found at 0x%08x", start);
local_found++; local_found++;
@ -311,10 +423,13 @@ void memory_string_searcher::OnSearch()
} }
} }
addr = static_cast<u32>(std::min<u64>(end_mem - 0x1000, addr_max));
// Check if at last page // Check if at last page
if (addr_max >= 0u - 0x1000) break; if (addr_max >= m_size - 0x1000)
{
break;
}
addr = addr_max;
} }
found += local_found; found += local_found;

View file

@ -1,11 +1,15 @@
#pragma once #pragma once
#include "util/types.hpp"
#include <QDialog> #include <QDialog>
class QLineEdit; class QLineEdit;
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
class CPUDisAsm;
class memory_string_searcher : public QDialog class memory_string_searcher : public QDialog
{ {
Q_OBJECT Q_OBJECT
@ -14,9 +18,33 @@ class memory_string_searcher : public QDialog
QCheckBox* m_chkbox_case_insensitive = nullptr; QCheckBox* m_chkbox_case_insensitive = nullptr;
QComboBox* m_cbox_input_mode = nullptr; QComboBox* m_cbox_input_mode = nullptr;
std::shared_ptr<CPUDisAsm> m_disasm;
const void* m_ptr;
usz m_size;
public: public:
explicit memory_string_searcher(QWidget* parent); explicit memory_string_searcher(QWidget* parent, std::shared_ptr<CPUDisAsm> disasm, std::string_view title = {});
private Q_SLOTS: private Q_SLOTS:
void OnSearch(); 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 <typename... Args> requires (std::is_constructible_v<memory_string_searcher, Args&&...>)
memory_searcher_handle(Args&&... args)
: m_mss(new memory_string_searcher(std::forward<Args>(args)...))
{
}
~memory_searcher_handle() { m_mss->close(); m_mss->deleteLater(); }
private:
const std::add_pointer_t<memory_string_searcher> m_mss;
};

View file

@ -87,7 +87,7 @@ struct memory_viewer_handle
static constexpr u32 id_step = 1; static constexpr u32 id_step = 1;
static constexpr u32 id_count = 2048; static constexpr u32 id_count = 2048;
template <typename... Args> template <typename... Args> requires (std::is_constructible_v<memory_viewer_panel, Args&&...>)
memory_viewer_handle(Args&&... args) memory_viewer_handle(Args&&... args)
: m_mvp(new memory_viewer_panel(std::forward<Args>(args)...)) : m_mvp(new memory_viewer_panel(std::forward<Args>(args)...))
{ {