diff --git a/bin/docs/debugger.txt b/bin/docs/debugger.txt index a8fd3e3ca6..82c4c94ac7 100644 --- a/bin/docs/debugger.txt +++ b/bin/docs/debugger.txt @@ -11,6 +11,7 @@ disassembly view: page up move visible area up one page page down move visible area down one page f10 step over + f11 step into tab toggle display symbols left click select line/toggle breakpoint if line is already highlighted right click open context menu @@ -29,3 +30,11 @@ memory view: any overwrite ansi byte left click select byte/nibble right click open context menu + +breakpoint list: + + up select previous item + down select next item + delete remove selected breakpoint + return edit selected breakpoint + space toggle enable state of selected breakpoint diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 3c91315276..d9ddada26f 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -326,6 +326,7 @@ set(pcsx2GuiSources gui/Debugger/CtrlDisassemblyView.cpp gui/Debugger/CtrlRegisterList.cpp gui/Debugger/CtrlMemView.cpp + gui/Debugger/DebuggerLists.cpp gui/Debugger/DisassemblyDialog.cpp gui/Debugger/DebugEvents.cpp gui/ExecutorThread.cpp @@ -381,6 +382,7 @@ set(pcsx2GuiHeaders gui/Debugger/CtrlDisassemblyView.h gui/Debugger/CtrlRegisterList.h gui/Debugger/CtrlMemView.h + gui/Debugger/DebuggerLists.h gui/Debugger/DisassemblyDialog.h gui/Debugger/DebugEvents.h gui/i18n.h diff --git a/pcsx2/DebugTools/Breakpoints.cpp b/pcsx2/DebugTools/Breakpoints.cpp index 6bed7d3cbf..9bfaf6a563 100644 --- a/pcsx2/DebugTools/Breakpoints.cpp +++ b/pcsx2/DebugTools/Breakpoints.cpp @@ -385,6 +385,10 @@ const std::vector CBreakPoints::GetBreakpoints() return breakPoints_; } +// including them earlier causes some ambiguities +#include "App.h" +#include "Debugger/DisassemblyDialog.h" + void CBreakPoints::Update(u32 addr) { bool resume = false; @@ -402,6 +406,5 @@ void CBreakPoints::Update(u32 addr) if (resume) r5900Debug.resumeCpu(); - // Redraw in order to show the breakpoint. - // host->UpdateDisassembly(); + wxGetApp().GetDisassemblyPtr()->update(); } diff --git a/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp b/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp index 49e06a582c..0901f71803 100644 --- a/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp +++ b/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp @@ -1059,3 +1059,11 @@ void CtrlDisassemblyView::editBreakpoint() postEvent(debEVT_UPDATE,0); } } + +void CtrlDisassemblyView::getOpcodeText(u32 address, char* dest) +{ + DisassemblyLineInfo line; + address = manager.getStartAddress(address); + manager.getLine(address,displaySymbols,line); + sprintf(dest,"%s %s",line.name.c_str(),line.params.c_str()); +} \ No newline at end of file diff --git a/pcsx2/gui/Debugger/CtrlDisassemblyView.h b/pcsx2/gui/Debugger/CtrlDisassemblyView.h index 0e83180fca..d5e6e194b4 100644 --- a/pcsx2/gui/Debugger/CtrlDisassemblyView.h +++ b/pcsx2/gui/Debugger/CtrlDisassemblyView.h @@ -22,6 +22,7 @@ public: void scanFunctions(); void clearFunctions() { manager.clear(); }; void redraw(); + void getOpcodeText(u32 address, char* dest); u32 getInstructionSizeAt(u32 address) { diff --git a/pcsx2/gui/Debugger/DebuggerLists.cpp b/pcsx2/gui/Debugger/DebuggerLists.cpp new file mode 100644 index 0000000000..8e0c1c9855 --- /dev/null +++ b/pcsx2/gui/Debugger/DebuggerLists.cpp @@ -0,0 +1,334 @@ +#include "PrecompiledHeader.h" +#include "DebuggerLists.h" +#include "BreakpointWindow.h" +#include "DebugEvents.h" + +void insertListViewColumns(wxListCtrl* list, GenericListViewColumn* columns, int count) +{ + int totalWidth = list->GetSize().x; + + for (int i = 0; i < count; i++) + { + wxListItem column; + column.SetText(columns[i].name); + column.SetWidth(totalWidth * columns[i].size); + + list->InsertColumn(i,column); + } +} + +void resizeListViewColumns(wxListCtrl* list, GenericListViewColumn* columns, int count, int totalWidth) +{ + for (int i = 0; i < count; i++) + { + list->SetColumnWidth(i,totalWidth*columns[i].size); + } +} + +// +// BreakpointList +// + +BEGIN_EVENT_TABLE(BreakpointList, wxWindow) + EVT_SIZE(BreakpointList::sizeEvent) + EVT_KEY_DOWN(BreakpointList::keydownEvent) +END_EVENT_TABLE() + +enum { BPL_TYPE, BPL_OFFSET, BPL_SIZELABEL, BPL_OPCODE, BPL_CONDITION, BPL_HITS, BPL_ENABLED, BPL_COLUMNCOUNT }; + +GenericListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = { + { L"Type", 0.12f }, + { L"Offset", 0.12f }, + { L"Size/Label", 0.18f }, + { L"Opcode", 0.28f }, + { L"Condition", 0.17f }, + { L"Hits", 0.05f }, + { L"Enabled", 0.08f } +}; + +BreakpointList::BreakpointList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly) + : wxListView(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxLC_VIRTUAL|wxLC_REPORT|wxLC_SINGLE_SEL), cpu(_cpu), disasm(_disassembly) +{ + insertListViewColumns(this,breakpointColumns,BPL_COLUMNCOUNT); +} + +void BreakpointList::sizeEvent(wxSizeEvent& evt) +{ + resizeListViewColumns(this,breakpointColumns,BPL_COLUMNCOUNT,evt.GetSize().x); +} + +void BreakpointList::update() +{ + int newRows = getTotalBreakpointCount(); + SetItemCount(newRows); + Refresh(); +} + +int BreakpointList::getTotalBreakpointCount() +{ + int count = (int)CBreakPoints::GetMemChecks().size(); + for (size_t i = 0; i < CBreakPoints::GetBreakpoints().size(); i++) + { + if (!displayedBreakPoints_[i].temporary) count++; + } + + return count; +} + +wxString BreakpointList::OnGetItemText(long item, long col) const +{ + wchar_t dest[256]; + bool isMemory; + int index = getBreakpointIndex(item,isMemory); + if (index == -1) return L"Invalid"; + + switch (col) + { + case BPL_TYPE: + { + if (isMemory) { + switch ((int)displayedMemChecks_[index].cond) { + case MEMCHECK_READ: + wcscpy(dest,L"Read"); + break; + case MEMCHECK_WRITE: + wcscpy(dest,L"Write"); + break; + case MEMCHECK_READWRITE: + wcscpy(dest,L"Read/Write"); + break; + case MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE: + wcscpy(dest,L"Write Change"); + break; + case MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE: + wcscpy(dest,L"Read/Write Change"); + break; + } + } else { + wcscpy(dest,L"Execute"); + } + } + break; + case BPL_OFFSET: + { + if (isMemory) { + swprintf(dest,256,L"0x%08X",displayedMemChecks_[index].start); + } else { + swprintf(dest,256,L"0x%08X",displayedBreakPoints_[index].addr); + } + } + break; + case BPL_SIZELABEL: + { + if (isMemory) { + auto mc = displayedMemChecks_[index]; + if (mc.end == 0) + swprintf(dest,256,L"0x%08X",1); + else + swprintf(dest,256,L"0x%08X",mc.end-mc.start); + } else { + const std::string sym = symbolMap.GetLabelString(displayedBreakPoints_[index].addr); + if (!sym.empty()) + { + swprintf(dest,256,L"%S",sym.c_str()); + } else { + wcscpy(dest,L"-"); + } + } + } + break; + case BPL_OPCODE: + { + if (isMemory) { + wcscpy(dest,L"-"); + } else { + char temp[256]; + disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp); + swprintf(dest,256,L"%S",temp); + } + } + break; + case BPL_CONDITION: + { + if (isMemory || displayedBreakPoints_[index].hasCond == false) { + wcscpy(dest,L"-"); + } else { + swprintf(dest,256,L"%S",displayedBreakPoints_[index].cond.expressionString); + } + } + break; + case BPL_HITS: + { + if (isMemory) { + swprintf(dest,256,L"%d",displayedMemChecks_[index].numHits); + } else { + swprintf(dest,256,L"-"); + } + } + break; + case BPL_ENABLED: + { + if (isMemory) { + swprintf(dest,256,L"%S",displayedMemChecks_[index].cond & MEMCHECK_BREAK ? "true" : "false"); + } else { + swprintf(dest,256,L"%S",displayedBreakPoints_[index].enabled ? "true" : "false"); + } + } + break; + default: + return L"Invalid"; + } + + return dest; +} + +void BreakpointList::keydownEvent(wxKeyEvent& evt) +{ + int sel = GetFirstSelected(); + switch (evt.GetKeyCode()) + { + case WXK_DELETE: + if (sel+1 == GetItemCount()) + Select(sel-1); + removeBreakpoint(sel); + break; + case WXK_UP: + if (sel > 0) + Select(sel-1); + break; + case WXK_DOWN: + if (sel+1 < GetItemCount()) + Select(sel+1); + break; + case WXK_RETURN: + editBreakpoint(sel); + break; + case WXK_SPACE: + toggleEnabled(sel); + break; + } +} + +int BreakpointList::getBreakpointIndex(int itemIndex, bool& isMemory) const +{ + // memory breakpoints first + if (itemIndex < (int)displayedMemChecks_.size()) + { + isMemory = true; + return itemIndex; + } + + itemIndex -= (int)displayedMemChecks_.size(); + + size_t i = 0; + while (i < displayedBreakPoints_.size()) + { + if (displayedBreakPoints_[i].temporary) + { + i++; + continue; + } + + // the index is 0 when there are no more breakpoints to skip + if (itemIndex == 0) + { + isMemory = false; + return (int)i; + } + + i++; + itemIndex--; + } + + return -1; +} + +void BreakpointList::reloadBreakpoints() +{ + // Update the items we're displaying from the debugger. + displayedBreakPoints_ = CBreakPoints::GetBreakpoints(); + displayedMemChecks_= CBreakPoints::GetMemChecks(); + update(); +} + +void BreakpointList::editBreakpoint(int itemIndex) +{ + bool isMemory; + int index = getBreakpointIndex(itemIndex, isMemory); + if (index == -1) return; + + BreakpointWindow win(this,cpu); + if (isMemory) + { + auto mem = displayedMemChecks_[index]; + win.loadFromMemcheck(mem); + if (win.ShowModal() == wxID_OK) + { + CBreakPoints::RemoveMemCheck(mem.start,mem.end); + win.addBreakpoint(); + } + } else { + auto bp = displayedBreakPoints_[index]; + win.loadFromBreakpoint(bp); + if (win.ShowModal() == wxID_OK) + { + CBreakPoints::RemoveBreakPoint(bp.addr); + win.addBreakpoint(); + } + } +} + +void BreakpointList::toggleEnabled(int itemIndex) +{ + bool isMemory; + int index = getBreakpointIndex(itemIndex, isMemory); + if (index == -1) return; + + if (isMemory) { + MemCheck mcPrev = displayedMemChecks_[index]; + CBreakPoints::ChangeMemCheck(mcPrev.start, mcPrev.end, mcPrev.cond, MemCheckResult(mcPrev.result ^ MEMCHECK_BREAK)); + } else { + BreakPoint bpPrev = displayedBreakPoints_[index]; + CBreakPoints::ChangeBreakPoint(bpPrev.addr, !bpPrev.enabled); + } +} + +void BreakpointList::gotoBreakpointAddress(int itemIndex) +{ + bool isMemory; + int index = getBreakpointIndex(itemIndex,isMemory); + if (index == -1) return; + + if (isMemory) + { + u32 address = displayedMemChecks_[index].start; + postEvent(debEVT_GOTOINMEMORYVIEW,address); + } else { + u32 address = displayedBreakPoints_[index].addr; + postEvent(debEVT_GOTOINDISASM,address); + } +} + +void BreakpointList::removeBreakpoint(int itemIndex) +{ + bool isMemory; + int index = getBreakpointIndex(itemIndex,isMemory); + if (index == -1) return; + + if (isMemory) + { + auto mc = displayedMemChecks_[index]; + CBreakPoints::RemoveMemCheck(mc.start, mc.end); + } else { + u32 address = displayedBreakPoints_[index].addr; + CBreakPoints::RemoveBreakPoint(address); + } +} + +void BreakpointList::postEvent(wxEventType type, int value) +{ + wxCommandEvent event( type, GetId() ); + event.SetEventObject(this); + event.SetInt(value); + wxPostEvent(this,event); +} \ No newline at end of file diff --git a/pcsx2/gui/Debugger/DebuggerLists.h b/pcsx2/gui/Debugger/DebuggerLists.h new file mode 100644 index 0000000000..d0e22d33ec --- /dev/null +++ b/pcsx2/gui/Debugger/DebuggerLists.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include "DebugTools/DebugInterface.h" +#include "DebugTools/Breakpoints.h" +#include "CtrlDisassemblyView.h" + +struct GenericListViewColumn +{ + wchar_t *name; + float size; + int flags; +}; + +class BreakpointList: public wxListView +{ +public: + BreakpointList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly); + void reloadBreakpoints(); + void update(); + DECLARE_EVENT_TABLE() +protected: + wxString OnGetItemText(long item, long col) const; + + void sizeEvent(wxSizeEvent& evt); + void keydownEvent(wxKeyEvent& evt); +private: + int getBreakpointIndex(int itemIndex, bool& isMemory) const; + int getTotalBreakpointCount(); + void editBreakpoint(int itemIndex); + void toggleEnabled(int itemIndex); + void gotoBreakpointAddress(int itemIndex); + void removeBreakpoint(int itemIndex); + void postEvent(wxEventType type, int value); + + std::vector displayedBreakPoints_; + std::vector displayedMemChecks_; + DebugInterface* cpu; + CtrlDisassemblyView* disasm; +}; diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.cpp b/pcsx2/gui/Debugger/DisassemblyDialog.cpp index d703f6116f..7324d37c64 100644 --- a/pcsx2/gui/Debugger/DisassemblyDialog.cpp +++ b/pcsx2/gui/Debugger/DisassemblyDialog.cpp @@ -74,11 +74,22 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu) // create bottom section bottomTabs->AddPage(memory,L"Memory"); + + breakpointList = new BreakpointList(bottomTabs,cpu,disassembly); + bottomTabs->AddPage(breakpointList,L"Breakpoints"); + mainSizer->Add(bottomTabs,1,wxEXPAND); mainSizer->Layout(); } +void CpuTabPage::update() +{ + breakpointList->reloadBreakpoints(); + Refresh(); +} + + DisassemblyDialog::DisassemblyDialog(wxWindow* parent): wxFrame( parent, wxID_ANY, L"Debugger", wxDefaultPosition,wxDefaultSize,wxRESIZE_BORDER|wxCLOSE_BOX|wxCAPTION|wxSYSTEM_MENU ), currentCpu(NULL) @@ -203,7 +214,7 @@ void DisassemblyDialog::onPageChanging(wxCommandEvent& evt) if (currentCpu != NULL) { currentCpu->getDisassembly()->SetFocus(); - currentCpu->Refresh(); + currentCpu->update(); } } @@ -365,7 +376,7 @@ void DisassemblyDialog::update() { stepOverButton->Enable(true); breakpointButton->Enable(true); - currentCpu->Refresh(); + currentCpu->update(); } else { stepOverButton->Enable(false); breakpointButton->Enable(false); diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.h b/pcsx2/gui/Debugger/DisassemblyDialog.h index 383f3d0a30..84fe86e16a 100644 --- a/pcsx2/gui/Debugger/DisassemblyDialog.h +++ b/pcsx2/gui/Debugger/DisassemblyDialog.h @@ -7,6 +7,7 @@ #include "CtrlRegisterList.h" #include "CtrlMemView.h" #include "DebugEvents.h" +#include "DebuggerLists.h" class DebuggerHelpDialog: public wxDialog { @@ -23,12 +24,14 @@ public: CtrlRegisterList* getRegisterList() { return registerList; }; CtrlMemView* getMemoryView() { return memory; }; wxNotebook* getBottomTabs() { return bottomTabs; }; + void update(); private: DebugInterface* cpu; CtrlDisassemblyView* disassembly; CtrlRegisterList* registerList; CtrlMemView* memory; wxNotebook* bottomTabs; + BreakpointList* breakpointList; }; class DisassemblyDialog : public wxFrame diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index 8986891de2..e8387e3af5 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -178,6 +178,8 @@ %(RootDir)%(Directory)\%(Filename).h %(RootDir)%(Directory)\%(Filename).h + + diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index fd5c7a3837..0bb9d6153f 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -207,6 +207,12 @@ AppHost\Dialogs + + AppHost\Debugger + + + AppHost\Debugger + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj index 97e9d79ffe..4f96f2d25d 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj @@ -424,6 +424,7 @@ + @@ -708,6 +709,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters index f50f4bb320..57073888b7 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters @@ -844,13 +844,15 @@ System\Ps2\Debug + + AppHost\Debugger + System\Ps2\EmotionEngine\DMAC\Sif System\Ps2\Iop\PS1 Components - - + Misc @@ -1255,10 +1257,12 @@ System\Ps2\Debug + + AppHost\Debugger + System\Ps2\Iop\PS1 Components - - + AppHost\Resources diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj index 673300daf3..0cd829f243 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj @@ -424,6 +424,7 @@ + @@ -708,6 +709,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters index e34733cd28..52c0bb3abf 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters @@ -850,7 +850,9 @@ System\Ps2\Iop\PS1 Components - + + AppHost\Debugger + Misc @@ -1258,7 +1260,9 @@ System\Ps2\Iop\PS1 Components - + + AppHost\Debugger + AppHost\Resources