forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			324 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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->g_pCodeWindow;
 | 
						|
  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();
 | 
						|
}
 |