From ea9cf0c8f90c12685e1d240cf2bd248db6b58cc1 Mon Sep 17 00:00:00 2001 From: emoose Date: Mon, 7 Oct 2024 22:26:41 +0100 Subject: [PATCH] [GDBStub] Allow register writes, disable ctx promotion for --debug Register changes won't always register if context promotion pass is enabled, fortunately this was noted by benvanik at https://github.com/xenia-project/xenia/blob/3d30b2eec3ab1f83140b09745bee881fb5d5dde2/src/xenia/debug/ui/debug_window.cc#L1004 Disabling context promotion pass if --debug is set seems to allow register updates fine, without too much performance loss on my end. Still need to implement PC and CR reg writes, and the WriteRegistersAll cmd Not completely sure if all register writes actually take effect yet, seems GPR are fine at least but unsure about others --- src/xenia/cpu/ppc/ppc_translator.cc | 7 ++- src/xenia/debug/gdb/gdbstub.cc | 69 +++++++++++++++++++++++++++-- src/xenia/debug/gdb/gdbstub.h | 3 ++ 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/xenia/cpu/ppc/ppc_translator.cc b/src/xenia/cpu/ppc/ppc_translator.cc index c559abe89..09753066c 100644 --- a/src/xenia/cpu/ppc/ppc_translator.cc +++ b/src/xenia/cpu/ppc/ppc_translator.cc @@ -33,6 +33,8 @@ DEFINE_bool(disable_context_promotion, false, "some sports games, but will reduce performance.", "CPU"); +DECLARE_bool(debug); + namespace xe { namespace cpu { namespace ppc { @@ -59,7 +61,10 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { // Passes are executed in the order they are added. Multiple of the same // pass type may be used. - if (!cvars::disable_context_promotion) { + + // Disable context promotion for debug, otherwise register changes won't apply + // correctly + if (!cvars::disable_context_promotion && !cvars::debug) { if (validate) { compiler_->AddPass(std::make_unique()); } diff --git a/src/xenia/debug/gdb/gdbstub.cc b/src/xenia/debug/gdb/gdbstub.cc index 448835088..7121d0668 100644 --- a/src/xenia/debug/gdb/gdbstub.cc +++ b/src/xenia/debug/gdb/gdbstub.cc @@ -523,6 +523,54 @@ std::string GDBStub::RegisterRead(xe::cpu::ThreadDebugInfo* thread, // gpr return string_util::to_hex_string((uint32_t)thread->guest_context.r[rid]); } +std::string GDBStub::RegisterWrite(xe::cpu::ThreadDebugInfo* thread, + uint32_t rid, const std::string_view value) { + auto* guest_context = thread->thread->thread_state()->context(); + switch (rid) { + // pc + case 64: + return kGdbReplyOK; // TODO: figure a way to change this + case 65: + guest_context->msr = string_util::from_string(value, true); + thread->guest_context.msr = guest_context->msr; + return kGdbReplyOK; + case 66: + // CR + return kGdbReplyOK; // TODO: figure a way to change this + case 67: + guest_context->lr = string_util::from_string(value, true); + thread->guest_context.lr = guest_context->lr; + return kGdbReplyOK; + case 68: + guest_context->ctr = string_util::from_string(value, true); + thread->guest_context.ctr = guest_context->ctr; + return kGdbReplyOK; + // xer + case 69: + return kGdbReplyOK; + case 70: + guest_context->fpscr.value = + string_util::from_string(value, true); + thread->guest_context.fpscr.value = guest_context->fpscr.value; + return kGdbReplyOK; + } + + if (rid > 70) { + return kGdbReplyError; + } + + // fpr + if (rid > 31) { + guest_context->f[rid - 32] = string_util::from_string(value, true); + thread->guest_context.f[rid - 32] = guest_context->f[rid - 32]; + return kGdbReplyOK; + } + + // gpr + guest_context->r[rid] = string_util::from_string(value, true); + thread->guest_context.r[rid] = guest_context->r[rid]; + return kGdbReplyOK; +} std::string GDBStub::RegisterRead(const std::string& data) { auto* thread = cache_.cur_thread_info(); @@ -537,6 +585,22 @@ std::string GDBStub::RegisterRead(const std::string& data) { return result; } +std::string GDBStub::RegisterWrite(const std::string& data) { + auto* thread = cache_.cur_thread_info(); + if (!thread) { + return kGdbReplyError; + } + + auto value_sep = data.find('='); + if (value_sep == std::string::npos) { + return kGdbReplyError; + } + + uint32_t rid = + string_util::from_string(data.substr(0, value_sep), true); + return RegisterWrite(thread, rid, data.data() + value_sep + 1); +} + std::string GDBStub::RegisterReadAll() { auto* thread = cache_.cur_thread_info(); if (!thread) { @@ -901,10 +965,7 @@ std::string GDBStub::HandleGDBCommand(const GDBCommand& command) { // Read register {"p", [&](const GDBCommand& cmd) { return RegisterRead(cmd.data); }}, // Write register - {"P", - [&](const GDBCommand& cmd) { - return kGdbReplyOK; // TODO: we'll just tell it write was fine - }}, + {"P", [&](const GDBCommand& cmd) { return RegisterWrite(cmd.data); }}, // Read all registers {"g", [&](const GDBCommand& cmd) { return RegisterReadAll(); }}, diff --git a/src/xenia/debug/gdb/gdbstub.h b/src/xenia/debug/gdb/gdbstub.h index 3d0ed97dd..027953805 100644 --- a/src/xenia/debug/gdb/gdbstub.h +++ b/src/xenia/debug/gdb/gdbstub.h @@ -63,7 +63,10 @@ class GDBStub : public cpu::DebugListener { std::string DebuggerDetached(); std::string RegisterRead(xe::cpu::ThreadDebugInfo* thread, uint32_t rid); + std::string RegisterWrite(xe::cpu::ThreadDebugInfo* thread, uint32_t rid, + const std::string_view value); std::string RegisterRead(const std::string& data); + std::string RegisterWrite(const std::string& data); std::string RegisterReadAll(); std::string ExecutionPause(); std::string ExecutionContinue();