Add all the files

This commit is contained in:
Exzap 2022-08-22 22:21:23 +02:00
parent e3db07a16a
commit d60742f52b
1445 changed files with 430238 additions and 0 deletions

View file

@ -0,0 +1,264 @@
#include "gui/wxgui.h"
#include "gui/debugger/BreakpointWindow.h"
#include <sstream>
#include "gui/debugger/DebuggerWindow2.h"
#include "gui/guiWrapper.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cemu/ExpressionParser/ExpressionParser.h"
enum
{
MENU_ID_CREATE_MEM_BP_READ = 1,
MENU_ID_CREATE_MEM_BP_WRITE,
};
enum ItemColumns
{
ColumnEnabled = 0,
ColumnAddress,
ColumnType,
ColumnComment,
};
BreakpointWindow::BreakpointWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size)
: wxFrame(&parent, wxID_ANY, "Breakpoints", wxDefaultPosition, wxSize(420, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT)
{
this->SetSizeHints(wxDefaultSize, wxDefaultSize);
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
m_breakpoints = new wxCheckedListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
wxListItem col0;
col0.SetId(ColumnEnabled);
col0.SetText(_("On"));
col0.SetWidth(32);
m_breakpoints->InsertColumn(ColumnEnabled, col0);
wxListItem col1;
col1.SetId(ColumnAddress);
col1.SetText(_("Address"));
col1.SetWidth(75);
m_breakpoints->InsertColumn(ColumnAddress, col1);
wxListItem col2;
col2.SetId(ColumnType);
col2.SetText(_("Type"));
col2.SetWidth(42);
m_breakpoints->InsertColumn(ColumnType, col2);
wxListItem col3;
col3.SetId(ColumnComment);
col3.SetText(_("Comment"));
col3.SetWidth(250);
m_breakpoints->InsertColumn(ColumnComment, col3);
main_sizer->Add(m_breakpoints, 1, wxEXPAND);
this->SetSizer(main_sizer);
this->wxWindowBase::Layout();
this->Centre(wxBOTH);
if (parent.GetConfig().data().pin_to_main)
OnMainMove(main_position, main_size);
m_breakpoints->Bind(wxEVT_COMMAND_LIST_ITEM_CHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this);
m_breakpoints->Bind(wxEVT_COMMAND_LIST_ITEM_UNCHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this);
m_breakpoints->Bind(wxEVT_LEFT_DCLICK, &BreakpointWindow::OnLeftDClick, this);
m_breakpoints->Bind(wxEVT_RIGHT_DOWN, &BreakpointWindow::OnRightDown, this);
OnUpdateView();
}
BreakpointWindow::~BreakpointWindow()
{
m_breakpoints->Unbind(wxEVT_COMMAND_LIST_ITEM_CHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this);
m_breakpoints->Unbind(wxEVT_COMMAND_LIST_ITEM_UNCHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this);
}
void BreakpointWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size)
{
wxSize size(420, 250);
this->SetSize(size);
wxPoint position = main_position;
position.x -= 420;
position.y += main_size.y - 250;
this->SetPosition(position);
}
void BreakpointWindow::OnUpdateView()
{
Freeze();
m_breakpoints->DeleteAllItems();
if (!debuggerState.breakpoints.empty())
{
uint32_t i = 0;
for (const auto bpBase : debuggerState.breakpoints)
{
DebuggerBreakpoint* bp = bpBase;
while (bp)
{
wxListItem item;
item.SetId(i++);
const auto index = m_breakpoints->InsertItem(item);
m_breakpoints->SetItem(index, ColumnAddress, wxString::Format("%08x", bp->address));
const char* typeName = "UKN";
if (bp->bpType == DEBUGGER_BP_T_NORMAL)
typeName = "X";
else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT)
typeName = "XS";
else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ)
typeName = "R";
else if (bp->bpType == DEBUGGER_BP_T_MEMORY_WRITE)
typeName = "W";
m_breakpoints->SetItem(index, ColumnType, typeName);
m_breakpoints->SetItem(index, ColumnComment, bp->comment);
m_breakpoints->SetItemPtrData(item, (wxUIntPtr)bp);
m_breakpoints->Check(index, bp->enabled);
bp = bp->next;
}
}
}
Thaw();
}
void BreakpointWindow::OnGameLoaded()
{
OnUpdateView();
}
void BreakpointWindow::OnBreakpointToggled(wxListEvent& event)
{
const int32_t index = event.GetIndex();
if (0 <= index && index < m_breakpoints->GetItemCount())
{
const bool state = m_breakpoints->IsChecked(index);
wxString line = m_breakpoints->GetItemText(index, ColumnAddress);
DebuggerBreakpoint* bp = (DebuggerBreakpoint*)m_breakpoints->GetItemData(index);
const uint32 address = std::stoul(line.c_str().AsChar(), nullptr, 16);
debugger_toggleBreakpoint(address, state, bp);
}
}
void BreakpointWindow::OnLeftDClick(wxMouseEvent& event)
{
const auto position = event.GetPosition();
const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2;
if (index < 0 || index >= m_breakpoints->GetItemCount())
return;
sint32 x = position.x;
const auto enabled_width = m_breakpoints->GetColumnWidth(ColumnEnabled);
if (x <= enabled_width)
return;
x -= enabled_width;
const auto address_width = m_breakpoints->GetColumnWidth(ColumnAddress);
if(x <= address_width)
{
const auto item = m_breakpoints->GetItemText(index, ColumnAddress);
const auto address = std::stoul(item.ToStdString(), nullptr, 16);
debuggerState.debugSession.instructionPointer = address;
debuggerWindow_moveIP();
return;
}
x -= address_width;
const auto type_width = m_breakpoints->GetColumnWidth(ColumnType);
if (x <= type_width)
return;
x -= type_width;
const auto comment_width = m_breakpoints->GetColumnWidth(ColumnComment);
if(x <= comment_width)
{
if (index >= debuggerState.breakpoints.size())
return;
const auto item = m_breakpoints->GetItemText(index, ColumnAddress);
const auto address = std::stoul(item.ToStdString(), nullptr, 16);
auto it = debuggerState.breakpoints.begin();
std::advance(it, index);
wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), wxString::Format(_("Set comment for breakpoint at address %08x"), address), (*it)->comment);
if (set_value_dialog.ShowModal() == wxID_OK)
{
(*it)->comment = set_value_dialog.GetValue().ToStdWstring();
m_breakpoints->SetItem(index, ColumnComment, set_value_dialog.GetValue());
}
return;
}
}
void BreakpointWindow::OnRightDown(wxMouseEvent& event)
{
wxMenu menu;
menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)"));
menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)"));
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this);
PopupMenu(&menu);
}
void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt)
{
switch (evt.GetId())
{
case MENU_ID_CREATE_MEM_BP_READ:
MemoryBreakpointDialog(false);
return;
case MENU_ID_CREATE_MEM_BP_WRITE:
MemoryBreakpointDialog(true);
return;
}
}
void BreakpointWindow::MemoryBreakpointDialog(bool isWrite)
{
wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Memory breakpoint"), wxEmptyString);
if (goto_dialog.ShowModal() == wxID_OK)
{
ExpressionParser parser;
auto value = goto_dialog.GetValue().ToStdString();
std::transform(value.begin(), value.end(), value.begin(), tolower);
try
{
debugger_addParserSymbols(parser);
const auto result = (uint32)parser.Evaluate(value);
debug_printf("goto eval result: %x\n", result);
debugger_createMemoryBreakpoint(result, isWrite == false, isWrite == true);
this->OnUpdateView();
}
catch (const std::exception& e)
{
//ctx.errorHandler.printError(nullptr, -1, fmt::format("Unexpected error in expression \"{}\"", expressionString));
//return EXPRESSION_RESOLVE_RESULT::EXPRESSION_ERROR;
wxMessageBox(e.what(), "Invalid expression");
}
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "gui/wxcomponents/checkedlistctrl.h"
class DebuggerWindow2;
class BreakpointWindow : public wxFrame
{
public:
BreakpointWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size);
virtual ~BreakpointWindow();
void OnMainMove(const wxPoint& position, const wxSize& main_size);
void OnUpdateView();
void OnGameLoaded();
private:
void OnBreakpointToggled(wxListEvent& event);
void OnLeftDClick(wxMouseEvent& event);
void OnRightDown(wxMouseEvent& event);
void OnContextMenuClick(wxCommandEvent& evt);
void MemoryBreakpointDialog(bool isWrite);
wxCheckedListCtrl* m_breakpoints;
};

View file

@ -0,0 +1,676 @@
#include "gui/wxgui.h"
#include "gui/debugger/DebuggerWindow2.h"
#include <filesystem>
#include "config/ActiveSettings.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/OS/RPL/rpl_debug_symbols.h"
#include "gui/debugger/RegisterWindow.h"
#include "gui/debugger/DumpWindow.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/OS/RPL/rpl.h"
#include "gui/debugger/DisasmCtrl.h"
#include "gui/debugger/SymbolWindow.h"
#include "gui/debugger/BreakpointWindow.h"
#include "gui/debugger/ModuleWindow.h"
#include "util/helpers/helpers.h"
#if BOOST_OS_LINUX
#include "resource/linux/resources.h"
#endif
enum
{
// file
MENU_ID_FILE_EXIT = wxID_HIGHEST + 8000,
// settings
MENU_ID_OPTIONS_PIN_TO_MAINWINDOW,
MENU_ID_OPTIONS_BREAK_ON_START,
// window
MENU_ID_WINDOW_REGISTERS,
MENU_ID_WINDOW_DUMP,
MENU_ID_WINDOW_STACK,
MENU_ID_WINDOW_BREAKPOINTS,
MENU_ID_WINDOW_MODULE,
MENU_ID_WINDOW_SYMBOL,
// tool
TOOL_ID_GOTO,
TOOL_ID_BP,
TOOL_ID_PAUSE,
TOOL_ID_STEP_OVER,
TOOL_ID_STEP_INTO,
};
wxDEFINE_EVENT(wxEVT_DEBUGGER_CLOSE, wxCloseEvent);
wxDEFINE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_MOVE_IP, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_RUN, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent);
wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame)
EVT_SHOW(DebuggerWindow2::OnShow)
EVT_CLOSE(DebuggerWindow2::OnClose)
EVT_COMMAND(wxID_ANY, wxEVT_UPDATE_VIEW, DebuggerWindow2::OnUpdateView)
EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_CHANGE, DebuggerWindow2::OnBreakpointChange)
EVT_COMMAND(wxID_ANY, wxEVT_MOVE_IP, DebuggerWindow2::OnMoveIP)
EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_TOOL_CLICKED, DebuggerWindow2::OnToolClicked)
EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_HIT, DebuggerWindow2::OnBreakpointHit)
EVT_COMMAND(wxID_ANY, wxEVT_RUN, DebuggerWindow2::OnRunProgram)
EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_LOADED, DebuggerWindow2::OnNotifyModuleLoaded)
EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_UNLOADED, DebuggerWindow2::OnNotifyModuleUnloaded)
// file menu
EVT_MENU(MENU_ID_FILE_EXIT, DebuggerWindow2::OnExit)
// setting
EVT_MENU(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, DebuggerWindow2::OnOptionsInput)
// window
EVT_MENU_RANGE(MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_MODULE, DebuggerWindow2::OnWindowMenu)
wxEND_EVENT_TABLE()
DebuggerWindow2* g_debugger_window;
void DebuggerConfig::Load(XMLConfigParser& parser)
{
pin_to_main = parser.get("PinToMainWindow", true);
break_on_start = parser.get("break_on_start", true);
auto window_parser = parser.get("Windows");
show_register = window_parser.get("Registers", true);
show_dump = window_parser.get("MemoryDump", true);
show_stack = window_parser.get("Stack", true);
show_breakpoints = window_parser.get("Breakpoints", true);
show_modules = window_parser.get("Modules", true);
show_symbols = window_parser.get("Symbols", true);
}
void DebuggerConfig::Save(XMLConfigParser& parser)
{
parser.set("PinToMainWindow", pin_to_main);
parser.set("break_on_start", break_on_start);
auto window_parser = parser.set("Windows");
window_parser.set("Registers", show_register);
window_parser.set("MemoryDump", show_dump);
window_parser.set("Stack", show_stack);
window_parser.set("Breakpoints", show_breakpoints);
window_parser.set("Modules", show_modules);
window_parser.set("Symbols", show_symbols);
}
void DebuggerModuleStorage::Load(XMLConfigParser& parser)
{
auto breakpoints_parser = parser.get("Breakpoints");
for (auto element = breakpoints_parser.get("Entry"); element.valid(); element = breakpoints_parser.get("Entry", element))
{
const auto address_string = element.get("Address", "");
if (*address_string == '\0')
continue;
uint32 relative_address = std::stoul(address_string, nullptr, 16);
if (relative_address == 0)
continue;
auto type = element.get("Type", 0);
auto enabled = element.get("Enabled", true);
const auto comment = element.get("Comment", "");
// calculate absolute address
uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data);
uint32 address = module_base_address + relative_address;
// don't change anything if there's already a breakpoint
if (debugger_getFirstBP(address) != nullptr)
continue;
// register breakpoints in debugger
if (type == DEBUGGER_BP_T_NORMAL)
debugger_createExecuteBreakpoint(address);
else if (type == DEBUGGER_BP_T_MEMORY_READ)
debugger_createMemoryBreakpoint(address, true, false);
else if (type == DEBUGGER_BP_T_MEMORY_WRITE)
debugger_createMemoryBreakpoint(address, false, true);
else
continue;
DebuggerBreakpoint* debugBreakpoint = debugger_getFirstBP(address);
if (!enabled)
debugger_toggleBreakpoint(address, false, debugBreakpoint);
debugBreakpoint->comment = boost::nowide::widen(comment);
}
auto comments_parser = parser.get("Comments");
for (XMLConfigParser element = comments_parser.get("Entry"); element.valid(); element = comments_parser.get("Entry", element))
{
const auto address_string = element.get("Address", "");
if (*address_string == '\0')
continue;
uint32 address = std::stoul(address_string, nullptr, 16);
if (address == 0)
continue;
const auto comment = element.get("Comment", "");
if (*comment == '\0')
continue;
rplDebugSymbol_createComment(address, boost::nowide::widen(comment).c_str());
}
}
void DebuggerModuleStorage::Save(XMLConfigParser& parser)
{
auto breakpoints_parser = parser.set("Breakpoints");
for (const auto& bp : debuggerState.breakpoints)
{
// check breakpoint type
if (bp->dbType != DEBUGGER_BP_T_DEBUGGER)
continue;
// check whether the breakpoint is part of the current module being saved
RPLModule* address_module;
if (bp->bpType == DEBUGGER_BP_T_NORMAL) address_module = RPLLoader_FindModuleByCodeAddr(bp->address);
else if (bp->isMemBP()) address_module = RPLLoader_FindModuleByDataAddr(bp->address);
else continue;
if (!address_module || !(address_module->moduleName2 == this->module_name && address_module->patchCRC == this->crc_hash))
continue;
uint32_t relative_address = bp->address - (bp->isMemBP() ? address_module->regionMappingBase_data : address_module->regionMappingBase_text.GetMPTR());
auto entry = breakpoints_parser.set("Entry");
entry.set("Address", fmt::format("{:#10x}", relative_address));
entry.set("Comment", boost::nowide::narrow(bp->comment).c_str());
entry.set("Type", bp->bpType);
entry.set("Enabled", bp->enabled);
if (this->delete_breakpoints_after_saving) debugger_deleteBreakpoint(bp);
this->delete_breakpoints_after_saving = false;
}
auto comments_parser = parser.set("Comments");
for (const auto& comment_entry : rplDebugSymbol_getSymbols())
{
// check comment type
const auto comment_address = comment_entry.first;
const auto comment = static_cast<rplDebugSymbolComment*>(comment_entry.second);
if (!comment || comment->type != RplDebugSymbolComment)
continue;
// check whether it's part of the current module being saved
RPLModule* address_module = RPLLoader_FindModuleByCodeAddr(comment_entry.first);
if (!address_module || !(address_module->moduleName2 == module_name && address_module->patchCRC == this->crc_hash))
continue;
uint32_t relative_address = comment_address - address_module->regionMappingBase_text.GetMPTR();
auto entry = comments_parser.set("Entry");
entry.set("Address", fmt::format("{:#10x}", relative_address));
entry.set("Comment", boost::nowide::narrow(comment->comment).c_str());
}
}
void DebuggerWindow2::CreateToolBar()
{
m_toolbar = wxFrame::CreateToolBar(wxTB_HORIZONTAL, wxID_ANY);
m_toolbar->SetToolBitmapSize(wxSize(1, 1));
m_toolbar->AddTool(TOOL_ID_GOTO, wxEmptyString, wxBITMAP_PNG(DEBUGGER_GOTO), wxNullBitmap, wxITEM_NORMAL, _("GoTo (CTRL + G)"), "test", NULL);
m_toolbar->AddSeparator();
m_toolbar->AddTool(TOOL_ID_BP, wxEmptyString, wxBITMAP_PNG(DEBUGGER_BP_RED), wxNullBitmap, wxITEM_NORMAL, _("Toggle Breakpoint (F9)"), wxEmptyString, NULL);
m_toolbar->AddSeparator();
m_pause = wxBITMAP_PNG(DEBUGGER_PAUSE);
m_run = wxBITMAP_PNG(DEBUGGER_PLAY);
m_toolbar->AddTool(TOOL_ID_PAUSE, wxEmptyString, m_pause, wxNullBitmap, wxITEM_NORMAL, _("Break (F5)"), wxEmptyString, NULL);
m_toolbar->AddTool(TOOL_ID_STEP_INTO, wxEmptyString, wxBITMAP_PNG(DEBUGGER_STEP_INTO), wxNullBitmap, wxITEM_NORMAL, _("Step Into (F11)"), wxEmptyString, NULL);
m_toolbar->AddTool(TOOL_ID_STEP_OVER, wxEmptyString, wxBITMAP_PNG(DEBUGGER_STEP_OVER), wxNullBitmap, wxITEM_NORMAL, _("Step Over (F10)"), wxEmptyString, NULL);
m_toolbar->AddSeparator();
m_toolbar->Realize();
m_toolbar->EnableTool(TOOL_ID_STEP_INTO, false);
m_toolbar->EnableTool(TOOL_ID_STEP_OVER, false);
}
void DebuggerWindow2::SaveModuleStorage(const RPLModule* module, bool delete_breakpoints)
{
auto path = GetModuleStoragePath(module->moduleName2, module->patchCRC);
for (auto& module_storage : m_modules_storage)
{
if (module_storage->data().module_name == module->moduleName2 && module_storage->data().crc_hash == module->patchCRC)
{
module_storage->data().delete_breakpoints_after_saving = delete_breakpoints;
module_storage->Save(path);
if (delete_breakpoints) m_modules_storage.erase(std::find(m_modules_storage.begin(), m_modules_storage.end(), module_storage));
}
}
}
void DebuggerWindow2::LoadModuleStorage(const RPLModule* module)
{
auto path = GetModuleStoragePath(module->moduleName2, module->patchCRC);
bool already_loaded = std::any_of(m_modules_storage.begin(), m_modules_storage.end(), [path](const std::unique_ptr<XMLDebuggerModuleConfig>& debug) { return debug->GetFilename() == path; });
if (!path.empty() && !already_loaded)
{
m_modules_storage.emplace_back(std::move(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false })));
}
}
DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size)
: wxFrame(&parent, wxID_ANY, wxT("PPC Debugger"), wxDefaultPosition, wxSize(1280, 300), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT),
m_module_address(0)
{
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
const auto file = ActiveSettings::GetPath("debugger/config.xml");
m_config.SetFilename(file.generic_wstring());
m_config.Load();
debuggerState.breakOnEntry = m_config.data().break_on_start;
m_main_position = parent.GetPosition();
m_main_size = parent.GetSize();
auto y = std::max<uint32>(300, (display_size.GetHeight() - 500 - 300) * 0.8);
this->SetSize(1280, y);
CreateMenuBar();
CreateToolBar();
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
// load configs for already loaded modules
const auto module_count = RPLLoader_GetModuleCount();
const auto module_list = RPLLoader_GetModuleList();
for (sint32 i = 0; i < module_count; i++)
{
const auto module = module_list[i];
LoadModuleStorage(module);
}
wxString label_text = _("> no modules loaded");
if (module_count != 0)
{
RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR);
if (current_rpl_module)
label_text = wxString::Format("> %s", current_rpl_module->moduleName2.c_str());
else
label_text = _("> unknown module");
}
m_module_label = new wxStaticText(this, wxID_ANY, label_text);
m_module_label->SetBackgroundColour(*wxWHITE);
m_module_label->SetForegroundColour(wxColour(0xFFbf52fe));
main_sizer->Add(m_module_label, 0, wxEXPAND | wxALL, 5);
m_disasm_ctrl = new DisasmCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle);
main_sizer->Add(m_disasm_ctrl, 1, wxEXPAND);
this->SetSizer(main_sizer);
this->wxWindowBase::Layout();
m_register_window = new RegisterWindow(*this, m_main_position, m_main_size);
m_dump_window = new DumpWindow(*this, m_main_position, m_main_size);
m_breakpoint_window = new BreakpointWindow(*this, m_main_position, m_main_size);
m_module_window = new ModuleWindow(*this, m_main_position, m_main_size);
m_symbol_window = new SymbolWindow(*this, m_main_position, m_main_size);
const bool value = m_config.data().pin_to_main;
m_config.data().pin_to_main = true;
OnParentMove(m_main_position, m_main_size);
m_config.data().pin_to_main = value;
g_debugger_window = this;
}
DebuggerWindow2::~DebuggerWindow2()
{
debuggerState.breakOnEntry = false;
g_debugger_window = nullptr;
// save configs for all modules that are still loaded
// doesn't delete breakpoints since that should (in the future) be done by unloading the rpl modules when exiting the current game
const auto module_count = RPLLoader_GetModuleCount();
const auto module_list = RPLLoader_GetModuleList();
for (sint32 i = 0; i < module_count; i++)
{
const auto module = module_list[i];
if (module)
SaveModuleStorage(module, false);
}
if (m_register_window && m_register_window->IsShown())
m_register_window->Close(true);
if (m_dump_window && m_dump_window->IsShown())
m_dump_window->Close(true);
if (m_breakpoint_window && m_breakpoint_window->IsShown())
m_breakpoint_window->Close(true);
if (m_module_window && m_module_window->IsShown())
m_module_window->Close(true);
if (m_symbol_window && m_symbol_window->IsShown())
m_symbol_window->Close(true);
m_config.Save();
}
void DebuggerWindow2::OnClose(wxCloseEvent& event)
{
debuggerState.breakOnEntry = false;
const wxCloseEvent parentEvent(wxEVT_DEBUGGER_CLOSE);
wxPostEvent(m_parent, parentEvent);
event.Skip();
}
void DebuggerWindow2::OnMoveIP(wxCommandEvent& event)
{
const auto ip = debuggerState.debugSession.instructionPointer;
UpdateModuleLabel(ip);
m_disasm_ctrl->CenterOffset(ip);
}
void DebuggerWindow2::OnParentMove(const wxPoint& main_position, const wxSize& main_size)
{
m_main_position = main_position;
m_main_size = main_size;
if (!m_config.data().pin_to_main)
return;
wxSize size(m_main_size.x, GetSize().GetHeight());
SetSize(size);
wxPoint position = m_main_position;
position.y -= size.y;
SetPosition(position);
m_register_window->OnMainMove(main_position, main_size);
m_dump_window->OnMainMove(main_position, main_size);
m_breakpoint_window->OnMainMove(main_position, main_size);
m_module_window->OnMainMove(main_position, main_size);
m_symbol_window->OnMainMove(main_position, main_size);
}
void DebuggerWindow2::OnNotifyModuleLoaded(wxCommandEvent& event)
{
RPLModule* module = (RPLModule*)event.GetClientData();
LoadModuleStorage(module);
m_module_window->OnGameLoaded();
m_symbol_window->OnGameLoaded();
m_disasm_ctrl->Init();
}
void DebuggerWindow2::OnNotifyModuleUnloaded(wxCommandEvent& event)
{
RPLModule* module = (RPLModule*)event.GetClientData();
SaveModuleStorage(module, true);
m_module_window->OnGameLoaded();
m_symbol_window->OnGameLoaded();
m_disasm_ctrl->Init();
}
void DebuggerWindow2::OnGameLoaded()
{
m_disasm_ctrl->Init();
m_dump_window->OnGameLoaded();
m_module_window->OnGameLoaded();
m_breakpoint_window->OnGameLoaded();
m_symbol_window->OnGameLoaded();
RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR);
if(current_rpl_module)
m_module_label->SetLabel(wxString::Format("> %s", current_rpl_module->moduleName2.c_str()));
this->SendSizeEvent();
}
XMLDebuggerConfig& DebuggerWindow2::GetConfig()
{
return m_config;
}
bool DebuggerWindow2::Show(bool show)
{
const bool result = wxFrame::Show(show);
if (show)
{
m_register_window->Show(m_config.data().show_register);
m_dump_window->Show(m_config.data().show_dump);
m_breakpoint_window->Show(m_config.data().show_breakpoints);
m_module_window->Show(m_config.data().show_modules);
m_symbol_window->Show(m_config.data().show_symbols);
}
else
{
m_register_window->Show(false);
m_dump_window->Show(false);
m_breakpoint_window->Show(false);
m_module_window->Show(false);
m_symbol_window->Show(false);
}
return result;
}
std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const
{
if (module_name.empty() || crc_hash == 0) return std::wstring();
return ActiveSettings::GetPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring();
}
void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event)
{
const auto ip = debuggerState.debugSession.instructionPointer;
UpdateModuleLabel(ip);
m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Run (F5)"));
m_toolbar->SetToolNormalBitmap(TOOL_ID_PAUSE, m_run);
m_toolbar->EnableTool(TOOL_ID_STEP_INTO, true);
m_toolbar->EnableTool(TOOL_ID_STEP_OVER, true);
m_disasm_ctrl->CenterOffset(ip);
}
void DebuggerWindow2::OnRunProgram(wxCommandEvent& event)
{
m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Break (F5)"));
m_toolbar->SetToolNormalBitmap(TOOL_ID_PAUSE, m_pause);
m_toolbar->EnableTool(TOOL_ID_STEP_INTO, false);
m_toolbar->EnableTool(TOOL_ID_STEP_OVER, false);
}
void DebuggerWindow2::OnToolClicked(wxCommandEvent& event)
{
switch(event.GetId())
{
case TOOL_ID_GOTO:
m_disasm_ctrl->GoToAddressDialog();
break;
case TOOL_ID_PAUSE:
if (debugger_isTrapped())
debugger_resume();
else
debugger_forceBreak();
break;
case TOOL_ID_STEP_INTO:
if (debugger_isTrapped())
debuggerState.debugSession.stepInto = true;
break;
case TOOL_ID_STEP_OVER:
if (debugger_isTrapped())
debuggerState.debugSession.stepOver = true;
break;
}
}
void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event)
{
m_breakpoint_window->OnUpdateView();
UpdateModuleLabel();
}
void DebuggerWindow2::OnOptionsInput(wxCommandEvent& event)
{
switch(event.GetId())
{
case MENU_ID_OPTIONS_PIN_TO_MAINWINDOW:
{
const bool value = !m_config.data().pin_to_main;
m_config.data().pin_to_main = value;
if(value)
OnParentMove(m_main_position, m_main_size);
break;
}
case MENU_ID_OPTIONS_BREAK_ON_START:
{
const bool value = !m_config.data().break_on_start;
m_config.data().break_on_start = value;
debuggerState.breakOnEntry = value;
break;
}
default:
return;
}
m_config.Save();
}
void DebuggerWindow2::OnWindowMenu(wxCommandEvent& event)
{
switch (event.GetId())
{
case MENU_ID_WINDOW_REGISTERS:
{
const bool value = !m_config.data().show_register;
m_config.data().show_register = value;
m_register_window->Show(value);
break;
}
case MENU_ID_WINDOW_DUMP:
{
const bool value = !m_config.data().show_dump;
m_config.data().show_dump = value;
m_dump_window->Show(value);
break;
}
case MENU_ID_WINDOW_BREAKPOINTS:
{
const bool value = !m_config.data().show_breakpoints;
m_config.data().show_breakpoints = value;
m_breakpoint_window->Show(value);
break;
}
case MENU_ID_WINDOW_MODULE:
{
const bool value = !m_config.data().show_modules;
m_config.data().show_modules = value;
m_module_window->Show(value);
break;
}
case MENU_ID_WINDOW_SYMBOL:
{
const bool value = !m_config.data().show_symbols;
m_config.data().show_symbols = value;
m_symbol_window->Show(value);
break;
}
default:
return;
}
m_config.Save();
}
void DebuggerWindow2::OnUpdateView(wxCommandEvent& event)
{
UpdateModuleLabel();
m_disasm_ctrl->OnUpdateView();
m_register_window->OnUpdateView();
m_breakpoint_window->OnUpdateView();
}
void DebuggerWindow2::OnExit(wxCommandEvent& event)
{
this->Close();
}
void DebuggerWindow2::OnShow(wxShowEvent& event)
{
m_register_window->Show();
m_dump_window->Show();
m_breakpoint_window->Show();
m_module_window->Show();
m_symbol_window->Show();
event.Skip();
}
void DebuggerWindow2::CreateMenuBar()
{
auto menu_bar = new wxMenuBar;
// file
wxMenu* file_menu = new wxMenu;
file_menu->Append(MENU_ID_FILE_EXIT, _("&Exit"));
file_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnExit, this);
menu_bar->Append(file_menu, _("&File"));
// options
wxMenu* options_menu = new wxMenu;
options_menu->Append(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, _("&Pin to main window"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().pin_to_main);
options_menu->Append(MENU_ID_OPTIONS_BREAK_ON_START, _("Break on &entry point"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().break_on_start);
menu_bar->Append(options_menu, _("&Options"));
// window
wxMenu* window_menu = new wxMenu;
window_menu->Append(MENU_ID_WINDOW_REGISTERS, _("&Registers"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_register);
window_menu->Append(MENU_ID_WINDOW_DUMP, _("&Memory Dump"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_dump);
window_menu->Append(MENU_ID_WINDOW_BREAKPOINTS, _("&Breakpoints"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_breakpoints);
window_menu->Append(MENU_ID_WINDOW_MODULE, _("Module&list"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_modules);
window_menu->Append(MENU_ID_WINDOW_SYMBOL, _("&Symbols"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_symbols);
menu_bar->Append(window_menu, _("&Window"));
SetMenuBar(menu_bar);
options_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnOptionsInput, this);
window_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnWindowMenu, this);
}
void DebuggerWindow2::UpdateModuleLabel(uint32 address)
{
if(address == 0)
address = m_disasm_ctrl->GetViewBaseAddress();
RPLModule* module = RPLLoader_FindModuleByCodeAddr(address);
if (module && m_module_address != module->regionMappingBase_text.GetMPTR())
{
m_module_label->SetLabel(wxString::Format("> %s", module->moduleName2.c_str()));
m_module_address = module->regionMappingBase_text.GetMPTR();
}
else if (address >= mmuRange_CODECAVE.getBase() && address < mmuRange_CODECAVE.getEnd())
{
m_module_label->SetLabel(wxString::Format("> %s", "Cemu codecave"));
m_module_address = mmuRange_CODECAVE.getBase();
}
}

View file

@ -0,0 +1,116 @@
#pragma once
#include "gui/debugger/DisasmCtrl.h"
#include "config/XMLConfig.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/OS/RPL/rpl.h"
#include <wx/bitmap.h>
#include <wx/frame.h>
class BreakpointWindow;
class RegisterWindow;
class DumpWindow;
class ModuleWindow;
class SymbolWindow;
class wxStaticText;
wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_RUN, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_MOVE_IP, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent);
struct DebuggerConfig
{
DebuggerConfig()
: pin_to_main(true), break_on_start(true), show_register(true), show_dump(true), show_stack(true), show_breakpoints(true), show_modules(true) {}
bool pin_to_main;
bool break_on_start;
bool show_register;
bool show_dump;
bool show_stack;
bool show_breakpoints;
bool show_modules;
bool show_symbols;
void Load(XMLConfigParser& parser);
void Save(XMLConfigParser& parser);
};
typedef XMLDataConfig<DebuggerConfig, &DebuggerConfig::Load, &DebuggerConfig::Save> XMLDebuggerConfig;
struct DebuggerModuleStorage
{
std::string module_name;
uint32_t crc_hash;
const RPLModule* rpl_module;
bool delete_breakpoints_after_saving;
void Load(XMLConfigParser& parser);
void Save(XMLConfigParser& parser);
};
typedef XMLDataConfig<DebuggerModuleStorage, &DebuggerModuleStorage::Load, &DebuggerModuleStorage::Save> XMLDebuggerModuleConfig;
class DebuggerWindow2 : public wxFrame
{
public:
void CreateToolBar();
void LoadModuleStorage(const RPLModule* module);
void SaveModuleStorage(const RPLModule* module, bool delete_breakpoints);
DebuggerWindow2(wxFrame& parent, const wxRect& display_size);
~DebuggerWindow2();
void OnParentMove(const wxPoint& position, const wxSize& size);
void OnGameLoaded();
XMLDebuggerConfig& GetConfig();
bool Show(bool show = true) override;
std::wstring GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const;
private:
void OnBreakpointHit(wxCommandEvent& event);
void OnRunProgram(wxCommandEvent& event);
void OnToolClicked(wxCommandEvent& event);
void OnBreakpointChange(wxCommandEvent& event);
void OnOptionsInput(wxCommandEvent& event);
void OnWindowMenu(wxCommandEvent& event);
void OnUpdateView(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnShow(wxShowEvent& event);
void OnClose(wxCloseEvent& event);
void OnMoveIP(wxCommandEvent& event);
void OnNotifyModuleLoaded(wxCommandEvent& event);
void OnNotifyModuleUnloaded(wxCommandEvent& event);
void CreateMenuBar();
void UpdateModuleLabel(uint32 address = 0);
XMLDebuggerConfig m_config;
std::vector<std::unique_ptr<XMLDebuggerModuleConfig>> m_modules_storage;
wxPoint m_main_position;
wxSize m_main_size;
RegisterWindow* m_register_window;
DumpWindow* m_dump_window;
BreakpointWindow* m_breakpoint_window;
ModuleWindow* m_module_window;
SymbolWindow* m_symbol_window;
DisasmCtrl* m_disasm_ctrl;
wxToolBar* m_toolbar;
wxBitmap m_run, m_pause;
uint32 m_module_address;
wxStaticText* m_module_label;
wxDECLARE_EVENT_TABLE();
};

View file

@ -0,0 +1,806 @@
#include "gui/wxgui.h"
#include "gui/debugger/DisasmCtrl.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "Cafe/OS/RPL/rpl_debug_symbols.h"
#include "Cemu/PPCAssembler/ppcAssembler.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "gui/debugger/DebuggerWindow2.h"
#include "util/helpers/helpers.h"
#include "gui/guiWrapper.h"
#include "Cemu/ExpressionParser/ExpressionParser.h"
#include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h"
#include <wx/mstream.h> // for wxMemoryInputStream
#define MAX_SYMBOL_LEN (120)
#define COLOR_DEBUG_ACTIVE_BP 0xFFFFA0FF
#define COLOR_DEBUG_ACTIVE 0xFFFFA080
#define COLOR_DEBUG_BP 0xFF8080FF
#define SYNTAX_COLOR_GPR 0xFF000066
#define SYNTAX_COLOR_FPR 0xFF006666
#define SYNTAX_COLOR_SPR 0xFF666600
#define SYNTAX_COLOR_CR 0xFF666600
#define SYNTAX_COLOR_IMM 0xFF006600
#define SYNTAX_COLOR_IMM_OFFSET 0xFF006600
#define SYNTAX_COLOR_CIMM 0xFF880000
#define SYNTAX_COLOR_PSEUDO 0xFFA0A0A0 // color for pseudo code
#define SYNTAX_COLOR_SYMBOL 0xFF0000A0 // color for function symbol
#define OFFSET_ADDRESS (60)
#define OFFSET_ADDRESS_RELATIVE (90)
#define OFFSET_DISASSEMBLY (300)
#define OFFSET_DISASSEMBLY_OPERAND (80)
wxBitmap* g_ipArrowBitmap = nullptr;
uint8 _arrowRightPNG[] =
{
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0B,
0x08, 0x03, 0x00, 0x00, 0x00, 0x41, 0x3C, 0xFD, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00,
0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC,
0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4C, 0x54, 0x45, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xA5, 0x67, 0xB9, 0xCF, 0x00, 0x00, 0x00, 0x02,
0x74, 0x52, 0x4E, 0x53, 0xFF, 0x00, 0xE5, 0xB7, 0x30, 0x4A, 0x00, 0x00,
0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00,
0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x00, 0x2C, 0x49,
0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0x60, 0x84, 0x03, 0x08, 0x13, 0x59,
0x00, 0xCC, 0x46, 0x11, 0x00, 0x71, 0x80, 0x24, 0x32, 0xC0, 0x10, 0x60,
0xC0, 0x10, 0xC0, 0x00, 0x58, 0xCC, 0x80, 0xD8, 0x00, 0x02, 0x60, 0x3E,
0x7E, 0x77, 0x00, 0x31, 0x23, 0x23, 0x00, 0x21, 0x95, 0x00, 0x5B, 0x20,
0x73, 0x8D, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
0x42, 0x60, 0x82
};
DisasmCtrl::DisasmCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style)
: TextList(parent, id, pos, size, style), m_mouse_line(-1), m_mouse_line_drawn(-1), m_active_line(-1)
{
Init();
if (!g_ipArrowBitmap)
{
wxMemoryInputStream strm(_arrowRightPNG, sizeof(_arrowRightPNG));
wxImage img(strm, wxBITMAP_TYPE_PNG);
g_ipArrowBitmap = new wxBitmap(img);
}
auto tooltip_sizer = new wxBoxSizer(wxVERTICAL);
tooltip_sizer->Add(new wxStaticText(m_tooltip_window, wxID_ANY, wxEmptyString), 0, wxALL, 5);
m_tooltip_window->SetSizer(tooltip_sizer);
}
void DisasmCtrl::Init()
{
SelectCodeRegion(mmuRange_TEXT_AREA.getBase());
}
void DisasmCtrl::SelectCodeRegion(uint32 newAddress)
{
if (newAddress >= mmuRange_TEXT_AREA.getBase() && newAddress < mmuRange_TEXT_AREA.getEnd())
{
currentCodeRegionStart = MEMORY_CODEAREA_ADDR;
currentCodeRegionEnd = RPLLoader_GetMaxCodeOffset();
currentCodeRegionEnd = std::max(currentCodeRegionEnd, currentCodeRegionStart + 0x1000);
}
MMURange* mmuRange = memory_getMMURangeByAddress(newAddress);
if (mmuRange)
{
currentCodeRegionStart = mmuRange->getBase();
currentCodeRegionEnd = mmuRange->getEnd();
}
else
{
currentCodeRegionStart = 0;
currentCodeRegionEnd = 0;
}
// update line tracking
sint32 element_count = currentCodeRegionEnd - currentCodeRegionStart;
if (element_count <= 0x00010000)
element_count = 0x00010000;
if (this->SetElementCount(element_count / 4))
{
Scroll(0, 0);
RefreshControl();
}
}
void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, RPLModule* rplModule)
{
wxPoint position = linePosition;
bool hasPatch = debugger_hasPatch(virtualAddress);
PPCDisassembledInstruction disasmInstr;
const DebuggerBreakpoint* bp = debugger_getFirstBP(virtualAddress);
while (bp)
{
if (bp->isExecuteBP() && bp->enabled)
break;
bp = bp->next;
}
uint32 opcode;
if (bp)
opcode = bp->originalOpcodeValue;
else
opcode = memory_readU32(virtualAddress);
ppcAssembler_disassemble(virtualAddress, opcode, &disasmInstr);
const bool is_active_bp = debuggerState.debugSession.isTrapped && debuggerState.debugSession.instructionPointer == virtualAddress;
// write virtual address
wxColour background_colour;
if (is_active_bp && bp != nullptr)
background_colour = wxColour(0xFFFFA0FF);
else if (is_active_bp)
background_colour = wxColour(0xFF80A0FF);
else if (bp != nullptr)
background_colour = wxColour(0xFF8080FF);
else if(virtualAddress == m_lastGotoTarget)
background_colour = wxColour(0xFFE0E0E0);
else
background_colour = wxColour(COLOR_WHITE);
DrawLineBackground(dc, position, background_colour);
dc.SetTextForeground(COLOR_BLACK);
dc.DrawText(wxString::Format("%08x", virtualAddress), position);
position.x += OFFSET_ADDRESS;
dc.SetTextForeground(COLOR_GREY);
if (rplModule)
dc.DrawText(wxString::Format("+0x%-8x", virtualAddress - rplModule->regionMappingBase_text.GetMPTR()), position);
else
dc.DrawText("???", position);
position.x += OFFSET_ADDRESS_RELATIVE;
// draw arrow to clearly indicate instruction pointer
if(is_active_bp)
dc.DrawBitmap(*g_ipArrowBitmap, wxPoint(position.x - 24, position.y + 2), false);
// handle data symbols
auto debugSymbolDataType = DebugSymbolStorage::GetDataType(virtualAddress);
if (debugSymbolDataType == DEBUG_SYMBOL_TYPE::FLOAT)
{
dc.SetTextForeground(hasPatch ? wxColour(0xFF2020FF) : wxColour(0xFF400000));
dc.DrawText(fmt::format(".float"), position);
position.x += OFFSET_DISASSEMBLY_OPERAND;
dc.SetTextForeground(hasPatch ? wxColour(0xFF2020FF) : wxColour(SYNTAX_COLOR_IMM));
dc.DrawText(fmt::format("{}", memory_readFloat(virtualAddress)), position);
return;
}
else if (debugSymbolDataType == DEBUG_SYMBOL_TYPE::U32)
{
dc.SetTextForeground(hasPatch ? wxColour(0xFF2020FF) : wxColour(0xFF400000));
dc.DrawText(fmt::format(".uint"), position);
position.x += OFFSET_DISASSEMBLY_OPERAND;
dc.SetTextForeground(hasPatch ? wxColour(0xFF2020FF) : wxColour(SYNTAX_COLOR_IMM));
dc.DrawText(fmt::format("{}", memory_readU32(virtualAddress)), position);
return;
}
sint32 start_width = position.x;
dc.SetTextForeground(hasPatch ? wxColour(0xFF2020FF) : wxColour(0xFF400000));
char opName[32];
strcpy(opName, ppcAssembler_getInstructionName(disasmInstr.ppcAsmCode));
std::transform(opName, opName + sizeof(opName), opName, tolower);
dc.DrawText(wxString::Format("%-12s", opName), position);
position.x += OFFSET_DISASSEMBLY_OPERAND;
bool isRLWINM = disasmInstr.ppcAsmCode == PPCASM_OP_RLWINM || disasmInstr.ppcAsmCode == PPCASM_OP_RLWINM_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_CLRLWI || disasmInstr.ppcAsmCode == PPCASM_OP_CLRLWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_CLRRWI || disasmInstr.ppcAsmCode == PPCASM_OP_CLRRWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_EXTLWI || disasmInstr.ppcAsmCode == PPCASM_OP_EXTLWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_EXTRWI || disasmInstr.ppcAsmCode == PPCASM_OP_EXTRWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_SLWI || disasmInstr.ppcAsmCode == PPCASM_OP_SLWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_SRWI || disasmInstr.ppcAsmCode == PPCASM_OP_SRWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_ROTRWI || disasmInstr.ppcAsmCode == PPCASM_OP_ROTRWI_ ||
disasmInstr.ppcAsmCode == PPCASM_OP_ROTLWI || disasmInstr.ppcAsmCode == PPCASM_OP_ROTLWI_;
bool forceDecDisplay = isRLWINM;
if (disasmInstr.ppcAsmCode == PPCASM_OP_UKN)
{
// show raw bytes
WriteText(dc, wxString::Format("%02x %02x %02x %02x", (opcode >> 24) & 0xFF, (opcode >> 16) & 0xFF, (opcode >> 8) & 0xFF, (opcode >> 0) & 0xFF), position, SYNTAX_COLOR_PSEUDO);
}
bool is_first_operand = true;
for (sint32 o = 0; o < PPCASM_OPERAND_COUNT; o++)
{
if (((disasmInstr.operandMask >> o) & 1) == 0)
continue;
if (!is_first_operand)
WriteText(dc, ", ", position, COLOR_BLACK);
is_first_operand = false;
switch (disasmInstr.operand[o].type)
{
case PPCASM_OPERAND_TYPE_GPR:
WriteText(dc, wxString::Format("r%d", disasmInstr.operand[o].registerIndex), position, SYNTAX_COLOR_GPR);
break;
case PPCASM_OPERAND_TYPE_FPR:
WriteText(dc, wxString::Format("f%d", disasmInstr.operand[o].registerIndex), position, SYNTAX_COLOR_FPR);
break;
case PPCASM_OPERAND_TYPE_SPR:
WriteText(dc, wxString::Format("spr%d", disasmInstr.operand[o].registerIndex), position, SYNTAX_COLOR_SPR);
break;
case PPCASM_OPERAND_TYPE_CR:
WriteText(dc, wxString::Format("cr%d", disasmInstr.operand[o].registerIndex), position, SYNTAX_COLOR_CR);
break;
case PPCASM_OPERAND_TYPE_IMM:
{
wxString string;
if (disasmInstr.operand[o].isSignedImm)
{
sint32 sImm = disasmInstr.operand[o].immS32;
if (disasmInstr.operand[o].immWidth == 16 && (sImm & 0x8000))
sImm |= 0xFFFF0000;
if ((sImm > -10 && sImm < 10) || forceDecDisplay)
string = wxString::Format("%d", sImm);
else
{
if (sImm < 0)
string = wxString::Format("-0x%x", -sImm);
else
string = wxString::Format("0x%x", sImm);
}
}
else
{
uint32 uImm = disasmInstr.operand[o].immS32;
if ((uImm >= 0 && uImm < 10) || forceDecDisplay)
string = wxString::Format("%u", uImm);
else
string = wxString::Format("0x%x", uImm);
}
WriteText(dc, string, position, SYNTAX_COLOR_IMM);
break;
}
case PPCASM_OPERAND_TYPE_PSQMODE:
{
if (disasmInstr.operand[o].immS32)
WriteText(dc, "single", position, SYNTAX_COLOR_IMM);
else
WriteText(dc, "paired", position, SYNTAX_COLOR_IMM);
break;
}
case PPCASM_OPERAND_TYPE_CIMM:
{
wxString string;
// use symbol for function calls if available
uint32 callDest = disasmInstr.operand[o].immU32;
RPLStoredSymbol* storedSymbol = nullptr;
if (disasmInstr.ppcAsmCode == PPCASM_OP_BL || disasmInstr.ppcAsmCode == PPCASM_OP_BLA)
storedSymbol = rplSymbolStorage_getByAddress(callDest);
if (storedSymbol)
{
// if symbol is within same module then don't display libname prefix
RPLModule* rplModuleCurrent = RPLLoader_FindModuleByCodeAddr(virtualAddress); // cache this
if (rplModuleCurrent && callDest >= rplModuleCurrent->regionMappingBase_text.GetMPTR() && callDest < (rplModuleCurrent->regionMappingBase_text.GetMPTR() + rplModuleCurrent->regionSize_text))
string = wxString((char*)storedSymbol->symbolName);
else
string = wxString::Format("%s.%s", (char*)storedSymbol->libName, (char*)storedSymbol->symbolName);
// truncate name after 36 characters
if (string.Length() >= 36)
{
string.Truncate(34);
string.Append("..");
}
}
else
{
string = wxString::Format("0x%08x", disasmInstr.operand[o].immU32);
}
WriteText(dc, string, position, SYNTAX_COLOR_CIMM);
if (disasmInstr.ppcAsmCode != PPCASM_OP_BL)
{
wxString x = wxString(" ");
if (callDest <= virtualAddress)
x.Append(wxUniChar(0x2191)); // arrow up
else
x.Append(wxUniChar(0x2193)); // arrow down
WriteText(dc, x, position, COLOR_BLACK);
}
break;
}
case PPCASM_OPERAND_TYPE_MEM:
{
// offset
wxString string;
if (disasmInstr.operand[o].isSignedImm && disasmInstr.operand[o].immS32 >= 0)
string = wxString::Format("+0x%x", disasmInstr.operand[o].immS32);
else
string = wxString::Format("-0x%x", -disasmInstr.operand[o].immS32);
WriteText(dc, string, position, SYNTAX_COLOR_IMM_OFFSET);
WriteText(dc, "(", position, COLOR_BLACK);
// register
WriteText(dc, wxString::Format("r%d", disasmInstr.operand[o].registerIndex), position, SYNTAX_COLOR_GPR);
WriteText(dc, ")", position, COLOR_BLACK);
break;
}
default:
// TODO
WriteText(dc, "<TODO>", position, wxColour(0xFF444444));
}
}
position.x = start_width + OFFSET_DISASSEMBLY;
const auto comment = static_cast<rplDebugSymbolComment*>(rplDebugSymbol_getForAddress(virtualAddress));
if (comment && comment->type == RplDebugSymbolComment)
WriteText(dc, comment->comment, position, COLOR_BLACK);
else if (isRLWINM)
{
sint32 rS, rA, SH, MB, ME;
rS = (opcode >> 21) & 0x1f;
rA = (opcode >> 16) & 0x1f;
SH = (opcode >> 11) & 0x1f;
MB = (opcode >> 6) & 0x1f;
ME = (opcode >> 1) & 0x1f;
uint32 mask = ppcAssembler_generateMaskRLW(MB, ME);
wxString string;
if (SH == 0)
string = wxString::Format("r%d=r%d&0x%x", rA, rS, mask);
else if ((0xFFFFFFFF << (uint32)disasmInstr.operand[2].immS32) == mask)
string = wxString::Format("r%d=r%d<<%d", rA, rS, SH);
else if ((0xFFFFFFFF >> (32 - SH) == mask))
string = wxString::Format("r%d=r%d>>%d", rA, rS, 32 - SH);
else
string = wxString::Format("r%d=(r%d<<<%d)&0x%x", rA, rS, SH, mask);
WriteText(dc, string, position, COLOR_GREY);
}
else if (disasmInstr.ppcAsmCode == PPCASM_OP_SUBF || disasmInstr.ppcAsmCode == PPCASM_OP_SUBF_)
{
sint32 rD, rA, rB;
rD = (opcode >> 21) & 0x1f;
rA = (opcode >> 16) & 0x1f;
rB = (opcode >> 11) & 0x1f;
wxString string;
string = wxString::Format("r%d=r%d-r%d", rD, rB, rA);
WriteText(dc, string, position, COLOR_GREY);
}
}
void DisasmCtrl::DrawLabelName(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, RPLStoredSymbol* storedSymbol)
{
wxString symbol_string = wxString::Format("%s:", (char*)storedSymbol->symbolName);
if (symbol_string.Length() > MAX_SYMBOL_LEN)
{
symbol_string.Truncate(MAX_SYMBOL_LEN - 3);
symbol_string.Append("..:");
}
wxPoint tmpPos(linePosition);
WriteText(dc, symbol_string, tmpPos, SYNTAX_COLOR_SYMBOL);
}
void DisasmCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position)
{
wxPoint position(0, 0);
RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(GetViewBaseAddress());
if (currentCodeRegionStart == currentCodeRegionEnd)
return;
sint32 viewFirstLine = GetViewStart().y;
sint32 lineOffset = start - viewFirstLine;
cemu_assert_debug(lineOffset >= 0);
sint32 instructionIndex = 0;
sint32 numLinesToUpdate = lineOffset + count;
numLinesToUpdate = std::min(numLinesToUpdate, (sint32)m_elements_visible);
if(m_lineToAddress.size() != m_elements_visible)
m_lineToAddress.resize(m_elements_visible);
sint32 lineIndex = 0;
while(lineIndex < numLinesToUpdate)
{
const uint32 virtualAddress = GetViewBaseAddress() + instructionIndex * 4;
instructionIndex++;
if (virtualAddress < currentCodeRegionStart ||
virtualAddress >= currentCodeRegionEnd)
{
NextLine(position, &start_position);
m_lineToAddress[lineIndex] = std::nullopt;
lineIndex++;
continue;
}
// check if valid memory address
if (!memory_isAddressRangeAccessible(virtualAddress, 4))
{
NextLine(position, &start_position);
m_lineToAddress[lineIndex] = std::nullopt;
lineIndex++;
continue;
}
// draw symbol as label
RPLStoredSymbol* storedSymbol = rplSymbolStorage_getByAddress(virtualAddress);
if (storedSymbol)
{
if(lineIndex >= lineOffset)
DrawLabelName(dc, position, virtualAddress, storedSymbol);
m_lineToAddress[lineIndex] = virtualAddress;
lineIndex++;
if (lineIndex >= numLinesToUpdate)
break;
NextLine(position, &start_position);
}
m_lineToAddress[lineIndex] = virtualAddress;
if (lineIndex >= lineOffset)
DrawDisassemblyLine(dc, position, virtualAddress, current_rpl_module);
NextLine(position, &start_position);
lineIndex++;
}
// draw vertical separator lines: offset | disassembly | comment
dc.SetPen(*wxLIGHT_GREY_PEN);
wxPoint line_from = start_position;
line_from.x = OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE - 5;
wxPoint line_to = line_from;
line_to.y += (count + 1) * m_line_height;
dc.DrawLine(line_from, line_to);
line_from.x += OFFSET_DISASSEMBLY;
line_to.x += OFFSET_DISASSEMBLY;
dc.DrawLine(line_from, line_to);
}
void DisasmCtrl::OnMouseMove(const wxPoint& start_position, uint32 line)
{
/*m_mouse_line = line;
if (m_mouse_line_drawn != -1)
RefreshLine(m_mouse_line_drawn);
if (m_mouse_line != -1)
RefreshLine(m_mouse_line);*/
wxPoint position = start_position;
// address
if (position.x <= OFFSET_ADDRESS)
{
return;
}
position.x -= OFFSET_ADDRESS;
// relative offset
if (position.x <= OFFSET_ADDRESS_RELATIVE)
{
return;
}
position.x -= OFFSET_ADDRESS_RELATIVE;
// disassembly code
if (position.x <= OFFSET_DISASSEMBLY)
{
if(m_mouse_down)
{
}
if (position.x <= OFFSET_DISASSEMBLY_OPERAND)
{
return;
}
position.x -= OFFSET_DISASSEMBLY_OPERAND;
}
}
void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position)
{
auto optVirtualAddress = LinePixelPosToAddress(position.y);
switch (key_code)
{
case WXK_F9:
{
if (optVirtualAddress)
{
debugger_toggleExecuteBreakpoint(*optVirtualAddress);
RefreshControl();
wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE);
wxPostEvent(this->m_parent, evt);
}
return;
}
case 'G':
{
if(IsKeyDown(WXK_CONTROL))
{
GoToAddressDialog();
return;
}
}
}
// debugger currently in break state
if (debuggerState.debugSession.isTrapped)
{
switch (key_code)
{
case WXK_F5:
{
debuggerState.debugSession.run = true;
return;
}
case WXK_F10:
{
debuggerState.debugSession.stepOver = true;
return;
}
case WXK_F11:
{
debuggerState.debugSession.stepInto = true;
}
}
}
else
{
switch (key_code)
{
case WXK_F5:
{
debuggerState.debugSession.shouldBreak = true;
}
}
}
}
void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
{
wxPoint pos = position;
auto optVirtualAddress = LinePixelPosToAddress(position.y - GetViewStart().y * m_line_height);
if (!optVirtualAddress)
return;
MPTR virtualAddress = *optVirtualAddress;
// address
if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE)
{
// address + address relative
debugger_toggleExecuteBreakpoint(virtualAddress);
RefreshLine(line);
wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE);
wxPostEvent(this->m_parent, evt);
return;
}
else if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE + OFFSET_DISASSEMBLY)
{
// double-clicked on disassembly (operation and operand data)
wxString currentInstruction = wxEmptyString;
wxTextEntryDialog set_value_dialog(this, _("Enter a new instruction."), _(wxString::Format("Overwrite instruction at address %08x", virtualAddress)), currentInstruction);
if (set_value_dialog.ShowModal() == wxID_OK)
{
PPCAssemblerInOut ctx = { 0 };
ctx.virtualAddress = virtualAddress;
if (ppcAssembler_assembleSingleInstruction(set_value_dialog.GetValue().c_str(), &ctx))
{
debugger_createPatch(virtualAddress, { ctx.outputData.data(), ctx.outputData.size() });
RefreshLine(line);
}
}
return;
}
else
{
// comment
const auto comment = static_cast<rplDebugSymbolComment*>(rplDebugSymbol_getForAddress(virtualAddress));
wxString old_comment = wxEmptyString;
if (comment && comment->type == RplDebugSymbolComment)
old_comment = comment->comment;
wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), _(wxString::Format("Create comment at address %08x", virtualAddress)), old_comment);
if (set_value_dialog.ShowModal() == wxID_OK)
{
rplDebugSymbol_createComment(virtualAddress, set_value_dialog.GetValue().wc_str());
RefreshLine(line);
}
return;
}
}
void DisasmCtrl::CopyToClipboard(std::string text) {
#if BOOST_OS_WINDOWS
if (OpenClipboard(nullptr))
{
EmptyClipboard();
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1);
if (hGlobal)
{
memcpy(GlobalLock(hGlobal), text.c_str(), text.size() + 1);
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
GlobalFree(hGlobal);
}
CloseClipboard();
}
#endif
}
void DisasmCtrl::OnContextMenu(const wxPoint& position, uint32 line)
{
wxPoint pos = position;
auto optVirtualAddress = LinePixelPosToAddress(position.y - GetViewStart().y * m_line_height);
if (!optVirtualAddress)
return;
MPTR virtualAddress = *optVirtualAddress;
// address
if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE)
{
CopyToClipboard(fmt::format("{:#10x}", virtualAddress));
return;
}
else if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE + OFFSET_DISASSEMBLY)
{
// double-clicked on disassembly (operation and operand data)
return;
}
else
{
// comment
return;
}
}
bool DisasmCtrl::OnShowTooltip(const wxPoint& position, uint32 line)
{
return false;
}
void DisasmCtrl::ScrollWindow(int dx, int dy, const wxRect* prect)
{
// scroll and repaint everything
RefreshControl(nullptr);
TextList::ScrollWindow(dx, dy, nullptr);
}
wxSize DisasmCtrl::DoGetBestSize() const
{
return TextList::DoGetBestSize();
}
void DisasmCtrl::OnUpdateView()
{
RefreshControl();
}
uint32 DisasmCtrl::GetViewBaseAddress() const
{
if (GetViewStart().y < 0)
return MPTR_NULL;
return currentCodeRegionStart + GetViewStart().y * 4;
}
std::optional<MPTR> DisasmCtrl::LinePixelPosToAddress(sint32 posY)
{
if (posY < 0)
return std::nullopt;
sint32 lineIndex = posY / m_line_height;
if (lineIndex >= m_lineToAddress.size())
return std::nullopt;
return m_lineToAddress[lineIndex];
}
uint32 DisasmCtrl::AddressToScrollPos(uint32 offset) const
{
return (offset - currentCodeRegionStart) / 4;
}
void DisasmCtrl::CenterOffset(uint32 offset)
{
if (offset < currentCodeRegionStart || offset >= currentCodeRegionEnd)
SelectCodeRegion(offset);
const sint32 line = AddressToScrollPos(offset);
if (line < 0 || line >= (sint32)m_element_count)
return;
if (m_active_line != -1)
RefreshLine(m_active_line);
DoScroll(0, std::max(0, line - (sint32)(m_elements_visible / 2)));
m_active_line = line;
RefreshLine(m_active_line);
debug_printf("scroll to %x\n", debuggerState.debugSession.instructionPointer);
}
void DisasmCtrl::GoToAddressDialog()
{
wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString);
if (goto_dialog.ShowModal() == wxID_OK)
{
ExpressionParser parser;
auto value = goto_dialog.GetValue().ToStdString();
std::transform(value.begin(), value.end(), value.begin(), tolower);
const auto module_count = RPLLoader_GetModuleCount();
const auto module_list = RPLLoader_GetModuleList();
std::vector<double> module_tmp(module_count);
for (int i = 0; i < module_count; i++)
{
const auto module = module_list[i];
if (module)
{
module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR();
parser.AddConstant(module->moduleName2, module_tmp[i]);
}
}
double grp_tmp[32];
PPCSnapshot& ppc_snapshot = debuggerState.debugSession.ppcSnapshot;
for (int i = 0; i < 32; i++)
{
char var_name[32];
sprintf(var_name, "r%d", i);
grp_tmp[i] = ppc_snapshot.gpr[i];
parser.AddConstant(var_name, grp_tmp[i]);
}
try
{
const auto result = (uint32)parser.Evaluate(value);
debug_printf("goto eval result: %x\n", result);
m_lastGotoTarget = result;
CenterOffset(result);
debuggerWindow_updateViewThreadsafe2();
}
catch (const std::exception& )
{
}
}
}

View file

@ -0,0 +1,48 @@
#pragma once
#include "gui/components/TextList.h"
class DisasmCtrl : public TextList
{
public:
DisasmCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style);
void Init();
wxSize DoGetBestSize() const override;
void OnUpdateView();
uint32 GetViewBaseAddress() const;
std::optional<MPTR> LinePixelPosToAddress(sint32 posY);
uint32 AddressToScrollPos(uint32 offset) const;
void CenterOffset(uint32 offset);
void GoToAddressDialog();
protected:
void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) override;
void OnMouseMove(const wxPoint& position, uint32 line) override;
void OnKeyPressed(sint32 key_code, const wxPoint& position) override;
void OnMouseDClick(const wxPoint& position, uint32 line) override;
void OnContextMenu(const wxPoint& position, uint32 line) override;
bool OnShowTooltip(const wxPoint& position, uint32 line) override;
void ScrollWindow(int dx, int dy, const wxRect* prect) override;
void DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, struct RPLModule* rplModule);
void DrawLabelName(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, struct RPLStoredSymbol* storedSymbol);
void SelectCodeRegion(uint32 newAddress);
private:
void CopyToClipboard(std::string text);
sint32 m_mouse_line, m_mouse_line_drawn;
sint32 m_active_line;
uint32 m_lastGotoTarget{};
// code region info
uint32 currentCodeRegionStart;
uint32 currentCodeRegionEnd;
// line to address mapping
std::vector<std::optional<MPTR>> m_lineToAddress;
};

View file

@ -0,0 +1,316 @@
#include "gui/wxgui.h"
#include "gui/debugger/DumpCtrl.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "util/helpers/helpers.h"
#include "Cemu/ExpressionParser/ExpressionParser.h"
#define COLOR_BLACK 0xFF000000
#define COLOR_GREY 0xFFA0A0A0
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_DEBUG_ACTIVE_BP 0xFFFFA0FF
#define COLOR_DEBUG_ACTIVE 0xFFFFA080
#define COLOR_DEBUG_BP 0xFF8080FF
#define SYNTAX_COLOR_GPR 0xFF000066
#define SYNTAX_COLOR_FPR 0xFF006666
#define SYNTAX_COLOR_SPR 0xFF666600
#define SYNTAX_COLOR_CR 0xFF666600
#define SYNTAX_COLOR_IMM 0xFF006600
#define SYNTAX_COLOR_IMM_OFFSET 0xFF006600
#define SYNTAX_COLOR_CIMM 0xFF880000
#define SYNTAX_COLOR_PSEUDO 0xFFA0A0A0 // color for pseudo code
#define SYNTAX_COLOR_SYMBOL 0xFF0000A0 // color for function symbol
#define OFFSET_ADDRESS (60)
#define OFFSET_ADDRESS_RELATIVE (90)
#define OFFSET_MEMORY (450)
#define OFFSET_DISASSEMBLY_OPERAND (80)
DumpCtrl::DumpCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style)
: TextList(parent, id, pos, size, style)
{
MMURange* range = memory_getMMURangeByAddress(0x10000000);
if (range)
{
m_memoryRegion.baseAddress = range->getBase();
m_memoryRegion.size = range->getSize();
Init();
}
else
{
m_memoryRegion.baseAddress = 0x10000000;
m_memoryRegion.size = 0x1000;
Init();
}
}
void DumpCtrl::Init()
{
uint32 element_count = m_memoryRegion.size;
this->SetElementCount(element_count / 0x10);
Scroll(0, 0);
RefreshControl();
}
void DumpCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position)
{
wxPoint position = start_position;
uint32 endAddr = m_memoryRegion.baseAddress + m_memoryRegion.size;
RPLModule* currentCodeRPL = RPLLoader_FindModuleByCodeAddr(m_memoryRegion.baseAddress + start);
RPLModule* currentDataRPL = RPLLoader_FindModuleByDataAddr(m_memoryRegion.baseAddress + start);
for (sint32 i = 0; i <= count; i++)
{
const uint32 virtual_address = m_memoryRegion.baseAddress + (start + i) * 0x10;
dc.SetTextForeground(wxColour(COLOR_BLACK));
dc.DrawText(wxString::Format("%08x", virtual_address), position);
position.x += OFFSET_ADDRESS;
dc.SetTextForeground(wxColour(COLOR_GREY));
if (currentCodeRPL)
{
dc.DrawText(wxString::Format("+0x%-8x", virtual_address - currentCodeRPL->regionMappingBase_text.GetMPTR()), position);
}
else if (currentDataRPL)
{
dc.DrawText(wxString::Format("+0x%-8x", virtual_address - currentDataRPL->regionMappingBase_data), position);
}
else
{
dc.DrawText("???", position);
}
position.x += OFFSET_ADDRESS_RELATIVE;
sint32 start_width = position.x;
if (!memory_isAddressRangeAccessible(virtual_address, 0x10))
{
for (sint32 f=0; f<0x10; f++)
{
wxPoint p(position);
WriteText(dc, wxString::Format("?? "), p);
position.x += (m_char_width * 3);
}
}
else
{
std::array<uint8, 0x10> data;
memory_readBytes(virtual_address, data);
for (auto b : data)
{
wxPoint p(position);
WriteText(dc, wxString::Format("%02x ", b), p);
position.x += (m_char_width * 3);
}
position.x = start_width = OFFSET_MEMORY;
dc.SetTextForeground(wxColour(COLOR_BLACK));
for (auto b : data)
{
if (isprint(b))
dc.DrawText(wxString::Format("%c ", b), position);
else
dc.DrawText(".", position);
position.x += m_char_width;
}
}
// display goto indicator
if (m_lastGotoOffset >= virtual_address && m_lastGotoOffset < (virtual_address + 16))
{
sint32 indicatorX = start_position.x + OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE + (m_lastGotoOffset - virtual_address) * m_char_width * 3;
wxPoint line1(start_position.x + indicatorX - 2, position.y);
wxPoint line2(line1.x, line1.y + m_line_height);
dc.SetPen(*wxRED_PEN);
dc.DrawLine(line1, line2);
dc.DrawLine(line1, wxPoint(line1.x + 3, line1.y));
dc.DrawLine(line2, wxPoint(line2.x + 3, line2.y));
}
NextLine(position, &start_position);
}
// draw vertical separator lines for 4 byte blocks
sint32 cursorOffsetHexBytes = start_position.x + OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE;
wxPoint line_from(
cursorOffsetHexBytes + m_char_width * (3 * 4 - 1) + m_char_width / 2,
start_position.y
);
wxPoint line_to(line_from.x, line_from.y + m_line_height * (count + 1));
dc.SetPen(*wxLIGHT_GREY_PEN);
for (sint32 i = 0; i < 3; i++)
{
dc.DrawLine(line_from, line_to);
line_from.x += m_char_width * (3 * 4);
line_to.x += m_char_width * (3 * 4);
}
}
void DumpCtrl::OnMouseMove(const wxPoint& start_position, uint32 line)
{
wxPoint position = start_position;
// address
if (position.x <= OFFSET_ADDRESS)
{
return;
}
position.x -= OFFSET_ADDRESS;
// relative offset
if (position.x <= OFFSET_ADDRESS_RELATIVE)
{
return;
}
position.x -= OFFSET_ADDRESS_RELATIVE;
// byte code
if (position.x <= OFFSET_MEMORY)
{
}
// string view
position.x -= OFFSET_MEMORY;
}
void DumpCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
{
wxPoint pos = position;
if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE)
return;
pos.x -= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE;
if(pos.x <= OFFSET_MEMORY)
{
const uint32 byte_index = (pos.x / m_char_width) / 3;
const uint32 offset = LineToOffset(line) + byte_index;
const uint8 value = memory_readU8(offset);
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set byte at address %08x", offset)), wxString::Format("%02x", value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const uint8 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
memory_writeU8(offset, new_value);
wxRect update_rect(0, line * m_line_height, GetSize().x, m_line_height);
RefreshControl(&update_rect);
}
return;
}
}
void DumpCtrl::GoToAddressDialog()
{
wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString);
if (goto_dialog.ShowModal() == wxID_OK)
{
try
{
ExpressionParser parser;
auto value = goto_dialog.GetValue().ToStdString();
std::transform(value.begin(), value.end(), value.begin(), tolower);
//parser.SetExpr(value);
const auto module_count = RPLLoader_GetModuleCount();
const auto module_list = RPLLoader_GetModuleList();
std::vector<double> module_tmp(module_count);
for (int i = 0; i < module_count; i++)
{
const auto module = module_list[i];
if (module)
{
module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR();
parser.AddConstant(module->moduleName2, module_tmp[i]);
}
}
double grp_tmp[32];
PPCSnapshot& ppc_snapshot = debuggerState.debugSession.ppcSnapshot;
for (int i = 0; i < 32; i++)
{
char var_name[32];
sprintf(var_name, "r%d", i);
grp_tmp[i] = ppc_snapshot.gpr[i];
parser.AddConstant(var_name, grp_tmp[i]);
}
const auto result = (uint32)parser.Evaluate(value);
debug_printf("goto eval result: %x\n", result);
m_lastGotoOffset = result;
CenterOffset(result);
}
catch (const std::exception ex)
{
wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
}
}
}
void DumpCtrl::CenterOffset(uint32 offset)
{
// check if the offset is valid
if (!memory_isAddressRangeAccessible(offset, 1))
return;
// set region and line
MMURange* range = memory_getMMURangeByAddress(offset);
if (m_memoryRegion.baseAddress != range->getBase() || m_memoryRegion.size != range->getSize())
{
m_memoryRegion.baseAddress = range->getBase();
m_memoryRegion.size = range->getSize();
Init();
}
const sint32 line = OffsetToLine(offset);
if (line < 0 || line >= (sint32)m_element_count)
return;
DoScroll(0, std::max(0, line - ((sint32)m_elements_visible / 2)));
RefreshControl();
//RefreshLine(line);
debug_printf("scroll to %x\n", debuggerState.debugSession.instructionPointer);
}
uint32 DumpCtrl::LineToOffset(uint32 line)
{
return m_memoryRegion.baseAddress + line * 0x10;
}
uint32 DumpCtrl::OffsetToLine(uint32 offset)
{
return (offset - m_memoryRegion.baseAddress) / 0x10;
}
void DumpCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position)
{
switch (key_code)
{
case 'G':
{
if (IsKeyDown(WXK_CONTROL))
{
GoToAddressDialog();
return;
}
}
}
}
wxSize DumpCtrl::DoGetBestSize() const
{
return TextList::DoGetBestSize();
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "gui/components/TextList.h"
class DumpCtrl : public TextList
{
public:
DumpCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style);
void Init();
wxSize DoGetBestSize() const override;
protected:
void GoToAddressDialog();
void CenterOffset(uint32 offset);
uint32 LineToOffset(uint32 line);
uint32 OffsetToLine(uint32 offset);
void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) override;
void OnMouseMove(const wxPoint& position, uint32 line) override;
void OnMouseDClick(const wxPoint& position, uint32 line) override;
void OnKeyPressed(sint32 key_code, const wxPoint& position) override;
private:
struct
{
uint32 baseAddress;
uint32 size;
}m_memoryRegion;
uint32 m_lastGotoOffset{0};
};

View file

@ -0,0 +1,48 @@
#include "gui/wxgui.h"
#include "gui/debugger/DumpWindow.h"
#include "gui/debugger/DebuggerWindow2.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "gui/debugger/DumpCtrl.h"
enum
{
// REGISTER
REGISTER_ADDRESS_R0 = wxID_HIGHEST + 8200,
REGISTER_LABEL_R0 = REGISTER_ADDRESS_R0 + 32,
REGISTER_LABEL_FPR0_0 = REGISTER_LABEL_R0 + 32,
REGISTER_LABEL_FPR1_0 = REGISTER_LABEL_R0 + 32,
};
DumpWindow::DumpWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size)
: wxFrame(&parent, wxID_ANY, wxT("Memory Dump"), wxDefaultPosition, wxSize(600, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT)
{
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
m_dump_ctrl = new DumpCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle);
main_sizer->Add(m_dump_ctrl, 1, wxEXPAND);
this->SetSizer(main_sizer);
this->wxWindowBase::Layout();
this->Centre(wxBOTH);
if (parent.GetConfig().data().pin_to_main)
OnMainMove(main_position, main_size);
}
void DumpWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size)
{
wxSize size(600, 250);
this->SetSize(size);
wxPoint position = main_position;
position.y += main_size.GetHeight();
this->SetPosition(position);
}
void DumpWindow::OnGameLoaded()
{
m_dump_ctrl->Init();
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "gui/debugger/DumpCtrl.h"
class DebuggerWindow2;
class DumpWindow : public wxFrame
{
public:
DumpWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size);
void OnMainMove(const wxPoint& position, const wxSize& main_size);
void OnGameLoaded();
private:
wxScrolledWindow* m_scrolled_window;
DumpCtrl* m_dump_ctrl;
};

View file

@ -0,0 +1,137 @@
#include "gui/wxgui.h"
#include "gui/guiWrapper.h"
#include "gui/debugger/ModuleWindow.h"
#include <sstream>
#include "gui/debugger/DebuggerWindow2.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/GraphicPack/GraphicPack2.h"
enum ItemColumns
{
ColumnName = 0,
ColumnAddress,
ColumnSize,
};
ModuleWindow::ModuleWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size)
: wxFrame(&parent, wxID_ANY, "Modules", wxDefaultPosition, wxSize(420, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT)
{
this->SetSizeHints(wxDefaultSize, wxDefaultSize);
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
m_modules = new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
wxListItem col0;
col0.SetId(ColumnName);
col0.SetText(_("Name"));
col0.SetWidth(125);
m_modules->InsertColumn(ColumnName, col0);
wxListItem col1;
col1.SetId(ColumnAddress);
col1.SetText(_("Address"));
col1.SetWidth(75);
col1.SetAlign(wxLIST_FORMAT_RIGHT);
m_modules->InsertColumn(ColumnAddress, col1);
wxListItem col2;
col2.SetId(ColumnSize);
col2.SetText(_("Size"));
col2.SetWidth(75);
col2.SetAlign(wxLIST_FORMAT_RIGHT);
m_modules->InsertColumn(ColumnSize, col2);
main_sizer->Add(m_modules, 1, wxEXPAND);
this->SetSizer(main_sizer);
this->wxWindowBase::Layout();
this->Centre(wxBOTH);
if (parent.GetConfig().data().pin_to_main)
OnMainMove(main_position, main_size);
m_modules->Bind(wxEVT_LEFT_DCLICK, &ModuleWindow::OnLeftDClick, this);
OnGameLoaded();
}
void ModuleWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size)
{
wxSize size(420, 250);
this->SetSize(size);
wxPoint position = main_position;
position.x -= 420;
position.y += main_size.y;
this->SetPosition(position);
}
void ModuleWindow::OnGameLoaded()
{
Freeze();
m_modules->DeleteAllItems();
const auto module_count = RPLLoader_GetModuleCount();
const auto module_list = RPLLoader_GetModuleList();
for (int i = 0; i < module_count; i++)
{
const auto module = module_list[i];
if (module)
{
wxListItem item;
item.SetId(i);
item.SetText(module->moduleName2.c_str());
const auto index = m_modules->InsertItem(item);
m_modules->SetItem(index, ColumnAddress, wxString::Format("%08x", module->regionMappingBase_text.GetMPTR()));
m_modules->SetItem(index, ColumnSize, wxString::Format("%x", module->regionSize_text));
}
}
sint32 patch_count = 0;
for (auto& gfx_pack : GraphicPack2::GetGraphicPacks())
{
for (auto& patch_group : gfx_pack->GetPatchGroups())
{
if (patch_group->isApplied())
{
wxListItem item;
item.SetId(module_count + patch_count);
item.SetText(std::string(patch_group->getName()));
const auto index = m_modules->InsertItem(item);
m_modules->SetItem(index, ColumnAddress, wxString::Format("%08x", patch_group->getCodeCaveBase()));
m_modules->SetItem(index, ColumnSize, wxString::Format("%x", patch_group->getCodeCaveSize()));
patch_count++;
}
}
}
Thaw();
}
void ModuleWindow::OnLeftDClick(wxMouseEvent& event)
{
long selected = m_modules->GetFirstSelected();
if (selected == -1)
return;
const auto text = m_modules->GetItemText(selected, ColumnAddress);
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
if (address == 0)
return;
debuggerState.debugSession.instructionPointer = address;
debuggerWindow_moveIP();
}

View file

@ -0,0 +1,17 @@
#pragma once
class DebuggerWindow2;
class ModuleWindow : public wxFrame
{
public:
ModuleWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size);
void OnMainMove(const wxPoint& position, const wxSize& main_size);
void OnGameLoaded();
private:
void OnLeftDClick(wxMouseEvent& event);
wxListView* m_modules;
};

View file

@ -0,0 +1,255 @@
#include "gui/wxgui.h"
#include "gui/debugger/RegisterCtrl.h"
#include <sstream>
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "gui/debugger/DebuggerWindow2.h"
#define COLOR_DEBUG_ACTIVE_BP 0xFFFFA0FF
#define COLOR_DEBUG_ACTIVE 0xFFFFA080
#define COLOR_DEBUG_BP 0xFF8080FF
#define SYNTAX_COLOR_GPR 0xFF000066
#define SYNTAX_COLOR_FPR 0xFF006666
#define SYNTAX_COLOR_SPR 0xFF666600
#define SYNTAX_COLOR_CR 0xFF666600
#define SYNTAX_COLOR_IMM 0xFF006600
#define SYNTAX_COLOR_IMM_OFFSET 0xFF006600
#define SYNTAX_COLOR_CIMM 0xFF880000
#define SYNTAX_COLOR_PSEUDO 0xFFA0A0A0 // color for pseudo code
#define SYNTAX_COLOR_SYMBOL 0xFF0000A0 // color for function symbol
#define OFFSET_REGISTER (60)
#define OFFSET_REGISTER_LABEL (100)
#define OFFSET_FPR (120)
RegisterCtrl::RegisterCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style)
: TextList(parent, id, pos, size, style), m_prev_snapshot({})
{
this->SetElementCount(32 + 1 + 32);
RefreshControl();
}
void RegisterCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position)
{
wxPoint position = start_position;
//RPLModule* current_rpl_module = rpl3_getModuleByCodeAddr(MEMORY_CODEAREA_ADDR + start);
for (sint32 i = 0; i <= count; i++)
{
const uint32 line = start + i;
if (line < 32)
{
dc.SetTextForeground(COLOR_BLACK);
dc.DrawText(wxString::Format("R%d", line), position);
position.x += OFFSET_REGISTER;
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[line];
if(register_value != m_prev_snapshot.gpr[line])
dc.SetTextForeground(COLOR_RED);
else
dc.SetTextForeground(COLOR_BLACK);
dc.DrawText(wxString::Format("%08x", register_value), position);
position.x += OFFSET_REGISTER_LABEL;
// check if address is a code offset
RPLModule* code_module = RPLLoader_FindModuleByCodeAddr(register_value);
if (code_module)
dc.DrawText(wxString::Format("<%s> + %x", code_module->moduleName2.c_str(), register_value - code_module->regionMappingBase_text.GetMPTR()), position);
// check if address is a string
uint32 string_offset = register_value;
if (string_offset >= MEMORY_DATA_AREA_ADDR && string_offset < (MEMORY_DATA_AREA_ADDR+MEMORY_DATA_AREA_SIZE) )
{
bool is_valid_string = true;
std::stringstream buffer;
uint32 string_offset = register_value;
while (string_offset < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE))
{
const uint8 c = memory_readU8(string_offset++);
if (isprint(c))
{
buffer << c;
}
else if( c == '\0' )
{
buffer << c;
break;
}
else
{
is_valid_string = false;
break;
}
}
if(is_valid_string && buffer.tellp() > 1)
{
dc.DrawText(wxString::Format("s \"%s\"", buffer.str().c_str()), position);
}
else
{
// check for widestring
is_valid_string = true;
string_offset = register_value;
std::wstringstream wbuffer;
while (string_offset < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE))
{
const uint16 c = memory_readU16(string_offset);
string_offset += 2;
if (iswprint(c))
{
wbuffer << c;
}
else if (c == L'\0')
{
wbuffer << c;
break;
}
else
{
is_valid_string = false;
break;
}
}
if (is_valid_string && buffer.tellp() > 1)
{
dc.DrawText(wxString::Format(L"ws \"%s\"", wbuffer.str().c_str()), position);
}
}
}
}
else if( 32 + 1 <= line && line <= 32 + 1 + 32)
{
const uint32 register_index = line - 32 - 1;
dc.SetTextForeground(COLOR_BLACK);
dc.DrawText(wxString::Format("F0_%d", register_index), position);
position.x += OFFSET_REGISTER;
const uint64 fpr0_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0int;
if (fpr0_value != m_prev_snapshot.fpr[register_index].fp0int)
dc.SetTextForeground(COLOR_RED);
else
dc.SetTextForeground(COLOR_BLACK);
//dc.DrawText(wxString::Format("%016llx", fpr0_value), position);
dc.DrawText(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0), position);
position.x += OFFSET_FPR;
dc.SetTextForeground(COLOR_BLACK);
dc.DrawText(wxString::Format("F1_%d", register_index), position);
position.x += OFFSET_REGISTER;
const uint64 fpr1_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1int;
if (fpr1_value != m_prev_snapshot.fpr[register_index].fp1int)
dc.SetTextForeground(COLOR_RED);
else
dc.SetTextForeground(COLOR_BLACK);
//dc.DrawText(wxString::Format("%016llx", fpr1_value), position);
dc.DrawText(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1), position);
}
else
{
// empty line
}
NextLine(position, &start_position);
}
memcpy(&m_prev_snapshot, &debuggerState.debugSession.ppcSnapshot, sizeof(m_prev_snapshot));
}
void RegisterCtrl::OnMouseMove(const wxPoint& position, uint32 line)
{
if (!m_mouse_down)
return;
wxPoint pos = position;
if(pos.x <= OFFSET_REGISTER)
{
}
pos.x -= OFFSET_REGISTER;
if (pos.x <= OFFSET_REGISTER_LABEL)
{
}
pos.x -= OFFSET_REGISTER_LABEL;
}
void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
{
if (!debuggerState.debugSession.isTrapped)
return;
if(line < 32)
{
const uint32 register_index = line;
if (position.x <= OFFSET_REGISTER + OFFSET_REGISTER_LABEL)
{
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index];
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set R%d value", register_index)), wxString::Format("%08x", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
debuggerState.debugSession.hCPU->gpr[register_index] = new_value;
debuggerState.debugSession.ppcSnapshot.gpr[register_index] = new_value;
wxRect update_rect(0, line * m_line_height, GetSize().x, m_line_height);
RefreshControl(&update_rect);
}
}
}
// one line empty between = line 32
else if (32 + 1 <= line && line <= 32 + 1 + 32)
{
const uint32 register_index = line - 32 - 1;
if (position.x <= OFFSET_REGISTER + OFFSET_FPR)
{
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP0_%d value", register_index)), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
debuggerState.debugSession.hCPU->fpr[register_index].fp0 = new_value;
debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0 = new_value;
wxRect update_rect(0, line * m_line_height, GetSize().x, m_line_height);
RefreshControl(&update_rect);
}
}
else
{
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP1_%d value", register_index)), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
debuggerState.debugSession.hCPU->fpr[register_index].fp1 = new_value;
debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1 = new_value;
wxRect update_rect(0, line * m_line_height, GetSize().x, m_line_height);
RefreshControl(&update_rect);
}
}
}
}
void RegisterCtrl::OnGameLoaded()
{
RefreshControl();
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "gui/components/TextList.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
class RegisterCtrl : public TextList
{
public:
RegisterCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style);
void OnGameLoaded();
protected:
void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) override;
void OnMouseMove(const wxPoint& position, uint32 line) override;
void OnMouseDClick(const wxPoint& position, uint32 line) override;
private:
PPCSnapshot m_prev_snapshot;
};

View file

@ -0,0 +1,450 @@
#include "gui/wxgui.h"
#include "gui/debugger/RegisterWindow.h"
#include <sstream>
#include "gui/debugger/DebuggerWindow2.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/HW/Espresso/EspressoISA.h"
enum
{
// REGISTER
kRegisterValueR0 = wxID_HIGHEST + 8400,
kRegisterLabelR0 = kRegisterValueR0 + 32,
kRegisterLabelLR = kRegisterLabelR0 + 32,
kRegisterValueLR = kRegisterLabelLR + 1,
kRegisterValueFPR0_0 = kRegisterValueLR + 32,
kRegisterValueFPR1_0 = kRegisterValueFPR0_0 + 32,
kRegisterValueCR0 = kRegisterValueFPR1_0 + 32,
kContextMenuZero,
kContextMenuInc,
kContextMenuDec,
kContextMenuCopy,
kContextMenuGotoDisasm,
kContextMenuGotoDump,
};
RegisterWindow::RegisterWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size)
: wxFrame(&parent, wxID_ANY, "Register View", wxDefaultPosition, wxSize(400, 975), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT),
m_prev_snapshot({}), m_show_double_values(true), m_context_ctrl(nullptr)
{
SetSizeHints(wxDefaultSize, wxDefaultSize);
SetMaxSize({ 400, 975 });
wxWindowBase::SetBackgroundColour(*wxWHITE);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
auto scrolled_win = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
auto gpr_sizer = new wxFlexGridSizer(0, 3, 0, 0);
// GPRs
for(sint32 i = 0; i < 32; ++i)
{
gpr_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("R%d", i)), 0, wxLEFT, 5);
auto value = new wxTextCtrl(scrolled_win, kRegisterValueR0 + i, wxString::Format("%08x", 0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
value->SetBackgroundColour(*wxWHITE);
value->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this);
//value->Bind(wxEVT_CONTEXT_MENU, &RegisterWindow::OnValueContextMenu, this);
gpr_sizer->Add(value, 0, wxLEFT|wxRIGHT, 5);
auto label = new wxTextCtrl(scrolled_win, kRegisterLabelR0 + i, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
label->SetMinSize(wxSize(500, -1));
label->SetBackgroundColour(*wxWHITE);
gpr_sizer->Add(label, 0, wxEXPAND);
}
{
// LR
gpr_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("LR")), 0, wxLEFT, 5);
auto value = new wxTextCtrl(scrolled_win, kRegisterValueLR, wxString::Format("%08x", 0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
value->SetBackgroundColour(*wxWHITE);
value->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this);
//value->Bind(wxEVT_CONTEXT_MENU, &RegisterWindow::OnValueContextMenu, this);
gpr_sizer->Add(value, 0, wxLEFT | wxRIGHT, 5);
auto label = new wxTextCtrl(scrolled_win, kRegisterLabelLR, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
label->SetMinSize(wxSize(500, -1));
label->SetBackgroundColour(*wxWHITE);
gpr_sizer->Add(label, 0, wxEXPAND);
}
sizer->Add(gpr_sizer, 1, wxEXPAND);
auto button = new wxButton(scrolled_win, wxID_ANY, "FP view mode");
button->Bind(wxEVT_BUTTON, &RegisterWindow::OnFPViewModePress, this);
sizer->Add(button, 0, wxALL, 5);
auto fp_sizer = new wxFlexGridSizer(0, 3, 0, 0);
for (sint32 i = 0; i < 32; ++i)
{
fp_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("FP%d", i)), 0, wxLEFT, 5);
auto value0 = new wxTextCtrl(scrolled_win, kRegisterValueFPR0_0 + i, wxString::Format("%lf", 0.0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
value0->SetBackgroundColour(*wxWHITE);
value0->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this);
fp_sizer->Add(value0, 0, wxLEFT | wxRIGHT, 5);
auto value1 = new wxTextCtrl(scrolled_win, kRegisterValueFPR1_0 + i, wxString::Format("%lf", 0.0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
value1->SetBackgroundColour(*wxWHITE);
value1->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this);
fp_sizer->Add(value1, 0, wxLEFT | wxRIGHT, 5);
}
sizer->Add(fp_sizer, 0, wxEXPAND);
auto cr_sizer = new wxFlexGridSizer(0, 2, 0, 0);
// CRs
for (sint32 i = 0; i < 8; ++i)
{
cr_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("CR%d", i)), 0, wxLEFT, 5);
auto value = new wxTextCtrl(scrolled_win, kRegisterValueCR0 + i, "-", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
value->SetBackgroundColour(*wxWHITE);
//value->Bind(wxEVT_CONTEXT_MENU, &RegisterWindow::OnValueContextMenu, this);
cr_sizer->Add(value, 0, wxRIGHT, 5);
}
sizer->Add(cr_sizer, 0, wxEXPAND);
scrolled_win->SetSizerAndFit(sizer);
scrolled_win->SetScrollRate(0, GetCharWidth());
main_sizer->Add(scrolled_win, 1, wxEXPAND);
SetSizer(main_sizer);
Layout();
if (parent.GetConfig().data().pin_to_main)
OnMainMove(main_position, main_size);
//Bind(wxEVT_COMMAND_MENU_SELECTED, &RegisterWindow::OnValueContextMenuSelected, this, kContextMenuZero, kContextMenuGotoDump);
}
void RegisterWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size)
{
wxSize size(400, 255 + main_size.y + 250);
this->SetSize(size);
wxPoint position = main_position;
position.x += main_size.x;
position.y -= 255;
this->SetPosition(position);
}
void RegisterWindow::UpdateIntegerRegister(wxTextCtrl* label, wxTextCtrl* value, uint32 registerValue, bool hasChanged)
{
//const auto value = dynamic_cast<wxTextCtrl*>(GetWindowChild(kRegisterValueR0 + i));
//wxASSERT(value);
//const bool has_changed = register_value != m_prev_snapshot.gpr[i];
if (hasChanged)
value->SetForegroundColour(COLOR_RED);
else if (value->GetForegroundColour() != COLOR_BLACK)
value->SetForegroundColour(COLOR_BLACK);
value->SetLabelText(wxString::Format("%08x", registerValue));
//const auto label = dynamic_cast<wxTextCtrl*>(GetWindowChild(kRegisterLabelR0 + i));
//wxASSERT(label);
label->SetForegroundColour(hasChanged ? COLOR_RED : COLOR_BLACK);
// check if address is a string
if (registerValue >= MEMORY_DATA_AREA_ADDR && registerValue < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE))
{
bool is_valid_string = true;
std::stringstream buffer;
uint32 string_offset = registerValue;
while (string_offset < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE))
{
const uint8 c = memory_readU8(string_offset++);
if (isprint(c))
buffer << c;
else if (c == '\0')
{
buffer << c;
break;
}
else
{
is_valid_string = false;
break;
}
}
if (is_valid_string && buffer.tellp() > 1)
{
label->SetLabelText(wxString::Format("\"%s\"", buffer.str().c_str()));
return;
}
// check for widestring
is_valid_string = true;
string_offset = registerValue;
std::wstringstream wbuffer;
while (string_offset < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE - 1))
{
const uint16 c = memory_readU16(string_offset);
string_offset += 2;
if (iswprint(c))
wbuffer << c;
else if (c == L'\0')
{
wbuffer << c;
break;
}
else
{
is_valid_string = false;
break;
}
}
if (is_valid_string && buffer.tellp() > 1)
{
label->SetLabelText(wxString::Format(L"ws\"%s\"", wbuffer.str().c_str()));
return;
}
}
// check if address is a code offset
RPLModule* code_module = RPLLoader_FindModuleByCodeAddr(registerValue);
if (code_module)
{
label->SetLabelText(wxString::Format("<%s> + %x", code_module->moduleName2.c_str(), registerValue - code_module->regionMappingBase_text.GetMPTR()));
return;
}
label->SetLabelText(wxEmptyString);
}
void RegisterWindow::OnUpdateView()
{
// m_register_ctrl->RefreshControl();
for (int i = 0; i < 32; ++i)
{
const uint32 registerValue = debuggerState.debugSession.ppcSnapshot.gpr[i];
const bool hasChanged = registerValue != m_prev_snapshot.gpr[i];
const auto value = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueR0 + i));
wxASSERT(value);
const auto label = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterLabelR0 + i));
wxASSERT(label);
UpdateIntegerRegister(label, value, registerValue, hasChanged);
}
// update LR
{
const uint32 registerValue = debuggerState.debugSession.ppcSnapshot.spr_lr;
const bool hasChanged = registerValue != m_prev_snapshot.spr_lr;
const auto value = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueLR));
wxASSERT(value);
const auto label = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterLabelLR));
wxASSERT(label);
UpdateIntegerRegister(label, value, registerValue, hasChanged);
}
for (int i = 0; i < 32; ++i)
{
const uint64_t register_value = debuggerState.debugSession.ppcSnapshot.fpr[i].fp0int;
const auto value = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueFPR0_0 + i));
wxASSERT(value);
const bool has_changed = register_value != m_prev_snapshot.fpr[i].fp0int;
if (has_changed)
value->SetForegroundColour(COLOR_RED);
else if (value->GetForegroundColour() != COLOR_BLACK)
value->SetForegroundColour(COLOR_BLACK);
else
continue;
if(m_show_double_values)
value->SetLabelText(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp0));
else
value->SetLabelText(wxString::Format("%016llx", register_value));
}
for (int i = 0; i < 32; ++i)
{
const uint64_t register_value = debuggerState.debugSession.ppcSnapshot.fpr[i].fp1int;
const auto value = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueFPR1_0 + i));
wxASSERT(value);
const bool has_changed = register_value != m_prev_snapshot.fpr[i].fp1int;
if (has_changed)
value->SetForegroundColour(COLOR_RED);
else if (value->GetForegroundColour() != COLOR_BLACK)
value->SetForegroundColour(COLOR_BLACK);
else
continue;
if (m_show_double_values)
value->SetLabelText(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp1));
else
value->SetLabelText(wxString::Format("%016llx", register_value));
}
// update CRs
for (int i = 0; i < 8; ++i)
{
const auto value = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueCR0 + i));
wxASSERT(value);
auto cr_bits_ptr = debuggerState.debugSession.ppcSnapshot.cr + i * 4;
auto cr_bits_ptr_cmp = m_prev_snapshot.cr + i * 4;
const bool has_changed = !std::equal(cr_bits_ptr, cr_bits_ptr + 4, cr_bits_ptr_cmp);
if (has_changed)
value->SetForegroundColour(COLOR_RED);
else if (value->GetForegroundColour() != COLOR_BLACK)
value->SetForegroundColour(COLOR_BLACK);
else
continue;
std::vector<std::string> joinArray = {};
if (cr_bits_ptr[Espresso::CR_BIT_INDEX_LT] != 0)
joinArray.emplace_back("LT");
if (cr_bits_ptr[Espresso::CR_BIT_INDEX_GT] != 0)
joinArray.emplace_back("GT");
if (cr_bits_ptr[Espresso::CR_BIT_INDEX_EQ] != 0)
joinArray.emplace_back("EQ");
if (cr_bits_ptr[Espresso::CR_BIT_INDEX_SO] != 0)
joinArray.emplace_back("SO");
if (joinArray.empty())
value->SetLabelText("-");
else
value->SetLabelText(fmt::format("{}", fmt::join(joinArray, ", ")));
}
memcpy(&m_prev_snapshot, &debuggerState.debugSession.ppcSnapshot, sizeof(m_prev_snapshot));
}
void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
{
if (!debuggerState.debugSession.isTrapped)
{
event.Skip();
return;
}
const auto id = event.GetId();
if(kRegisterValueR0 <= id && id < kRegisterValueR0 + 32)
{
const uint32 register_index = id - kRegisterValueR0;
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index];
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set R%d value", register_index)), wxString::Format("%08x", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
debuggerState.debugSession.hCPU->gpr[register_index] = new_value;
debuggerState.debugSession.ppcSnapshot.gpr[register_index] = new_value;
OnUpdateView();
}
return;
}
if (kRegisterValueFPR0_0 <= id && id < kRegisterValueFPR0_0 + 32)
{
const uint32 register_index = id - kRegisterValueFPR0_0;
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP0_%d value", register_index)), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
debuggerState.debugSession.hCPU->fpr[register_index].fp0 = new_value;
debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0 = new_value;
OnUpdateView();
}
return;
}
if (kRegisterValueFPR1_0 <= id && id < kRegisterValueFPR1_0 + 32)
{
const uint32 register_index = id - kRegisterValueFPR1_0;
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP1_%d value", register_index)), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
debuggerState.debugSession.hCPU->fpr[register_index].fp1 = new_value;
debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1 = new_value;
OnUpdateView();
}
return;
}
event.Skip();
}
void RegisterWindow::OnFPViewModePress(wxCommandEvent& event)
{
m_show_double_values = !m_show_double_values;
for (int i = 0; i < 32; ++i)
{
const auto value0 = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueFPR0_0 + i));
const auto value1 = dynamic_cast<wxTextCtrl*>(FindWindow(kRegisterValueFPR1_0 + i));
wxASSERT(value0);
wxASSERT(value1);
if (m_show_double_values)
{
value0->SetLabelText(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp0));
value1->SetLabelText(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp1));
}
else
{
value0->SetLabelText(wxString::Format("%016llx", debuggerState.debugSession.ppcSnapshot.fpr[i].fp0int));
value1->SetLabelText(wxString::Format("%016llx", debuggerState.debugSession.ppcSnapshot.fpr[i].fp1int));
}
}
}
void RegisterWindow::OnValueContextMenu(wxContextMenuEvent& event)
{
wxMenu menu;
menu.Append(kContextMenuZero, _("&Zero"));
menu.Append(kContextMenuInc, _("&Increment"));
menu.Append(kContextMenuDec, _("&Decrement"));
menu.AppendSeparator();
menu.Append(kContextMenuCopy, _("&Copy"));
menu.AppendSeparator();
menu.Append(kContextMenuGotoDisasm, _("&Goto Disasm"));
menu.Append(kContextMenuGotoDump, _("G&oto Dump"));
m_context_ctrl = dynamic_cast<wxTextCtrl*>(event.GetEventObject());
PopupMenu(&menu);
}
void RegisterWindow::OnValueContextMenuSelected(wxCommandEvent& event)
{
wxASSERT(m_context_ctrl);
switch (event.GetId())
{
case kContextMenuZero:
break;
case kContextMenuInc:
break;
case kContextMenuDec:
break;
case kContextMenuCopy:
break;
case kContextMenuGotoDisasm:
break;
case kContextMenuGotoDump:
break;
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
class DebuggerWindow2;
class RegisterWindow : public wxFrame
{
public:
RegisterWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size);
void OnMainMove(const wxPoint& position, const wxSize& main_size);
void OnUpdateView();
private:
void OnMouseDClickEvent(wxMouseEvent& event);
void OnFPViewModePress(wxCommandEvent& event);
void OnValueContextMenu(wxContextMenuEvent& event);
void OnValueContextMenuSelected(wxCommandEvent& event);
void UpdateIntegerRegister(wxTextCtrl* label, wxTextCtrl* value, uint32 registerValue, bool hasChanged);
PPCSnapshot m_prev_snapshot;
bool m_show_double_values;
wxTextCtrl* m_context_ctrl;
};

View file

@ -0,0 +1,163 @@
#include "gui/debugger/SymbolCtrl.h"
#include "gui/guiWrapper.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
enum ItemColumns
{
ColumnName = 0,
ColumnAddress,
ColumnModule,
};
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) :
wxListCtrl(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
{
wxListItem col0;
col0.SetId(ColumnName);
col0.SetText(_("Name"));
col0.SetWidth(200);
InsertColumn(ColumnName, col0);
wxListItem col1;
col1.SetId(ColumnAddress);
col1.SetText(_("Address"));
col1.SetWidth(75);
col1.SetAlign(wxLIST_FORMAT_RIGHT);
InsertColumn(ColumnAddress, col1);
wxListItem col2;
col2.SetId(ColumnModule);
col2.SetText(_("Module"));
col2.SetWidth(75);
col2.SetAlign(wxLIST_FORMAT_RIGHT);
InsertColumn(ColumnModule, col2);
Bind(wxEVT_LIST_ITEM_ACTIVATED, &SymbolListCtrl::OnLeftDClick, this);
Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &SymbolListCtrl::OnRightClick, this);
m_list_filter = "";
OnGameLoaded();
SetItemCount(m_data.size());
}
void SymbolListCtrl::OnGameLoaded()
{
m_data.clear();
long itemId = 0;
const auto symbol_map = rplSymbolStorage_lockSymbolMap();
for (auto const& [address, symbol_info] : symbol_map)
{
if (symbol_info == nullptr || symbol_info->symbolName == nullptr)
continue;
auto new_entry = m_data.try_emplace(
symbol_info->address,
(char*)(symbol_info->symbolName),
(char*)(symbol_info->libName),
"",
false
);
new_entry.first->second.searchName += new_entry.first->second.name;
new_entry.first->second.searchName += new_entry.first->second.libName;
new_entry.first->second.searchName.MakeLower();
if (m_list_filter == "")
new_entry.first->second.visible = true;
else if (new_entry.first->second.searchName.Contains(m_list_filter))
new_entry.first->second.visible = true;
}
rplSymbolStorage_unlockSymbolMap();
SetItemCount(m_data.size());
RefreshItems(GetTopItem(), GetTopItem() + GetCountPerPage() + 1);
}
wxString SymbolListCtrl::OnGetItemText(long item, long column) const
{
if (item >= GetItemCount())
return wxEmptyString;
long visible_idx = 0;
for (const auto& [address, symbol] : m_data)
{
if (!symbol.visible)
continue;
if (item == visible_idx)
{
if (column == ColumnName)
return wxString(symbol.name);
if (column == ColumnAddress)
return wxString::Format("%08x", address);
if (column == ColumnModule)
return wxString(symbol.libName);
}
visible_idx++;
}
return wxEmptyString;
}
void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
{
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selected == -1)
return;
const auto text = GetItemText(selected, ColumnAddress);
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
if (address == 0)
return;
debuggerState.debugSession.instructionPointer = address;
debuggerWindow_moveIP();
}
void SymbolListCtrl::OnRightClick(wxListEvent& event)
{
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selected == -1)
return;
auto text = GetItemText(selected, ColumnAddress);
text = "0x" + text;
#if BOOST_OS_WINDOWS
if (OpenClipboard(nullptr))
{
EmptyClipboard();
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, text.size()+1);
if (hGlobal)
{
memcpy(GlobalLock(hGlobal), text.c_str(), text.size() + 1);
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
GlobalFree(hGlobal);
}
CloseClipboard();
}
#endif
}
void SymbolListCtrl::ChangeListFilter(std::string filter)
{
m_list_filter = wxString(filter).MakeLower();
size_t visible_entries = m_data.size();
for (auto& [address, symbol] : m_data)
{
if (m_list_filter == "")
symbol.visible = true;
else if (symbol.searchName.Contains(m_list_filter))
symbol.visible = true;
else
{
visible_entries--;
symbol.visible = false;
}
}
SetItemCount(visible_entries);
RefreshItems(GetTopItem(), GetTopItem() + GetCountPerPage() + 1);
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <wx/listctrl.h>
class SymbolListCtrl : public wxListCtrl
{
public:
SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size);
void OnGameLoaded();
void ChangeListFilter(std::string filter);
private:
struct SymbolItem {
SymbolItem() {}
SymbolItem(wxString name, wxString libName, wxString searchName, bool visible) : name(name), libName(libName), searchName(searchName), visible(visible) {}
wxString name;
wxString libName;
wxString searchName;
bool visible;
};
std::map<MPTR, SymbolItem> m_data;
wxString m_list_filter;
wxString OnGetItemText(long item, long column) const;
void OnLeftDClick(wxListEvent& event);
void OnRightClick(wxListEvent& event);
};

View file

@ -0,0 +1,55 @@
#include "gui/wxgui.h"
#include "gui/guiWrapper.h"
#include "gui/debugger/SymbolWindow.h"
#include "gui/debugger/DebuggerWindow2.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
enum ItemColumns
{
ColumnName = 0,
ColumnAddress,
ColumnModule,
};
SymbolWindow::SymbolWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size)
: wxFrame(&parent, wxID_ANY, wxT("Symbol Window"), wxDefaultPosition, wxSize(600, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT)
{
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
m_symbol_ctrl = new SymbolListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
main_sizer->Add(m_symbol_ctrl, 1, wxEXPAND);
m_filter = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_NO_VSCROLL);
m_filter->Bind(wxEVT_TEXT, &SymbolWindow::OnFilterChanged, this);
main_sizer->Add(m_filter, 0, wxALL | wxEXPAND, 5);
this->SetSizer(main_sizer);
this->wxWindowBase::Layout();
this->Centre(wxHORIZONTAL);
if (parent.GetConfig().data().pin_to_main)
OnMainMove(main_position, main_size);
}
void SymbolWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size)
{
wxSize size(420, 250);
this->SetSize(size);
wxPoint position = main_position;
position.x -= 420;
this->SetPosition(position);
}
void SymbolWindow::OnGameLoaded()
{
m_symbol_ctrl->OnGameLoaded();
}
void SymbolWindow::OnFilterChanged(wxCommandEvent& event)
{
m_symbol_ctrl->ChangeListFilter(m_filter->GetValue().ToStdString());
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "gui/debugger/SymbolCtrl.h"
class DebuggerWindow2;
class SymbolWindow : public wxFrame
{
public:
SymbolWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size);
void OnMainMove(const wxPoint& position, const wxSize& main_size);
void OnGameLoaded();
void OnLeftDClick(wxListEvent& event);
private:
wxTextCtrl* m_filter;
SymbolListCtrl* m_symbol_ctrl;
void OnFilterChanged(wxCommandEvent& event);
};