/* 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);
}
}
//////////////////////////////////////////////////////////////////////