dolphin/Source/Core/DolphinWX/Debugger/WatchView.cpp

324 lines
7.3 KiB
C++

// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <wx/colour.h>
#include <wx/grid.h>
#include <wx/menu.h>
#include "Common/GekkoDisassembler.h"
#include "Core/Core.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/PowerPC.h"
#include "DolphinWX/Debugger/BreakpointWindow.h"
#include "DolphinWX/Debugger/CodeWindow.h"
#include "DolphinWX/Debugger/DebuggerUIUtil.h"
#include "DolphinWX/Debugger/MemoryWindow.h"
#include "DolphinWX/Debugger/RegisterView.h"
#include "DolphinWX/Debugger/WatchView.h"
#include "DolphinWX/Debugger/WatchWindow.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/WxUtils.h"
enum
{
IDM_DELETEWATCH = 1,
IDM_ADDMEMCHECK,
IDM_VIEWMEMORY,
};
static std::string GetWatchName(int count)
{
return PowerPC::watches.GetWatches().at(count - 1).name;
}
static u32 GetWatchAddr(int count)
{
return PowerPC::watches.GetWatches().at(count - 1).address;
}
static u32 GetWatchValue(int count)
{
return PowerPC::HostRead_U32(GetWatchAddr(count));
}
static void AddWatchAddr(int count, u32 value)
{
PowerPC::watches.Add(value);
}
static void UpdateWatchAddr(int count, u32 value)
{
PowerPC::watches.Update(count - 1, value);
}
static void SetWatchName(int count, const std::string& value)
{
if ((count - 1) < (int)PowerPC::watches.GetWatches().size())
{
PowerPC::watches.UpdateName(count - 1, value);
}
else
{
PowerPC::watches.Add(0);
PowerPC::watches.UpdateName(PowerPC::watches.GetWatches().size() - 1, value);
}
}
static void SetWatchValue(int count, u32 value)
{
PowerPC::HostWrite_U32(value, GetWatchAddr(count));
}
static wxString GetValueByRowCol(int row, int col)
{
if (row == 0)
{
// Column Labels
switch (col)
{
case 0:
return _("Label");
case 1:
return _("Address");
case 2:
return _("Hexadecimal");
case 3:
return _("Decimal");
case 4:
// i18n: Data type used in computing
return _("String");
default:
return wxEmptyString;
}
}
else if (row <= (int)PowerPC::watches.GetWatches().size())
{
if (Core::IsRunning())
{
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("%u", GetWatchValue(row));
case 4:
{
u32 addr = GetWatchAddr(row);
if (PowerPC::HostIsRAMAddress(addr))
return PowerPC::HostGetString(addr, 32).c_str();
else
return wxEmptyString;
}
default:
return wxEmptyString;
}
}
}
return wxEmptyString;
}
wxString CWatchTable::GetValue(int row, int col)
{
return GetValueByRowCol(row, col);
}
void CWatchTable::SetValue(int row, int col, const wxString& strNewVal)
{
u32 newVal = 0;
if (col == 0 || TryParse("0x" + WxStrToStr(strNewVal), &newVal))
{
if (row > 0)
{
switch (col)
{
case 0:
{
SetWatchName(row, std::string(WxStrToStr(strNewVal)));
break;
}
case 1:
{
if (row > (int)PowerPC::watches.GetWatches().size())
{
AddWatchAddr(row, newVal);
row = (int)PowerPC::watches.GetWatches().size();
}
else
{
UpdateWatchAddr(row, newVal);
}
break;
}
case 2:
{
SetWatchValue(row, newVal);
break;
}
default:
break;
}
}
}
}
void CWatchTable::UpdateWatch()
{
for (int i = 0; i < (int)PowerPC::watches.GetWatches().size(); ++i)
{
m_CachedWatchHasChanged[i] = (m_CachedWatch[i] != GetWatchValue(i + 1));
m_CachedWatch[i] = GetWatchValue(i + 1);
}
}
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;
if (col == 1)
red = m_CachedWatchHasChanged[row];
attr->SetTextColour(red ? *wxRED : *wxBLACK);
if (row > (int)(PowerPC::watches.GetWatches().size() + 1) || !Core::IsRunning())
{
attr->SetReadOnly(true);
attr->SetBackgroundColour(*wxLIGHT_GREY);
}
}
return attr;
}
CWatchView::CWatchView(wxWindow* parent, wxWindowID id) : wxGrid(parent, id)
{
m_watch_table = new CWatchTable();
SetTable(m_watch_table, true);
SetRowLabelSize(0);
SetColLabelSize(0);
DisableDragRowSize();
Bind(wxEVT_GRID_CELL_RIGHT_CLICK, &CWatchView::OnMouseDownR, this);
Bind(wxEVT_MENU, &CWatchView::OnPopupMenu, this);
}
void CWatchView::Repopulate()
{
if (Core::IsRunning())
{
m_watch_table->UpdateWatch();
ForceRefresh();
}
}
void CWatchView::OnMouseDownR(wxGridEvent& event)
{
// popup menu
int row = event.GetRow();
int col = event.GetCol();
m_selectedRow = row;
if (col == 1 || col == 2)
{
wxString strNewVal = GetValueByRowCol(row, col);
TryParse("0x" + WxStrToStr(strNewVal), &m_selectedAddress);
}
wxMenu menu;
if (row != 0 && row != (int)(PowerPC::watches.GetWatches().size() + 1))
{
// i18n: This kind of "watch" is used for watching emulated memory.
// It's not related to timekeeping devices.
menu.Append(IDM_DELETEWATCH, _("&Delete watch"));
}
if (row != 0 && row != (int)(PowerPC::watches.GetWatches().size() + 1) && (col == 1 || col == 2))
{
menu.Append(IDM_ADDMEMCHECK, _("Add memory &breakpoint"));
menu.Append(IDM_VIEWMEMORY, _("View &memory"));
}
PopupMenu(&menu);
}
void CWatchView::OnPopupMenu(wxCommandEvent& event)
{
// FIXME: This is terrible. Generate events instead.
CFrame* cframe = wxGetApp().GetCFrame();
CCodeWindow* code_window = cframe->m_code_window;
CWatchWindow* watch_window = code_window->GetPanel<CWatchWindow>();
CMemoryWindow* memory_window = code_window->GetPanel<CMemoryWindow>();
CBreakPointWindow* breakpoint_window = code_window->GetPanel<CBreakPointWindow>();
switch (event.GetId())
{
case IDM_DELETEWATCH:
{
wxString strNewVal = GetValueByRowCol(m_selectedRow, 1);
if (TryParse("0x" + WxStrToStr(strNewVal), &m_selectedAddress))
{
PowerPC::watches.Remove(m_selectedAddress);
if (watch_window)
watch_window->NotifyUpdate();
Refresh();
}
break;
}
case IDM_ADDMEMCHECK:
{
TMemCheck MemCheck;
MemCheck.start_address = m_selectedAddress;
MemCheck.end_address = m_selectedAddress;
MemCheck.is_ranged = false;
MemCheck.is_break_on_read = true;
MemCheck.is_break_on_write = true;
MemCheck.log_on_hit = true;
MemCheck.break_on_hit = true;
PowerPC::memchecks.Add(MemCheck);
if (breakpoint_window)
breakpoint_window->NotifyUpdate();
Refresh();
break;
}
case IDM_VIEWMEMORY:
if (memory_window)
memory_window->JumpToAddress(m_selectedAddress);
Refresh();
break;
}
event.Skip();
}