#include "stdafx.h" #include #include #include #include "DMALog.h" #include "DebuggerUI.h" #include "Symbols.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(debugger), CDialogResize(), CToolTipDialog(), 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(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(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(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(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(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(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(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 data = std::make_unique(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(lpNMHDR); CloseTab(nmrc->nItem); return FALSE; } LRESULT CDebugMemoryView::OnStatusBarClick(LPNMHDR lpNMHDR) { NMMOUSE * nmm = reinterpret_cast(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(); }