project64/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.cpp

1233 lines
34 KiB
C++

#include "stdafx.h"
#include <Project64-core/ExceptionHandler.h>
#include <Common/MemoryManagement.h>
#include <UserInterface/WTLControls/HexEditCtrl.h>
#include "DebuggerUI.h"
#include "Symbols.h"
#include "DMALog.h"
CDebugMemoryView::jump_item_t CDebugMemoryView::JumpItems[] = {
{ 0x80000000, 0x00000000, 0x0800000, "RDRAM" },
{ 0xA3F00000, 0x03F00000, 0x0000028, "RDRAM registers" },
{ 0xA4000000, 0x04000000, 0x0001000, "SP DMEM" },
{ 0xA4001000, 0x04001000, 0x0001000, "SP IMEM" },
{ 0xA4040000, 0x04040000, 0x0000020, "SP registers" },
{ 0xA4080000, 0x04080000, 0x0000004, "SP PC register" },
{ 0xA4100000, 0x04100000, 0x0000020, "DP control registers" },
{ 0xA4300000, 0x04300000, 0x0000010, "MI registers" },
{ 0xA4400000, 0x04400000, 0x0000038, "VI registers" },
{ 0xA4500000, 0x04500000, 0x0000018, "AI registers" },
{ 0xA4600000, 0x04600000, 0x0000034, "PI registers" },
{ 0xA4700000, 0x04700000, 0x0000020, "RI registers" },
{ 0xA4800000, 0x04800000, 0x0000010, "SI registers" },
{ 0xA5000500, 0x05000500, 0x000004C, "DD registers" },
{ 0xA8000000, 0x08000000, 0x1000000, "Cartridge save data" },
{ 0xB0000000, 0x10000000, 0xFC00000, "Cartridge ROM" },
{ 0xBFC00000, 0x1FC00000, 0x00007C0, "PIF ROM" },
{ 0xBFC007C0, 0x1FC007C0, 0x0000040, "PIF RAM" },
{ 0, NULL}
};
CDebugMemoryView::CDebugMemoryView(CDebuggerUI * debugger) :
CDebugDialog<CDebugMemoryView>(debugger),
CDialogResize<CDebugMemoryView>(),
CToolTipDialog<CDebugMemoryView>(),
m_Breakpoints(nullptr),
m_WriteTargetColorStride(0),
m_ReadTargetColorStride(0),
m_SymbolColorStride(0),
m_SymbolColorPhase(0),
m_bIgnoreAddressInput(false),
m_HotAddress(0),
m_bVirtualMemory(true),
m_ContextMenuAddress(0),
m_bSafeEditMode(false)
{
m_Breakpoints = m_Debugger->Breakpoints();
}
CDebugMemoryView::~CDebugMemoryView()
{
}
void CDebugMemoryView::ShowAddress(uint32_t address, bool bVirtual)
{
if (m_hWnd == nullptr)
{
return;
}
SendMessage(WM_SHOWADDRESS, (WPARAM)address, (LPARAM)bVirtual);
}
bool CDebugMemoryView::GetByte(uint32_t address, uint8_t* value)
{
if (m_bVirtualMemory)
{
return m_Debugger->DebugLoad_VAddr(address, *value);
}
else
{
return m_Debugger->DebugLoad_PAddr(address, *value);
}
}
bool CDebugMemoryView::SetByte(uint32_t address, uint8_t value)
{
if (m_bVirtualMemory)
{
return m_Debugger->DebugStore_VAddr(address, value);
}
else
{
return m_Debugger->DebugStore_PAddr(address, value);
}
}
void CDebugMemoryView::CopyTextToClipboard(const char* text)
{
size_t length = strlen(text);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, length + 1);
strcpy((char*)GlobalLock(hMem), text);
GlobalUnlock(hMem);
OpenClipboard();
EmptyClipboard();
SetClipboardData(CF_TEXT, hMem);
CloseClipboard();
}
void CDebugMemoryView::CopyBytesToClipboard(uint32_t startAddress, uint32_t endAddress, bool bHex, bool bIncludeAddresses, bool bRowAddresses)
{
uint32_t baseAddress = m_HexEditCtrl.GetBaseAddress();
int groupSize = m_HexEditCtrl.GetNumBytesPerGroup();
int rowSize = m_HexEditCtrl.GetNumBytesPerRow();
stdstr str = "";
for (uint32_t address = startAddress; address <= endAddress; address++)
{
int offsetFromBase = address - baseAddress;
int offsetFromSelStart = address - startAddress;
uint8_t value;
GetByte(address, &value);
if (bIncludeAddresses)
{
if ((bRowAddresses && offsetFromBase % rowSize == 0) ||
(!bRowAddresses && offsetFromBase % groupSize == 0) ||
(offsetFromSelStart == 0))
{
str += stdstr_f("%08X: ", address);
}
}
if (bHex)
{
str += stdstr_f("%02X", value);
}
else
{
str += CHexEditCtrl::ByteAscii(value);
}
if ((offsetFromBase + 1) % rowSize == 0 || (bIncludeAddresses && !bRowAddresses && (offsetFromBase + 1) % groupSize == 0))
{
str += "\r\n";
}
else if (bHex && (offsetFromBase + 1) % groupSize == 0)
{
str += " ";
}
}
CopyTextToClipboard(str.Trim(" \r\n").c_str());
}
void CDebugMemoryView::CopyGameSharkCodeToClipboard(uint32_t startAddress, uint32_t endAddress)
{
stdstr str = "";
if (startAddress & 1)
{
uint8_t value = 0;
GetByte(startAddress, &value);
str += stdstr_f("%08X %04X\r\n", startAddress, value);
startAddress++;
}
for (uint32_t address = startAddress; address < endAddress; address += 2)
{
uint8_t value0 = 0, value1 = 0;
GetByte(address + 0, &value0);
GetByte(address + 1, &value1);
str += stdstr_f("%08X %02X%02X\r\n", address | 0x01000000, value0, value1);
}
if (!(endAddress & 1))
{
uint8_t value = 0;
GetByte(endAddress, &value);
str += stdstr_f("%08X %04X\r\n", endAddress, value);
}
CopyTextToClipboard(str.Trim("\n").c_str());
}
void CDebugMemoryView::FillRange(uint32_t startAddress, uint32_t endAddress, uint8_t value)
{
for (uint32_t address = startAddress; address <= endAddress; address++)
{
SetByte(address, value);
}
}
void CDebugMemoryView::FollowPointer(bool bContextMenuAddress)
{
uint32_t address;
if (bContextMenuAddress)
{
address = m_ContextMenuAddress & (~3);
}
else
{
uint32_t selStartAddress, selEndAddress;
m_HexEditCtrl.GetSelectionRange(&selStartAddress, &selEndAddress);
address = selStartAddress & (~3);
}
uint32_t pointer;
bool bValid;
if (m_bVirtualMemory)
{
bValid = m_Debugger->DebugLoad_VAddr(address, pointer);
}
else
{
bValid = m_Debugger->DebugLoad_VAddr(address, pointer);
}
if (bValid)
{
OpenNewTab(pointer, m_bVirtualMemory, 4, true, true);
}
}
void CDebugMemoryView::JumpToSelection(void)
{
uint32_t startAddress, endAddress;
bool bHaveSelection = m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
uint32_t targetAddress = bHaveSelection ? startAddress : m_HexEditCtrl.GetCaretAddress();
m_MemAddr.SetValue(targetAddress, DisplayMode::ZeroExtend);
}
bool CDebugMemoryView::GetSafeEditValue(uint32_t address, uint8_t* value)
{
if (m_SafeEditQueue.size() == 0)
{
return false;
}
for(size_t i = m_SafeEditQueue.size(); i-- > 0;)
{
edit_t edit = m_SafeEditQueue[i];
if (address >= edit.startAddress && address <= edit.endAddress)
{
*value = edit.value;
return true;
}
}
return false;
}
void CDebugMemoryView::ApplySafeEdits(void)
{
for (size_t i = 0; i < m_SafeEditQueue.size(); i++)
{
edit_t edit = m_SafeEditQueue[i];
if (edit.type == SE_FILL)
{
FillRange(edit.startAddress, edit.endAddress, edit.value);
}
}
m_SafeEditQueue.clear();
}
void CDebugMemoryView::SetupJumpMenu(bool bVirtual)
{
m_CmbJump.SetRedraw(FALSE);
m_CmbJump.ResetContent();
for (int i = 0;; i++)
{
jump_item_t* item = &JumpItems[i];
if (item->caption == nullptr)
{
break;
}
m_CmbJump.AddString(stdstr_f("%08X %s", bVirtual ? item->vaddr : item->paddr, item->caption).ToUTF16().c_str());
}
m_CmbJump.SetRedraw(TRUE);
}
int CDebugMemoryView::GetJumpItemIndex(uint32_t address, bool bVirtual)
{
for (int nItem = 0;; nItem++)
{
if (JumpItems[nItem].caption == nullptr)
{
break;
}
uint32_t start = bVirtual ? JumpItems[nItem].vaddr : JumpItems[nItem].paddr;
uint32_t end = start + JumpItems[nItem].size - 1;
if (address >= start && address <= end)
{
return nItem;
}
}
return -1;
}
LRESULT CDebugMemoryView::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
DlgResize_Init(false, true);
DlgSavePos_Init(DebuggerUI_MemoryPos);
DlgToolTip_Init();
m_HexEditCtrl.Attach(GetDlgItem(IDC_HEXEDIT));
m_TabCtrl.Attach(GetDlgItem(IDC_MEMTABS));
m_MemAddr.Attach(GetDlgItem(IDC_ADDR_EDIT));
m_VirtualCheckbox.Attach(GetDlgItem(IDC_CHK_VADDR));
m_StatusBar.Attach(GetDlgItem(IDC_STATUSBAR));
m_CmbJump.Attach(GetDlgItem(IDC_CMB_JUMP));
m_SymbolColorStride = 0;
m_SymbolColorPhase = 0;
HWND hScrlBar = GetDlgItem(IDC_SCRL_BAR);
if (hScrlBar)
{
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
si.nMin = 0;
si.nMax = 0xFFFF;
si.nPos = 0x8000;
si.nPage = 100;
::SetScrollInfo(hScrlBar, SB_CTL, &si, TRUE);
}
m_MemAddr.SetDisplayType(CEditNumber32::DisplayHex);
m_MemAddr.SetValue(0x80000000, DisplayMode::ZeroExtend);
m_VirtualCheckbox.SetCheck(BST_CHECKED);
float dpiScale = CClientDC(m_hWnd).GetDeviceCaps(LOGPIXELSX) / 96.0f;
int statusPaneWidths[MEMSB_NUM_PANES] = {
(int)(MEMSB_HOTADDR_W * dpiScale),
(int)(MEMSB_BLOCK_W * dpiScale),
(int)(MEMSB_BLOCKLEN_W * dpiScale),
(int)(MEMSB_DMAINFO_W * dpiScale),
(int)(MEMSB_SAFEMODE_W * dpiScale)
};
m_StatusBar.SetParts(MEMSB_NUM_PANES, statusPaneWidths);
SetupJumpMenu(true);
m_CmbJump.SetCurSel(0);
m_TabData.clear();
AddTab(0x80000000, true, 4);
m_HexEditCtrl.Draw();
LoadWindowPos();
WindowCreated();
return TRUE;
}
LRESULT CDebugMemoryView::OnDestroy(void)
{
m_HexEditCtrl.Detach();
m_MemAddr.Detach();
m_VirtualCheckbox.Detach();
m_TabCtrl.Detach();
m_StatusBar.Detach();
m_CmbJump.Detach();
return 0;
}
LRESULT CDebugMemoryView::OnShowAddress(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
uint32_t address = (uint32_t)wParam;
bool bVirtual = (lParam != 0);
m_CmbJump.SetCurSel(GetJumpItemIndex(address, bVirtual));
OpenNewTab(address, bVirtual, 4, true, true);
return FALSE;
}
void CDebugMemoryView::OnExitSizeMove()
{
SaveWindowPos(true);
}
LRESULT CDebugMemoryView::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND, BOOL& /*bHandled*/)
{
switch (wID)
{
case IDC_CHK_VADDR:
m_bVirtualMemory = (m_VirtualCheckbox.GetCheck() == BST_CHECKED);
SetupJumpMenu(m_bVirtualMemory);
m_CmbJump.SetCurSel(GetJumpItemIndex(m_MemAddr.GetValue(), m_bVirtualMemory));
break;
case IDC_SYMBOLS_BTN:
m_Debugger->OpenSymbolsWindow();
break;
case IDCANCEL:
EndDialog(0);
break;
case ID_POPUPMENU_TOGGLERBP:
m_Breakpoints->RBPToggle(m_ContextMenuAddress);
break;
case ID_POPUPMENU_TOGGLEWBP:
m_Breakpoints->WBPToggle(m_ContextMenuAddress);
break;
case ID_POPUPMENU_CLEARALLBPS:
m_Breakpoints->RBPClear();
m_Breakpoints->WBPClear();
break;
case ID_POPUPMENU_TOGGLELOCK:
m_Breakpoints->ToggleMemLock(m_ContextMenuAddress);
break;
case ID_POPUPMENU_CLEARLOCKS:
m_Breakpoints->ClearMemLocks();
break;
case ID_POPUPMENU_JUMPHERE:
JumpToSelection();
break;
case ID_POPUPMENU_FOLLOWPOINTER:
FollowPointer();
break;
case ID_POPUPMENU_VIEWDISASM:
m_Debugger->Debug_ShowCommandsLocation(m_ContextMenuAddress, true);
break;
case ID_POPUPMENU_ADDSYMBOL:
m_AddSymbolDlg.DoModal(m_Debugger, m_ContextMenuAddress);
break;
case ID_POPUPMENU_COPY:
m_HexEditCtrl.Copy();
break;
case ID_POPUPMENU_COPYGAMESHARKCODE:
{
uint32_t startAddress, endAddress;
m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
CopyGameSharkCodeToClipboard(startAddress, endAddress);
}
break;
case ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES:
{
uint32_t startAddress, endAddress;
m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
CopyBytesToClipboard(startAddress, endAddress, m_HexEditCtrl.GetFocusedColumn() == HX_COL_HEXDATA, true, false);
}
break;
case ID_POPUPMENU_COPYDATAWITHROWADDRESSES:
{
uint32_t startAddress, endAddress;
m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
CopyBytesToClipboard(startAddress, endAddress, m_HexEditCtrl.GetFocusedColumn() == HX_COL_HEXDATA, true, true);
}
break;
case ID_POPUPMENU_PASTE:
m_HexEditCtrl.Paste();
break;
case ID_POPUPMENU_SAFEMODE:
m_HexEditCtrl.SendMessage(WM_KEYDOWN, VK_INSERT, 0);
break;
case ID_POPUPMENU_ZEROFILL:
m_HexEditCtrl.SendMessage(WM_KEYDOWN, VK_DELETE, 0);
break;
case ID_POPUPMENU_BYTEGROUPSIZE_1:
m_HexEditCtrl.SetByteGroupSize(1);
break;
case ID_POPUPMENU_BYTEGROUPSIZE_2:
m_HexEditCtrl.SetByteGroupSize(2);
break;
case ID_POPUPMENU_BYTEGROUPSIZE_4:
m_HexEditCtrl.SetByteGroupSize(4);
break;
case ID_POPUPMENU_BYTEGROUPSIZE_8:
m_HexEditCtrl.SetByteGroupSize(8);
break;
case ID_POPUPMENU_DUMP:
m_Debugger->OpenMemoryDump();
break;
case ID_POPUPMENU_SEARCH:
m_Debugger->OpenMemorySearch();
break;
}
return FALSE;
}
void CDebugMemoryView::OnAddrChanged(UINT /*Code*/, int /*id*/, HWND /*ctl*/)
{
if (m_bIgnoreAddressInput)
{
m_bIgnoreAddressInput = false;
return;
}
uint32_t address = m_MemAddr.GetValue();
m_HexEditCtrl.SetBaseAddress(address);
UpdateCurrentTab(address);
m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory));
}
void CDebugMemoryView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)
{
if (pScrollBar != GetDlgItem(IDC_SCRL_BAR))
{
return;
}
uint32_t address = m_MemAddr.GetValue();
int numBytesPerRow = m_HexEditCtrl.GetNumBytesPerRow();
int numVisibleBytes = m_HexEditCtrl.GetNumVisibleBytes();
switch (nSBCode)
{
case SB_LINEDOWN:
m_MemAddr.SetValue(address < 0xFFFFFFEF ? address + numBytesPerRow : 0xFFFFFFFF, DisplayMode::ZeroExtend);
break;
case SB_LINEUP:
m_MemAddr.SetValue(address > (uint32_t)numBytesPerRow ? address - numBytesPerRow : 0, DisplayMode::ZeroExtend);
break;
case SB_PAGEDOWN:
m_MemAddr.SetValue(address < 0xFFFFFEFF ? address + numVisibleBytes : 0xFFFFFFFF, DisplayMode::ZeroExtend);
break;
case SB_PAGEUP:
m_MemAddr.SetValue(address >(uint32_t)numVisibleBytes ? address - numVisibleBytes : 0, DisplayMode::ZeroExtend);
break;
case SB_THUMBPOSITION:
m_MemAddr.SetValue((DWORD)nPos << 0x10, DisplayMode::ZeroExtend);
break;
default:
break;
}
m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory));
}
LRESULT CDebugMemoryView::OnHxCtrlKeyPressed(LPNMHDR lpNMHDR)
{
NMHXCTRLKEYPRESSED* nmck = reinterpret_cast<NMHXCTRLKEYPRESSED*>(lpNMHDR);
uint32_t address = m_HexEditCtrl.GetCaretAddress();
if (nmck->nChar >= '1' && nmck->nChar <= '9')
{
int nBytes = nmck->nChar - '0';
m_HexEditCtrl.SetByteGroupSize(nBytes);
return FALSE;
}
switch (nmck->nChar)
{
case 'G':
JumpToSelection();
break;
case 'W':
m_Breakpoints->WBPToggle(address);
break;
case 'N':
AddTab(0x80000000, true, 4);
break;
case 'R':
m_Breakpoints->RBPToggle(address);
break;
case 'E':
m_Breakpoints->ToggleMemLock(address);
break;
case 'Q':
m_Breakpoints->WBPClear();
m_Breakpoints->RBPClear();
m_Breakpoints->ClearMemLocks();
break;
case 'F':
// TODO: put selection in the textbox
m_Debugger->OpenMemorySearch();
break;
case 'S':
// TODO: set start and end address to selection
m_Debugger->OpenMemoryDump();
break;
case 'T':
OpenDuplicateTab();
break;
case 'Z':
if (m_SafeEditQueue.size() != 0)
{
m_SafeEditQueue.pop_back();
}
break;
case VK_F4:
CloseCurrentTab();
break;
case VK_SPACE:
FollowPointer(false);
break;
case VK_TAB:
{
int curSel = m_TabCtrl.GetCurSel();
if (m_TabCtrl.SetCurSel(curSel + 1) == -1)
{
m_TabCtrl.SetCurSel(0);
}
TabSelChanged();
}
break;
}
return FALSE;
}
LRESULT CDebugMemoryView::OnHxSetNibble(LPNMHDR lpNMHDR)
{
if (g_MMU == nullptr)
{
return FALSE;
}
NMHXSETNIBBLE* nmsn = reinterpret_cast<NMHXSETNIBBLE*>(lpNMHDR);
uint8_t curValue;
bool bValid = GetByte(nmsn->address, &curValue);
if (!bValid)
{
return false;
}
uint8_t mask = (nmsn->bLoNibble ? 0xF0 : 0x0F);
uint8_t newValue = (curValue & mask) | (nmsn->value << (nmsn->bLoNibble ? 0 : 4));
if (nmsn->bInsert)
{
if (GetSafeEditValue(nmsn->address, &curValue))
{
newValue = (curValue & mask) | (nmsn->value << (nmsn->bLoNibble ? 0 : 4));
}
m_SafeEditQueue.push_back({ SE_FILL, nmsn->address, nmsn->address, newValue });
}
else
{
SetByte(nmsn->address, newValue);
}
return FALSE;
}
LRESULT CDebugMemoryView::OnHxSetByte(LPNMHDR lpNMHDR)
{
NMHXSETBYTE* nmsb = reinterpret_cast<NMHXSETBYTE*>(lpNMHDR);
if (g_MMU == nullptr)
{
return FALSE;
}
if (nmsb->bInsert)
{
m_SafeEditQueue.push_back({ SE_FILL, nmsb->address, nmsb->address, nmsb->value });
}
else
{
SetByte(nmsb->address, nmsb->value);
}
return FALSE;
}
LRESULT CDebugMemoryView::OnHxFillRange(LPNMHDR lpNMHDR)
{
NMHXFILLRANGE* nmfr = reinterpret_cast<NMHXFILLRANGE*>(lpNMHDR);
if (nmfr->bInsert)
{
m_SafeEditQueue.push_back({ SE_FILL, nmfr->startAddress, nmfr->endAddress, nmfr->value });
return FALSE;
}
FillRange(nmfr->startAddress, nmfr->endAddress, nmfr->value);
return FALSE;
}
LRESULT CDebugMemoryView::OnHxInsertModeChanged(LPNMHDR /*lpNMHDR*/)
{
m_SafeEditQueue.clear();
m_bSafeEditMode = m_HexEditCtrl.GetInsertMode();
m_StatusBar.SetText(MEMSB_SAFEMODE, m_bSafeEditMode ? L"Safe mode" : L"");
return FALSE;
}
LRESULT CDebugMemoryView::OnHxSelectionChanged(LPNMHDR /*lpNMHDR*/)
{
uint32_t startAddress, endAddress;
bool bHaveSelection = m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
stdstr strBlock, strLength;
if (bHaveSelection)
{
strBlock = stdstr_f("%08X:%08X", startAddress, endAddress);
strLength = stdstr_f("%X", endAddress - startAddress + 1);
m_StatusBar.SetText(MEMSB_BLOCK, strBlock.ToUTF16().c_str());
m_StatusBar.SetText(MEMSB_BLOCKLEN, strLength.ToUTF16().c_str());
}
else
{
strBlock = stdstr_f("%08X", startAddress);
m_StatusBar.SetText(MEMSB_BLOCK, strBlock.ToUTF16().c_str());
m_StatusBar.SetText(MEMSB_BLOCKLEN, L"");
}
uint32_t romAddr, offset;
DMALOGENTRY* entry = m_Debugger->DMALog()->GetEntryByRamAddress(startAddress, &romAddr, &offset);
m_StatusBar.SetText(MEMSB_DMAINFO, entry != nullptr ? L"Have DMA" : L"");
return FALSE;
}
LRESULT CDebugMemoryView::OnHxGroupSizeChanged(LPNMHDR /*lpNMHDR*/)
{
int groupSize = m_HexEditCtrl.GetNumBytesPerGroup();
int nItem = m_TabCtrl.GetCurSel();
if (nItem == -1)
{
return FALSE;
}
m_TabData[nItem].numBytesPerGroup = groupSize;
return FALSE;
}
LRESULT CDebugMemoryView::OnHxEnterPressed(LPNMHDR /*lpNMHDR*/)
{
ApplySafeEdits();
return FALSE;
}
LRESULT CDebugMemoryView::OnHxRedrawStarted(LPNMHDR /*lpNMHDR*/)
{
m_SymbolColorPhase = 0;
return FALSE;
}
LRESULT CDebugMemoryView::OnHxGetByteInfo(LPNMHDR lpNMHDR)
{
NMHXGETBYTEINFO *nmgbi = reinterpret_cast<NMHXGETBYTEINFO*>(lpNMHDR);
bool bHaveWriteTarget = false, bHaveReadTarget = false;
uint32_t cpuReadWriteAddress = 0;
int cpuReadWriteNumBytes = 0;
if (g_Settings->LoadBool(Debugger_SteppingOps))
{
COpInfo opInfo(R4300iOp::m_Opcode);
if (opInfo.IsStoreCommand())
{
cpuReadWriteAddress = opInfo.GetLoadStoreAddress();
cpuReadWriteNumBytes = opInfo.NumBytesToStore();
bHaveWriteTarget = true;
}
else if (opInfo.IsLoadCommand())
{
cpuReadWriteAddress = opInfo.GetLoadStoreAddress();
cpuReadWriteNumBytes = opInfo.NumBytesToLoad();
bHaveReadTarget = true;
}
}
for (uint32_t i = 0; i < nmgbi->numBytes; i++)
{
uint32_t address = nmgbi->address + i;
uint32_t paddress = address;
HXBYTEINFO* oldByte = &nmgbi->oldBytes[i];
HXBYTEINFO* newByte = &nmgbi->newBytes[i];
newByte->bkColor = BKCOLOR_DEFAULT;
newByte->color = COLOR_DEFAULT;
if (m_bVirtualMemory && (g_MMU == nullptr || !g_MMU->VAddrToPAddr(address, paddress)))
{
newByte->bValid = false;
continue;
}
newByte->bValid = GetByte(address, &newByte->value);
if (!newByte->bValid)
{
continue;
}
// Always use virtual addresses for breakpoint and symbol info
// TODO: should be the other way around
uint32_t vaddress = m_bVirtualMemory ? address : address + 0x80000000;
CSymbol symbol;
if (m_Debugger->SymbolTable()->GetSymbolByAddress(vaddress, &symbol))
{
m_SymbolColorStride = symbol.TypeSize();
m_SymbolColorPhase = m_SymbolColorPhase ? 0 : 1;
}
if (bHaveWriteTarget && address == cpuReadWriteAddress)
{
m_WriteTargetColorStride = cpuReadWriteNumBytes;
}
else if (bHaveReadTarget && address == cpuReadWriteAddress)
{
m_ReadTargetColorStride = cpuReadWriteNumBytes;
}
bool bLocked = m_Breakpoints->MemLockExists(vaddress, 1);
bool bReadBP = m_Breakpoints->ReadBPExists8(vaddress) == CBreakpoints::BP_SET;
bool bWriteBP = m_Breakpoints->WriteBPExists8(vaddress) == CBreakpoints::BP_SET;
if (bLocked)
{
newByte->bkColor = BKCOLOR_LOCKED;
newByte->color = COLOR_BP;
}
else if (bReadBP && bWriteBP)
{
newByte->bkColor = BKCOLOR_RWBP;
newByte->color = COLOR_BP;
}
else if (bReadBP)
{
newByte->bkColor = BKCOLOR_RBP;
newByte->color = COLOR_BP;
}
else if (bWriteBP)
{
newByte->bkColor = BKCOLOR_WBP;
newByte->color = COLOR_BP;
}
else if (m_ReadTargetColorStride > 0)
{
newByte->bkColor = BKCOLOR_CPUREAD;
}
else if (m_WriteTargetColorStride > 0)
{
newByte->bkColor = BKCOLOR_CPUWRITE;
}
else if (m_SymbolColorStride > 0)
{
newByte->bkColor = m_SymbolColorPhase ? BKCOLOR_SYMBOL0 : BKCOLOR_SYMBOL1;
}
if (g_Rom != nullptr && paddress >= 0x10000000 && paddress < 0x10000000 + g_Rom->GetRomSize())
{
newByte->color = COLOR_READONLY;
}
if (!nmgbi->bIgnoreDiff && oldByte->value != newByte->value)
{
newByte->color = COLOR_CHANGED;
}
if (m_SymbolColorStride > 0)
{
m_SymbolColorStride--;
}
if (m_ReadTargetColorStride > 0)
{
m_ReadTargetColorStride--;
}
if (m_WriteTargetColorStride > 0)
{
m_WriteTargetColorStride--;
}
uint8_t safeEditValue;
if (GetSafeEditValue(address, &safeEditValue))
{
newByte->bValid = true;
newByte->value = safeEditValue;
newByte->bkColor = RGB(0xFF, 0xCC, 0xFF);
newByte->color = RGB(0xFF, 0x00, 0xFF);
}
newByte->bHidden = false;
}
return FALSE;
}
LRESULT CDebugMemoryView::OnHxRightClick(LPNMHDR lpNMHDR)
{
NMHXRCLICK *nmrc = reinterpret_cast<NMHXRCLICK*>(lpNMHDR);
m_ContextMenuAddress = nmrc->address;
HMENU hMenu = LoadMenu(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_MEM_BP_POPUP));
HMENU hPopupMenu = GetSubMenu(hMenu, 0);
bool bHaveLock = m_Breakpoints->MemLockExists(m_ContextMenuAddress, 1);
bool bHaveReadBP = (m_Breakpoints->ReadBPExists8(m_ContextMenuAddress) != CBreakpoints::BPSTATE::BP_NOT_SET);
bool bHaveWriteBP = (m_Breakpoints->WriteBPExists8(m_ContextMenuAddress) != CBreakpoints::BPSTATE::BP_NOT_SET);
if (m_Breakpoints->ReadMem().size() == 0 && m_Breakpoints->WriteMem().size() == 0)
{
EnableMenuItem(hPopupMenu, ID_POPUPMENU_CLEARALLBPS, MF_DISABLED | MF_GRAYED);
}
if (m_Breakpoints->NumMemLocks() == 0)
{
EnableMenuItem(hPopupMenu, ID_POPUPMENU_CLEARLOCKS, MF_DISABLED | MF_GRAYED);
}
CheckMenuItem(hPopupMenu, ID_POPUPMENU_TOGGLELOCK, bHaveLock ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hPopupMenu, ID_POPUPMENU_TOGGLERBP, bHaveReadBP ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hPopupMenu, ID_POPUPMENU_TOGGLEWBP, bHaveWriteBP ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hPopupMenu, ID_POPUPMENU_SAFEMODE, m_bSafeEditMode ? MF_CHECKED : MF_UNCHECKED);
POINT mouse;
GetCursorPos(&mouse);
TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN, mouse.x, mouse.y, 0, m_hWnd, nullptr);
DestroyMenu(hMenu);
return FALSE;
}
LRESULT CDebugMemoryView::OnHxHotAddrChanged(LPNMHDR /*lpNMHDR*/)
{
m_HotAddress = m_HexEditCtrl.GetHotAddress();
stdstr strAddrInfo = "";
CSymbol symbol;
if (m_Debugger->SymbolTable()->GetSymbolByAddress(m_HotAddress, &symbol))
{
strAddrInfo += stdstr_f("%08X %s %s", symbol.m_Address, symbol.TypeName(), symbol.m_Name);
}
else
{
strAddrInfo += stdstr_f("%08X\n", m_HotAddress);
}
m_StatusBar.SetText(MEMSB_HOTADDR, strAddrInfo.ToUTF16().c_str());
return FALSE;
}
LRESULT CDebugMemoryView::OnHxBaseAddrChanged(LPNMHDR /*lpNMHDR*/)
{
// Address was updated from the control
uint32_t address = m_HexEditCtrl.GetBaseAddress();
m_bIgnoreAddressInput = true;
m_MemAddr.SetValue(address, DisplayMode::ZeroExtend);
m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory));
UpdateCurrentTab(address);
return FALSE;
}
LRESULT CDebugMemoryView::OnHxCopy(LPNMHDR /*lpNMHDR*/)
{
HXCOLUMN column = m_HexEditCtrl.GetFocusedColumn();
uint32_t startAddress, endAddress;
m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
CopyBytesToClipboard(startAddress, endAddress, column == HX_COL_HEXDATA);
return FALSE;
}
LRESULT CDebugMemoryView::OnHxPaste(LPNMHDR lpNMHDR)
{
NMHXPASTE *nmp = reinterpret_cast<NMHXPASTE*>(lpNMHDR);
if (g_MMU == nullptr)
{
return FALSE;
}
OpenClipboard();
HANDLE hData = GetClipboardData(CF_TEXT);
char* text = (char*)GlobalLock(hData);
int retDataLength = 0;
if (nmp->column == HX_COL_HEXDATA)
{
// TODO: move this function to some utility class
int length = CMemoryScanner::ParseHexString(nullptr, text);
if (length != 0)
{
std::unique_ptr<char[]> data = std::make_unique<char[]>(length);
CMemoryScanner::ParseHexString(data.get(), text);
for (int i = 0; i < length; i++)
{
SetByte(nmp->address + i, data.get()[i]);
}
}
retDataLength = length;
}
else if (nmp->column == HX_COL_ASCII)
{
size_t length = strlen(text);
for (size_t i = 0; i < length; i++)
{
SetByte(nmp->address + i, text[i]);
}
retDataLength = length;
}
GlobalUnlock(hData);
CloseClipboard();
return retDataLength;
}
LRESULT CDebugMemoryView::OnTabSelChange(LPNMHDR /*lpNMHDR*/)
{
TabSelChanged();
return FALSE;
}
void CDebugMemoryView::TabSelChanged(void)
{
TCITEM item = { 0 };
item.mask = TCIF_PARAM;
int nItem = m_TabCtrl.GetCurSel();
if (m_TabCtrl.GetItem(nItem, &item))
{
tab_info_t tabInfo = m_TabData[nItem];
uint32_t address = tabInfo.address;
m_MemAddr.SetValue(address, DisplayMode::ZeroExtend);
if (m_bVirtualMemory != tabInfo.bVirtual)
{
m_bVirtualMemory = tabInfo.bVirtual;
m_VirtualCheckbox.SetCheck(m_bVirtualMemory ? BST_CHECKED : BST_UNCHECKED);
SetupJumpMenu(m_bVirtualMemory);
}
m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory));
m_HexEditCtrl.SetByteGroupSize(tabInfo.numBytesPerGroup);
}
}
int CDebugMemoryView::AddTab(uint32_t address, bool bVirtual, int numBytesPerGroup)
{
stdstr szAddress;
szAddress.Format("%08X", address);
m_TabData.push_back({ address, bVirtual, numBytesPerGroup });
return m_TabCtrl.AddItem(TCIF_TEXT | TCIF_PARAM, szAddress.ToUTF16().c_str(), 0, (LPARAM)address);
}
int CDebugMemoryView::InsertTab(int nItem, uint32_t address, bool bVirtual, int numBytesPerGroup)
{
m_TabData.insert(m_TabData.begin() + nItem + 1, { address, bVirtual, numBytesPerGroup });
m_TabCtrl.SetRedraw(FALSE);
m_TabCtrl.DeleteAllItems();
for (size_t i = 0; i < m_TabData.size(); i++)
{
m_TabCtrl.AddItem(TCIF_TEXT, stdstr_f("%08X", m_TabData[i].address).ToUTF16().c_str(), 0, 0);
}
m_TabCtrl.SetRedraw(TRUE);
return nItem + 1;
}
void CDebugMemoryView::DeleteTab(int nItem)
{
m_TabData.erase(m_TabData.begin() + nItem);
m_TabCtrl.DeleteItem(nItem);
}
void CDebugMemoryView::UpdateCurrentTab(uint32_t address)
{
std::wstring szAddress = stdstr_f("%08X", address).ToUTF16();
int nItem = m_TabCtrl.GetCurSel();
if (nItem == -1)
{
return;
}
TCITEM item = { 0 };
item.mask = TCIF_TEXT;
item.pszText = (LPWSTR)szAddress.c_str();
m_TabCtrl.SetRedraw(FALSE);
m_TabCtrl.SetItem(nItem, &item);
m_TabData[nItem].address = address;
m_TabData[nItem].bVirtual = m_bVirtualMemory;
m_TabCtrl.SetRedraw(TRUE);
}
void CDebugMemoryView::OpenNewTab(uint32_t address, bool bVirtual, int numBytesPerGroup, bool bInsert, bool bOpenExisting)
{
int nItem;
if (bOpenExisting)
{
for (size_t i = 0; i < m_TabData.size(); i++)
{
if (m_TabData[i].address == address && m_TabData[i].bVirtual == bVirtual)
{
m_TabCtrl.SetCurSel(i);
TabSelChanged();
return;
}
}
}
if (bInsert)
{
int nCurSelItem = m_TabCtrl.GetCurSel();
nItem = InsertTab(nCurSelItem, address, bVirtual, numBytesPerGroup);
}
else
{
nItem = AddTab(address, bVirtual, numBytesPerGroup);
}
m_TabCtrl.SetCurSel(nItem);
TabSelChanged();
}
void CDebugMemoryView::OpenDuplicateTab(void)
{
int nItem = m_TabCtrl.GetCurSel();
tab_info_t tabInfo = m_TabData[nItem];
int nItemNew = InsertTab(nItem, tabInfo.address, tabInfo.bVirtual, tabInfo.numBytesPerGroup);
m_TabCtrl.SetCurSel(nItemNew);
TabSelChanged();
}
void CDebugMemoryView::CloseTab(int nItem)
{
int itemCount = m_TabCtrl.GetItemCount();
int nSelItem = m_TabCtrl.GetCurSel();
if (itemCount < 2 || nItem == -1)
{
return;
}
if (nItem == nSelItem)
{
if (nItem == m_TabCtrl.GetItemCount() - 1)
{
// Last tab
m_TabCtrl.SetCurSel(nItem - 1);
}
else if (nItem == nSelItem)
{
m_TabCtrl.SetCurSel(nItem + 1);
}
TabSelChanged();
}
DeleteTab(nItem);
}
void CDebugMemoryView::CloseCurrentTab(void)
{
CloseTab(m_TabCtrl.GetCurSel());
}
LRESULT CDebugMemoryView::OnTabDblClick(LPNMHDR /*lpNMHDR*/)
{
OpenDuplicateTab();
return FALSE;
}
LRESULT CDebugMemoryView::OnTabRClick(LPNMHDR lpNMHDR)
{
NMMTRCLICK *nmrc = reinterpret_cast<NMMTRCLICK*>(lpNMHDR);
CloseTab(nmrc->nItem);
return FALSE;
}
LRESULT CDebugMemoryView::OnStatusBarClick(LPNMHDR lpNMHDR)
{
NMMOUSE *nmm = reinterpret_cast<NMMOUSE*>(lpNMHDR);
uint32_t startAddress, endAddress;
bool bHaveSelection = m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress);
if (nmm->dwItemSpec == MEMSB_DMAINFO)
{
uint32_t romAddress, blockOffset;
DMALOGENTRY* entry = m_Debugger->DMALog()->GetEntryByRamAddress(startAddress, &romAddress, &blockOffset);
if (entry == nullptr)
{
return FALSE;
}
stdstr strDmaTitle = stdstr_f("DMA information for 0x%08X", startAddress);
stdstr strDmaInfo = stdstr_f("Block:\nROM 0x%08X -> RAM 0x%08X ( 0x%X bytes )\n\nROM address of byte:\n0x%08X ( 0x%08X + 0x%08X )",
entry->romAddr, entry->ramAddr, entry->length, romAddress, entry->romAddr, blockOffset);
MessageBox(strDmaInfo.ToUTF16().c_str(), strDmaTitle.ToUTF16().c_str(), MB_OK);
}
else if (nmm->dwItemSpec == MEMSB_BLOCK)
{
stdstr strAddrRange;
if (bHaveSelection)
{
strAddrRange = stdstr_f("%08X:%08X", startAddress, endAddress);
}
else
{
strAddrRange = stdstr_f("%08X", startAddress);
}
CopyTextToClipboard(strAddrRange.c_str());
}
return FALSE;
}
void CDebugMemoryView::OnJumpComboSelChange(UINT /*uNotifyCode*/, int /*nID*/, CWindow /*wndCtl*/)
{
int nItem = m_CmbJump.GetCurSel();
uint32_t address;
if (m_bVirtualMemory)
{
address = JumpItems[nItem].vaddr;
}
else
{
address = JumpItems[nItem].paddr;
}
m_MemAddr.SetValue(address, DisplayMode::ZeroExtend);
m_HexEditCtrl.SetFocus();
}