1246 lines
34 KiB
C++
1246 lines
34 KiB
C++
/****************************************************************************
|
|
* *
|
|
* Project64 - A Nintendo 64 emulator. *
|
|
* http://www.pj64-emu.com/ *
|
|
* Copyright (C) 2012 Project64. All rights reserved. *
|
|
* *
|
|
* License: *
|
|
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
|
* *
|
|
****************************************************************************/
|
|
#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(NULL),
|
|
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 == NULL)
|
|
{
|
|
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 == NULL)
|
|
{
|
|
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 == NULL)
|
|
{
|
|
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 addrs 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 == NULL)
|
|
{
|
|
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 == NULL)
|
|
{
|
|
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 != NULL ? 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 == NULL || !g_MMU->TranslateVaddr(address, paddress)))
|
|
{
|
|
newByte->bValid = false;
|
|
continue;
|
|
}
|
|
|
|
newByte->bValid = GetByte(address, &newByte->value);
|
|
|
|
if (!newByte->bValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// always use virtual addresses for breakpoint & 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 != NULL && 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(NULL), 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, NULL);
|
|
DestroyMenu(hMenu);
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT CDebugMemoryView::OnHxHotAddrChanged(LPNMHDR /*lpNMHDR*/)
|
|
{
|
|
m_HotAddress = m_HexEditCtrl.GetHotAddress();
|
|
stdstr strAddrInfo = "";
|
|
|
|
CSymbol symbol;
|
|
if (m_Debugger->SymbolTable()->GetSymbolByOverlappedAddress(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 == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
OpenClipboard();
|
|
HANDLE hData = GetClipboardData(CF_TEXT);
|
|
char* text = (char*)GlobalLock(hData);
|
|
int retDataLength = 0;
|
|
|
|
if (nmp->column == HX_COL_HEXDATA)
|
|
{
|
|
char* data = NULL;
|
|
// todo move this function to some utility class
|
|
int length = CMemoryScanner::ParseHexString(NULL, text);
|
|
|
|
if (length != 0)
|
|
{
|
|
data = (char*)malloc(length);
|
|
CMemoryScanner::ParseHexString(data, text);
|
|
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
SetByte(nmp->address + i, data[i]);
|
|
}
|
|
|
|
free(data);
|
|
}
|
|
|
|
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 == NULL)
|
|
{
|
|
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();
|
|
}
|