/* This file is part of UKNCBTL. UKNCBTL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with UKNCBTL. If not, see . */ // MemoryView.cpp #include "stdafx.h" #include #include #include "Main.h" #include "Views.h" #include "ToolWindow.h" #include "Dialogs.h" #include "Emulator.h" #include "emubase/Emubase.h" ////////////////////////////////////////////////////////////////////// HWND g_hwndMemory = (HWND)INVALID_HANDLE_VALUE; // Memory view window handler WNDPROC m_wndprocMemoryToolWindow = NULL; // Old window proc address of the ToolWindow HWND m_hwndMemoryViewer = (HWND)INVALID_HANDLE_VALUE; HWND m_hwndMemoryToolbar = (HWND)INVALID_HANDLE_VALUE; int m_cxChar = 0; int m_cyLineMemory = 0; // Line height in pixels int m_nPageSize = 100; // Page size in lines int m_Mode = MEMMODE_ROM; // See MemoryViewMode enum int m_NumeralMode = MEMMODENUM_OCT; WORD m_wBaseAddress = 0xFFFF; WORD m_wCurrentAddress = 0xFFFF; BOOL m_okMemoryByteMode = FALSE; int m_PostionIncrement = 100; // Increment by X to the next word void MemoryView_AdjustWindowLayout(); BOOL MemoryView_OnKeyDown(WPARAM vkey, LPARAM lParam); void MemoryView_OnLButtonDown(int mousex, int mousey); void MemoryView_OnRButtonDown(int mousex, int mousey); BOOL MemoryView_OnMouseWheel(WPARAM wParam, LPARAM lParam); BOOL MemoryView_OnVScroll(WORD scrollcmd, WORD scrollpos); void MemoryView_CopyValueToClipboard(WPARAM command); void MemoryView_GotoAddress(WORD wAddress); void MemoryView_ScrollTo(WORD wBaseAddress); void MemoryView_UpdateWindowText(); LPCTSTR MemoryView_GetMemoryModeName(); void MemoryView_UpdateScrollPos(); void MemoryView_UpdateToolbar(); void MemoryView_GetCurrentValueRect(LPRECT pRect, int cxChar, int cyLine); WORD MemoryView_GetWordFromMemory(WORD address, BOOL& okValid, int& addrtype, WORD& wChanged); void MemoryView_OnDraw(HDC hdc); ////////////////////////////////////////////////////////////////////// void MemoryView_RegisterClass() { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = MemoryViewViewerWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = g_hInst; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = NULL; wcex.lpszClassName = CLASSNAME_MEMORYVIEW; wcex.hIconSm = NULL; RegisterClassEx(&wcex); } void MemoryView_Create(HWND hwndParent, int x, int y, int width, int height) { ASSERT(hwndParent != NULL); m_Mode = Settings_GetDebugMemoryMode(); if (m_Mode > MEMMODE_LAST) m_Mode = MEMMODE_LAST; m_okMemoryByteMode = Settings_GetDebugMemoryByte(); m_NumeralMode = Settings_GetDebugMemoryNumeral(); g_hwndMemory = CreateWindow( CLASSNAME_TOOLWINDOW, NULL, WS_CHILD | WS_VISIBLE, x, y, width, height, hwndParent, NULL, g_hInst, NULL); MemoryView_UpdateWindowText(); // ToolWindow subclassing m_wndprocMemoryToolWindow = (WNDPROC) LongToPtr( SetWindowLongPtr( g_hwndMemory, GWLP_WNDPROC, PtrToLong(MemoryViewWndProc)) ); RECT rcClient; GetClientRect(g_hwndMemory, &rcClient); m_hwndMemoryViewer = CreateWindowEx( WS_EX_STATICEDGE, CLASSNAME_MEMORYVIEW, NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 0, 0, rcClient.right, rcClient.bottom, g_hwndMemory, NULL, g_hInst, NULL); m_hwndMemoryToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TRANSPARENT | TBSTYLE_TOOLTIPS | CCS_NOPARENTALIGN | CCS_NODIVIDER | CCS_VERT, 4, 4, 32, 32 * 8, m_hwndMemoryViewer, (HMENU) 102, g_hInst, NULL); TBADDBITMAP addbitmap; addbitmap.hInst = g_hInst; addbitmap.nID = IDB_TOOLBAR; SendMessage(m_hwndMemoryToolbar, TB_ADDBITMAP, 2, (LPARAM) &addbitmap); SendMessage(m_hwndMemoryToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0); SendMessage(m_hwndMemoryToolbar, TB_SETBUTTONSIZE, 0, (LPARAM) MAKELONG (26, 26)); TBBUTTON buttons[9]; ZeroMemory(buttons, sizeof(buttons)); for (int i = 0; i < sizeof(buttons) / sizeof(TBBUTTON); i++) { buttons[i].fsState = TBSTATE_ENABLED | TBSTATE_WRAP; buttons[i].fsStyle = BTNS_BUTTON | TBSTYLE_GROUP; buttons[i].iString = -1; } buttons[0].idCommand = ID_DEBUG_MEMORY_GOTO; buttons[0].iBitmap = ToolbarImageGotoAddress; buttons[1].fsStyle = BTNS_SEP; buttons[2].idCommand = ID_DEBUG_MEMORY_RAM; buttons[2].iBitmap = ToolbarImageMemoryRam; buttons[3].idCommand = ID_DEBUG_MEMORY_ROM; buttons[3].iBitmap = ToolbarImageMemoryRom; buttons[4].idCommand = ID_DEBUG_MEMORY_CPU; buttons[4].iBitmap = ToolbarImageMemoryCpu; buttons[5].idCommand = ID_DEBUG_MEMORY_PPU; buttons[5].iBitmap = ToolbarImageMemoryPpu; buttons[6].fsStyle = BTNS_SEP; buttons[7].idCommand = ID_DEBUG_MEMORY_WORDBYTE; buttons[7].iBitmap = ToolbarImageWordByte; buttons[8].idCommand = ID_DEBUG_MEMORY_HEXMODE; buttons[8].iBitmap = ToolbarImageHexMode; SendMessage(m_hwndMemoryToolbar, TB_ADDBUTTONS, (WPARAM) sizeof(buttons) / sizeof(TBBUTTON), (LPARAM) &buttons); MemoryView_ScrollTo(Settings_GetDebugMemoryBase()); MemoryView_GotoAddress(Settings_GetDebugMemoryAddress()); MemoryView_UpdateToolbar(); } // Adjust position of client windows void MemoryView_AdjustWindowLayout() { RECT rc; GetClientRect(g_hwndMemory, &rc); if (m_hwndMemoryViewer != (HWND) INVALID_HANDLE_VALUE) SetWindowPos(m_hwndMemoryViewer, NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER); } LRESULT CALLBACK MemoryViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); LRESULT lResult; switch (message) { case WM_DESTROY: g_hwndMemory = (HWND) INVALID_HANDLE_VALUE; // We are closed! Bye-bye!.. return CallWindowProc(m_wndprocMemoryToolWindow, hWnd, message, wParam, lParam); case WM_SIZE: lResult = CallWindowProc(m_wndprocMemoryToolWindow, hWnd, message, wParam, lParam); MemoryView_AdjustWindowLayout(); return lResult; default: return CallWindowProc(m_wndprocMemoryToolWindow, hWnd, message, wParam, lParam); } //return (LRESULT)FALSE; } LRESULT CALLBACK MemoryViewViewerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_COMMAND: if (wParam == ID_DEBUG_COPY_VALUE || wParam == ID_DEBUG_COPY_ADDRESS) // Copy command from context menu MemoryView_CopyValueToClipboard(wParam); else if (wParam == ID_DEBUG_GOTO_ADDRESS) // "Go to Address" from context menu MemoryView_SelectAddress(); else if (wParam == ID_DEBUG_MEMORY_HEXMODE) MemoryView_SwitchNumeralMode(); else ::PostMessage(g_hwnd, WM_COMMAND, wParam, lParam); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); MemoryView_OnDraw(hdc); EndPaint(hWnd, &ps); } break; case WM_LBUTTONDOWN: MemoryView_OnLButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; case WM_RBUTTONDOWN: MemoryView_OnRButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; case WM_KEYDOWN: return (LRESULT) MemoryView_OnKeyDown(wParam, lParam); case WM_MOUSEWHEEL: return (LRESULT) MemoryView_OnMouseWheel(wParam, lParam); case WM_VSCROLL: return (LRESULT) MemoryView_OnVScroll(LOWORD(wParam), HIWORD(wParam)); case WM_SETFOCUS: case WM_KILLFOCUS: ::InvalidateRect(hWnd, NULL, TRUE); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return (LRESULT)FALSE; } // Returns even address 0-65534, or -1 if out of area int MemoryView_GetAddressByPoint(int mousex, int mousey) { int line = mousey / m_cyLineMemory - 1; if (line < 0) line = 0; else if (line >= m_nPageSize) return -1; int pos = (mousex - (32 + 4) - 9 * m_cxChar + m_cxChar / 2) / m_PostionIncrement; if (pos < 0) pos = 0; else if (pos > 7) pos = 7; return (WORD)(m_wBaseAddress + line * 16 + pos * 2); } BOOL MemoryView_OnKeyDown(WPARAM vkey, LPARAM /*lParam*/) { switch (vkey) { case VK_ESCAPE: ConsoleView_Activate(); break; case VK_HOME: MemoryView_GotoAddress(0); break; case VK_END: MemoryView_GotoAddress(0177777); break; case VK_LEFT: MemoryView_GotoAddress((WORD)(m_wCurrentAddress - 2)); break; case VK_RIGHT: MemoryView_GotoAddress((WORD)(m_wCurrentAddress + 2)); break; case VK_UP: MemoryView_GotoAddress((WORD)(m_wCurrentAddress - 16)); break; case VK_DOWN: MemoryView_GotoAddress((WORD)(m_wCurrentAddress + 16)); break; case VK_SPACE: MemoryView_SetViewMode((MemoryViewMode)((m_Mode == MEMMODE_LAST) ? 0 : m_Mode + 1)); break; case VK_PRIOR: MemoryView_GotoAddress((WORD)(m_wCurrentAddress - m_nPageSize * 16)); break; case VK_NEXT: MemoryView_GotoAddress((WORD)(m_wCurrentAddress + m_nPageSize * 16)); break; case 0x47: // G - Go To Address MemoryView_SelectAddress(); break; case 0x42: // B - change byte/word mode case 0x57: // W MemoryView_SwitchWordByte(); break; case 0x48: // H - Hex case 0x4F: // O - Octal MemoryView_SwitchNumeralMode(); break; default: return TRUE; } return FALSE; } void MemoryView_OnLButtonDown(int mousex, int mousey) { ::SetFocus(m_hwndMemoryViewer); int addr = MemoryView_GetAddressByPoint(mousex, mousey); if (addr >= 0) MemoryView_GotoAddress((WORD)addr); } void MemoryView_OnRButtonDown(int mousex, int mousey) { ::SetFocus(m_hwndMemoryViewer); POINT pt = { mousex, mousey }; HMENU hMenu = ::CreatePopupMenu(); int addr = MemoryView_GetAddressByPoint(mousex, mousey); if (addr >= 0) { MemoryView_GotoAddress((WORD)addr); RECT rcValue; MemoryView_GetCurrentValueRect(&rcValue, m_cxChar, m_cyLineMemory); pt.x = rcValue.left; pt.y = rcValue.bottom; int addrtype; BOOL okValid; WORD wChanged; uint16_t value = MemoryView_GetWordFromMemory((uint16_t)addr, okValid, addrtype, wChanged); TCHAR buffer[24]; if (okValid) { LPCTSTR vformat = (m_NumeralMode == MEMMODENUM_OCT) ? _T("Copy Value: %06o") : _T("Copy Value: %04x"); _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, vformat, value); ::AppendMenu(hMenu, 0, ID_DEBUG_COPY_VALUE, buffer); } LPCTSTR aformat = (m_NumeralMode == MEMMODENUM_OCT) ? _T("Copy Address: %06o") : _T("Copy Address: %04x"); _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, aformat, addr); ::AppendMenu(hMenu, 0, ID_DEBUG_COPY_ADDRESS, buffer); ::AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); } ::AppendMenu(hMenu, 0, ID_DEBUG_GOTO_ADDRESS, _T("Go to Address...\tG")); ::ClientToScreen(m_hwndMemoryViewer, &pt); ::TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, m_hwndMemoryViewer, NULL); VERIFY(::DestroyMenu(hMenu)); } BOOL MemoryView_OnMouseWheel(WPARAM wParam, LPARAM /*lParam*/) { short zDelta = GET_WHEEL_DELTA_WPARAM(wParam); int nDelta = zDelta / 120; if (nDelta > 5) nDelta = 5; if (nDelta < -5) nDelta = -5; MemoryView_GotoAddress((WORD)(m_wCurrentAddress - nDelta * 2 * 16)); return FALSE; } BOOL MemoryView_OnVScroll(WORD scrollcmd, WORD scrollpos) { switch (scrollcmd) { case SB_LINEDOWN: MemoryView_GotoAddress((WORD)(m_wCurrentAddress + 16)); break; case SB_LINEUP: MemoryView_GotoAddress((WORD)(m_wCurrentAddress - 16)); break; case SB_PAGEDOWN: MemoryView_GotoAddress((WORD)(m_wCurrentAddress + m_nPageSize * 16)); break; case SB_PAGEUP: MemoryView_GotoAddress((WORD)(m_wCurrentAddress - m_nPageSize * 16)); break; case SB_THUMBPOSITION: MemoryView_GotoAddress((WORD)(scrollpos * 16)); break; } return FALSE; } void MemoryView_UpdateWindowText() { TCHAR buffer[64]; LPCTSTR format = (m_NumeralMode == MEMMODENUM_OCT) ? _T("Memory - %s - %06o") : _T("Memory - %s - %04x"); _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, format, MemoryView_GetMemoryModeName(), m_wCurrentAddress); ::SetWindowText(g_hwndMemory, buffer); } void MemoryView_UpdateToolbar() { int command = ID_DEBUG_MEMORY_RAM; switch (m_Mode) { case MEMMODE_RAM0: case MEMMODE_RAM1: case MEMMODE_RAM2: command = ID_DEBUG_MEMORY_RAM; break; case MEMMODE_ROM: command = ID_DEBUG_MEMORY_ROM; break; case MEMMODE_CPU: command = ID_DEBUG_MEMORY_CPU; break; case MEMMODE_PPU: command = ID_DEBUG_MEMORY_PPU; break; } SendMessage(m_hwndMemoryToolbar, TB_CHECKBUTTON, command, TRUE); SendMessage(m_hwndMemoryToolbar, TB_CHECKBUTTON, ID_DEBUG_MEMORY_HEXMODE, (Settings_GetDebugMemoryNumeral() == MEMMODENUM_OCT ? 0 : 1)); } void MemoryView_SetViewMode(MemoryViewMode mode) { if (mode > MEMMODE_LAST) mode = MEMMODE_LAST; m_Mode = mode; Settings_SetDebugMemoryMode((WORD)m_Mode); InvalidateRect(m_hwndMemoryViewer, NULL, TRUE); MemoryView_UpdateWindowText(); MemoryView_UpdateToolbar(); } void MemoryView_SwitchRamMode() { if (m_Mode >= MEMMODE_RAM2) MemoryView_SetViewMode(MEMMODE_RAM0); else MemoryView_SetViewMode((MemoryViewMode)(m_Mode + 1)); } LPCTSTR MemoryView_GetMemoryModeName() { switch (m_Mode) { case MEMMODE_RAM0: return _T("RAM0"); case MEMMODE_RAM1: return _T("RAM1"); case MEMMODE_RAM2: return _T("RAM2"); case MEMMODE_ROM: return _T("ROM"); case MEMMODE_CPU: return _T("CPU"); case MEMMODE_PPU: return _T("PPU"); default: return _T("UKWN"); // Unknown mode } } void MemoryView_GotoAddress(WORD wAddress) { m_wCurrentAddress = wAddress & ((WORD)~1); // Address should be even Settings_SetDebugMemoryAddress(m_wCurrentAddress); int addroffset = wAddress - m_wBaseAddress; if (addroffset < 0) { WORD baseaddr = (m_wCurrentAddress & 0xFFF0); // Base address should be 16-byte aligned MemoryView_ScrollTo(baseaddr); } else if (addroffset >= m_nPageSize * 16) { WORD baseaddr = (WORD)(m_wCurrentAddress & 0xFFF0); MemoryView_ScrollTo(baseaddr); } MemoryView_UpdateWindowText(); } // Scroll window to the given base address void MemoryView_ScrollTo(WORD wBaseAddress) { if (wBaseAddress == m_wBaseAddress) return; m_wBaseAddress = wBaseAddress; Settings_SetDebugMemoryBase(wBaseAddress); InvalidateRect(m_hwndMemoryViewer, NULL, TRUE); MemoryView_UpdateScrollPos(); } void MemoryView_CopyValueToClipboard(WPARAM command) { WORD address = m_wCurrentAddress; WORD value; if (command == ID_DEBUG_COPY_ADDRESS) { value = address; } else { // Get word from memory int addrtype; BOOL okValid; WORD wChanged; value = MemoryView_GetWordFromMemory(address, okValid, addrtype, wChanged); if (!okValid) { AlertWarning(_T("Failed to get value: invalid memory type.")); return; } } TCHAR buffer[7]; if (m_NumeralMode == MEMMODENUM_OCT) PrintOctalValue(buffer, value); else PrintHexValue(buffer, value); CopyTextToClipboard(buffer); } void MemoryView_SwitchWordByte() { m_okMemoryByteMode = !m_okMemoryByteMode; Settings_SetDebugMemoryByte(m_okMemoryByteMode); InvalidateRect(m_hwndMemoryViewer, NULL, TRUE); } void MemoryView_SwitchNumeralMode() { int newMode = m_NumeralMode ^ 1; m_NumeralMode = newMode; InvalidateRect(m_hwndMemoryViewer, NULL, TRUE); Settings_SetDebugMemoryNumeral((WORD)newMode); MemoryView_UpdateWindowText(); MemoryView_UpdateToolbar(); } void MemoryView_SelectAddress() { WORD value = m_wCurrentAddress; if (InputBoxOctal(m_hwndMemoryViewer, _T("Go To Address"), &value)) MemoryView_GotoAddress(value); ::SetFocus(m_hwndMemoryViewer); } void MemoryView_UpdateScrollPos() { SCROLLINFO si; ZeroMemory(&si, sizeof(si)); si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; si.nPage = m_nPageSize; si.nPos = m_wBaseAddress / 16; si.nMin = 0; si.nMax = 0x10000 / 16 - 1; SetScrollInfo(m_hwndMemoryViewer, SB_VERT, &si, TRUE); } void MemoryView_GetCurrentValueRect(LPRECT pRect, int cxChar, int cyLine) { ASSERT(pRect != NULL); int addroffset = m_wCurrentAddress - m_wBaseAddress; int line = addroffset / 16; int pos = addroffset & 15; pRect->left = 32 + 4 + cxChar * 9 + m_PostionIncrement * (pos / 2) - cxChar / 2; pRect->right = pRect->left + m_PostionIncrement - 1; pRect->top = (line + 1) * cyLine - 1; pRect->bottom = pRect->top + cyLine + 1; } WORD MemoryView_GetWordFromMemory(WORD address, BOOL& okValid, int& addrtype, WORD& wChanged) { WORD word = 0; okValid = TRUE; addrtype = ADDRTYPE_NONE; wChanged = 0; bool okHalt = false; switch (m_Mode) { case MEMMODE_RAM0: case MEMMODE_RAM1: case MEMMODE_RAM2: word = g_pBoard->GetRAMWord(m_Mode, address); wChanged = Emulator_GetChangeRamStatus(m_Mode, address); break; case MEMMODE_ROM: // ROM - only 32 Kbytes if (address < 0100000) okValid = FALSE; else { addrtype = ADDRTYPE_ROM; word = g_pBoard->GetROMWord(address - 0100000); } break; case MEMMODE_CPU: okHalt = g_pBoard->GetCPU()->IsHaltMode(); word = g_pBoard->GetCPUMemoryController()->GetWordView(address, okHalt, FALSE, &addrtype); okValid = (addrtype != ADDRTYPE_IO) && (addrtype != ADDRTYPE_DENY); wChanged = Emulator_GetChangeRamStatus(ADDRTYPE_RAM12, address); break; case MEMMODE_PPU: okHalt = g_pBoard->GetPPU()->IsHaltMode(); word = g_pBoard->GetPPUMemoryController()->GetWordView(address, okHalt, FALSE, &addrtype); okValid = (addrtype != ADDRTYPE_IO) && (addrtype != ADDRTYPE_DENY); if (address < 0100000) wChanged = Emulator_GetChangeRamStatus(ADDRTYPE_RAM0, address); break; } return word; } void MemoryView_OnDraw(HDC hdc) { ASSERT(g_pBoard != NULL); HFONT hFont = CreateMonospacedFont(); HGDIOBJ hOldFont = SelectObject(hdc, hFont); int cxChar, cyLine; GetFontWidthAndHeight(hdc, &cxChar, &cyLine); COLORREF colorText = Settings_GetColor(ColorDebugText); COLORREF colorChanged = Settings_GetColor(ColorDebugValueChanged); COLORREF colorMemoryRom = Settings_GetColor(ColorDebugMemoryRom); COLORREF colorMemoryIO = Settings_GetColor(ColorDebugMemoryIO); COLORREF colorMemoryNA = Settings_GetColor(ColorDebugMemoryNA); COLORREF colorHighlight = Settings_GetColor(ColorDebugHighlight); RECT rcClip; GetClipBox(hdc, &rcClip); RECT rcClient; GetClientRect(m_hwndMemoryViewer, &rcClient); if (m_NumeralMode == MEMMODENUM_OCT) m_PostionIncrement = cxChar * 7; else m_PostionIncrement = cxChar * 5; if (m_okMemoryByteMode) m_PostionIncrement += cxChar; int xRight = 32 + 4 + cxChar * 27 + m_PostionIncrement * 8 + cxChar / 2; HGDIOBJ hOldBrush = ::SelectObject(hdc, ::GetSysColorBrush(COLOR_BTNFACE)); ::PatBlt(hdc, 32, 0, 4, rcClient.bottom, PATCOPY); ::PatBlt(hdc, xRight, 0, 4, rcClient.bottom, PATCOPY); HBRUSH hbrHighlight = ::CreateSolidBrush(colorHighlight); ::SelectObject(hdc, hbrHighlight); ::SetBkMode(hdc, TRANSPARENT); m_cxChar = cxChar; m_cyLineMemory = cyLine; TCHAR buffer[7]; const TCHAR* ADDRESS_LINE_OCT_WORDS = _T(" addr 0 2 4 6 10 12 14 16"); const TCHAR* ADDRESS_LINE_OCT_BYTES = _T(" addr 0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17"); const TCHAR* ADDRESS_LINE_HEX_WORDS = _T(" addr 0 2 4 6 8 a c e"); const TCHAR* ADDRESS_LINE_HEX_BYTES = _T(" addr 0 1 2 3 4 5 6 7 8 9 a b c d e f"); if (m_NumeralMode == MEMMODENUM_OCT && !m_okMemoryByteMode) TextOut(hdc, cxChar * 5, 0, ADDRESS_LINE_OCT_WORDS, (int)_tcslen(ADDRESS_LINE_OCT_WORDS)); else if (m_NumeralMode == MEMMODENUM_OCT && m_okMemoryByteMode) TextOut(hdc, cxChar * 5, 0, ADDRESS_LINE_OCT_BYTES, (int)_tcslen(ADDRESS_LINE_OCT_BYTES)); else if (m_okMemoryByteMode) TextOut(hdc, cxChar * 5, 0, ADDRESS_LINE_HEX_BYTES, (int)_tcslen(ADDRESS_LINE_HEX_BYTES)); else TextOut(hdc, cxChar * 5, 0, ADDRESS_LINE_HEX_WORDS, (int)_tcslen(ADDRESS_LINE_HEX_WORDS)); m_nPageSize = rcClient.bottom / cyLine - 1; WORD address = m_wBaseAddress; int y = 1 * cyLine; for (;;) // Draw lines { uint16_t lineAddress = address; if (m_NumeralMode == MEMMODENUM_OCT) DrawOctalValue(hdc, 6 * cxChar, y, address); else DrawHexValue(hdc, 7 * cxChar, y, address); int x = 14 * cxChar; TCHAR wchars[16]; for (int j = 0; j < 8; j++) // Draw words as octal value { // Get word from memory int addrtype; BOOL okValid; WORD wChanged; WORD word = MemoryView_GetWordFromMemory(address, okValid, addrtype, wChanged); if (address == m_wCurrentAddress) ::PatBlt(hdc, x - cxChar / 2, y, m_PostionIncrement, cyLine, PATCOPY); if (okValid) { if (addrtype == ADDRTYPE_ROM) ::SetTextColor(hdc, colorMemoryRom); else ::SetTextColor(hdc, (wChanged != 0) ? colorChanged : colorText); if (m_NumeralMode == MEMMODENUM_OCT && !m_okMemoryByteMode) DrawOctalValue(hdc, x, y, word); else if (m_NumeralMode == MEMMODENUM_OCT && m_okMemoryByteMode) { PrintOctalValue(buffer, (word & 0xff)); TextOut(hdc, x, y, buffer + 3, 3); PrintOctalValue(buffer, (word >> 8)); TextOut(hdc, x + 4 * cxChar, y, buffer + 3, 3); } else if (m_NumeralMode == MEMMODENUM_HEX && !m_okMemoryByteMode) DrawHexValue(hdc, x, y, word); else if (m_NumeralMode == MEMMODENUM_HEX && m_okMemoryByteMode) { PrintHexValue(buffer, word); TextOut(hdc, x, y, buffer + 2, 2); TextOut(hdc, x + 3 * cxChar, y, buffer, 2); } } else // !okValid { if (addrtype == ADDRTYPE_IO) { ::SetTextColor(hdc, colorMemoryIO); TextOut(hdc, x, y, _T(" IO"), 4); } else { ::SetTextColor(hdc, colorMemoryNA); TextOut(hdc, x, y, _T(" NA"), 4); } } // Prepare characters to draw at right BYTE ch1 = LOBYTE(word); TCHAR wch1 = Translate_KOI8R(ch1); if (ch1 < 32) wch1 = _T('·'); wchars[j * 2] = wch1; BYTE ch2 = HIBYTE(word); TCHAR wch2 = Translate_KOI8R(ch2); if (ch2 < 32) wch2 = _T('·'); wchars[j * 2 + 1] = wch2; address += 2; x += m_PostionIncrement; } // Highlight characters at right if (lineAddress <= m_wCurrentAddress && m_wCurrentAddress < lineAddress + 16) { int xHighlight = x + cxChar + (m_wCurrentAddress - lineAddress) * cxChar; ::PatBlt(hdc, xHighlight, y, cxChar * 2, cyLine, PATCOPY); } // Draw characters at right ::SetTextColor(hdc, colorText); ::SetBkMode(hdc, TRANSPARENT); int xch = x + cxChar; TextOut(hdc, xch, y, wchars, 16); y += cyLine; if (y > rcClip.bottom) break; } ::SelectObject(hdc, hOldBrush); VERIFY(::DeleteObject(hbrHighlight)); SelectObject(hdc, hOldFont); VERIFY(::DeleteObject(hFont)); if (::GetFocus() == m_hwndMemoryViewer) { RECT rcFocus; MemoryView_GetCurrentValueRect(&rcFocus, cxChar, cyLine); DrawFocusRect(hdc, &rcFocus); } } //////////////////////////////////////////////////////////////////////