#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 // 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, "", position, wxColour(0xFF444444)); } } position.x = start_width + OFFSET_DISASSEMBLY; const auto comment = static_cast(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(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 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 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& ) { } } }