mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-11 08:39:13 +01:00
c1922783f8
Fix Frame Advance and FifoPlayer pause/unpause/stop. CPU::EnableStepping is not atomic but is called from multiple threads which races and leaves the system in a random state; also instruction stepping was unstable, m_StepEvent had an almost random value because of the dual purpose it served which could cause races where CPU::Run would SingleStep when it was supposed to be sleeping. FifoPlayer never FinishStateMove()d which was causing it to deadlock. Rather than partially reimplementing CPU::Run, just use CPUCoreBase and then call CPU::Run(). More DRY and less likely to have weird bugs specific to the player (i.e the previous freezing on pause/stop). Refactor PowerPC::state into CPU since it manages the state of the CPU Thread which is controlled by CPU, not PowerPC. This simplifies the architecture somewhat and eliminates races that can be caused by calling PowerPC state functions directly instead of using CPU's (because they bypassed the EnableStepping lock).
308 lines
6.6 KiB
C++
308 lines
6.6 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/Frame.h"
|
|
#include "DolphinWX/WxUtils.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"
|
|
|
|
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).iAddress;
|
|
}
|
|
|
|
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: 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::Update()
|
|
{
|
|
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))
|
|
menu.Append(IDM_DELETEWATCH, _("&Delete watch"));
|
|
|
|
if (row != 0 && row != (int)(PowerPC::watches.GetWatches().size() + 1) && (col == 1 || col == 2))
|
|
{
|
|
#ifdef ENABLE_MEM_CHECK
|
|
menu.Append(IDM_ADDMEMCHECK, _("Add memory &breakpoint"));
|
|
#endif
|
|
menu.Append(IDM_VIEWMEMORY, _("View &memory"));
|
|
}
|
|
PopupMenu(&menu);
|
|
}
|
|
|
|
void CWatchView::OnPopupMenu(wxCommandEvent& event)
|
|
{
|
|
CFrame* main_frame = static_cast<CFrame*>(GetGrandParent()->GetParent());
|
|
CCodeWindow* code_window = main_frame->g_pCodeWindow;
|
|
CWatchWindow* watch_window = code_window->m_WatchWindow;
|
|
CMemoryWindow* memory_window = code_window->m_MemoryWindow;
|
|
CBreakPointWindow* breakpoint_window = code_window->m_BreakpointWindow;
|
|
|
|
wxString strNewVal;
|
|
TMemCheck MemCheck;
|
|
|
|
switch (event.GetId())
|
|
{
|
|
case IDM_DELETEWATCH:
|
|
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:
|
|
MemCheck.StartAddress = m_selectedAddress;
|
|
MemCheck.EndAddress = m_selectedAddress;
|
|
MemCheck.bRange = false;
|
|
MemCheck.OnRead = true;
|
|
MemCheck.OnWrite = true;
|
|
MemCheck.Log = true;
|
|
MemCheck.Break = 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();
|
|
}
|