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
{
protected:
const cpu_disasm_mode m_mode{};
cpu_disasm_mode m_mode{};
const std::add_pointer_t<const u8> m_offset{};
const u32 m_start_pc;
const std::add_pointer_t<const cpu_thread> m_cpu{};
u32 m_op = 0;
void Write(const std::string& value)
void format_by_mode()
{
switch (m_mode)
{
@ -32,28 +33,27 @@ protected:
static_cast<u8>(m_op >> 24),
static_cast<u8>(m_op >> 16),
static_cast<u8>(m_op >> 8),
static_cast<u8>(m_op >> 0), value);
static_cast<u8>(m_op >> 0), last_opcode);
break;
}
case cpu_disasm_mode::interpreter:
{
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
last_opcode.insert(0, fmt::format("[%08x] %02x %02x %02x %02x: ", dump_pc,
static_cast<u8>(m_op >> 24),
static_cast<u8>(m_op >> 16),
static_cast<u8>(m_op >> 8),
static_cast<u8>(m_op >> 0), value);
static_cast<u8>(m_op >> 0)));
break;
}
case cpu_disasm_mode::compiler_elf:
{
last_opcode = value + '\n';
last_opcode += '\n';
break;
}
case cpu_disasm_mode::normal:
{
last_opcode = value;
break;
}
default: fmt::throw_exception("Unreachable");
@ -64,26 +64,23 @@ public:
std::string last_opcode{};
u32 dump_pc{};
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0>
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
CPUDisAsm& change_mode(cpu_disasm_mode mode)
{
return T{mode, dis.m_offset, dis.m_cpu};
m_mode = mode;
return *this;
}
protected:
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0, const cpu_thread* cpu = nullptr)
: m_mode(mode)
, m_offset(offset)
, m_offset(offset - start_pc)
, m_start_pc(start_pc)
, m_cpu(cpu)
{
}
CPUDisAsm(const CPUDisAsm&) = delete;
CPUDisAsm& operator=(const CPUDisAsm&) = delete;
virtual ~CPUDisAsm() = default;
virtual u32 DisAsmBranchTarget(s32 /*imm*/);
// TODO: Add builtin fmt helpper for best performance
@ -109,16 +106,17 @@ protected:
return fmt::format("%s%s", v < 0 ? "-" : "", av);
}
std::string FixOp(std::string op) const
// Signify the formatting function the minimum required amount of characters to print for an instruction
// Padding with spaces
int PadOp(std::string_view op = {}, int min_spaces = 0) const
{
if (m_mode != cpu_disasm_mode::normal)
{
op.resize(std::max<usz>(op.length(), 10), ' ');
}
return op;
return m_mode == cpu_disasm_mode::normal ? (static_cast<int>(op.size()) + min_spaces) : 10;
}
public:
virtual ~CPUDisAsm() = default;
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"
// Error codes
enum CellAudioInError
enum CellAudioInError : u32
{
CELL_AUDIO_IN_ERROR_NOT_IMPLEMENTED = 0x8002b260,
CELL_AUDIO_IN_ERROR_ILLEGAL_CONFIGURATION = 0x8002b261,

View File

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

View File

@ -5,232 +5,264 @@
class PPCDisAsm : public CPUDisAsm
{
protected:
PPCDisAsm(cpu_disasm_mode mode, const u8* offset) : CPUDisAsm(mode, offset)
PPCDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : CPUDisAsm(mode, offset, start_pc)
{
}
virtual u32 DisAsmBranchTarget(const s32 imm) override = 0;
void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3)
usz insert_char_if(usz pos, bool insert, char c)
{
Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3));
if (!insert)
{
return pos;
}
ensure(std::exchange(last_opcode[pos], c) == ' ' && last_opcode[pos + 1] == ' ');
return pos + 1;
}
void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm)
usz insert_char_if(std::string_view op, bool insert, char c = '.')
{
Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm));
return insert_char_if(op.size(), insert, c);
}
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
{
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3);
}
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
{
Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
}
void DisAsm_V2(const std::string& op, u32 v0, u32 v1)
void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
{
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
}
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm)
void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
{
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm)));
fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
}
void DisAsm_V1(const std::string& op, u32 v0)
void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
{
Write(fmt::format("%s v%d", FixOp(op), v0));
fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
}
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2)
void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
{
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2));
fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
}
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc)
void DisAsm_V1(std::string_view op, u32 v0)
{
Write(fmt::format("%s%s cr%d,f%d,f%d", FixOp(op), (rc ? "." : ""), cr0, f0, f1));
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
}
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1)
void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2)
{
fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2);
}
void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc)
{
fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1)
{
DisAsm_CR1_F2_RC(op, cr0, f0, f1, false);
}
void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1)
void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1)
{
Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1));
fmt::append(last_opcode, "%-*s %d,r%d,r%d", PadOp(), op, i0, r0, r1);
}
void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0)
void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0)
{
Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s %d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0));
}
void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc)
void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc)
{
Write(fmt::format("%s%s %d,r%d", FixOp(op), (rc ? "." : ""), i0, r0));
fmt::append(last_opcode, "%-*s %d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0);
insert_char_if(op, !!rc);
}
void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0)
void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0)
{
DisAsm_INT1_R1_RC(op, i0, r0, false);
}
void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
{
Write(fmt::format("%s%s f%d,f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2, f3));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3);
insert_char_if(op, !!rc);
}
void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc)
void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc)
{
Write(fmt::format("%s%s f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2);
insert_char_if(op, !!rc);
}
void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2)
void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2)
{
DisAsm_F3_RC(op, f0, f1, f2, false);
}
void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc)
void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc)
{
Write(fmt::format("%s%s f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1));
fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_F2(const std::string& op, u32 f0, u32 f1)
void DisAsm_F2(std::string_view op, u32 f0, u32 f1)
{
DisAsm_F2_RC(op, f0, f1, false);
}
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1)
{
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1);
return;
}
Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1);
}
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc)
{
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s%s f%d,r%d,%s", FixOp(op), (rc ? "." : ""), f0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0));
insert_char_if(op, !!rc);
return;
}
Write(fmt::format("%s%s f%d,%s(r%d)", FixOp(op), (rc ? "." : ""), f0, SignedHex(imm0), r0));
fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0);
insert_char_if(op, !!rc);
}
void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0)
void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0)
{
DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false);
}
void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc)
void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc)
{
Write(fmt::format("%s%s f%d", FixOp(op), (rc ? "." : ""), f0));
fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0);
insert_char_if(op, !!rc);
}
void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc)
void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc)
{
Write(fmt::format("%s%s r%d", FixOp(op), (rc ? "." : ""), r0));
fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0);
insert_char_if(op, !!rc);
}
void DisAsm_R1(const std::string& op, u32 r0)
void DisAsm_R1(std::string_view op, u32 r0)
{
DisAsm_R1_RC(op, r0, false);
}
void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 oe, u32 rc)
void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 oe, u32 rc)
{
Write(fmt::format("%s%s%s r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1));
fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1);
insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.');
}
void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc)
void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc)
{
DisAsm_R2_OE_RC(op, r0, r1, false, rc);
}
void DisAsm_R2(const std::string& op, u32 r0, u32 r1)
void DisAsm_R2(std::string_view op, u32 r0, u32 r1)
{
DisAsm_R2_RC(op, r0, r1, false);
}
void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc)
void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc)
{
Write(fmt::format("%s%s%s r%d,r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1, r2));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1, r2);
insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.');
}
void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, r2, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc)
void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc)
{
DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc);
}
void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2)
void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2)
{
DisAsm_R3_RC(op, r0, r1, r2, false);
}
void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1, i2));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
{
DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false);
}
void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1)
void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1)
{
DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false);
}
void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc)
void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0));
fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0)
void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0)
{
DisAsm_R2_INT1_RC(op, r0, r1, i0, false);
}
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0)
{
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0));
return;
}
Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1));
fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1);
}
void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0)
void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0)
{
Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0));
}
void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0)
void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0)
{
Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0));
fmt::append(last_opcode, "%-*s %d,r%d #%x", PadOp(), op, imm0, r0, imm0);
}
void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0)
void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0)
{
Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0));
}
void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc)
void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc)
{
Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1));
fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1)
void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1)
{
DisAsm_CR1_R2_RC(op, cr0, r0, r1, false);
}
void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1)
void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1)
{
Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1));
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
}
void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2)
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
{
Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2));
fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2);
}
void DisAsm_INT1(const std::string& op, const int i0)
void DisAsm_INT1(std::string_view op, const int i0)
{
Write(fmt::format("%s %d", FixOp(op), i0));
fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0);
}
void DisAsm_BRANCH(const std::string& op, const int pc)
void DisAsm_BRANCH(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
}
void DisAsm_BRANCH_A(const std::string& op, const int pc)
void DisAsm_BRANCH_A(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), pc));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
}
void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc)
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
{
Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
}
void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc)
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
{
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
}
};

View File

@ -4,6 +4,8 @@
#include "PPUAnalyser.h"
#include "Emu/IdManager.h"
#include "util/asm.hpp"
const ppu_decoder<PPUDisAsm> s_ppu_disasm;
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;
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)
{
last_opcode.clear();
if (pc < m_start_pc)
{
return 0;
}
if (m_offset == vm::g_sudo_addr && !vm::check_addr(pc, vm::page_executable))
{
return 0;
}
dump_pc = pc;
be_t<u32> op{};
std::memcpy(&op, m_offset + pc, 4);
m_op = op;
(this->*(s_ppu_disasm.decode(m_op)))({ m_op });
if (m_mode != cpu_disasm_mode::interpreter && m_mode != cpu_disasm_mode::normal)
if (m_offset != vm::g_sudo_addr)
{
// Exported functions lookup is not allowed in this case
format_by_mode();
return 4;
}
@ -32,29 +54,50 @@ u32 PPUDisAsm::disasm(u32 pc)
last_opcode += it->second;
}
format_by_mode();
return 4;
}
std::pair<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 {};
}
if (pc == umax)
{
pc = dump_pc;
// Default arg: choose pc of previous instruction
if (dump_pc == 0)
{
// Do not underflow
return {};
}
pc = dump_pc - 4;
}
if (!vm::check_addr(pc, vm::page_executable))
{
return {};
}
#if __cpp_using_enum >= 201907
using enum const_op;
#else
constexpr const_op none = const_op::none, form = const_op::form, xor_mask = const_op::xor_mask;
#endif
// Scan PPU executable memory backwards until unmapped or non-executable memory block is encountered
for (u32 i = pc - 4; vm::check_addr(i, vm::page_executable); i -= 4)
for (u32 i = pc; i >= m_start_pc && (m_offset != vm::g_sudo_addr || vm::check_addr(i, vm::page_executable));)
{
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
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
#define GET_CONST_REG(var, reg) \
#define GET_CONST_OP_REG(var, reg, op) \
{\
/* Search for the constant value of the register*/\
const auto [is_const, value] = try_get_const_gpr_value(reg, i);\
const auto [const_op, value] = try_get_const_op_gpr_value(reg, i - 4, TTL - 1);\
\
if (!is_const)\
if (const_op != const_op::op)\
{\
/* Cannot compute constant value if register is not constant*/\
/* Cannot compute constant value if register/operation is not constant*/\
return {};\
}\
\
var = value;\
} void() /*<- Require a semicolon*/
#define GET_CONST_REG(var, reg) GET_CONST_OP_REG(var, reg, form)
switch (type)
{
case ppu_itype::ADDI:
@ -104,7 +149,7 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_ra, op.ra);
}
return { true, reg_ra + op.simm16 };
return { form, reg_ra + op.simm16 };
}
case ppu_itype::ADDIS:
{
@ -120,10 +165,16 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_ra, op.ra);
}
return { true, reg_ra + op.simm16 * 65536 };
return { form, reg_ra + op.simm16 * 65536 };
}
case ppu_itype::ORI:
{
if (op.rs == op.ra && !op.uimm16)
{
// NO-OP
break;
}
if (op.ra != reg)
{
// Destination register is not relevant to us
@ -134,9 +185,50 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_rs, op.rs);
return { true, reg_rs | op.uimm16 };
return { form, reg_rs | op.uimm16 };
}
case ppu_itype::ORIS:
{
if (op.rs == op.ra && !op.uimm16)
{
// NO-OP
break;
}
if (op.ra != reg)
{
break;
}
u64 reg_rs = 0;
GET_CONST_REG(reg_rs, op.rs);
return { form, reg_rs | (u64{op.uimm16} << 16)};
}
case ppu_itype::XORIS:
{
if (op.ra != reg)
{
break;
}
const auto [const_op, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1);
if (const_op == none)
{
return { xor_mask, (u64{op.uimm16} << 16) };
}
if (const_op != form)
{
// Unexpected
return {};
}
return { form, reg_rs ^ (u64{op.uimm16} << 16)};
}
case ppu_itype::RLDICR:
{
if (op.ra != reg)
{
@ -147,7 +239,64 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
GET_CONST_REG(reg_rs, op.rs);
return { true, reg_rs | (u64{op.uimm16} << 16)};
return { form, utils::rol64(reg_rs, op.sh64) & (~0ull << (op.mbe64 ^ 63)) };
}
case ppu_itype::OR:
{
if (op.rs == op.rb && op.rs == op.ra)
{
// NO-OP
break;
}
if (op.ra != reg)
{
break;
}
u64 reg_rs = 0, reg_rb = 0;
GET_CONST_REG(reg_rs, op.rs);
// Try to optimize if it's a register move operation
if (op.rs != op.rb)
{
GET_CONST_REG(reg_rb, op.rb);
}
return { form, reg_rs | reg_rb };
}
case ppu_itype::XOR:
{
if (op.ra != reg)
{
break;
}
if (op.rs == op.rb)
{
return { form, 0 };
}
const auto [const_op_rs, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1);
const auto [const_op_rb, reg_rb] = try_get_const_op_gpr_value(op.rb, i - 4, TTL - 1);
if (const_op_rs == form && const_op_rb == form)
{
// Normally it is not the case
return { form, reg_rs ^ reg_rb };
}
if (const_op_rs == form && const_op_rb == none)
{
return { xor_mask, reg_rs };
}
else if (const_op_rb == form && const_op_rs == none)
{
return { xor_mask, reg_rb };
}
return {};
}
default:
{
@ -161,11 +310,45 @@ std::pair<bool, u64> PPUDisAsm::try_get_const_gpr_value(u32 reg, u32 pc) const
break;
}
}
if (i == 0)
{
return {};
}
i -= 4;
}
return {};
}
enum CellError : u32;
void comment_constant(std::string& last_opcode, u64 value)
{
// Test if potentially a CELL error
if ((value >> 28) == 0xf'ffff'fff8u || (value >> 28) == 0x8u)
{
const usz old_size = last_opcode.size();
// Comment as CELL error
fmt::append(last_opcode, " #%s (0x%x)", CellError{static_cast<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)
{
std::pair<const char*, char> info{};
@ -1045,11 +1228,40 @@ void PPUDisAsm::SUBFIC(ppu_opcode_t op)
void PPUDisAsm::CMPLI(ppu_opcode_t op)
{
DisAsm_CR1_R1_IMM(op.l10 ? "cmpldi" : "cmplwi", op.crfd, op.ra, op.uimm16);
// Try to obtain the true constant value we are comparing against, comment on success
// Upper 16/48 bits of it
if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF))
{
// Fixup value (merge the lower 16-bits of that value)
value |= op.uimm16;
if (!op.l10)
{
value = static_cast<u32>(value);
}
comment_constant(last_opcode, value);
}
}
void PPUDisAsm::CMPI(ppu_opcode_t op)
{
DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.simm16);
// See CMPLI
if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF))
{
// Signed fixup
value ^= s64{op.simm16};
if (!op.l10)
{
value = static_cast<u32>(value);
}
comment_constant(last_opcode, value);
}
}
void PPUDisAsm::ADDIC(ppu_opcode_t op)
@ -1091,7 +1303,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk);
return;
}
@ -1099,7 +1311,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
if (!inst)
{
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk);
return;
}
@ -1143,11 +1355,11 @@ void PPUDisAsm::SC(ppu_opcode_t op)
// Try to get constant syscall index
if (auto [is_const, index] = try_get_const_gpr_value(11); is_const && index < 1024u)
{
Write(fmt::format("sc #%s", ppu_syscall_code{index}));
fmt::append(last_opcode, "%-*s #%s", PadOp(), ppu_syscall_code{index});
return;
}
Write("sc");
last_opcode += "sc";
}
void PPUDisAsm::B(ppu_opcode_t op)
@ -1158,7 +1370,7 @@ void PPUDisAsm::B(ppu_opcode_t op)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("b 0x%x, %d, %d", li, aa, lk));
fmt::append(last_opcode, "b 0x%x, %d, %d", li, aa, lk);
return;
}
@ -1196,7 +1408,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
if (bo == 0b10100)
{
Write(lk ? "blrl" : "blr");
last_opcode += (lk ? "blrl" : "blr");
return;
}
@ -1204,7 +1416,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
if (!inst)
{
Write(fmt::format("bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
fmt::append(last_opcode, "bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk);
return;
}
@ -1239,7 +1451,7 @@ void PPUDisAsm::CRANDC(ppu_opcode_t op)
void PPUDisAsm::ISYNC(ppu_opcode_t)
{
Write("isync");
last_opcode += "isync";
}
void PPUDisAsm::CRXOR(ppu_opcode_t op)
@ -1299,7 +1511,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
if (bo == 0b10100)
{
Write(lk ? "bctrl" : "bctr");
last_opcode += (lk ? "bctrl" : "bctr");
return;
}
@ -1308,7 +1520,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
if (!inst || inst[1] == 'd')
{
// Invalid or unknown bcctr form
Write(fmt::format("bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
fmt::append(last_opcode, "bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk);
return;
}
@ -1347,14 +1559,20 @@ void PPUDisAsm::RLWNM(ppu_opcode_t op)
void PPUDisAsm::ORI(ppu_opcode_t op)
{
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; }
if (op.uimm16 == 0) return DisAsm_R2("mr", op.ra, op.rs);
DisAsm_R2_IMM("ori", op.ra, op.rs, op.uimm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value | op.uimm16);
}
}
void PPUDisAsm::ORIS(ppu_opcode_t op)
{
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; }
DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16);
}
@ -1898,10 +2116,10 @@ void PPUDisAsm::OR(ppu_opcode_t op)
{
switch (op.opcode)
{
case 0x7f9ce378: return Write("db8cyc");
case 0x7fbdeb78: return Write("db10cyc");
case 0x7fdef378: return Write("db12cyc");
case 0x7ffffb78: return Write("db16cyc");
case 0x7f9ce378: last_opcode += "db8cyc"; return;
case 0x7fbdeb78: last_opcode += "db10cyc"; return;
case 0x7fdef378: last_opcode += "db12cyc"; return;
case 0x7ffffb78: last_opcode += "db16cyc"; return;
default: DisAsm_R2_RC("mr", op.ra, op.rb, op.rc);
}
}
@ -2011,7 +2229,7 @@ void PPUDisAsm::LFSUX(ppu_opcode_t op)
void PPUDisAsm::SYNC(ppu_opcode_t op)
{
Write(op.l10 ? "lwsync" : "sync");
last_opcode += (op.l10 ? "lwsync" : "sync");
}
void PPUDisAsm::LFDX(ppu_opcode_t op)
@ -2101,7 +2319,7 @@ void PPUDisAsm::LVRXL(ppu_opcode_t op)
void PPUDisAsm::DSS(ppu_opcode_t)
{
Write("dss()");
last_opcode += "dss";
}
void PPUDisAsm::SRAWI(ppu_opcode_t op)
@ -2116,7 +2334,7 @@ void PPUDisAsm::SRADI(ppu_opcode_t op)
void PPUDisAsm::EIEIO(ppu_opcode_t)
{
Write("eieio");
last_opcode += "eieio";
}
void PPUDisAsm::STVLXL(ppu_opcode_t op)
@ -2361,7 +2579,7 @@ void PPUDisAsm::FNMADDS(ppu_opcode_t op)
void PPUDisAsm::MTFSB1(ppu_opcode_t op)
{
Write(fmt::format("mtfsb1%s %d", op.rc ? "." : "", op.crbd));
fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb1." : "mtfsb1", op.crbd);
}
void PPUDisAsm::MCRFS(ppu_opcode_t op)
@ -2371,12 +2589,12 @@ void PPUDisAsm::MCRFS(ppu_opcode_t op)
void PPUDisAsm::MTFSB0(ppu_opcode_t op)
{
Write(fmt::format("mtfsb0%s %d", op.rc ? "." : "", op.crbd));
fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb0." : "mtfsb0", op.crbd);
}
void PPUDisAsm::MTFSFI(ppu_opcode_t op)
{
Write(fmt::format("mtfsfi%s cr%d,%d,%d", op.rc ? "." : "", op.crfd, op.i, op.l15));
fmt::append(last_opcode, "%-*s cr%d,%d,%d", PadOp(), op.rc ? "mtfsfi." : "mtfsfi", op.crfd, op.i, op.l15);
}
void PPUDisAsm::MFFS(ppu_opcode_t op)
@ -2386,7 +2604,7 @@ void PPUDisAsm::MFFS(ppu_opcode_t op)
void PPUDisAsm::MTFSF(ppu_opcode_t op)
{
Write(fmt::format("mtfsf%s %d,f%d,%d,%d", op.rc ? "." : "", op.rc, op.flm, op.frb, op.l6, op.l15));
fmt::append(last_opcode, "%-*s %d,f%d,%d,%d", PadOp(), op.rc ? "mtfsf." : "mtfsf", op.rc, op.flm, op.frb, op.l6, op.l15);
}
void PPUDisAsm::FCMPU(ppu_opcode_t op)
@ -2515,10 +2733,10 @@ void PPUDisAsm::UNK(ppu_opcode_t)
if (dump_pc % 8 == 4 && index < ppu_function_manager::get().size())
{
Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index));
fmt::append(last_opcode, "Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index);
return;
}
}
Write("?? ??");
last_opcode += "?? ??";
}

View File

@ -29,265 +29,303 @@ private:
}
private:
void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3)
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
{
Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3);
}
void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm)
void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
{
Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
}
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
{
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
}
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
{
Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm));
fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
}
void DisAsm_V2(const std::string& op, u32 v0, u32 v1)
void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
{
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1));
fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
}
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm)
void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
{
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm)));
fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
}
void DisAsm_V1(const std::string& op, u32 v0)
void DisAsm_V1(std::string_view op, u32 v0)
{
Write(fmt::format("%s v%d", FixOp(op), v0));
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
}
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2)
void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2)
{
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2));
fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2);
}
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc)
void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc)
{
Write(fmt::format("%s cr%d,f%d,f%d", FixOp(op + (rc ? "." : "")), cr0, f0, f1));
fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1)
void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1)
{
DisAsm_CR1_F2_RC(op, cr0, f0, f1, false);
}
void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1)
void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1)
{
Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1));
fmt::append(last_opcode, "%-*s%d,r%d,r%d", PadOp(), op, i0, r0, r1);
}
void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0)
void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0)
{
Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s%d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0));
}
void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc)
void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc)
{
Write(fmt::format("%s %d,r%d", FixOp(op + (rc ? "." : "")), i0, r0));
fmt::append(last_opcode, "%-*s%d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0);
insert_char_if(op, !!rc);
}
void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0)
void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0)
{
DisAsm_INT1_R1_RC(op, i0, r0, false);
}
void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
{
Write(fmt::format("%s f%d,f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2, f3));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3);
insert_char_if(op, !!rc);
}
void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc)
void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc)
{
Write(fmt::format("%s f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2);
insert_char_if(op, !!rc);
}
void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2)
void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2)
{
DisAsm_F3_RC(op, f0, f1, f2, false);
}
void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc)
void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc)
{
Write(fmt::format("%s f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1));
fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_F2(const std::string& op, u32 f0, u32 f1)
void DisAsm_F2(std::string_view op, u32 f0, u32 f1)
{
DisAsm_F2_RC(op, f0, f1, false);
}
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1)
{
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1);
return;
}
Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1);
}
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc)
{
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,%s", FixOp(op + (rc ? "." : "")), f0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0));
insert_char_if(op, !!rc);
return;
}
Write(fmt::format("%s f%d,%s(r%d)", FixOp(op + (rc ? "." : "")), f0, SignedHex(imm0), r0));
fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0);
insert_char_if(op, !!rc);
}
void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0)
void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0)
{
DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false);
}
void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc)
void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc)
{
Write(fmt::format("%s f%d", FixOp(op + (rc ? "." : "")), f0));
fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0);
insert_char_if(op, !!rc);
}
void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc)
void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc)
{
Write(fmt::format("%s r%d", FixOp(op + (rc ? "." : "")), r0));
fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0);
insert_char_if(op, !!rc);
}
void DisAsm_R1(const std::string& op, u32 r0)
void DisAsm_R1(std::string_view op, u32 r0)
{
DisAsm_R1_RC(op, r0, false);
}
void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 _oe, u32 rc)
void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 _oe, u32 rc)
{
Write(fmt::format("%s r%d,r%d", FixOp(op + (_oe ? "o" : "") + (rc ? "." : "")), r0, r1));
fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1);
insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.');
}
void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc)
void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc)
{
DisAsm_R2_OE_RC(op, r0, r1, false, rc);
}
void DisAsm_R2(const std::string& op, u32 r0, u32 r1)
void DisAsm_R2(std::string_view op, u32 r0, u32 r1)
{
DisAsm_R2_RC(op, r0, r1, false);
}
void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc)
void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc)
{
Write(fmt::format("%s r%d,r%d,r%d", FixOp(op + (rc ? "." : "") + (_oe ? "o" : "")), r0, r1, r2));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1, r2);
insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.');
}
void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s r%d,r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, r2, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc)
void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc)
{
DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc);
}
void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2)
void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2)
{
DisAsm_R3_RC(op, r0, r1, r2, false);
}
void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
{
Write(fmt::format("%s r%d,r%d,%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1, i2));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
{
DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false);
}
void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1)
void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1)
{
DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false);
}
void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc)
void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc)
{
Write(fmt::format("%s r%d,r%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0));
fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0)
void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0)
{
DisAsm_R2_INT1_RC(op, r0, r1, i0, false);
}
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0)
{
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0));
return;
}
Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1));
fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1);
}
void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0)
void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0)
{
Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0));
}
void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0)
void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0)
{
Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0));
fmt::append(last_opcode, "%-*s%d,r%d #%x", PadOp(), op, imm0, r0, imm0);
}
void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0)
void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0)
{
Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0));
}
void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc)
void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc)
{
Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1));
fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_R1(const std::string& op, u32 cr0, u32 r0)
void DisAsm_CR1_R1(std::string_view op, u32 cr0, u32 r0)
{
Write(fmt::format("%s cr%d,r%d", FixOp(op), cr0, r0));
fmt::append(last_opcode, "%-*s cr%d,r%d", PadOp(), op, cr0, r0);
}
void DisAsm_R1_CR1(const std::string& op, u32 r0, u32 cr0)
void DisAsm_R1_CR1(std::string_view op, u32 r0, u32 cr0)
{
Write(fmt::format("%s r%d,cr%d", FixOp(op), r0, cr0));
fmt::append(last_opcode, "%-*s r%d,cr%d", PadOp(), op, r0, cr0);
}
void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1)
void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1)
{
DisAsm_CR1_R2_RC(op, cr0, r0, r1, false);
}
void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1)
void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1)
{
Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1));
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
}
void DisAsm_BI1(const std::string& op, const int i0)
void DisAsm_BI1(std::string_view op, const int i0)
{
Write(fmt::format("%s cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0)));
fmt::append(last_opcode, "%-*s cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0));
}
void DisAsm_BI2(const std::string& op, const int i0, const int i1)
void DisAsm_BI2(std::string_view op, const int i0, const int i1)
{
Write(fmt::format("%s cr%d[%s],cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1)));
fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1));
}
void DisAsm_BI3(const std::string& op, const int i0, const int i1, const int i2)
void DisAsm_BI3(std::string_view op, const int i0, const int i1, const int i2)
{
Write(fmt::format("%s cr%d[%s],cr%d[%s],cr%d[%s]", FixOp(op),
i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2)));
fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s],cr%d[%s]", PadOp(), op,
i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2));
}
void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2)
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
{
Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2));
fmt::append(last_opcode, "%-*s%d,%d,%d", PadOp(), op, i0, i1, i2);
}
void DisAsm_INT1(const std::string& op, const int i0)
void DisAsm_INT1(std::string_view op, const int i0)
{
Write(fmt::format("%s %d", FixOp(op), i0));
fmt::append(last_opcode, "%-*s%d", PadOp(), op, i0);
}
void DisAsm_BRANCH(const std::string& op, const int pc)
void DisAsm_BRANCH(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
}
void DisAsm_BRANCH_A(const std::string& op, const int pc)
void DisAsm_BRANCH_A(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), pc));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
}
void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc)
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
{
Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s%d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
}
void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc)
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
{
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
}
void DisAsm_CR_BRANCH_A(const std::string& op, u32 cr, const int pc)
void DisAsm_CR_BRANCH_A(std::string_view op, u32 cr, const int pc)
{
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, pc));
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, pc);
}
void DisAsm_BI_BRANCH(const std::string& op, u32 bi, const int pc)
void DisAsm_BI_BRANCH(std::string_view op, u32 bi, const int pc)
{
Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc));
}
void DisAsm_BI_BRANCH_A(const std::string& op, u32 bi, const int pc)
void DisAsm_BI_BRANCH_A(std::string_view op, u32 bi, const int pc)
{
Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), pc));
fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), pc);
}
public:
u32 disasm(u32 pc) override;
std::pair<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 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)
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;
static u64 update_time = 0;
struct info_t
{
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 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;
res.clear();
res.reserve(ppu_module_manager::get().size());
for (auto& pair : ppu_module_manager::get())
{

View File

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

View File

@ -12,30 +12,58 @@ const spu_decoder<spu_iflag> s_spu_iflag;
u32 SPUDisAsm::disasm(u32 pc)
{
last_opcode.clear();
if (pc < m_start_pc || pc >= SPU_LS_SIZE)
{
return 0;
}
dump_pc = pc;
be_t<u32> op;
std::memcpy(&op, m_offset + pc, 4);
m_op = op;
(this->*(s_spu_disasm.decode(m_op)))({ m_op });
format_by_mode();
return 4;
}
std::pair<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 {};
}
if (pc == umax)
{
pc = dump_pc;
// Default arg: choose pc of previous instruction
if (dump_pc == 0)
{
// Do not underflow
return {};
}
pc = dump_pc - 4;
}
// Scan LS backwards from this instruction (until PC=0)
// Search for the first register modification or branch instruction
for (s32 i = pc - 4; i >= 0; i -= 4)
for (s32 i = static_cast<s32>(pc); i >= static_cast<s32>(m_start_pc); i -= 4)
{
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
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;
}
// Get constant register value
#define GET_CONST_REG(var, reg) \
{\
/* Search for the constant value of the register*/\
const auto [is_const, value] = try_get_const_value(reg, i - 4, TTL - 1);\
\
if (!is_const)\
{\
/* Cannot compute constant value if register is not constant*/\
return {};\
}\
\
var = value;\
} void() /*<- Require a semicolon*/
//const auto flag = s_spu_iflag.decode(opcode);
// TODO: It detects spurious register modifications
@ -127,25 +170,13 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
}
case spu_itype::IOHL:
{
// Avoid multi-recursion for now
if (dump_pc != pc)
{
return {};
}
v128 reg_val{};
if (i >= 4)
{
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
// But don't limit to it
const auto [is_const, value] = try_get_const_value(reg, i);
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
// But don't limit to it
GET_CONST_REG(reg_val, op0.rt);
if (is_const)
{
return { true, value | v128::from32p(op0.i16) };
}
}
return {};
return { true, reg_val | v128::from32p(op0.i16) };
}
case spu_itype::STQA:
case spu_itype::STQD:
@ -156,6 +187,27 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
// Do not modify RT
break;
}
case spu_itype::SHLQBYI:
{
if (op0.si7)
{
// Unimplemented, doubt needed
return {};
}
// Move register value operation
v128 reg_val{};
GET_CONST_REG(reg_val, op0.ra);
return { true, reg_val };
}
case spu_itype::ORI:
{
v128 reg_val{};
GET_CONST_REG(reg_val, op0.ra);
return { true, reg_val | v128::from32p(op0.si10) };
}
default: return {};
}
}
@ -236,6 +288,8 @@ SPUDisAsm::insert_mask_info SPUDisAsm::try_get_insert_mask_info(const v128& mask
void SPUDisAsm::WRCH(spu_opcode_t op)
{
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
const auto [is_const, value] = try_get_const_value(op.rt);
if (is_const)
@ -244,7 +298,7 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
{
case MFC_Cmd:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], MFC(value._u8[12])).c_str());
fmt::append(last_opcode, " #%s", MFC(value._u8[12]));
return;
}
case MFC_WrListStallAck:
@ -252,62 +306,75 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
{
const u32 v = value._u32[3];
if (v && !(v & (v - 1)))
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s (tag=%u)", spu_reg_name[op.rt], SignedHex(v), std::countr_zero(v)).c_str()); // Single-tag mask
fmt::append(last_opcode, " #%s (tag=%u)", SignedHex(v), std::countr_zero(v)); // Single-tag mask
else
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(v)).c_str()); // Multi-tag mask (or zero)
fmt::append(last_opcode, " #%s", SignedHex(v)); // Multi-tag mask (or zero)
return;
}
case MFC_EAH:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str());
fmt::append(last_opcode, " #%s", SignedHex(value._u32[3]));
return;
}
case MFC_Size:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u16[6])).c_str());
fmt::append(last_opcode, " #%s", SignedHex(value._u16[6]));
return;
}
case MFC_TagID:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%u", spu_reg_name[op.rt], value._u8[12]).c_str());
fmt::append(last_opcode, " #%u", value._u8[12]);
return;
}
case MFC_WrTagUpdate:
{
const auto upd = fmt::format("%s", mfc_tag_update(value._u32[3]));
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], upd == "empty" ? "IMMEDIATE" : upd).c_str());
fmt::append(last_opcode, " #%s", upd == "empty" ? "IMMEDIATE" : upd);
return;
}
default:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str());
fmt::append(last_opcode, " #%s", SignedHex(value._u32[3]));
return;
}
}
}
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
}
enum CellError : u32;
void SPUDisAsm::IOHL(spu_opcode_t op)
{
DisAsm("iohl", spu_reg_name[op.rt], op.i16);
const auto [is_const, value] = try_get_const_value(op.rt);
if (is_const)
u32 val0 = value._u32[0];
// Only print constant for a 4 equal 32-bit constants array
if (is_const && value == v128::from32p(val0))
{
// Only print constant for a 4 equal 32-bit constants array
if (value == v128::from32p(value._u32[0]))
// Fixup value
val0 |= op.i16;
// Test if potentially a CELL error
if ((val0 >> 28) == 0x8u)
{
DisAsm("iohl", spu_reg_name[op.rt], fmt::format("%s #%s", SignedHex(+op.i16), (value._u32[0] | op.i16)).c_str());
return;
// Comment as CELL error
fmt::append(last_opcode, " #%s (0x%x)", CellError{val0}, val0);
}
else
{
// Comment constant formation
fmt::append(last_opcode, " #0x%x", val0);
}
}
DisAsm("iohl", spu_reg_name[op.rt], op.i16);
}
void SPUDisAsm::SHUFB(spu_opcode_t op)
{
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
const auto [is_const, value] = try_get_const_value(op.rc);
if (is_const)
@ -319,15 +386,13 @@ void SPUDisAsm::SHUFB(spu_opcode_t op)
if ((size >= 4u && !src) || (size == 2u && src == 1u) || (size == 1u && src == 3u))
{
// Comment insertion pattern for CWD-alike instruction
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u]", spu_reg_name[op.rc], size * 8, dst).c_str());
fmt::append(last_opcode, " #i%u[%u]", size * 8, dst);
return;
}
// Comment insertion pattern for unknown instruction formations
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u] = [%u]", spu_reg_name[op.rc], size * 8, dst, src).c_str());
fmt::append(last_opcode, " #i%u[%u] = [%u]", size * 8, dst, src);
return;
}
}
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
}

View File

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

View File

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

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 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<>
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)
{
switch (error)

View File

@ -15,20 +15,34 @@ u32 RSXDisAsm::disasm(u32 pc)
{
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 (pc < m_start_pc)
{
return false;
}
if (addr == umax) return 0;
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;
}
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)
{
return 0;
}
if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)
{
u32 jumpAddr = m_op & RSX_METHOD_OLD_JUMP_OFFSET_MASK;
@ -57,24 +71,15 @@ u32 RSXDisAsm::disasm(u32 pc)
}
else if ((m_op & RSX_METHOD_NOP_MASK) == RSX_METHOD_NOP_CMD)
{
if (m_mode == cpu_disasm_mode::list)
{
return 0;
}
u32 i = 1;
for (pc += 4; i < 4096; i++, pc += 4)
for (pc += 4; m_mode != cpu_disasm_mode::list && pc && i < 4096; i++, pc += 4)
{
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
if (addr == umax)
if (!try_read_op(pc))
{
break;
}
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
if ((m_op & RSX_METHOD_NOP_MASK) != RSX_METHOD_NOP_CMD)
{
break;
@ -106,17 +111,13 @@ u32 RSXDisAsm::disasm(u32 pc)
for (u32 i = 0; i < (m_mode == cpu_disasm_mode::list ? count : 1); i++, pc += 4)
{
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
if (addr == umax)
if (!try_read_op(pc))
{
last_opcode.clear();
Write("?? ??", -1);
return 4;
}
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
const u32 id = id_start + (non_inc ? 0 : i);
if (rsx::methods[id] == &rsx::invalid_method)
@ -134,6 +135,16 @@ u32 RSXDisAsm::disasm(u32 pc)
}
}
std::pair<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)
{
switch (m_mode)

View File

@ -5,7 +5,7 @@
class RSXDisAsm final : public CPUDisAsm
{
public:
RSXDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu) : CPUDisAsm(mode, offset, cpu)
RSXDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc, const cpu_thread* cpu) : CPUDisAsm(mode, offset, start_pc, cpu)
{
}
@ -14,4 +14,6 @@ private:
public:
u32 disasm(u32 pc) override;
std::pair<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 "instruction_editor_dialog.h"
#include "memory_viewer_panel.h"
#include "memory_string_searcher.h"
#include "gui_settings.h"
#include "debugger_list.h"
#include "breakpoint_list.h"
@ -306,6 +307,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
QLabel* l = new QLabel(tr(
"Keys Ctrl+G: Go to typed address."
"\nKeys Ctrl+B: Open breakpoints settings."
"\nKeys Ctrl+S: Search memory string utility."
"\nKeys Alt+S: Capture SPU images of selected SPU."
"\nKey D: SPU MFC commands logger, MFC debug setting must be enabled."
"\nKey D: Also PPU calling history logger, interpreter and non-zero call history size must be used."
@ -364,6 +366,20 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
open_breakpoints_settings();
return;
}
case Qt::Key_S:
{
if (m_disasm && (cpu->id_type() == 2 || cpu->id_type() == 1))
{
if (cpu->id_type() == 2)
{
// Save shared pointer to shared memory handle, ensure the destructor will not be called until the SPUDisAsm is destroyed
static_cast<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;
}
}
@ -911,7 +927,7 @@ void debugger_frame::OnSelectUnit()
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;

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);
}
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
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;
class CPUDisAsm;
std::shared_ptr<CPUDisAsm> make_basic_ppu_disasm();
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)
@ -2221,8 +2224,8 @@ void main_window::CreateConnects()
connect(ui->toolsStringSearchAct, &QAction::triggered, this, [this]
{
memory_string_searcher* mss = new memory_string_searcher(this);
mss->show();
if (!Emu.IsStopped())
idm::make<memory_searcher_handle>(this, make_basic_ppu_disasm());
});
connect(ui->toolsDecryptSprxLibsAct, &QAction::triggered, this, &main_window::DecryptSPRXLibraries);

View File

@ -1,6 +1,8 @@
#include "memory_string_searcher.h"
#include "Emu/Memory/vm.h"
#include "Emu/Memory/vm_reservation.h"
#include "Emu/CPU/CPUDisAsm.h"
#include "Emu/IdManager.h"
#include "Utilities/Thread.h"
#include "Utilities/StrUtil.h"
@ -13,6 +15,7 @@
#include <QCheckBox>
#include <charconv>
#include <unordered_map>
#include "util/logs.hpp"
#include "util/sysinfo.hpp"
@ -26,15 +29,28 @@ enum : int
as_hex,
as_f64,
as_f32,
as_inst,
};
memory_string_searcher::memory_string_searcher(QWidget* parent)
memory_string_searcher::memory_string_searcher(QWidget* parent, std::shared_ptr<CPUDisAsm> disasm, std::string_view title)
: QDialog(parent)
, m_disasm(std::move(disasm))
{
setWindowTitle(tr("String Searcher"));
if (title.empty())
{
setWindowTitle(tr("String Searcher"));
}
else
{
setWindowTitle(tr("String Searcher Of %1").arg(title.data()));
}
setObjectName("memory_string_searcher");
setAttribute(Qt::WA_DeleteOnClose);
// Extract memory view from the disassembler
std::tie(m_ptr, m_size) = m_disasm->get_memory_span();
m_addr_line = new QLineEdit(this);
m_addr_line->setFixedWidth(QLabel("This is the very length of the lineedit due to hidpi reasons.").sizeHint().width());
m_addr_line->setPlaceholderText(tr("Search..."));
@ -52,10 +68,12 @@ memory_string_searcher::memory_string_searcher(QWidget* parent)
m_cbox_input_mode->addItem("HEX bytes/integer", QVariant::fromValue(+as_hex));
m_cbox_input_mode->addItem("Double", QVariant::fromValue(+as_f64));
m_cbox_input_mode->addItem("Float", QVariant::fromValue(+as_f32));
m_cbox_input_mode->addItem("Instruction", QVariant::fromValue(+as_inst));
m_cbox_input_mode->setToolTip(tr("String: search the memory for the specified string."
"\nHEX bytes/integer: search the memory for hexadecimal values. Spaces, commas, \"0x\", \"0X\", \"\\x\" ensure separation of bytes but they are not mandatory."
"\nDouble: reinterpret the string as 64-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."
"\nFloat: reinterpret the string as 32-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."));
"\nFloat: reinterpret the string as 32-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."
"\nInstruction: search an instruction contains the text of the string."));
QHBoxLayout* hbox_panel = new QHBoxLayout();
hbox_panel->addWidget(m_addr_line);
@ -68,6 +86,15 @@ memory_string_searcher::memory_string_searcher(QWidget* parent)
connect(button_search, &QAbstractButton::clicked, this, &memory_string_searcher::OnSearch);
layout()->setSizeConstraint(QLayout::SetFixedSize);
// Show by default
show();
// Expected to be created by IDM, emulation stop will close it
connect(this, &memory_string_searcher::finished, [id = idm::last_id()](int)
{
idm::remove<memory_searcher_handle>(id);
});
}
void memory_string_searcher::OnSearch()
@ -97,6 +124,7 @@ void memory_string_searcher::OnSearch()
switch (mode)
{
case as_inst:
case as_string:
{
case_insensitive = m_chkbox_case_insensitive->isChecked();
@ -134,8 +162,8 @@ void memory_string_searcher::OnSearch()
if (const usz pos = wstr.find_first_not_of(hex_chars); pos != umax)
{
gui_log.error("String '%s' cannot be interpreted as hexadecimal byte string due to unknown character '%s'.",
m_addr_line->text().toStdString(), std::string_view{&wstr[pos], 1});
gui_log.error("String '%s' cannot be interpreted as hexadecimal byte string due to unknown character '%c'.",
m_addr_line->text().toStdString(), wstr[pos]);
return;
}
@ -189,8 +217,8 @@ void memory_string_searcher::OnSearch()
atomic_t<u32> found = 0;
atomic_t<u32> avail_addr = 0;
// There's no need for so many threads
const u32 max_threads = utils::aligned_div(utils::get_thread_count(), 2);
// There's no need for so many threads (except for instructions searching)
const u32 max_threads = utils::aligned_div(utils::get_thread_count(), mode != as_inst ? 2 : 1);
static constexpr u32 block_size = 0x2000000;
@ -198,18 +226,91 @@ void memory_string_searcher::OnSearch()
const named_thread_group workers("String Searcher "sv, max_threads, [&]()
{
if (mode == as_inst)
{
auto disasm = m_disasm->copy_type_erased();
disasm->change_mode(cpu_disasm_mode::normal);
const usz limit = std::min(m_size, m_ptr == vm::g_sudo_addr ? 0x4000'0000 : m_size);
while (true)
{
u32 addr;
const bool ok = avail_addr.fetch_op([&](u32& val)
{
if (val < limit && val != umax)
{
while (m_ptr == vm::g_sudo_addr && !vm::check_addr(val, vm::page_executable))
{
// Skip unmapped memory
val = utils::align(val + 1, 0x10000);
if (!val)
{
return false;
}
}
addr = val;
// Iterate 16k instructions at a time
val += 0x10000;
if (!val)
{
// Overflow detection
val = -1;
}
return true;
}
return false;
}).second;
if (!ok)
{
return;
}
for (u32 i = 0; i < 0x10000; i += 4)
{
if (disasm->disasm(addr + i))
{
auto& last = disasm->last_opcode;
if (case_insensitive)
{
std::transform(last.begin(), last.end(), last.begin(), ::tolower);
}
if (last.find(wstr) != umax)
{
gui_log.success("Found instruction at 0x%08x: '%s'", addr + i, disasm->last_opcode);
found++;
}
}
}
}
return;
}
u32 local_found = 0;
u32 addr = 0;
bool ok = false;
const u64 addr_limit = (m_size >= block_size ? m_size - block_size : 0);
while (true)
{
if (!(addr % block_size))
{
std::tie(addr, ok) = avail_addr.fetch_op([](u32& val)
std::tie(addr, ok) = avail_addr.fetch_op([&](u32& val)
{
if (val <= 0 - block_size)
if (val <= addr_limit)
{
// Iterate in 32MB blocks
val += block_size;
@ -228,8 +329,14 @@ void memory_string_searcher::OnSearch()
break;
}
if (![&addr = addr]()
if (![&]()
{
if (m_ptr != vm::g_sudo_addr)
{
// Always valid
return true;
}
// Skip unmapped memory
for (const u32 end = utils::align(addr + 1, block_size) - 0x1000; !vm::check_addr(addr, 0); addr += 0x1000)
{
@ -252,9 +359,9 @@ void memory_string_searcher::OnSearch()
continue;
}
u64 addr_max = addr;
const u64 end_mem = std::min<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
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;
}
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;
@ -282,12 +394,12 @@ void memory_string_searcher::OnSearch()
{
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
if (test_sv.size() >= wstr.size() && std::all_of(wstr.begin(), wstr.end(), [&](const char& c) { return c == ::tolower(test_sv[&c - wstr.data()]); }))
{
gui_log.success("Found at 0x%08x", start);
gui_log.success("Found at 0x%08x: '%s'", start, test_sv);
local_found++;
}
@ -301,7 +413,7 @@ void memory_string_searcher::OnSearch()
{
const u32 start = addr + first_char;
if (std::string_view{vm::get_super_ptr<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);
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
if (addr_max >= 0u - 0x1000) break;
if (addr_max >= m_size - 0x1000)
{
break;
}
addr = addr_max;
}
found += local_found;

View File

@ -1,11 +1,15 @@
#pragma once
#include "util/types.hpp"
#include <QDialog>
class QLineEdit;
class QCheckBox;
class QComboBox;
class CPUDisAsm;
class memory_string_searcher : public QDialog
{
Q_OBJECT
@ -14,9 +18,33 @@ class memory_string_searcher : public QDialog
QCheckBox* m_chkbox_case_insensitive = nullptr;
QComboBox* m_cbox_input_mode = nullptr;
std::shared_ptr<CPUDisAsm> m_disasm;
const void* m_ptr;
usz m_size;
public:
explicit memory_string_searcher(QWidget* parent);
explicit memory_string_searcher(QWidget* parent, std::shared_ptr<CPUDisAsm> disasm, std::string_view title = {});
private Q_SLOTS:
void OnSearch();
};
// Lifetime management with IDM
struct memory_searcher_handle
{
static constexpr u32 id_base = 1;
static constexpr u32 id_step = 1;
static constexpr u32 id_count = 2048;
template <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_count = 2048;
template <typename... Args>
template <typename... Args> requires (std::is_constructible_v<memory_viewer_panel, Args&&...>)
memory_viewer_handle(Args&&... args)
: m_mvp(new memory_viewer_panel(std::forward<Args>(args)...))
{