mirror of https://github.com/RPCS3/rpcs3.git
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:
parent
8a72bdb422
commit
ab50e5483e
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 += "?? ??";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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("?? ??");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)...))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue