mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
New RSX Debugger
This commit is contained in:
parent
67dd6754a6
commit
0652870204
@ -121,10 +121,6 @@ std::string dump_useful_thread_info()
|
||||
{
|
||||
result = cpu->dump_all();
|
||||
}
|
||||
else if (auto render = rsx::get_current_renderer(); render && render->is_current_thread())
|
||||
{
|
||||
result = render->dump_regs();
|
||||
}
|
||||
|
||||
guard = false;
|
||||
return result;
|
||||
@ -1422,7 +1418,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
return vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable);
|
||||
};
|
||||
|
||||
if (cpu)
|
||||
if (cpu && (cpu->id_type() == 1 || cpu->id_type() == 2))
|
||||
{
|
||||
vm::temporary_unlock(*cpu);
|
||||
u32 pf_port_id = 0;
|
||||
|
@ -399,6 +399,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/RSXTexture.cpp
|
||||
RSX/RSXThread.cpp
|
||||
RSX/rsx_utils.cpp
|
||||
RSX/RSXDisAsm.cpp
|
||||
RSX/Common/BufferUtils.cpp
|
||||
RSX/Common/FragmentProgramDecompiler.cpp
|
||||
RSX/Common/GLSLCommon.cpp
|
||||
|
@ -4,26 +4,30 @@
|
||||
#include <limits>
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
enum CPUDisAsmMode
|
||||
enum class cpu_disasm_mode
|
||||
{
|
||||
CPUDisAsm_DumpMode,
|
||||
CPUDisAsm_InterpreterMode,
|
||||
CPUDisAsm_NormalMode,
|
||||
CPUDisAsm_CompilerElfMode,
|
||||
dump,
|
||||
interpreter,
|
||||
normal,
|
||||
compiler_elf,
|
||||
list, // RSX exclusive
|
||||
};
|
||||
|
||||
class cpu_thread;
|
||||
|
||||
class CPUDisAsm
|
||||
{
|
||||
protected:
|
||||
const CPUDisAsmMode m_mode;
|
||||
const cpu_disasm_mode m_mode;
|
||||
const std::add_pointer_t<const u8> m_offset;
|
||||
const std::add_pointer_t<const cpu_thread> m_cpu;
|
||||
u32 m_op = 0;
|
||||
|
||||
virtual void Write(const std::string& value)
|
||||
{
|
||||
switch (m_mode)
|
||||
{
|
||||
case CPUDisAsm_DumpMode:
|
||||
case cpu_disasm_mode::dump:
|
||||
{
|
||||
last_opcode = fmt::format("\t%08x:\t%02x %02x %02x %02x\t%s\n", dump_pc,
|
||||
static_cast<u8>(m_op >> 24),
|
||||
@ -33,7 +37,7 @@ protected:
|
||||
break;
|
||||
}
|
||||
|
||||
case CPUDisAsm_InterpreterMode:
|
||||
case cpu_disasm_mode::interpreter:
|
||||
{
|
||||
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
|
||||
static_cast<u8>(m_op >> 24),
|
||||
@ -43,12 +47,12 @@ protected:
|
||||
break;
|
||||
}
|
||||
|
||||
case CPUDisAsm_CompilerElfMode:
|
||||
case cpu_disasm_mode::compiler_elf:
|
||||
{
|
||||
last_opcode = value + '\n';
|
||||
break;
|
||||
}
|
||||
case CPUDisAsm_NormalMode:
|
||||
case cpu_disasm_mode::normal:
|
||||
{
|
||||
last_opcode = value;
|
||||
break;
|
||||
@ -61,14 +65,21 @@ 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)
|
||||
{
|
||||
return T{mode, dis.m_offset, dis.m_cpu};
|
||||
}
|
||||
|
||||
protected:
|
||||
CPUDisAsm(CPUDisAsmMode mode, const u8* offset)
|
||||
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
|
||||
: m_mode(mode)
|
||||
, m_offset(offset)
|
||||
, m_cpu(cpu)
|
||||
{
|
||||
}
|
||||
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm) = 0;
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm) { return 0; };
|
||||
|
||||
// TODO: Add builtin fmt helpper for best performance
|
||||
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
||||
@ -95,7 +106,7 @@ protected:
|
||||
|
||||
std::string FixOp(std::string op) const
|
||||
{
|
||||
if (m_mode != CPUDisAsm_NormalMode)
|
||||
if (m_mode != cpu_disasm_mode::normal)
|
||||
{
|
||||
op.resize(std::max<usz>(op.length(), 10), ' ');
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "Emu/GDB.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/perf_meter.hpp"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
@ -49,7 +50,6 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_global_stop: return "G-EXIT";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
case cpu_flag::__bitset_enum_max: break;
|
||||
@ -541,7 +541,7 @@ void cpu_thread::operator()()
|
||||
cleanup.name = thread_ctrl::get_name();
|
||||
|
||||
// Check thread status
|
||||
while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop)) && thread_ctrl::state() != thread_state::aborting)
|
||||
while (!(state & cpu_flag::exit) && thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
// Check stop status
|
||||
if (!(state & cpu_flag::stop))
|
||||
@ -580,6 +580,11 @@ cpu_thread::cpu_thread(u32 id)
|
||||
g_threads_created++;
|
||||
}
|
||||
|
||||
void cpu_thread::cpu_wait()
|
||||
{
|
||||
thread_ctrl::wait();
|
||||
}
|
||||
|
||||
bool cpu_thread::check_state() noexcept
|
||||
{
|
||||
bool cpu_sleep_called = false;
|
||||
@ -642,7 +647,7 @@ bool cpu_thread::check_state() noexcept
|
||||
}
|
||||
|
||||
// Atomically clean wait flag and escape
|
||||
if (!(flags & (cpu_flag::exit + cpu_flag::dbg_global_stop + cpu_flag::ret + cpu_flag::stop)))
|
||||
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
|
||||
{
|
||||
// Check pause flags which hold thread inside check_state (ignore suspend on cpu_flag::temp)
|
||||
if (flags & (cpu_flag::pause + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::memory + (cpu_can_stop ? cpu_flag::suspend : cpu_flag::pause)))
|
||||
@ -721,7 +726,7 @@ bool cpu_thread::check_state() noexcept
|
||||
g_fxo->get<gdb_server>()->pause_from(this);
|
||||
}
|
||||
|
||||
thread_ctrl::wait();
|
||||
cpu_wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -772,7 +777,7 @@ void cpu_thread::notify()
|
||||
{
|
||||
thread_ctrl::notify(*static_cast<named_thread<spu_thread>*>(this));
|
||||
}
|
||||
else
|
||||
else if (id_type() != 0x55)
|
||||
{
|
||||
fmt::throw_exception("Invalid cpu_thread type");
|
||||
}
|
||||
@ -828,6 +833,11 @@ u32 cpu_thread::get_pc() const
|
||||
pc = &static_cast<const spu_thread*>(this)->pc;
|
||||
break;
|
||||
}
|
||||
case 0x55:
|
||||
{
|
||||
const auto ctrl = static_cast<const rsx::thread*>(this)->ctrl;
|
||||
return ctrl ? ctrl->get : UINT32_MAX;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -836,7 +846,15 @@ u32 cpu_thread::get_pc() const
|
||||
|
||||
std::string cpu_thread::dump_all() const
|
||||
{
|
||||
return {};
|
||||
std::string ret = cpu_thread::dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_regs();
|
||||
ret += '\n';
|
||||
ret += dump_callstack();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string cpu_thread::dump_regs() const
|
||||
@ -846,7 +864,16 @@ std::string cpu_thread::dump_regs() const
|
||||
|
||||
std::string cpu_thread::dump_callstack() const
|
||||
{
|
||||
return {};
|
||||
std::string ret;
|
||||
|
||||
fmt::append(ret, "Call stack:\n=========\n0x%08x (0x0) called\n", get_pc());
|
||||
|
||||
for (const auto& sp : dump_callstack_list())
|
||||
{
|
||||
fmt::append(ret, "> from 0x%08x (sp=0x%08x)\n", sp.first, sp.second);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::pair<u32, u32>> cpu_thread::dump_callstack_list() const
|
||||
@ -1035,7 +1062,7 @@ void cpu_thread::stop_all() noexcept
|
||||
{
|
||||
auto on_stop = [](u32, cpu_thread& cpu)
|
||||
{
|
||||
cpu.state += cpu_flag::dbg_global_stop;
|
||||
cpu.state += cpu_flag::exit;
|
||||
cpu.abort();
|
||||
};
|
||||
|
||||
|
@ -19,7 +19,6 @@ enum class cpu_flag : u32
|
||||
memory, // Thread must unlock memory mutex
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_global_stop, // Emulation stopped
|
||||
dbg_pause, // Thread paused
|
||||
dbg_step, // Thread forced to pause after one step (one instruction, etc)
|
||||
|
||||
@ -64,7 +63,7 @@ public:
|
||||
// Test stopped state
|
||||
bool is_stopped() const
|
||||
{
|
||||
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::dbg_global_stop));
|
||||
return !!(state & (cpu_flag::stop + cpu_flag::exit));
|
||||
}
|
||||
|
||||
// Test paused state
|
||||
@ -122,6 +121,9 @@ public:
|
||||
// Callback for cpu_flag::ret
|
||||
virtual void cpu_return() {}
|
||||
|
||||
// Callback for thread_ctrl::wait or RSX wait
|
||||
virtual void cpu_wait();
|
||||
|
||||
// For internal use
|
||||
struct suspend_work
|
||||
{
|
||||
@ -211,7 +213,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Stop all threads with cpu_flag::dbg_global_stop
|
||||
// Stop all threads with cpu_flag::exit
|
||||
static void stop_all() noexcept;
|
||||
|
||||
// Send signal to the profiler(s) to flush results
|
||||
|
@ -5,7 +5,7 @@
|
||||
class PPCDisAsm : public CPUDisAsm
|
||||
{
|
||||
protected:
|
||||
PPCDisAsm(CPUDisAsmMode mode, const u8* offset) : CPUDisAsm(mode, offset)
|
||||
PPCDisAsm(cpu_disasm_mode mode, const u8* offset) : CPUDisAsm(mode, offset)
|
||||
{
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ protected:
|
||||
}
|
||||
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
|
||||
{
|
||||
if(m_mode == CPUDisAsm_CompilerElfMode)
|
||||
if(m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
|
||||
return;
|
||||
@ -99,7 +99,7 @@ protected:
|
||||
}
|
||||
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
|
||||
{
|
||||
if(m_mode == CPUDisAsm_CompilerElfMode)
|
||||
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)));
|
||||
return;
|
||||
@ -177,7 +177,7 @@ protected:
|
||||
}
|
||||
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
|
||||
{
|
||||
if(m_mode == CPUDisAsm_CompilerElfMode)
|
||||
if(m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
|
||||
return;
|
||||
|
@ -935,7 +935,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
|
||||
const u32 aa = op.aa;
|
||||
const u32 lk = op.lk;
|
||||
|
||||
if (m_mode == CPUDisAsm_CompilerElfMode)
|
||||
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));
|
||||
return;
|
||||
@ -995,7 +995,7 @@ void PPUDisAsm::B(ppu_opcode_t op)
|
||||
const u32 aa = op.aa;
|
||||
const u32 lk = op.lk;
|
||||
|
||||
if (m_mode == CPUDisAsm_CompilerElfMode)
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("b 0x%x, %d, %d", li, aa, lk));
|
||||
return;
|
||||
|
@ -6,7 +6,7 @@
|
||||
class PPUDisAsm final : public PPCDisAsm
|
||||
{
|
||||
public:
|
||||
PPUDisAsm(CPUDisAsmMode mode, const u8* offset) : PPCDisAsm(mode, offset)
|
||||
PPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset)
|
||||
{
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ private:
|
||||
}
|
||||
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
|
||||
{
|
||||
if(m_mode == CPUDisAsm_CompilerElfMode)
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
|
||||
return;
|
||||
@ -117,7 +117,7 @@ private:
|
||||
}
|
||||
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
|
||||
{
|
||||
if(m_mode == CPUDisAsm_CompilerElfMode)
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s f%d,r%d,%s", FixOp(op + (rc ? "." : "")), f0, r0, SignedHex(imm0)));
|
||||
return;
|
||||
@ -195,7 +195,7 @@ private:
|
||||
}
|
||||
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
|
||||
{
|
||||
if(m_mode == CPUDisAsm_CompilerElfMode)
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
|
||||
return;
|
||||
|
@ -443,19 +443,6 @@ std::array<u32, 2> op_branch_targets(u32 pc, ppu_opcode_t op)
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string ppu_thread::dump_all() const
|
||||
{
|
||||
std::string ret = cpu_thread::dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_regs();
|
||||
ret += '\n';
|
||||
ret += dump_callstack();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ppu_thread::dump_regs() const
|
||||
{
|
||||
std::string ret;
|
||||
@ -505,7 +492,7 @@ std::string ppu_thread::dump_regs() const
|
||||
}
|
||||
else
|
||||
{
|
||||
PPUDisAsm dis_asm(CPUDisAsm_NormalMode, vm::g_sudo_addr);
|
||||
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
|
||||
dis_asm.disasm(reg);
|
||||
fmt::append(ret, " -> %s", dis_asm.last_opcode);
|
||||
}
|
||||
@ -834,7 +821,7 @@ void ppu_thread::exec_task()
|
||||
{
|
||||
if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm)
|
||||
{
|
||||
while (!(state & (cpu_flag::ret + cpu_flag::exit + cpu_flag::stop + cpu_flag::dbg_global_stop)))
|
||||
while (!(state & (cpu_flag::ret + cpu_flag::exit + cpu_flag::stop)))
|
||||
{
|
||||
reinterpret_cast<ppu_function_t>(ppu_ref(cia))(*this);
|
||||
}
|
||||
|
@ -118,7 +118,6 @@ public:
|
||||
static const u32 id_count = 100;
|
||||
static constexpr std::pair<u32, u32> id_invl_range = {12, 12};
|
||||
|
||||
virtual std::string dump_all() const override;
|
||||
virtual std::string dump_regs() const override;
|
||||
virtual std::string dump_callstack() const override;
|
||||
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
|
||||
|
@ -20,7 +20,7 @@ u32 SPUDisAsm::disasm(u32 pc)
|
||||
|
||||
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
|
||||
{
|
||||
if (m_mode != CPUDisAsm_InterpreterMode)
|
||||
if (m_mode != cpu_disasm_mode::interpreter)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ static constexpr const char* spu_ch_name[128] =
|
||||
class SPUDisAsm final : public PPCDisAsm
|
||||
{
|
||||
public:
|
||||
SPUDisAsm(CPUDisAsmMode mode, const u8* offset) : PPCDisAsm(mode, offset)
|
||||
SPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset)
|
||||
{
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ private:
|
||||
private:
|
||||
std::string& FixOp(std::string& op)
|
||||
{
|
||||
if (m_mode != CPUDisAsm_NormalMode)
|
||||
if (m_mode != cpu_disasm_mode::normal)
|
||||
{
|
||||
op.append(std::max<int>(10 - ::narrow<int>(op.size()), 0), ' ');
|
||||
}
|
||||
|
@ -3151,7 +3151,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(CPUDisAsm_DumpMode, 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;
|
||||
{
|
||||
|
@ -1218,18 +1218,6 @@ spu_imm_table_t::spu_imm_table_t()
|
||||
}
|
||||
}
|
||||
|
||||
std::string spu_thread::dump_all() const
|
||||
{
|
||||
std::string ret = cpu_thread::dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_regs();
|
||||
ret += '\n';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string spu_thread::dump_regs() const
|
||||
{
|
||||
std::string ret;
|
||||
@ -1289,7 +1277,7 @@ std::string spu_thread::dump_regs() const
|
||||
|
||||
if (i3 >= 0x80 && is_exec_code(i3))
|
||||
{
|
||||
SPUDisAsm dis_asm(CPUDisAsm_NormalMode, ls);
|
||||
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
|
||||
dis_asm.disasm(i3);
|
||||
fmt::append(ret, " -> %s", dis_asm.last_opcode);
|
||||
}
|
||||
@ -1700,7 +1688,7 @@ void spu_thread::cleanup()
|
||||
}
|
||||
|
||||
// Free range lock (and signals cleanup was called to the destructor)
|
||||
vm::free_range_lock(std::exchange(range_lock, nullptr));
|
||||
vm::free_range_lock(range_lock);
|
||||
}
|
||||
|
||||
spu_thread::~spu_thread()
|
||||
@ -1710,9 +1698,6 @@ spu_thread::~spu_thread()
|
||||
shm->unmap(ls);
|
||||
shm->unmap(ls - SPU_LS_SIZE);
|
||||
|
||||
// Free range lock if not freed already
|
||||
if (range_lock) vm::free_range_lock(range_lock);
|
||||
|
||||
perf_log.notice("Perf stats for transactions: success %u, failure %u", stx, ftx);
|
||||
perf_log.notice("Perf stats for PUTLLC reload: successs %u, failure %u", last_succ, last_fail);
|
||||
}
|
||||
|
@ -626,7 +626,6 @@ enum class spu_type : u32
|
||||
class spu_thread : public cpu_thread
|
||||
{
|
||||
public:
|
||||
virtual std::string dump_all() const override;
|
||||
virtual std::string dump_regs() const override;
|
||||
virtual std::string dump_callstack() const override;
|
||||
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
|
||||
|
@ -342,7 +342,7 @@ void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
|
||||
Emu.Stop();
|
||||
});
|
||||
|
||||
ppu.state += cpu_flag::dbg_global_stop;
|
||||
ppu.state += cpu_flag::exit;
|
||||
}
|
||||
|
||||
void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> arg, u32 arg_size, u32 arg4)
|
||||
@ -419,7 +419,7 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
|
||||
}
|
||||
});
|
||||
|
||||
ppu.state += cpu_flag::dbg_global_stop;
|
||||
ppu.state += cpu_flag::exit;
|
||||
}
|
||||
|
||||
error_code sys_process_spawns_a_self2(vm::ptr<u32> pid, u32 primary_prio, u64 flags, vm::ptr<void> stack, u32 stack_size, u32 mem_id, vm::ptr<void> param_sfo, vm::ptr<void> dbg_data)
|
||||
|
@ -55,24 +55,18 @@ void lv2_rsx_config::send_event(u64 data1, u64 event_flags, u64 data3) const
|
||||
{
|
||||
auto cpu = get_current_cpu_thread();
|
||||
|
||||
if (cpu && cpu->id_type() != 1)
|
||||
{
|
||||
cpu = nullptr;
|
||||
}
|
||||
|
||||
if (cpu)
|
||||
if (cpu && cpu->id_type() == 1)
|
||||
{
|
||||
// Deschedule
|
||||
lv2_obj::sleep(*cpu, 100);
|
||||
}
|
||||
else if (const auto rsx = rsx::get_current_renderer(); rsx->is_current_thread())
|
||||
{
|
||||
rsx->on_semaphore_acquire_wait();
|
||||
}
|
||||
|
||||
// Wait a bit before resending event
|
||||
thread_ctrl::wait_for(100);
|
||||
|
||||
if (cpu && cpu->id_type() == 0x55)
|
||||
cpu->cpu_wait();
|
||||
|
||||
if (Emu.IsStopped() || (cpu && cpu->check_state()))
|
||||
{
|
||||
error = 0;
|
||||
|
@ -1663,6 +1663,9 @@ namespace vm
|
||||
utils::memory_decommit(g_base_addr, 0x200000000);
|
||||
utils::memory_decommit(g_exec_addr, 0x200000000);
|
||||
utils::memory_decommit(g_stat_addr, 0x100000000);
|
||||
|
||||
std::memset(g_range_lock_set, 0, sizeof(g_range_lock_set));
|
||||
g_range_lock_bits = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
165
rpcs3/Emu/RSX/RSXDisAsm.cpp
Normal file
165
rpcs3/Emu/RSX/RSXDisAsm.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "RSXDisAsm.h"
|
||||
#include "RSXThread.h"
|
||||
#include "gcm_enums.h"
|
||||
#include "gcm_printing.h"
|
||||
#include "rsx_methods.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
void invalid_method(thread*, u32, u32);
|
||||
}
|
||||
|
||||
u32 RSXDisAsm::disasm(u32 pc)
|
||||
{
|
||||
last_opcode.clear();
|
||||
|
||||
u32 addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
|
||||
|
||||
if (addr == umax) return 0;
|
||||
|
||||
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
|
||||
dump_pc = pc;
|
||||
|
||||
if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK)
|
||||
{
|
||||
if (m_mode == cpu_disasm_mode::list)
|
||||
{
|
||||
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;
|
||||
Write(fmt::format("jump 0x%07x", jumpAddr), -1);
|
||||
}
|
||||
else if ((m_op & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD)
|
||||
{
|
||||
u32 jumpAddr = m_op & RSX_METHOD_NEW_JUMP_OFFSET_MASK;
|
||||
Write(fmt::format("jump 0x%07x", jumpAddr), -1);
|
||||
}
|
||||
else if ((m_op & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD)
|
||||
{
|
||||
u32 callAddr = m_op & RSX_METHOD_CALL_OFFSET_MASK;
|
||||
Write(fmt::format("call 0x%07x", callAddr), -1);
|
||||
}
|
||||
else if ((m_op & RSX_METHOD_RETURN_MASK) == RSX_METHOD_RETURN_CMD)
|
||||
{
|
||||
Write("ret", -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write("?? ??", -1);
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
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)
|
||||
{
|
||||
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
|
||||
|
||||
if (addr == umax)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
Write("nop", 0);
|
||||
else
|
||||
Write(fmt::format("nop x%u", i), 0);
|
||||
|
||||
return i * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 count = (m_op & RSX_METHOD_COUNT_MASK) >> RSX_METHOD_COUNT_SHIFT;
|
||||
const bool non_inc = (m_op & RSX_METHOD_NON_INCREMENT_CMD_MASK) == RSX_METHOD_NON_INCREMENT_CMD && count > 1;
|
||||
const u32 id_start = (m_op & 0x3ffff) >> 2;
|
||||
|
||||
if (count > 10 && id_start == NV4097_SET_OBJECT)
|
||||
{
|
||||
// Hack: 0 method with large count is unlikely to be a command
|
||||
// But is very common in floating point args, messing up debugger's code-flow
|
||||
Write("?? ??", -1);
|
||||
return 4;
|
||||
}
|
||||
|
||||
pc += 4;
|
||||
|
||||
for (u32 i = 0; i < (m_mode == cpu_disasm_mode::list ? count : 1); i++, pc += 4)
|
||||
{
|
||||
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
|
||||
|
||||
if (addr == umax)
|
||||
{
|
||||
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)
|
||||
{
|
||||
last_opcode.clear();
|
||||
Write("?? ??", -1);
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::string str = rsx::get_pretty_printing_function(id)(id, m_op);
|
||||
Write(str, m_mode == cpu_disasm_mode::list ? i : count, non_inc, id);
|
||||
}
|
||||
|
||||
return (count + 1) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
void RSXDisAsm::Write(const std::string& str, s32 count, bool is_non_inc, u32 id)
|
||||
{
|
||||
switch (m_mode)
|
||||
{
|
||||
case cpu_disasm_mode::interpreter:
|
||||
{
|
||||
last_opcode = count >= 0 ? fmt::format("[%08x] (%s%u)", dump_pc, is_non_inc ? "+" : "", count) :
|
||||
fmt::format("[%08x] (x)", dump_pc);
|
||||
|
||||
auto& res = last_opcode;
|
||||
|
||||
res.resize(7 + 11);
|
||||
std::replace(res.begin(), res.end(), '\0', ' ');
|
||||
|
||||
res += str;
|
||||
break;
|
||||
}
|
||||
case cpu_disasm_mode::list:
|
||||
{
|
||||
if (!last_opcode.empty())
|
||||
last_opcode += '\n';
|
||||
|
||||
fmt::append(last_opcode, "[%04x] 0x%08x: %s", id, m_op, str);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
17
rpcs3/Emu/RSX/RSXDisAsm.h
Normal file
17
rpcs3/Emu/RSX/RSXDisAsm.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Cell/PPCDisAsm.h"
|
||||
|
||||
class RSXDisAsm final : public CPUDisAsm
|
||||
{
|
||||
public:
|
||||
RSXDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu) : CPUDisAsm(mode, offset, cpu)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void Write(const std::string& str, s32 count, bool is_non_inc = false, u32 id = 0);
|
||||
|
||||
public:
|
||||
u32 disasm(u32 pc) override;
|
||||
};
|
@ -360,6 +360,7 @@ namespace rsx
|
||||
}
|
||||
|
||||
thread::thread()
|
||||
: cpu_thread(0x5555'5555)
|
||||
{
|
||||
g_access_violation_handler = [this](u32 address, bool is_writing)
|
||||
{
|
||||
@ -378,6 +379,8 @@ namespace rsx
|
||||
{
|
||||
m_overlay_manager = g_fxo->init<rsx::overlays::display_manager>(0);
|
||||
}
|
||||
|
||||
state -= cpu_flag::stop + cpu_flag::wait; // TODO: Remove workaround
|
||||
}
|
||||
|
||||
void thread::capture_frame(const std::string &name)
|
||||
@ -496,7 +499,7 @@ namespace rsx
|
||||
while (method_registers.current_draw_clause.next());
|
||||
}
|
||||
|
||||
void thread::operator()()
|
||||
void thread::cpu_task()
|
||||
{
|
||||
{
|
||||
// Wait for startup (TODO)
|
||||
@ -510,7 +513,7 @@ namespace rsx
|
||||
|
||||
thread_ctrl::wait_for(1000);
|
||||
|
||||
if (Emu.IsStopped())
|
||||
if (is_stopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -522,6 +525,17 @@ namespace rsx
|
||||
on_exit();
|
||||
}
|
||||
|
||||
void thread::cpu_wait()
|
||||
{
|
||||
if (external_interrupt_lock)
|
||||
{
|
||||
wait_pause();
|
||||
}
|
||||
|
||||
on_semaphore_acquire_wait();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
void thread::on_task()
|
||||
{
|
||||
m_rsx_thread = std::this_thread::get_id();
|
||||
@ -560,7 +574,7 @@ namespace rsx
|
||||
u64 start_time = get_system_time();
|
||||
|
||||
// TODO: exit condition
|
||||
while (!Emu.IsStopped() && !m_rsx_thread_exiting)
|
||||
while (!is_stopped())
|
||||
{
|
||||
const u64 period_time = 1000000 / g_cfg.video.vblank_rate;
|
||||
const u64 wait_sleep = period_time - u64{period_time >= host_min_quantum} * host_min_quantum;
|
||||
@ -602,7 +616,7 @@ namespace rsx
|
||||
// Save the difference before pause
|
||||
start_time = get_system_time() - start_time;
|
||||
|
||||
while (Emu.IsPaused() && !m_rsx_thread_exiting)
|
||||
while (Emu.IsPaused() && !is_stopped())
|
||||
{
|
||||
thread_ctrl::wait_for(wait_sleep);
|
||||
}
|
||||
@ -626,8 +640,7 @@ namespace rsx
|
||||
// Round to nearest to deal with forward/reverse scaling
|
||||
fesetround(FE_TONEAREST);
|
||||
|
||||
// TODO: exit condition
|
||||
while (true)
|
||||
while (!test_stopped())
|
||||
{
|
||||
// Wait for external pause events
|
||||
if (external_interrupt_lock)
|
||||
@ -651,20 +664,6 @@ namespace rsx
|
||||
|
||||
// Execute FIFO queue
|
||||
run_FIFO();
|
||||
|
||||
if (!Emu.IsRunning())
|
||||
{
|
||||
// Idle if emulation paused
|
||||
while (Emu.IsPaused())
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,6 +678,7 @@ namespace rsx
|
||||
|
||||
m_rsx_thread_exiting = true;
|
||||
g_fxo->get<rsx::dma_manager>()->join();
|
||||
state += cpu_flag::exit;
|
||||
}
|
||||
|
||||
void thread::fill_scale_offset_data(void *buffer, bool flip_y) const
|
||||
@ -2577,7 +2577,7 @@ namespace rsx
|
||||
recovered_fifo_cmds_history.push({fifo_ctrl->last_cmd(), current_time});
|
||||
}
|
||||
|
||||
std::vector<std::pair<u32, u32>> thread::dump_callstack() const
|
||||
std::vector<std::pair<u32, u32>> thread::dump_callstack_list() const
|
||||
{
|
||||
std::vector<std::pair<u32, u32>> result;
|
||||
|
||||
|
@ -592,13 +592,14 @@ namespace rsx
|
||||
|
||||
struct sampled_image_descriptor_base;
|
||||
|
||||
class thread
|
||||
class thread : public cpu_thread
|
||||
{
|
||||
u64 timestamp_ctrl = 0;
|
||||
u64 timestamp_subvalue = 0;
|
||||
|
||||
display_flip_info_t m_queued_flip{};
|
||||
|
||||
void cpu_task() override;
|
||||
protected:
|
||||
std::thread::id m_rsx_thread;
|
||||
atomic_t<bool> m_rsx_thread_exiting{ true };
|
||||
@ -615,7 +616,7 @@ namespace rsx
|
||||
// FIFO
|
||||
public:
|
||||
std::unique_ptr<FIFO::FIFO_control> fifo_ctrl;
|
||||
std::vector<std::pair<u32, u32>> dump_callstack() const;
|
||||
std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
|
||||
|
||||
protected:
|
||||
FIFO::flattening_helper m_flattener;
|
||||
@ -655,7 +656,8 @@ namespace rsx
|
||||
static void fifo_wake_delay(u64 div = 1);
|
||||
u32 get_fifo_cmd() const;
|
||||
|
||||
std::string dump_regs() const;
|
||||
std::string dump_regs() const override;
|
||||
void cpu_wait() override;
|
||||
|
||||
// Performance approximation counters
|
||||
struct
|
||||
@ -783,7 +785,6 @@ namespace rsx
|
||||
|
||||
reports::conditional_render_eval cond_render_ctrl;
|
||||
|
||||
void operator()();
|
||||
virtual u64 get_cycles() = 0;
|
||||
virtual ~thread();
|
||||
|
||||
|
@ -1159,15 +1159,11 @@ enum Method : u32
|
||||
|
||||
RSX_METHOD_INCREMENT_CMD_MASK = 0xe0030003,
|
||||
RSX_METHOD_INCREMENT_CMD = 0,
|
||||
RSX_METHOD_INCREMENT_COUNT_MASK = 0x1ffc0000,
|
||||
RSX_METHOD_INCREMENT_COUNT_SHIFT = 18,
|
||||
RSX_METHOD_INCREMENT_METHOD_MASK = 0x0000fffc,
|
||||
|
||||
RSX_METHOD_NON_INCREMENT_CMD_MASK = 0xe0030003,
|
||||
RSX_METHOD_NON_INCREMENT_CMD = 0x40000000,
|
||||
RSX_METHOD_NON_INCREMENT_COUNT_MASK = 0x1ffc0000,
|
||||
RSX_METHOD_NON_INCREMENT_COUNT_SHIFT = 18,
|
||||
RSX_METHOD_NON_INCREMENT_METHOD_MASK = 0x0000fffc,
|
||||
RSX_METHOD_COUNT_MASK = 0x1ffc0000,
|
||||
RSX_METHOD_COUNT_SHIFT = 18,
|
||||
RSX_METHOD_METHOD_MASK = 0x0000fffc,
|
||||
|
||||
RSX_METHOD_NEW_JUMP_CMD_MASK = 0xe0000003,
|
||||
RSX_METHOD_NEW_JUMP_CMD = 0x00000001,
|
||||
|
@ -77,25 +77,20 @@ namespace rsx
|
||||
u64 start = get_system_time();
|
||||
while (sema != arg)
|
||||
{
|
||||
if (Emu.IsStopped())
|
||||
return;
|
||||
|
||||
// Wait for external pause events
|
||||
if (rsx->external_interrupt_lock)
|
||||
if (rsx->is_stopped())
|
||||
{
|
||||
rsx->wait_pause();
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto tdr = static_cast<u64>(g_cfg.video.driver_recovery_timeout))
|
||||
{
|
||||
if (Emu.IsPaused())
|
||||
if (rsx->is_paused())
|
||||
{
|
||||
const u64 start0 = get_system_time();
|
||||
|
||||
while (Emu.IsPaused())
|
||||
while (rsx->is_paused())
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
rsx->cpu_wait();
|
||||
}
|
||||
|
||||
// Reset
|
||||
@ -112,8 +107,7 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
rsx->on_semaphore_acquire_wait();
|
||||
std::this_thread::yield();
|
||||
rsx->cpu_wait();
|
||||
}
|
||||
|
||||
rsx->fifo_wake_delay();
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "Emu/Cell/PPUAnalyser.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/RawSPUThread.h"
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
@ -1788,6 +1789,7 @@ bool Emulator::Pause()
|
||||
|
||||
idm::select<named_thread<ppu_thread>>(on_select);
|
||||
idm::select<named_thread<spu_thread>>(on_select);
|
||||
g_fxo->get<rsx::thread>()->state += cpu_flag::dbg_global_pause;
|
||||
|
||||
// Always Enable display sleep, not only if it was prevented.
|
||||
enable_display_sleep();
|
||||
@ -1809,7 +1811,7 @@ void Emulator::Resume()
|
||||
// Print and reset debug data collected
|
||||
if (m_state == system_state::paused && g_cfg.core.ppu_debug)
|
||||
{
|
||||
PPUDisAsm dis_asm(CPUDisAsm_DumpMode, vm::g_sudo_addr);
|
||||
PPUDisAsm dis_asm(cpu_disasm_mode::dump, vm::g_sudo_addr);
|
||||
|
||||
std::string dump;
|
||||
|
||||
@ -1856,6 +1858,8 @@ void Emulator::Resume()
|
||||
|
||||
idm::select<named_thread<ppu_thread>>(on_select);
|
||||
idm::select<named_thread<spu_thread>>(on_select);
|
||||
g_fxo->get<rsx::thread>()->state -= cpu_flag::dbg_global_pause;
|
||||
|
||||
GetCallbacks().on_resume();
|
||||
|
||||
if (g_cfg.misc.prevent_display_sleep)
|
||||
@ -1906,6 +1910,7 @@ void Emulator::Stop(bool restart)
|
||||
|
||||
GetCallbacks().on_stop();
|
||||
|
||||
g_fxo->get<rsx::thread>()->state += cpu_flag::exit;
|
||||
cpu_thread::stop_all();
|
||||
g_fxo->reset();
|
||||
|
||||
|
@ -13,7 +13,7 @@ u64 get_guest_system_time();
|
||||
enum class localized_string_id;
|
||||
enum class video_renderer;
|
||||
|
||||
enum class system_state
|
||||
enum class system_state : u32
|
||||
{
|
||||
running,
|
||||
paused,
|
||||
|
@ -88,6 +88,7 @@
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp" />
|
||||
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp" />
|
||||
<ClCompile Include="Emu\system_config_types.cpp" />
|
||||
<ClCompile Include="Emu\perf_meter.cpp" />
|
||||
<ClCompile Include="Emu\title.cpp" />
|
||||
@ -476,6 +477,7 @@
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_utils.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.h" />
|
||||
<ClInclude Include="Emu\RSX\RSXDisAsm.h" />
|
||||
<ClInclude Include="Emu\title.h" />
|
||||
<ClInclude Include="Emu\system_config.h" />
|
||||
<ClInclude Include="Emu\system_config_types.h" />
|
||||
|
@ -974,6 +974,9 @@
|
||||
<ClCompile Include="Emu\Io\interception.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -1564,9 +1567,6 @@
|
||||
<ClInclude Include="Emu\RSX\rsx_cache.h">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\dyn_lib.hpp">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -1897,6 +1897,12 @@
|
||||
<ClInclude Include="Emu\RSX\display.h">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\dyn_lib.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\RSXDisAsm.h">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||
|
@ -23,9 +23,9 @@ breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) :
|
||||
/**
|
||||
* It's unfortunate I need a method like this to sync these. Should ponder a cleaner way to do this.
|
||||
*/
|
||||
void breakpoint_list::UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm)
|
||||
void breakpoint_list::UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm)
|
||||
{
|
||||
this->cpu = cpu;
|
||||
m_cpu = cpu;
|
||||
m_disasm = disasm;
|
||||
}
|
||||
|
||||
@ -62,8 +62,6 @@ void breakpoint_list::AddBreakpoint(u32 pc)
|
||||
{
|
||||
m_breakpoint_handler->AddBreakpoint(pc);
|
||||
|
||||
const auto cpu = this->cpu.lock();
|
||||
|
||||
m_disasm->disasm(pc);
|
||||
|
||||
QString breakpointItemText = qstr(m_disasm->last_opcode);
|
||||
@ -86,9 +84,7 @@ void breakpoint_list::AddBreakpoint(u32 pc)
|
||||
*/
|
||||
void breakpoint_list::HandleBreakpointRequest(u32 loc)
|
||||
{
|
||||
const auto cpu = this->cpu.lock();
|
||||
|
||||
if (!cpu || cpu->id_type() != 1 || !vm::check_addr(loc, vm::page_allocated | vm::page_executable))
|
||||
if (!m_cpu || m_cpu->id_type() != 1 || !vm::check_addr(loc, vm::page_allocated | vm::page_executable))
|
||||
{
|
||||
// TODO: SPU breakpoints
|
||||
return;
|
||||
|
@ -15,7 +15,7 @@ class breakpoint_list : public QListWidget
|
||||
|
||||
public:
|
||||
breakpoint_list(QWidget* parent, breakpoint_handler* handler);
|
||||
void UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm);
|
||||
void UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm);
|
||||
void ClearBreakpoints();
|
||||
void AddBreakpoint(u32 addr);
|
||||
void RemoveBreakpoint(u32 addr);
|
||||
@ -33,6 +33,6 @@ private Q_SLOTS:
|
||||
private:
|
||||
breakpoint_handler* m_breakpoint_handler;
|
||||
|
||||
std::weak_ptr<cpu_thread> cpu;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm;
|
||||
cpu_thread* m_cpu;
|
||||
CPUDisAsm* m_disasm;
|
||||
};
|
||||
|
@ -14,11 +14,6 @@ call_stack_list::call_stack_list(QWidget* parent) : QListWidget(parent)
|
||||
connect(this, &QListWidget::itemDoubleClicked, this, &call_stack_list::OnCallStackListDoubleClicked);
|
||||
}
|
||||
|
||||
void call_stack_list::UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm)
|
||||
{
|
||||
this->cpu = cpu;
|
||||
}
|
||||
|
||||
void call_stack_list::HandleUpdate(std::vector<std::pair<u32, u32>> call_stack)
|
||||
{
|
||||
clear();
|
||||
|
@ -15,7 +15,6 @@ class call_stack_list : public QListWidget
|
||||
|
||||
public:
|
||||
call_stack_list(QWidget* parent);
|
||||
void UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm);
|
||||
|
||||
Q_SIGNALS:
|
||||
void RequestShowAddress(u32 addr, bool force = false);
|
||||
@ -23,6 +22,4 @@ public Q_SLOTS:
|
||||
void HandleUpdate(std::vector<std::pair<u32, u32>> call_stack);
|
||||
private Q_SLOTS:
|
||||
void OnCallStackListDoubleClicked();
|
||||
private:
|
||||
std::weak_ptr<cpu_thread> cpu;
|
||||
};
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/RSX/RSXDisAsm.h"
|
||||
#include "Emu/Cell/PPUDisAsm.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUDisAsm.h"
|
||||
@ -42,7 +43,7 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *
|
||||
EnableUpdateTimer(true);
|
||||
|
||||
m_mono = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
m_mono.setPointSize(10);
|
||||
m_mono.setPointSize(9);
|
||||
|
||||
QVBoxLayout* vbox_p_main = new QVBoxLayout();
|
||||
vbox_p_main->setContentsMargins(5, 5, 5, 5);
|
||||
@ -137,7 +138,7 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *
|
||||
|
||||
connect(m_btn_run, &QAbstractButton::clicked, [this]()
|
||||
{
|
||||
if (const auto cpu = this->cpu.lock())
|
||||
if (const auto cpu = get_cpu())
|
||||
{
|
||||
// Alter dbg_pause bit state (disable->enable, enable->disable)
|
||||
const auto old = cpu->state.xor_fetch(cpu_flag::dbg_pause);
|
||||
@ -229,7 +230,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cpu = this->cpu.lock();
|
||||
const auto cpu = get_cpu();
|
||||
int i = m_debugger_list->currentRow();
|
||||
|
||||
switch (event->key())
|
||||
@ -248,11 +249,13 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
"\nKey R: Registers Editor for selected thread."
|
||||
"\nKey N: Show next instruction the thread will execute after marked instruction, does nothing if target is not predictable."
|
||||
"\nKey M: Show the Memory Viewer with initial address pointing to the marked instruction."
|
||||
"\nKey I: Show RSX method detail."
|
||||
"\nKey F10: Perform single-stepping on instructions."
|
||||
"\nKey F11: Perform step-over on instructions. (skip function calls)"
|
||||
"\nKey F1: Show this help dialog."));
|
||||
"\nKey F1: Show this help dialog."
|
||||
"\nDouble-click: Set breakpoints."));
|
||||
|
||||
l->setFont([](QFont f) { f.setPointSize(9); return f; }(l->font()));
|
||||
gui::utils::set_font_size(*l, 9);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->addWidget(l);
|
||||
@ -291,8 +294,11 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
case Qt::Key_E:
|
||||
{
|
||||
instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, cpu, m_disasm.get());
|
||||
dlg->show();
|
||||
if (m_cpu)
|
||||
{
|
||||
instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, m_cpu, m_disasm.get());
|
||||
dlg->show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Qt::Key_F:
|
||||
@ -302,13 +308,16 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
return;
|
||||
}
|
||||
|
||||
static_cast<spu_thread*>(cpu.get())->debugger_float_mode ^= 1; // Switch mode
|
||||
static_cast<spu_thread*>(cpu)->debugger_float_mode ^= 1; // Switch mode
|
||||
return;
|
||||
}
|
||||
case Qt::Key_R:
|
||||
{
|
||||
register_editor_dialog* dlg = new register_editor_dialog(this, cpu, m_disasm.get());
|
||||
dlg->show();
|
||||
if (m_cpu)
|
||||
{
|
||||
register_editor_dialog* dlg = new register_editor_dialog(this, m_cpu, m_disasm.get());
|
||||
dlg->show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Qt::Key_S:
|
||||
@ -320,7 +329,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
return;
|
||||
}
|
||||
|
||||
static_cast<spu_thread*>(cpu.get())->capture_local_storage();
|
||||
static_cast<spu_thread*>(cpu)->capture_local_storage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -335,7 +344,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
res = op_branch_targets(pc, spu_opcode_t{static_cast<spu_thread*>(cpu.get())->_ref<u32>(pc)});
|
||||
res = op_branch_targets(pc, spu_opcode_t{static_cast<spu_thread*>(cpu)->_ref<u32>(pc)});
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
@ -358,7 +367,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
case Qt::Key_M:
|
||||
{
|
||||
// Memory viewer
|
||||
idm::make<memory_viewer_handle>(this, pc, cpu);
|
||||
if (m_cpu) idm::make<memory_viewer_handle>(this, pc, m_cpu);
|
||||
return;
|
||||
}
|
||||
case Qt::Key_F10:
|
||||
@ -375,8 +384,17 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
rsx::thread* debugger_frame::get_rsx()
|
||||
cpu_thread* debugger_frame::get_cpu()
|
||||
{
|
||||
// Wait flag is raised by the thread itself, acknowledging exit
|
||||
if (m_cpu)
|
||||
{
|
||||
if (+m_cpu->state + cpu_flag::wait + cpu_flag::exit != +m_cpu->state)
|
||||
{
|
||||
return m_cpu.get();
|
||||
}
|
||||
}
|
||||
|
||||
// m_rsx is raw pointer, when emulation is stopped it won't be cleared
|
||||
// Therefore need to do invalidation checks manually
|
||||
|
||||
@ -397,10 +415,9 @@ void debugger_frame::UpdateUI()
|
||||
{
|
||||
UpdateUnitList();
|
||||
|
||||
const auto cpu = this->cpu.lock();
|
||||
const auto rsx = this->get_rsx();
|
||||
const auto cpu = get_cpu();
|
||||
|
||||
if (!cpu && !rsx)
|
||||
if (!cpu)
|
||||
{
|
||||
if (m_last_pc != umax || !m_last_query_state.empty())
|
||||
{
|
||||
@ -409,25 +426,17 @@ void debugger_frame::UpdateUI()
|
||||
DoUpdate();
|
||||
}
|
||||
}
|
||||
else if (rsx)
|
||||
{
|
||||
if (m_last_pc != rsx->ctrl->get || !m_last_query_state.empty())
|
||||
{
|
||||
m_last_query_state.clear();
|
||||
m_last_pc = rsx->ctrl->get;
|
||||
DoUpdate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cia = cpu->get_pc();
|
||||
const auto size_context = cpu->id_type() == 1 ? sizeof(ppu_thread) : sizeof(spu_thread);
|
||||
const auto size_context = cpu->id_type() == 1 ? sizeof(ppu_thread) :
|
||||
cpu->id_type() == 2 ? sizeof(spu_thread) : sizeof(cpu_thread);
|
||||
|
||||
if (m_last_pc != cia || m_last_query_state.size() != size_context || std::memcmp(m_last_query_state.data(), cpu.get(), size_context))
|
||||
if (m_last_pc != cia || m_last_query_state.size() != size_context || std::memcmp(m_last_query_state.data(), cpu, size_context))
|
||||
{
|
||||
// Copy thread data
|
||||
m_last_query_state.resize(size_context);
|
||||
std::memcpy(m_last_query_state.data(), cpu.get(), size_context);
|
||||
std::memcpy(m_last_query_state.data(), cpu, size_context);
|
||||
|
||||
m_last_pc = cia;
|
||||
DoUpdate();
|
||||
@ -455,11 +464,13 @@ void debugger_frame::UpdateUnitList()
|
||||
{
|
||||
const u64 threads_created = cpu_thread::g_threads_created;
|
||||
const u64 threads_deleted = cpu_thread::g_threads_deleted;
|
||||
const system_state emu_state = Emu.GetStatus();
|
||||
|
||||
if (threads_created != m_threads_created || threads_deleted != m_threads_deleted)
|
||||
if (threads_created != m_threads_created || threads_deleted != m_threads_deleted || emu_state != m_emu_state)
|
||||
{
|
||||
m_threads_created = threads_created;
|
||||
m_threads_deleted = threads_deleted;
|
||||
m_emu_state = emu_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -472,6 +483,8 @@ void debugger_frame::UpdateUnitList()
|
||||
|
||||
const auto on_select = [&](u32 id, cpu_thread& cpu)
|
||||
{
|
||||
if (emu_state == system_state::stopped) return;
|
||||
|
||||
QVariant var_cpu = QVariant::fromValue<std::weak_ptr<cpu_thread>>(
|
||||
id >> 24 == 1 ? static_cast<std::weak_ptr<cpu_thread>>(idm::get_unlocked<named_thread<ppu_thread>>(id)) : idm::get_unlocked<named_thread<spu_thread>>(id));
|
||||
|
||||
@ -488,7 +501,7 @@ void debugger_frame::UpdateUnitList()
|
||||
idm::select<named_thread<ppu_thread>>(on_select);
|
||||
idm::select<named_thread<spu_thread>>(on_select);
|
||||
|
||||
if (auto render = g_fxo->get<rsx::thread>(); render && render->ctrl)
|
||||
if (auto render = g_fxo->get<rsx::thread>(); emu_state != system_state::stopped && render && render->ctrl)
|
||||
{
|
||||
QVariant var_cpu = QVariant::fromValue<rsx::thread*>(render);
|
||||
m_choice_units->addItem("RSX[0x55555555]", var_cpu);
|
||||
@ -503,24 +516,34 @@ void debugger_frame::UpdateUnitList()
|
||||
|
||||
void debugger_frame::OnSelectUnit()
|
||||
{
|
||||
if (m_choice_units->count() < 1) return;
|
||||
if (m_choice_units->count() < 1)
|
||||
{
|
||||
m_debugger_list->UpdateCPUData(nullptr, nullptr);
|
||||
m_breakpoint_list->UpdateCPUData(nullptr, nullptr);
|
||||
m_disasm.reset();
|
||||
m_cpu.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto weak = m_choice_units->currentData().value<std::weak_ptr<cpu_thread>>();
|
||||
const auto render = m_choice_units->currentData().value<rsx::thread*>();
|
||||
|
||||
if (!render && !weak.owner_before(cpu) && !cpu.owner_before(weak))
|
||||
if (m_emu_state != system_state::stopped)
|
||||
{
|
||||
// They match, nothing to do.
|
||||
return;
|
||||
}
|
||||
if (!render && !weak.owner_before(m_cpu) && !m_cpu.owner_before(weak))
|
||||
{
|
||||
// They match, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (render && render == this->get_rsx())
|
||||
{
|
||||
return;
|
||||
if (render && render == get_cpu())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_disasm.reset();
|
||||
cpu.reset();
|
||||
m_cpu.reset();
|
||||
m_rsx = nullptr;
|
||||
|
||||
if (!weak.expired())
|
||||
@ -531,16 +554,16 @@ void debugger_frame::OnSelectUnit()
|
||||
{
|
||||
if (cpu0.get() == idm::check<named_thread<ppu_thread>>(cpu0->id))
|
||||
{
|
||||
cpu = cpu0;
|
||||
m_disasm = std::make_unique<PPUDisAsm>(CPUDisAsm_InterpreterMode, vm::g_sudo_addr);
|
||||
m_cpu = cpu0;
|
||||
m_disasm = std::make_shared<PPUDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr);
|
||||
}
|
||||
}
|
||||
else if (cpu0->id_type() == 2)
|
||||
{
|
||||
if (cpu0.get() == idm::check<named_thread<spu_thread>>(cpu0->id))
|
||||
{
|
||||
cpu = cpu0;
|
||||
m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode, static_cast<const spu_thread*>(cpu0.get())->ls);
|
||||
m_cpu = cpu0;
|
||||
m_disasm = std::make_shared<SPUDisAsm>(cpu_disasm_mode::interpreter, static_cast<const spu_thread*>(cpu0.get())->ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -548,13 +571,17 @@ void debugger_frame::OnSelectUnit()
|
||||
else
|
||||
{
|
||||
m_rsx = render;
|
||||
|
||||
if (get_cpu())
|
||||
{
|
||||
m_disasm = std::make_shared<RSXDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr, m_rsx);
|
||||
}
|
||||
}
|
||||
|
||||
EnableButtons(true);
|
||||
|
||||
m_debugger_list->UpdateCPUData(this->cpu, m_disasm);
|
||||
m_breakpoint_list->UpdateCPUData(this->cpu, m_disasm);
|
||||
m_call_stack_list->UpdateCPUData(this->cpu, m_disasm);
|
||||
m_debugger_list->UpdateCPUData(get_cpu(), m_disasm.get());
|
||||
m_breakpoint_list->UpdateCPUData(get_cpu(), m_disasm.get());
|
||||
DoUpdate();
|
||||
UpdateUI();
|
||||
}
|
||||
@ -562,7 +589,7 @@ void debugger_frame::OnSelectUnit()
|
||||
void debugger_frame::DoUpdate()
|
||||
{
|
||||
// Check if we need to disable a step over bp
|
||||
if (auto cpu0 = cpu.lock(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint)
|
||||
if (auto cpu0 = get_cpu(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint)
|
||||
{
|
||||
m_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint);
|
||||
m_last_step_over_breakpoint = -1;
|
||||
@ -574,10 +601,9 @@ void debugger_frame::DoUpdate()
|
||||
|
||||
void debugger_frame::WritePanels()
|
||||
{
|
||||
const auto cpu = this->cpu.lock();
|
||||
const auto rsx = this->get_rsx();
|
||||
const auto cpu = get_cpu();
|
||||
|
||||
if (!cpu && !rsx)
|
||||
if (!cpu)
|
||||
{
|
||||
m_misc_state->clear();
|
||||
m_regs->clear();
|
||||
@ -588,15 +614,15 @@ void debugger_frame::WritePanels()
|
||||
|
||||
loc = m_misc_state->verticalScrollBar()->value();
|
||||
m_misc_state->clear();
|
||||
m_misc_state->setText(qstr(rsx ? "" : cpu->dump_misc()));
|
||||
m_misc_state->setText(qstr(cpu->dump_misc()));
|
||||
m_misc_state->verticalScrollBar()->setValue(loc);
|
||||
|
||||
loc = m_regs->verticalScrollBar()->value();
|
||||
m_regs->clear();
|
||||
m_regs->setText(qstr(rsx ? rsx->dump_regs() : cpu->dump_regs()));
|
||||
m_regs->setText(qstr(cpu->dump_regs()));
|
||||
m_regs->verticalScrollBar()->setValue(loc);
|
||||
|
||||
Q_EMIT CallStackUpdateRequested(rsx ? rsx->dump_callstack() : cpu->dump_callstack_list());
|
||||
Q_EMIT CallStackUpdateRequested(cpu->dump_callstack_list());
|
||||
}
|
||||
|
||||
void debugger_frame::ShowGotoAddressDialog()
|
||||
@ -615,7 +641,7 @@ void debugger_frame::ShowGotoAddressDialog()
|
||||
expression_input->setFont(m_mono);
|
||||
expression_input->setMaxLength(18);
|
||||
|
||||
if (auto thread = cpu.lock(); !thread || thread->id_type() != 2)
|
||||
if (auto thread = get_cpu(); !thread || thread->id_type() != 2)
|
||||
{
|
||||
expression_input->setValidator(new QRegExpValidator(QRegExp("^(0[xX])?0*[a-fA-F0-9]{0,8}$")));
|
||||
}
|
||||
@ -639,7 +665,7 @@ void debugger_frame::ShowGotoAddressDialog()
|
||||
|
||||
dlg->setLayout(vbox_panel);
|
||||
|
||||
const auto cpu = this->cpu.lock();
|
||||
const auto cpu = get_cpu();
|
||||
const QFont font = expression_input->font();
|
||||
|
||||
// -1 from get_pc() turns into 0
|
||||
@ -670,8 +696,7 @@ u64 debugger_frame::EvaluateExpression(const QString& expression)
|
||||
const u64 res = static_cast<u64>(fixed_expression.toULong(&ok, 16));
|
||||
|
||||
if (ok) return res;
|
||||
if (auto thread = get_rsx()) return thread->ctrl->get;
|
||||
if (auto thread = cpu.lock()) return thread->get_pc();
|
||||
if (auto thread = get_cpu()) return thread->get_pc();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -687,17 +712,16 @@ void debugger_frame::ClearCallStack()
|
||||
|
||||
void debugger_frame::ShowPC()
|
||||
{
|
||||
const auto cpu0 = cpu.lock();
|
||||
const auto cpu0 = get_cpu();
|
||||
|
||||
const u32 pc = get_rsx() ? +m_rsx->ctrl->get
|
||||
: (cpu0 ? cpu0->get_pc() : 0);
|
||||
const u32 pc = (cpu0 ? cpu0->get_pc() : 0);
|
||||
|
||||
m_debugger_list->ShowAddress(pc);
|
||||
}
|
||||
|
||||
void debugger_frame::DoStep(bool stepOver)
|
||||
{
|
||||
if (const auto cpu = this->cpu.lock())
|
||||
if (const auto cpu = get_cpu())
|
||||
{
|
||||
bool should_step_over = stepOver && cpu->id_type() == 1;
|
||||
|
||||
@ -741,7 +765,7 @@ void debugger_frame::EnableUpdateTimer(bool enable)
|
||||
|
||||
void debugger_frame::EnableButtons(bool enable)
|
||||
{
|
||||
if (cpu.expired()) enable = false;
|
||||
if (!get_cpu()) enable = false;
|
||||
|
||||
m_go_to_addr->setEnabled(enable);
|
||||
m_go_to_pc->setEnabled(enable);
|
||||
|
@ -25,6 +25,8 @@ namespace rsx
|
||||
class thread;
|
||||
}
|
||||
|
||||
enum class system_state : u32;
|
||||
|
||||
class debugger_frame : public custom_dock_widget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -49,12 +51,13 @@ class debugger_frame : public custom_dock_widget
|
||||
|
||||
u64 m_threads_created = -1;
|
||||
u64 m_threads_deleted = -1;
|
||||
system_state m_emu_state{};
|
||||
u32 m_last_pc = -1;
|
||||
std::vector<char> m_last_query_state;
|
||||
u32 m_last_step_over_breakpoint = -1;
|
||||
|
||||
std::shared_ptr<CPUDisAsm> m_disasm;
|
||||
std::weak_ptr<cpu_thread> cpu;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm; // Only shared to allow base/derived functionality
|
||||
std::shared_ptr<cpu_thread> m_cpu;
|
||||
rsx::thread* m_rsx = nullptr;
|
||||
|
||||
breakpoint_list* m_breakpoint_list;
|
||||
@ -64,7 +67,7 @@ class debugger_frame : public custom_dock_widget
|
||||
|
||||
std::shared_ptr<gui_settings> xgui_settings;
|
||||
|
||||
rsx::thread* get_rsx(); // Do not read m_rsx directy, use this instead.
|
||||
cpu_thread* get_cpu();
|
||||
public:
|
||||
explicit debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *parent = 0);
|
||||
|
||||
|
@ -1,15 +1,19 @@
|
||||
#include "debugger_list.h"
|
||||
#include "gui_settings.h"
|
||||
#include "qt_utils.h"
|
||||
#include "breakpoint_handler.h"
|
||||
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/CPU/CPUDisAsm.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/RSX/RSXDisAsm.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -28,9 +32,9 @@ debugger_list::debugger_list(QWidget* parent, std::shared_ptr<gui_settings> sett
|
||||
setSizeAdjustPolicy(QListWidget::AdjustToContents);
|
||||
}
|
||||
|
||||
void debugger_list::UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm)
|
||||
void debugger_list::UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm)
|
||||
{
|
||||
this->cpu = cpu;
|
||||
m_cpu = cpu;
|
||||
m_disasm = disasm;
|
||||
}
|
||||
|
||||
@ -43,14 +47,21 @@ void debugger_list::ShowAddress(u32 addr, bool force)
|
||||
{
|
||||
auto IsBreakpoint = [this](u32 pc)
|
||||
{
|
||||
return m_breakpoint_handler->HasBreakpoint(pc);
|
||||
return m_cpu && m_cpu->id_type() == 1 && m_breakpoint_handler->HasBreakpoint(pc);
|
||||
};
|
||||
|
||||
const bool center_pc = xgui_settings->GetValue(gui::d_centerPC).toBool();
|
||||
bool center_pc = xgui_settings->GetValue(gui::d_centerPC).toBool();
|
||||
|
||||
// How many spaces addr can move down without us needing to move the entire view
|
||||
const u32 addr_margin = (m_item_count / (center_pc ? 2 : 1) - 4); // 4 is just a buffer of 4 spaces at the bottom
|
||||
|
||||
if (m_cpu && m_cpu->id_type() == 0x55)
|
||||
{
|
||||
// RSX instructions' size is not consistent, this is the only valid mode for it
|
||||
force = true;
|
||||
center_pc = false;
|
||||
}
|
||||
|
||||
if (force || addr - m_pc > addr_margin * 4) // 4 is the number of bytes in each instruction
|
||||
{
|
||||
if (center_pc)
|
||||
@ -63,12 +74,10 @@ void debugger_list::ShowAddress(u32 addr, bool force)
|
||||
}
|
||||
}
|
||||
|
||||
const auto cpu = this->cpu.lock();
|
||||
|
||||
const auto default_foreground = palette().color(foregroundRole());
|
||||
const auto default_background = palette().color(backgroundRole());
|
||||
|
||||
if (!cpu || !m_disasm)
|
||||
if (!m_cpu || !m_disasm || +m_cpu->state + cpu_flag::exit + cpu_flag::wait == +m_cpu->state)
|
||||
{
|
||||
for (uint i = 0; i < m_item_count; ++i)
|
||||
{
|
||||
@ -79,14 +88,14 @@ void debugger_list::ShowAddress(u32 addr, bool force)
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool is_spu = cpu->id_type() != 1;
|
||||
const bool is_spu = m_cpu->id_type() == 2;
|
||||
const u32 address_limits = (is_spu ? 0x3fffc : ~3);
|
||||
m_pc &= address_limits;
|
||||
u32 pc = m_pc;
|
||||
|
||||
for (uint i = 0, count = 4; i<m_item_count; ++i, pc = (pc + count) & address_limits)
|
||||
{
|
||||
if (cpu->is_paused() && pc == cpu->get_pc())
|
||||
if (m_cpu->is_paused() && pc == m_cpu->get_pc())
|
||||
{
|
||||
item(i)->setForeground(m_text_color_pc);
|
||||
item(i)->setBackground(m_color_pc);
|
||||
@ -102,14 +111,14 @@ void debugger_list::ShowAddress(u32 addr, bool force)
|
||||
item(i)->setBackground(default_background);
|
||||
}
|
||||
|
||||
if (!is_spu && !vm::check_addr(pc))
|
||||
if (m_cpu->id_type() == 1 && !vm::check_addr(pc))
|
||||
{
|
||||
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc)));
|
||||
count = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_spu && !vm::check_addr(pc, vm::page_executable))
|
||||
if (m_cpu->id_type() == 1 && !vm::check_addr(pc, vm::page_executable))
|
||||
{
|
||||
const u32 data = *vm::get_super_ptr<atomic_be_t<u32>>(pc);
|
||||
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc,
|
||||
@ -123,6 +132,13 @@ void debugger_list::ShowAddress(u32 addr, bool force)
|
||||
|
||||
count = m_disasm->disasm(pc);
|
||||
|
||||
if (!count)
|
||||
{
|
||||
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ??? ?? ??", pc)));
|
||||
count = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(m_disasm->last_opcode));
|
||||
}
|
||||
}
|
||||
@ -130,6 +146,19 @@ void debugger_list::ShowAddress(u32 addr, bool force)
|
||||
setLineWidth(-1);
|
||||
}
|
||||
|
||||
void debugger_list::scroll(s32 steps)
|
||||
{
|
||||
while (m_cpu && m_cpu->id_type() == 0x55 && steps > 0)
|
||||
{
|
||||
// If scrolling forwards (downwards), we can skip entire commands
|
||||
// Backwards is impossible though
|
||||
m_pc += std::max<u32>(m_disasm->disasm(m_pc), 4);
|
||||
steps--;
|
||||
}
|
||||
|
||||
ShowAddress(m_pc + (steps * 4), true);
|
||||
}
|
||||
|
||||
void debugger_list::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (!isActiveWindow())
|
||||
@ -139,14 +168,84 @@ void debugger_list::keyPressEvent(QKeyEvent* event)
|
||||
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_PageUp: ShowAddress(m_pc - (m_item_count * 4), true); return;
|
||||
case Qt::Key_PageDown: ShowAddress(m_pc + (m_item_count * 4), true); return;
|
||||
case Qt::Key_Up: ShowAddress(m_pc - 4, true); return;
|
||||
case Qt::Key_Down: ShowAddress(m_pc + 4, true); return;
|
||||
case Qt::Key_PageUp: scroll(0 - m_item_count); return;
|
||||
case Qt::Key_PageDown: scroll(m_item_count); return;
|
||||
case Qt::Key_Up: scroll(1); return;
|
||||
case Qt::Key_Down: scroll(-1); return;
|
||||
case Qt::Key_I:
|
||||
{
|
||||
if (m_cpu && m_cpu->id_type() == 0x55)
|
||||
{
|
||||
create_rsx_command_detail(m_pc, currentRow());
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void debugger_list::showEvent(QShowEvent* event)
|
||||
{
|
||||
if (m_cmd_detail) m_cmd_detail->show();
|
||||
QListWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void debugger_list::hideEvent(QHideEvent* event)
|
||||
{
|
||||
if (m_cmd_detail) m_cmd_detail->hide();
|
||||
QListWidget::hideEvent(event);
|
||||
}
|
||||
|
||||
void debugger_list::create_rsx_command_detail(u32 pc, int row)
|
||||
{
|
||||
if (row < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (; row > 0; row--)
|
||||
{
|
||||
// Skip methods
|
||||
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);
|
||||
|
||||
// Either invalid or not a method
|
||||
if (rsx_dis.disasm(pc) <= 4) return;
|
||||
|
||||
if (m_cmd_detail)
|
||||
{
|
||||
// Edit the existing dialog
|
||||
m_detail_label->setText(QString::fromStdString(rsx_dis.last_opcode));
|
||||
m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint());
|
||||
return;
|
||||
}
|
||||
|
||||
m_cmd_detail = new QDialog(this);
|
||||
m_cmd_detail->setWindowTitle(tr("RSX Command Detail"));
|
||||
|
||||
m_detail_label = new QLabel(QString::fromStdString(rsx_dis.last_opcode), this);
|
||||
m_detail_label->setFont(font());
|
||||
gui::utils::set_font_size(*m_detail_label, 10);
|
||||
m_detail_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->addWidget(m_detail_label);
|
||||
m_cmd_detail->setLayout(layout);
|
||||
m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint());
|
||||
m_cmd_detail->show();
|
||||
|
||||
connect(m_cmd_detail, &QDialog::finished, [this](int)
|
||||
{
|
||||
// Cleanup
|
||||
std::exchange(m_cmd_detail, nullptr)->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void debugger_list::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
@ -169,7 +268,7 @@ void debugger_list::wheelEvent(QWheelEvent* event)
|
||||
const int value = numSteps.y();
|
||||
const auto direction = (event->modifiers() == Qt::ControlModifier);
|
||||
|
||||
ShowAddress(m_pc + (direction ? value : -value) * 4, true);
|
||||
scroll(direction ? value : -value);
|
||||
}
|
||||
|
||||
void debugger_list::resizeEvent(QResizeEvent* event)
|
||||
|
@ -10,6 +10,7 @@ class breakpoint_handler;
|
||||
class CPUDisAsm;
|
||||
class cpu_thread;
|
||||
class gui_settings;
|
||||
class QLabel;
|
||||
|
||||
class debugger_list : public QListWidget
|
||||
{
|
||||
@ -27,7 +28,7 @@ Q_SIGNALS:
|
||||
void BreakpointRequested(u32 loc);
|
||||
public:
|
||||
debugger_list(QWidget* parent, std::shared_ptr<gui_settings> settings, breakpoint_handler* handler);
|
||||
void UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm);
|
||||
void UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm);
|
||||
public Q_SLOTS:
|
||||
void ShowAddress(u32 addr, bool force = false);
|
||||
protected:
|
||||
@ -35,6 +36,10 @@ protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void scroll(s32 steps);
|
||||
void create_rsx_command_detail(u32 pc, int row);
|
||||
private:
|
||||
/**
|
||||
* It really upsetted me I had to copy this code to make debugger_list/frame not circularly dependent.
|
||||
@ -44,6 +49,8 @@ private:
|
||||
std::shared_ptr<gui_settings> xgui_settings;
|
||||
|
||||
breakpoint_handler* m_breakpoint_handler;
|
||||
std::weak_ptr<cpu_thread> cpu;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm;
|
||||
cpu_thread* m_cpu = nullptr;
|
||||
CPUDisAsm* m_disasm;
|
||||
QDialog* m_cmd_detail = nullptr;
|
||||
QLabel* m_detail_label = nullptr;
|
||||
};
|
||||
|
@ -17,14 +17,13 @@ instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, c
|
||||
: QDialog(parent)
|
||||
, m_pc(_pc)
|
||||
, m_disasm(_disasm)
|
||||
, cpu(_cpu)
|
||||
, m_cpu(_cpu)
|
||||
{
|
||||
setWindowTitle(tr("Edit instruction"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setMinimumSize(300, sizeHint().height());
|
||||
|
||||
const auto cpu = _cpu.get();
|
||||
m_cpu_offset = cpu->id_type() == 2 ? static_cast<spu_thread&>(*cpu).ls : vm::g_sudo_addr;
|
||||
m_cpu_offset = m_cpu->id_type() == 2 ? static_cast<spu_thread&>(*m_cpu).ls : vm::g_sudo_addr;
|
||||
QString instruction = qstr(fmt::format("%08x", *reinterpret_cast<be_t<u32>*>(m_cpu_offset + m_pc)));
|
||||
|
||||
QVBoxLayout* vbox_panel(new QVBoxLayout());
|
||||
@ -81,7 +80,7 @@ instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, c
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to parse PPU instruction."));
|
||||
return;
|
||||
}
|
||||
else if (cpu->id_type() == 1)
|
||||
else if (m_cpu->id_type() == 1)
|
||||
{
|
||||
if (!ppu_patch(m_pc, static_cast<u32>(opcode)))
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ private:
|
||||
QLabel* m_preview;
|
||||
|
||||
public:
|
||||
std::weak_ptr<cpu_thread> cpu;
|
||||
std::shared_ptr<cpu_thread> m_cpu;
|
||||
|
||||
instruction_editor_dialog(QWidget *parent, u32 _pc, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm);
|
||||
|
||||
|
@ -1261,9 +1261,7 @@ void main_window::OnEmuStop()
|
||||
const QString play_tooltip = Emu.IsReady() ? tr("Play %0").arg(title) : tr("Resume %0").arg(title);
|
||||
const QString restart_tooltip = tr("Restart %0").arg(title);
|
||||
|
||||
m_debugger_frame->EnableButtons(false);
|
||||
m_debugger_frame->ClearBreakpoints();
|
||||
m_debugger_frame->ClearCallStack();
|
||||
m_debugger_frame->UpdateUI();
|
||||
|
||||
ui->sysPauseAct->setText(Emu.IsReady() ? tr("&Play\tCtrl+E") : tr("&Resume\tCtrl+E"));
|
||||
ui->sysPauseAct->setIcon(m_icon_play);
|
||||
|
@ -49,6 +49,14 @@ namespace gui
|
||||
// Returns the width of the text
|
||||
int get_label_width(const QString& text, const QFont* font = nullptr);
|
||||
|
||||
template <typename T>
|
||||
void set_font_size(T& qobj, int size)
|
||||
{
|
||||
QFont font = qobj.font();
|
||||
font.setPointSize(size);
|
||||
qobj.setFont(font);
|
||||
}
|
||||
|
||||
// Returns the part of the image loaded from path that is inside the bounding box of its opaque areas
|
||||
QImage get_opaque_image_area(const QString& path);
|
||||
|
||||
|
@ -61,7 +61,7 @@ enum registers : int
|
||||
register_editor_dialog::register_editor_dialog(QWidget *parent, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm)
|
||||
: QDialog(parent)
|
||||
, m_disasm(_disasm)
|
||||
, cpu(_cpu)
|
||||
, m_cpu(_cpu)
|
||||
{
|
||||
setWindowTitle(tr("Edit registers"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
@ -97,7 +97,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
|
||||
|
||||
if (1)
|
||||
{
|
||||
if (_cpu->id_type() == 1)
|
||||
if (m_cpu->id_type() == 1)
|
||||
{
|
||||
for (int i = ppu_r0; i <= ppu_r31; i++) m_register_combo->addItem(qstr(fmt::format("r%d", i % 32)), i);
|
||||
for (int i = ppu_f0; i <= ppu_f31; i++) m_register_combo->addItem(qstr(fmt::format("f%d", i % 32)), i);
|
||||
@ -113,7 +113,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
|
||||
m_register_combo->addItem("Priority", +PPU_PRIO);
|
||||
//m_register_combo->addItem("Priority 2", +PPU_PRIO2);
|
||||
}
|
||||
else if (_cpu->id_type() == 2)
|
||||
else if (m_cpu->id_type() == 2)
|
||||
{
|
||||
for (int i = spu_r0; i <= spu_r127; i++) m_register_combo->addItem(qstr(fmt::format("r%d", i % 128)), i);
|
||||
m_register_combo->addItem("MFC Pending Events", +MFC_PEVENTS);
|
||||
@ -144,7 +144,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
|
||||
setModal(true);
|
||||
|
||||
// Events
|
||||
connect(button_ok, &QAbstractButton::clicked, this, [=, this](){OnOkay(_cpu); accept();});
|
||||
connect(button_ok, &QAbstractButton::clicked, this, [this](){ OnOkay(); accept(); });
|
||||
connect(button_cancel, &QAbstractButton::clicked, this, ®ister_editor_dialog::reject);
|
||||
connect(m_register_combo, &QComboBox::currentTextChanged, this, [this](const QString&)
|
||||
{
|
||||
@ -159,15 +159,14 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
|
||||
|
||||
void register_editor_dialog::updateRegister(int reg)
|
||||
{
|
||||
const auto cpu = this->cpu.lock();
|
||||
std::string str = sstr(tr("Error parsing register value!"));
|
||||
|
||||
if (!cpu)
|
||||
if (!m_cpu)
|
||||
{
|
||||
}
|
||||
else if (cpu->id_type() == 1)
|
||||
else if (m_cpu->id_type() == 1)
|
||||
{
|
||||
const auto& ppu = *static_cast<const ppu_thread*>(cpu.get());
|
||||
const auto& ppu = *static_cast<const ppu_thread*>(m_cpu.get());
|
||||
|
||||
if (reg >= ppu_r0 && reg <= ppu_v31)
|
||||
{
|
||||
@ -190,9 +189,9 @@ void register_editor_dialog::updateRegister(int reg)
|
||||
else if (reg == RESERVATION_LOST) str = sstr(ppu.raddr ? tr("Lose reservation on OK") : tr("Reservation is inactive"));
|
||||
else if (reg == PC) str = fmt::format("%08x", ppu.cia);
|
||||
}
|
||||
else if (cpu->id_type() == 2)
|
||||
else if (m_cpu->id_type() == 2)
|
||||
{
|
||||
const auto& spu = *static_cast<const spu_thread*>(cpu.get());
|
||||
const auto& spu = *static_cast<const spu_thread*>(m_cpu.get());
|
||||
|
||||
if (reg >= spu_r0 && reg <= spu_r127)
|
||||
{
|
||||
@ -216,10 +215,8 @@ void register_editor_dialog::updateRegister(int reg)
|
||||
m_value_line->setText(qstr(str));
|
||||
}
|
||||
|
||||
void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
|
||||
void register_editor_dialog::OnOkay()
|
||||
{
|
||||
const auto cpu = _cpu.get();
|
||||
|
||||
const int reg = m_register_combo->currentData().toInt();
|
||||
std::string value = sstr(m_value_line->text());
|
||||
|
||||
@ -247,12 +244,12 @@ void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
|
||||
}
|
||||
}
|
||||
|
||||
if (!cpu || value.empty())
|
||||
if (!m_cpu || value.empty())
|
||||
{
|
||||
}
|
||||
else if (cpu->id_type() == 1)
|
||||
else if (m_cpu->id_type() == 1)
|
||||
{
|
||||
auto& ppu = *static_cast<ppu_thread*>(cpu);
|
||||
auto& ppu = *static_cast<ppu_thread*>(m_cpu.get());
|
||||
|
||||
if (reg >= ppu_r0 && reg <= ppu_v31)
|
||||
{
|
||||
@ -337,9 +334,9 @@ void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (cpu->id_type() == 2)
|
||||
else if (m_cpu->id_type() == 2)
|
||||
{
|
||||
auto& spu = *static_cast<spu_thread*>(cpu);
|
||||
auto& spu = *static_cast<spu_thread*>(m_cpu.get());
|
||||
|
||||
if (reg >= spu_r0 && reg <= spu_r127)
|
||||
{
|
||||
|
@ -19,14 +19,13 @@ class register_editor_dialog : public QDialog
|
||||
QComboBox* m_register_combo;
|
||||
QLineEdit* m_value_line;
|
||||
|
||||
public:
|
||||
std::weak_ptr<cpu_thread> cpu;
|
||||
|
||||
public:
|
||||
register_editor_dialog(QWidget *parent, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm);
|
||||
|
||||
private:
|
||||
void OnOkay(const std::shared_ptr<cpu_thread>& _cpu);
|
||||
void OnOkay();
|
||||
|
||||
std::shared_ptr<cpu_thread> m_cpu;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateRegister(int reg);
|
||||
|
Loading…
Reference in New Issue
Block a user