diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 5d7b96d60d0f..9f7c4d0879fa 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -292,17 +292,22 @@ void ReadBigEData(u8 *data, const u32 em_address, const u32 size) memcpy(data, src, size); } -std::string GetString(u32 em_address) +std::string GetString(u32 em_address, u32 max_len) { std::string str; char c; - while ((c = Read_U8(em_address)) != '\0') + while ((c = ReadUnchecked_U8(em_address)) != '\0' && (max_len == 0 || max_len > str.length())) { str += c; em_address++; } + if (str.length() == max_len) + { + str += '\0'; + } + return str; } diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index 0973166ca9c1..e9d9d954750f 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -118,7 +118,7 @@ void Write_U64_Swap(const u64 _Data, const u32 _Address); // Useful helper functions, used by ARM JIT void Write_F64(const double _Data, const u32 _Address); -std::string GetString(u32 em_address); +std::string GetString(u32 em_address, u32 max_len = 0); void WriteBigEData(const u8 *_pData, const u32 _Address, const size_t size); void ReadBigEData(u8 *_pDest, const u32 _Address, const u32 size); diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 8a007d059336..97ef8c85e7f5 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -42,6 +42,8 @@ set(GUI_SRCS Debugger/MemoryWindow.cpp Debugger/RegisterView.cpp Debugger/RegisterWindow.cpp + Debugger/WatchView.cpp + Debugger/WatchWindow.cpp FifoPlayerDlg.cpp Frame.cpp FrameAui.cpp diff --git a/Source/Core/DolphinWX/Debugger/CodeWindow.cpp b/Source/Core/DolphinWX/Debugger/CodeWindow.cpp index c6ad04e8b70c..b34dfa29b518 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/CodeWindow.cpp @@ -52,6 +52,7 @@ #include "DolphinWX/Debugger/DebuggerUIUtil.h" #include "DolphinWX/Debugger/JitWindow.h" #include "DolphinWX/Debugger/RegisterWindow.h" +#include "DolphinWX/Debugger/WatchWindow.h" extern "C" // Bitmaps { @@ -93,6 +94,7 @@ CCodeWindow::CCodeWindow(const SCoreStartupParameter& _LocalCoreStartupParameter : wxPanel(parent, id, position, size, style, name) , Parent(parent) , m_RegisterWindow(nullptr) + , m_WatchWindow(nullptr) , m_BreakpointWindow(nullptr) , m_MemoryWindow(nullptr) , m_JitWindow(nullptr) @@ -152,6 +154,7 @@ void CCodeWindow::OnHostMessage(wxCommandEvent& event) Update(); if (codeview) codeview->Center(PC); if (m_RegisterWindow) m_RegisterWindow->NotifyUpdate(); + if (m_WatchWindow) m_WatchWindow->NotifyUpdate(); break; case IDM_UPDATEBREAKPOINTS: diff --git a/Source/Core/DolphinWX/Debugger/CodeWindow.h b/Source/Core/DolphinWX/Debugger/CodeWindow.h index 644d29b085dc..ac14fe1c0f49 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindow.h +++ b/Source/Core/DolphinWX/Debugger/CodeWindow.h @@ -19,6 +19,7 @@ class CFrame; class CRegisterWindow; +class CWatchWindow; class CBreakPointWindow; class CMemoryWindow; class CJitWindow; @@ -80,6 +81,7 @@ class CCodeWindow void ToggleCodeWindow(bool bShow); void ToggleRegisterWindow(bool bShow); + void ToggleWatchWindow(bool bShow); void ToggleBreakPointWindow(bool bShow); void ToggleMemoryWindow(bool bShow); void ToggleJitWindow(bool bShow); @@ -96,6 +98,7 @@ class CCodeWindow // Sub dialogs CRegisterWindow* m_RegisterWindow; + CWatchWindow* m_WatchWindow; CBreakPointWindow* m_BreakpointWindow; CMemoryWindow* m_MemoryWindow; CJitWindow* m_JitWindow; diff --git a/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp b/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp index fbf5820c0a55..099d184bee13 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp +++ b/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp @@ -51,6 +51,7 @@ #include "DolphinWX/Debugger/JitWindow.h" #include "DolphinWX/Debugger/MemoryWindow.h" #include "DolphinWX/Debugger/RegisterWindow.h" +#include "DolphinWX/Debugger/WatchWindow.h" // Save and load settings @@ -421,6 +422,8 @@ void CCodeWindow::OpenPages() Parent->ToggleLogConfigWindow(true); if (bShowOnStart[IDM_REGISTERWINDOW - IDM_LOGWINDOW]) ToggleRegisterWindow(true); + if (bShowOnStart[IDM_WATCHWINDOW - IDM_LOGWINDOW]) + ToggleWatchWindow(true); if (bShowOnStart[IDM_BREAKPOINTWINDOW - IDM_LOGWINDOW]) ToggleBreakPointWindow(true); if (bShowOnStart[IDM_MEMORYWINDOW - IDM_LOGWINDOW]) @@ -461,6 +464,24 @@ void CCodeWindow::ToggleRegisterWindow(bool bShow) } } +void CCodeWindow::ToggleWatchWindow(bool bShow) +{ + GetMenuBar()->FindItem(IDM_WATCHWINDOW)->Check(bShow); + if (bShow) + { + if (!m_WatchWindow) + m_WatchWindow = new CWatchWindow(Parent, IDM_WATCHWINDOW); + Parent->DoAddPage(m_WatchWindow, + iNbAffiliation[IDM_WATCHWINDOW - IDM_LOGWINDOW], + Parent->bFloatWindow[IDM_WATCHWINDOW - IDM_LOGWINDOW]); + } + else // Close + { + Parent->DoRemovePage(m_WatchWindow, false); + m_WatchWindow = nullptr; + } +} + void CCodeWindow::ToggleBreakPointWindow(bool bShow) { GetMenuBar()->FindItem(IDM_BREAKPOINTWINDOW)->Check(bShow); diff --git a/Source/Core/DolphinWX/Debugger/WatchView.cpp b/Source/Core/DolphinWX/Debugger/WatchView.cpp new file mode 100644 index 000000000000..36eb8019bda7 --- /dev/null +++ b/Source/Core/DolphinWX/Debugger/WatchView.cpp @@ -0,0 +1,194 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include + +#include "Common/GekkoDisassembler.h" +#include "Core/HW/Memmap.h" +#include "Core/PowerPC/PowerPC.h" +#include "DolphinWX/WxUtils.h" +#include "DolphinWX/Debugger/DebuggerUIUtil.h" +#include "DolphinWX/Debugger/WatchView.h" + +class wxWindow; + +const char *watch_name[1024]; +u32 watch_addr[1024]; + +static const char* GetWatchName(int count) +{ + return watch_name[count - 1]; +} + +static u32 GetWatchAddr(int count) +{ + return watch_addr[count - 1]; +} + +static u32 GetWatchValue(int count) +{ + return Memory::ReadUnchecked_U32(watch_addr[count - 1]); +} + +static void SetWatchName(int count, const char* value) +{ + watch_name[count - 1] = value; +} + +static void SetWatchAddr(int count, u32 value) +{ + watch_addr[count - 1] = value; +} + +static void SetWatchValue(int count, u32 value) +{ + Memory::WriteUnchecked_U32(value, GetWatchAddr(count)); +} + +wxString CWatchTable::GetValue(int row, int col) +{ + if (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) + { + if (row == 0) + { + // Column Labels + switch (col) + { + case 0: return wxString::Format("Label"); + case 1: return wxString::Format("Addr"); + case 2: return wxString::Format("Hex"); + case 3: return wxString::Format("Dec"); + case 4: return wxString::Format("Str"); + default: return wxEmptyString; + } + } + else if (row <= num_watches) + { + switch (col) + { + case 0: return wxString::Format("%s", GetWatchName(row)); + case 1: return wxString::Format("%08x", GetWatchAddr(row)); + case 2: return wxString::Format("%08x", GetWatchValue(row)); + case 3: return wxString::Format("%lu", GetWatchValue(row)); + case 4: + { + u32 addr = GetWatchAddr(row); + if (Memory::IsRAMAddress(addr)) + return wxString::Format("%s", Memory::GetString(addr, 32)); + else + return wxEmptyString; + } + default: return wxEmptyString; + } + } + } + return wxEmptyString; +} + +void CWatchTable::SetValue(int row, int col, const wxString& strNewVal) +{ + u32 newVal = 0; + if (col == 0 || TryParse("0x" + WxStrToStr(strNewVal), &newVal)) + { + if (row > 0) + { + if (row > num_watches) + { + num_watches++; + row = num_watches; + } + switch (col) + { + case 0: SetWatchName(row, strNewVal); break; + case 1: SetWatchAddr(row, newVal); break; + case 2: SetWatchValue(row, newVal); break; + default: break; + } + } + } +} + +void CWatchTable::UpdateWatch() +{ + for (int i = 0; i < num_watches; ++i) + { + m_CachedWatchHasChanged[i] = (m_CachedWatch[i] != GetWatchValue(i)); + m_CachedWatch[i] = GetWatchValue(i); + } +} + +wxGridCellAttr *CWatchTable::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind) +{ + wxGridCellAttr *attr = new wxGridCellAttr(); + + attr->SetBackgroundColour(*wxWHITE); + attr->SetFont(DebuggerFont); + + switch (col) + { + case 1: + attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTER); + break; + case 3: + case 4: + attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTER); + break; + default: + attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTER); + break; + } + + if (row == 0) + { + attr->SetReadOnly(true); + attr->SetBackgroundColour(*wxBLACK); + attr->SetTextColour(*wxWHITE); + } + else + { + bool red = false; + switch (col) + { + case 1: red = m_CachedWatchHasChanged[row]; break; + } + + attr->SetTextColour(red ? *wxRED : *wxBLACK); + + if (row > (num_watches + 1)) + { + attr->SetReadOnly(true); + attr->SetBackgroundColour(*wxLIGHT_GREY); + } + } + attr->IncRef(); + return attr; +} + +CWatchView::CWatchView(wxWindow *parent, wxWindowID id) + : wxGrid(parent, id) +{ + SetTable(new CWatchTable(), true); + SetRowLabelSize(0); + SetColLabelSize(0); + DisableDragRowSize(); + + if (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) + { + AutoSizeColumns(); + } +} + +void CWatchView::Update() +{ + if (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) + { + ForceRefresh(); + ((CWatchTable *)GetTable())->UpdateWatch(); + } +} diff --git a/Source/Core/DolphinWX/Debugger/WatchView.h b/Source/Core/DolphinWX/Debugger/WatchView.h new file mode 100644 index 000000000000..8d8b99ff2f67 --- /dev/null +++ b/Source/Core/DolphinWX/Debugger/WatchView.h @@ -0,0 +1,52 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" + +class wxWindow; + +class CWatchTable : public wxGridTableBase +{ + enum + { + NUM_SPECIALS = 1, + MAX_SPECIALS = 256, + }; + +public: + CWatchTable() + { + memset(m_CachedWatch, 0, sizeof(m_CachedWatch)); + memset(m_CachedWatchHasChanged, 0, sizeof(m_CachedWatchHasChanged)); + } + + int GetNumberCols() override { return 5; } + int GetNumberRows() override { return MAX_SPECIALS; } + wxString GetValue(int row, int col) override; + void SetValue(int row, int col, const wxString &) override; + wxGridCellAttr *GetAttr(int, int, wxGridCellAttr::wxAttrKind) override; + void UpdateWatch(); + +private: + u32 m_CachedWatch[MAX_SPECIALS]; + bool m_CachedWatchHasChanged[MAX_SPECIALS]; + int num_watches = 0; + + DECLARE_NO_COPY_CLASS(CWatchTable); +}; + +class CWatchView : public wxGrid +{ +public: + CWatchView(wxWindow* parent, wxWindowID id); + void Update() override; +}; diff --git a/Source/Core/DolphinWX/Debugger/WatchWindow.cpp b/Source/Core/DolphinWX/Debugger/WatchWindow.cpp new file mode 100644 index 000000000000..8f6f578b013c --- /dev/null +++ b/Source/Core/DolphinWX/Debugger/WatchWindow.cpp @@ -0,0 +1,46 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DolphinWX/Debugger/WatchView.h" +#include "DolphinWX/Debugger/WatchWindow.h" + +class wxWindow; + +BEGIN_EVENT_TABLE(CWatchWindow, wxPanel) +END_EVENT_TABLE() + + +CWatchWindow::CWatchWindow(wxWindow* parent, wxWindowID id, + const wxPoint& position, const wxSize& size, + long style, const wxString& name) + : wxPanel(parent, id, position, size, style, name) + , m_GPRGridView(nullptr) +{ + CreateGUIControls(); +} + +void CWatchWindow::CreateGUIControls() +{ + wxBoxSizer *sGrid = new wxBoxSizer(wxVERTICAL); + m_GPRGridView = new CWatchView(this, ID_GPR); + sGrid->Add(m_GPRGridView, 1, wxGROW); + SetSizer(sGrid); + + NotifyUpdate(); +} + +void CWatchWindow::NotifyUpdate() +{ + if (m_GPRGridView != nullptr) + m_GPRGridView->Update(); +} diff --git a/Source/Core/DolphinWX/Debugger/WatchWindow.h b/Source/Core/DolphinWX/Debugger/WatchWindow.h new file mode 100644 index 000000000000..9a3b9c4d6721 --- /dev/null +++ b/Source/Core/DolphinWX/Debugger/WatchWindow.h @@ -0,0 +1,42 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class CWatchView; +class wxWindow; + +class CWatchWindow + : public wxPanel +{ +public: + CWatchWindow(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL | wxNO_BORDER, + const wxString& name = _("Watch")); + + void NotifyUpdate(); + + +private: + DECLARE_EVENT_TABLE(); + + enum + { + ID_GPR = 1002 + }; + + CWatchView* m_GPRGridView; + void CreateGUIControls(); +}; diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index 69fa015d15da..52841107041e 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -68,6 +68,8 @@ + + @@ -119,6 +121,8 @@ + + diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index f2d5c6f3835b..f35318710aaf 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -78,6 +78,12 @@ GUI\Debugger + + GUI\Debugger + + + GUI\Debugger + GUI\InputConfig @@ -204,6 +210,12 @@ GUI\Debugger + + GUI\Debugger + + + GUI\Debugger + GUI\InputConfig diff --git a/Source/Core/DolphinWX/FrameAui.cpp b/Source/Core/DolphinWX/FrameAui.cpp index 34d52068c144..3c44ab592041 100644 --- a/Source/Core/DolphinWX/FrameAui.cpp +++ b/Source/Core/DolphinWX/FrameAui.cpp @@ -179,6 +179,9 @@ void CFrame::OnToggleWindow(wxCommandEvent& event) case IDM_REGISTERWINDOW: g_pCodeWindow->ToggleRegisterWindow(bShow); break; + case IDM_WATCHWINDOW: + g_pCodeWindow->ToggleWatchWindow(bShow); + break; case IDM_BREAKPOINTWINDOW: g_pCodeWindow->ToggleBreakPointWindow(bShow); break; @@ -208,6 +211,7 @@ void CFrame::ClosePages() { g_pCodeWindow->ToggleCodeWindow(false); g_pCodeWindow->ToggleRegisterWindow(false); + g_pCodeWindow->ToggleWatchWindow(false); g_pCodeWindow->ToggleBreakPointWindow(false); g_pCodeWindow->ToggleMemoryWindow(false); g_pCodeWindow->ToggleJitWindow(false); @@ -247,6 +251,8 @@ void CFrame::OnNotebookPageClose(wxAuiNotebookEvent& event) ToggleLogConfigWindow(false); if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_REGISTERWINDOW) g_pCodeWindow->ToggleRegisterWindow(false); + if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_WATCHWINDOW) + g_pCodeWindow->ToggleWatchWindow(false); if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_BREAKPOINTWINDOW) g_pCodeWindow->ToggleBreakPointWindow(false); if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_JITWINDOW) diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 135d9c203de2..683e9490e1ca 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -283,6 +283,7 @@ wxMenuBar* CFrame::CreateMenu() const wxString MenuText[] = { wxTRANSLATE("&Registers"), + wxTRANSLATE("&Watch"), wxTRANSLATE("&Breakpoints"), wxTRANSLATE("&Memory"), wxTRANSLATE("&JIT"), diff --git a/Source/Core/DolphinWX/Globals.h b/Source/Core/DolphinWX/Globals.h index 232c74e0af4c..73a3ed8bf6ed 100644 --- a/Source/Core/DolphinWX/Globals.h +++ b/Source/Core/DolphinWX/Globals.h @@ -149,6 +149,7 @@ enum IDM_LOGWINDOW, IDM_LOGCONFIGWINDOW, IDM_REGISTERWINDOW, + IDM_WATCHWINDOW, IDM_BREAKPOINTWINDOW, IDM_MEMORYWINDOW, IDM_JITWINDOW,