mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-06 15:01:18 +12:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
264
src/gui/debugger/BreakpointWindow.cpp
Normal file
264
src/gui/debugger/BreakpointWindow.cpp
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
26
src/gui/debugger/BreakpointWindow.h
Normal file
26
src/gui/debugger/BreakpointWindow.h
Normal 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;
|
||||
};
|
676
src/gui/debugger/DebuggerWindow2.cpp
Normal file
676
src/gui/debugger/DebuggerWindow2.cpp
Normal 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();
|
||||
}
|
||||
}
|
116
src/gui/debugger/DebuggerWindow2.h
Normal file
116
src/gui/debugger/DebuggerWindow2.h
Normal 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();
|
||||
};
|
||||
|
806
src/gui/debugger/DisasmCtrl.cpp
Normal file
806
src/gui/debugger/DisasmCtrl.cpp
Normal 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& )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
48
src/gui/debugger/DisasmCtrl.h
Normal file
48
src/gui/debugger/DisasmCtrl.h
Normal 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;
|
||||
};
|
316
src/gui/debugger/DumpCtrl.cpp
Normal file
316
src/gui/debugger/DumpCtrl.cpp
Normal 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();
|
||||
}
|
30
src/gui/debugger/DumpCtrl.h
Normal file
30
src/gui/debugger/DumpCtrl.h
Normal 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};
|
||||
};
|
48
src/gui/debugger/DumpWindow.cpp
Normal file
48
src/gui/debugger/DumpWindow.cpp
Normal 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();
|
||||
}
|
18
src/gui/debugger/DumpWindow.h
Normal file
18
src/gui/debugger/DumpWindow.h
Normal 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;
|
||||
};
|
137
src/gui/debugger/ModuleWindow.cpp
Normal file
137
src/gui/debugger/ModuleWindow.cpp
Normal 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();
|
||||
}
|
17
src/gui/debugger/ModuleWindow.h
Normal file
17
src/gui/debugger/ModuleWindow.h
Normal 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;
|
||||
};
|
255
src/gui/debugger/RegisterCtrl.cpp
Normal file
255
src/gui/debugger/RegisterCtrl.cpp
Normal 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();
|
||||
}
|
||||
|
18
src/gui/debugger/RegisterCtrl.h
Normal file
18
src/gui/debugger/RegisterCtrl.h
Normal 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;
|
||||
};
|
450
src/gui/debugger/RegisterWindow.cpp
Normal file
450
src/gui/debugger/RegisterWindow.cpp
Normal 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;
|
||||
}
|
||||
}
|
26
src/gui/debugger/RegisterWindow.h
Normal file
26
src/gui/debugger/RegisterWindow.h
Normal 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;
|
||||
};
|
163
src/gui/debugger/SymbolCtrl.cpp
Normal file
163
src/gui/debugger/SymbolCtrl.cpp
Normal 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);
|
||||
}
|
30
src/gui/debugger/SymbolCtrl.h
Normal file
30
src/gui/debugger/SymbolCtrl.h
Normal 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);
|
||||
};
|
55
src/gui/debugger/SymbolWindow.cpp
Normal file
55
src/gui/debugger/SymbolWindow.cpp
Normal 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());
|
||||
}
|
22
src/gui/debugger/SymbolWindow.h
Normal file
22
src/gui/debugger/SymbolWindow.h
Normal 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);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue