mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-01-10 17:09:25 +01:00
806 lines
23 KiB
C++
806 lines
23 KiB
C++
#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& )
|
|
{
|
|
}
|
|
}
|
|
} |