/* This file is part of BKBTL. BKBTL 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. BKBTL 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 BKBTL. If not, see . */ // DisasmView.cpp #include "stdafx.h" #include #include #include #include "Main.h" #include "Views.h" #include "ToolWindow.h" #include "Dialogs.h" #include "Emulator.h" #include "emubase/Emubase.h" ////////////////////////////////////////////////////////////////////// enum DisasmSubtitleType { SUBTYPE_NONE = 0, SUBTYPE_COMMENT = 1, SUBTYPE_BLOCKCOMMENT = 2, SUBTYPE_DATA = 4, }; struct DisasmSubtitleItem { uint16_t address; DisasmSubtitleType type; LPCTSTR comment; }; enum DisasmLineType { LINETYPE_NONE = 0, // Empty line LINETYPE_DATA = 1, // Line contains a data (non-instruction) LINETYPE_INSTR = 2, // Line contains a disassembled instruction LINETYPE_JUMP = 4, // Line has jump LINETYPE_SUBTITLE = 8, // Line has subtitle comment }; struct DisasmLineItem { int type; // Combination of DisasmLineType values uint16_t address; // Line address for LINETYPE_DATA int addrtype; // Address type for LINETYPE_DATA, see ADDRTYPE_XXX constants uint16_t value; // Data on the address for LINETYPE_DATA TCHAR strInstr[8]; // Disassembled instruction for LINETYPE_DISASM TCHAR strArg[32]; // Disassembled instruction arguments for LINETYPE_DISASM int jumpdelta; // Jump delta for LINETYPE_JUMP const DisasmSubtitleItem* pSubItem; // Link to subtitles item for LINETYPE_SUBTITLE }; HWND g_hwndDisasm = (HWND) INVALID_HANDLE_VALUE; // Disasm View window handle WNDPROC m_wndprocDisasmToolWindow = NULL; // Old window proc address of the ToolWindow HWND m_hwndDisasmViewer = (HWND) INVALID_HANDLE_VALUE; uint16_t m_wDisasmBaseAddr = 0; int m_nDisasmCurrentLineIndex = -1; // Line index for PC address int m_nDisasmSelectedLineIndex = -1; // Line selected by user bool m_okDisasmSubtitles = false; TCHAR* m_strDisasmSubtitles = nullptr; std::vector m_SubtitleItems; const int MAX_DISASMLINECOUNT = 50; DisasmLineItem* m_pDisasmLineItems = nullptr; BOOL m_okDisasmJumpPredict; TCHAR m_strDisasmHint[42] = { 0 }; TCHAR m_strDisasmHint2[42] = { 0 }; int m_cxDisasmBreakpointZone = 16; // Width of breakpoint zone at the left, for mouse click int m_cyDisasmLine = 10; // cyLine for the current font void DisasmView_UpdateWindowText(); BOOL DisasmView_OnKeyDown(WPARAM vkey, LPARAM lParam); void DisasmView_OnLButtonDown(int mousex, int mousey); void DisasmView_OnRButtonDown(int mousex, int mousey); void DisasmView_CopyToClipboard(WPARAM command); BOOL DisasmView_ParseSubtitles(); void DisasmView_DoDraw(HDC hdc); int DisasmView_DrawDisassemble(HDC hdc, const CProcessor* pProc, uint16_t current, uint16_t previous); ////////////////////////////////////////////////////////////////////// void DisasmView_RegisterClass() { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = DisasmViewViewerWndProc; 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_DISASMVIEW; wcex.hIconSm = NULL; RegisterClassEx(&wcex); } void DisasmView_Init() { m_pDisasmLineItems = static_cast(::calloc(MAX_DISASMLINECOUNT, sizeof(DisasmLineItem))); } void DisasmView_Done() { if (m_strDisasmSubtitles != nullptr) { free(m_strDisasmSubtitles); m_strDisasmSubtitles = nullptr; } m_SubtitleItems.clear(); if (m_pDisasmLineItems != nullptr) { free(m_pDisasmLineItems); m_pDisasmLineItems = nullptr; } } void DisasmView_Create(HWND hwndParent, int x, int y, int width, int height) { ASSERT(hwndParent != NULL); g_hwndDisasm = CreateWindow( CLASSNAME_TOOLWINDOW, NULL, WS_CHILD | WS_VISIBLE, x, y, width, height, hwndParent, NULL, g_hInst, NULL); DisasmView_UpdateWindowText(); // ToolWindow subclassing m_wndprocDisasmToolWindow = (WNDPROC) LongToPtr( SetWindowLongPtr( g_hwndDisasm, GWLP_WNDPROC, PtrToLong(DisasmViewWndProc)) ); RECT rcClient; GetClientRect(g_hwndDisasm, &rcClient); m_hwndDisasmViewer = CreateWindowEx( WS_EX_STATICEDGE, CLASSNAME_DISASMVIEW, NULL, WS_CHILD | WS_VISIBLE, 0, 0, rcClient.right, rcClient.bottom, g_hwndDisasm, NULL, g_hInst, NULL); } void DisasmView_Redraw() { RedrawWindow(g_hwndDisasm, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); } // Adjust position of client windows void DisasmView_AdjustWindowLayout() { RECT rc; GetClientRect(g_hwndDisasm, &rc); if (m_hwndDisasmViewer != (HWND) INVALID_HANDLE_VALUE) SetWindowPos(m_hwndDisasmViewer, NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER); } LRESULT CALLBACK DisasmViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); LRESULT lResult; switch (message) { case WM_DESTROY: g_hwndDisasm = (HWND) INVALID_HANDLE_VALUE; // We are closed! Bye-bye!.. return CallWindowProc(m_wndprocDisasmToolWindow, hWnd, message, wParam, lParam); case WM_SIZE: lResult = CallWindowProc(m_wndprocDisasmToolWindow, hWnd, message, wParam, lParam); DisasmView_AdjustWindowLayout(); return lResult; default: return CallWindowProc(m_wndprocDisasmToolWindow, hWnd, message, wParam, lParam); } //return (LRESULT)FALSE; } LRESULT CALLBACK DisasmViewViewerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); DisasmView_DoDraw(hdc); EndPaint(hWnd, &ps); } break; case WM_LBUTTONDOWN: DisasmView_OnLButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; case WM_RBUTTONDOWN: DisasmView_OnRButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; case WM_KEYDOWN: return (LRESULT) DisasmView_OnKeyDown(wParam, lParam); case WM_SETFOCUS: case WM_KILLFOCUS: ::InvalidateRect(hWnd, NULL, TRUE); break; case WM_COMMAND: if (wParam == ID_DEBUG_COPY_ADDRESS || wParam == ID_DEBUG_COPY_VALUE) DisasmView_CopyToClipboard(wParam); else // Forward commands to the main window ::PostMessage(g_hwnd, WM_COMMAND, wParam, lParam); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return (LRESULT)FALSE; } BOOL DisasmView_OnKeyDown(WPARAM vkey, LPARAM /*lParam*/) { switch (vkey) { case 0x53: // S - Load/Unload Subtitles DisasmView_LoadUnloadSubtitles(); break; case VK_ESCAPE: ConsoleView_Activate(); break; default: return TRUE; } return FALSE; } void DisasmView_OnLButtonDown(int mousex, int mousey) { ::SetFocus(m_hwndDisasmViewer); if (mousex >= m_cxDisasmBreakpointZone) return; int lineindex = (mousey - 2) / m_cyDisasmLine; if (lineindex < 0 || lineindex >= MAX_DISASMLINECOUNT) return; DisasmLineItem* pLineItem = m_pDisasmLineItems + lineindex; if (pLineItem->type == LINETYPE_NONE) return; // Try to and add/remove breakpoint for the line uint16_t address = pLineItem->address; if (!Emulator_IsBreakpoint(address)) { bool result = Emulator_AddCPUBreakpoint(address); if (!result) AlertWarningFormat(_T("Failed to add breakpoint at %06ho."), address); } else { bool result = Emulator_RemoveCPUBreakpoint(address); if (!result) AlertWarningFormat(_T("Failed to remove breakpoint at %06ho."), address); } DebugView_Redraw(); DisasmView_Redraw(); } void DisasmView_OnRButtonDown(int mousex, int mousey) { // Find out if we have a valid line under the mouse cursor int lineindex = (mousey - 2) / m_cyDisasmLine; DisasmLineItem* pLineItem = nullptr; if (lineindex >= 0 && lineindex < MAX_DISASMLINECOUNT) pLineItem = m_pDisasmLineItems + lineindex; if (pLineItem->type == LINETYPE_NONE) pLineItem = nullptr; m_nDisasmSelectedLineIndex = (pLineItem == nullptr) ? m_nDisasmCurrentLineIndex : lineindex; ::SetFocus(m_hwndDisasmViewer); HMENU hMenu = ::CreatePopupMenu(); if (pLineItem != nullptr) { TCHAR buffer[24]; _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, _T("Copy Address: %06o"), pLineItem->address); ::AppendMenu(hMenu, 0, ID_DEBUG_COPY_ADDRESS, buffer); if ((pLineItem->type & (LINETYPE_DATA | LINETYPE_INSTR)) != 0) // if the item has a valid value { _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, _T("Copy Value: %06o"), pLineItem->value); ::AppendMenu(hMenu, 0, ID_DEBUG_COPY_VALUE, buffer); } ::AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); } ::AppendMenu(hMenu, 0, ID_DEBUG_SUBTITLES, m_okDisasmSubtitles ? _T("Unload Subtitles\tS") : _T("Load Subtitles...\tS")); int linebottom = 2 + m_cyDisasmLine * (m_nDisasmSelectedLineIndex + 1); POINT pt = { mousex, linebottom }; ::ClientToScreen(m_hwndDisasmViewer, &pt); ::TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, m_hwndDisasmViewer, NULL); VERIFY(::DestroyMenu(hMenu)); } void DisasmView_CopyToClipboard(WPARAM command) { if (m_nDisasmSelectedLineIndex < 0 || m_nDisasmSelectedLineIndex >= MAX_DISASMLINECOUNT) return; DisasmLineItem* pLineItem = m_pDisasmLineItems + m_nDisasmSelectedLineIndex; if (pLineItem->type == LINETYPE_NONE) return; uint16_t value; if (command == ID_DEBUG_COPY_ADDRESS) value = pLineItem->address; else value = pLineItem->value; TCHAR buffer[7]; PrintOctalValue(buffer, value); CopyTextToClipboard(buffer); } void DisasmView_UpdateWindowText() { if (m_okDisasmSubtitles) ::SetWindowText(g_hwndDisasm, _T("Disassemble - Subtitles")); else ::SetWindowText(g_hwndDisasm, _T("Disassemble")); } void DisasmView_AddSubtitle(uint16_t addr, int type, LPCTSTR pCommentText) { DisasmSubtitleItem item; item.address = addr; item.type = static_cast(type); item.comment = pCommentText; m_SubtitleItems.push_back(item); } void DisasmView_LoadUnloadSubtitles() { if (m_okDisasmSubtitles) // Reset subtitles { ::free(m_strDisasmSubtitles); m_strDisasmSubtitles = nullptr; m_SubtitleItems.clear(); m_okDisasmSubtitles = FALSE; DisasmView_UpdateWindowText(); DisasmView_OnUpdate(); // We have to re-build the list of lines to show return; } // File Open dialog TCHAR bufFileName[MAX_PATH]; BOOL okResult = ShowOpenDialog(g_hwnd, _T("Open Disassemble Subtitles"), _T("Subtitles (*.lst)\0*.lst\0All Files (*.*)\0*.*\0\0"), bufFileName); if (! okResult) return; // Load subtitles text from the file HANDLE hSubFile = CreateFile(bufFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSubFile == INVALID_HANDLE_VALUE) { AlertWarning(_T("Failed to load subtitles file.")); return; } DWORD dwSubFileSize = ::GetFileSize(hSubFile, NULL); if (dwSubFileSize > 1024 * 1024) { ::CloseHandle(hSubFile); AlertWarning(_T("Subtitles file is too big (over 1 MB).")); return; } m_strDisasmSubtitles = static_cast(::calloc(dwSubFileSize + sizeof(TCHAR), 1)); DWORD dwBytesRead; ::ReadFile(hSubFile, m_strDisasmSubtitles, dwSubFileSize, &dwBytesRead, NULL); ASSERT(dwBytesRead == dwSubFileSize); ::CloseHandle(hSubFile); // Parse subtitles if (!DisasmView_ParseSubtitles()) { ::free(m_strDisasmSubtitles); m_strDisasmSubtitles = nullptr; m_SubtitleItems.clear(); AlertWarning(_T("Failed to parse subtitles file.")); return; } m_okDisasmSubtitles = TRUE; DisasmView_UpdateWindowText(); DisasmView_OnUpdate(); // We have to re-build the list of lines to show } // Разбор текста "субтитров". // На входе -- текст в m_strDisasmSubtitles в формате UTF16 LE, заканчивается символом с кодом 0. // На выходе -- массив описаний [адрес в памяти, тип, адрес строки комментария] в m_SubtitleItems. BOOL DisasmView_ParseSubtitles() { ASSERT(m_strDisasmSubtitles != nullptr); TCHAR* pText = m_strDisasmSubtitles; if (*pText == 0 || *pText == 0xFFFE) // EOF or Unicode Big Endian return FALSE; if (*pText == 0xFEFF) pText++; // Skip Unicode LE mark m_SubtitleItems.clear(); TCHAR* pBlockCommentStart = nullptr; for (;;) // Text reading loop - line by line { // Line starts if (*pText == 0) break; if (*pText == _T('\n') || *pText == _T('\r')) { pText++; continue; } if (*pText >= _T('0') && *pText <= _T('9')) // Цифра -- считаем что это адрес { // Парсим адрес TCHAR* pAddrStart = pText; while (*pText != 0 && *pText >= _T('0') && *pText <= _T('9')) pText++; if (*pText == 0) break; TCHAR chSave = *pText; *pText++ = 0; uint16_t address; ParseOctalValue(pAddrStart, &address); *pText = chSave; if (pBlockCommentStart != nullptr) // На предыдущей строке был комментарий к блоку { // Сохраняем комментарий к блоку в массиве DisasmView_AddSubtitle(address, SUBTYPE_BLOCKCOMMENT, pBlockCommentStart); pBlockCommentStart = nullptr; } // Пропускаем разделители while (*pText != 0 && (*pText == _T(' ') || *pText == _T('\t') || *pText == _T('$') || *pText == _T(':'))) pText++; BOOL okDirective = (*pText == _T('.')); // Ищем начало комментария и конец строки while (*pText != 0 && *pText != _T(';') && *pText != _T('\n') && *pText != _T('\r')) pText++; if (*pText == 0) break; if (*pText == _T('\n') || *pText == _T('\r')) // EOL, комментарий не обнаружен { pText++; if (okDirective) DisasmView_AddSubtitle(address, SUBTYPE_DATA, NULL); continue; } // Нашли начало комментария -- ищем конец строки или файла TCHAR* pCommentStart = pText; while (*pText != 0 && *pText != _T('\n') && *pText != _T('\r')) pText++; // Сохраняем комментарий в массиве DisasmView_AddSubtitle(address, (okDirective ? SUBTYPE_COMMENT | SUBTYPE_DATA : SUBTYPE_COMMENT), pCommentStart); if (*pText == 0) break; *pText = 0; // Обозначаем конец комментария pText++; } else // Не цифра -- пропускаем до конца строки { if (*pText == _T(';')) // Строка начинается с комментария - предположительно, комментарий к блоку pBlockCommentStart = pText; else pBlockCommentStart = nullptr; while (*pText != 0 && *pText != _T('\n') && *pText != _T('\r')) pText++; if (*pText == 0) break; if (*pText == _T('\n') || *pText == _T('\r')) // EOL { *pText = 0; // Обозначаем конец комментария - для комментария к блоку pText++; } } } return TRUE; } const DisasmSubtitleItem* DisasmView_FindSubtitle(uint16_t address, int typemask) { if (m_SubtitleItems.empty()) return nullptr; const DisasmSubtitleItem* pItem = m_SubtitleItems.data(); while (pItem->type != 0) { if (pItem->address > address) return nullptr; if (pItem->address == address && (pItem->type & typemask) != 0) return pItem; ++pItem; } return nullptr; } ////////////////////////////////////////////////////////////////////// // Update after Run or Step void DisasmView_OnUpdate() { CProcessor* pProc = g_pBoard->GetCPU(); ASSERT(pProc != nullptr); m_wDisasmBaseAddr = pProc->GetPC(); ASSERT(m_pDisasmLineItems != nullptr); memset(m_pDisasmLineItems, 0, sizeof(DisasmLineItem) * MAX_DISASMLINECOUNT); m_strDisasmHint[0] = 0; m_strDisasmHint2[0] = 0; uint16_t proccurrent = pProc->GetPC(); uint16_t current = m_wDisasmBaseAddr; uint16_t previous = g_wEmulatorPrevCpuPC; // Read from the processor memory to the buffer const int nWindowSize = 30; uint16_t memory[nWindowSize + 2]; int addrtype[nWindowSize + 2]; for (int idx = 0; idx < nWindowSize; idx++) { memory[idx] = g_pBoard->GetWordView( static_cast(current + idx * 2 - 10), pProc->IsHaltMode(), TRUE, addrtype + idx); } uint16_t address = current - 10; uint16_t disasmfrom = current; if (previous >= address && previous < current) disasmfrom = previous; // Prepare the list of lines in m_pDisasmLineItems int lineindex = 0; int length = 0; for (int index = 0; index < nWindowSize; index++) // Preparing lines { DisasmLineItem* pLineItem = m_pDisasmLineItems + lineindex; pLineItem->address = address; pLineItem->value = memory[index]; pLineItem->addrtype = addrtype[index]; bool okData = false; if (m_okDisasmSubtitles) { // Subtitles - find a comment for a block const DisasmSubtitleItem* pSubItem = DisasmView_FindSubtitle(address, SUBTYPE_BLOCKCOMMENT); if (pSubItem != nullptr && pSubItem->comment != nullptr) { pLineItem->type = LINETYPE_SUBTITLE; pLineItem->pSubItem = pSubItem; // Opening next line lineindex++; if (lineindex >= MAX_DISASMLINECOUNT) break; pLineItem = m_pDisasmLineItems + lineindex; pLineItem->address = address; pLineItem->value = memory[index]; pLineItem->addrtype = addrtype[index]; } // Subtitles - find a comment for an instruction or data pSubItem = DisasmView_FindSubtitle(address, SUBTYPE_COMMENT | SUBTYPE_DATA); if (pSubItem != nullptr && (pSubItem->type & SUBTYPE_DATA) != 0) { okData = true; pLineItem->type |= LINETYPE_DATA; } if (pSubItem != nullptr && (pSubItem->type & SUBTYPE_COMMENT) != 0 && pSubItem->comment != nullptr) { pLineItem->type |= LINETYPE_SUBTITLE; pLineItem->pSubItem = pSubItem; // Строку с субтитром мы можем использовать как опорную для дизассемблера if (disasmfrom > address) disasmfrom = address; } } if ((pLineItem->type & LINETYPE_DATA) == 0) pLineItem->type |= LINETYPE_INSTR; // if it's not a data then an instruction if (address >= disasmfrom && length == 0) { if (okData) // We have non-instruction on the address -- no need to disassemble { length = 1; } else { pLineItem->type |= LINETYPE_INSTR; length = DisassembleInstruction(memory + index, address, pLineItem->strInstr, pLineItem->strArg); if (!m_okDisasmSubtitles) //NOTE: Subtitles can move lines down { if (Disasm_CheckForJump(memory + index, &pLineItem->jumpdelta)) { pLineItem->type |= LINETYPE_JUMP; } if (address == proccurrent) // For current instruction, prepare the instruction hints { m_okDisasmJumpPredict = Disasm_GetJumpConditionHint(memory + index, pProc, g_pBoard, m_strDisasmHint); if (*m_strDisasmHint == 0) // we don't have the jump hint { Disasm_GetInstructionHint(memory + index, pProc, g_pBoard, m_strDisasmHint, m_strDisasmHint2); } } } } } if (length > 0) length--; address += 2; lineindex++; if (lineindex >= MAX_DISASMLINECOUNT) break; } } ////////////////////////////////////////////////////////////////////// // Draw functions void DisasmView_DrawJump(HDC hdc, int yFrom, int delta, int x, int cyLine, COLORREF color) { int dist = abs(delta); if (dist < 2) dist = 2; if (dist > 20) dist = 16; int yTo = yFrom + delta * cyLine; yFrom += cyLine / 2; HPEN hPenJump = ::CreatePen(PS_SOLID, 1, color); HGDIOBJ oldPen = ::SelectObject(hdc, hPenJump); POINT points[4]; points[0].x = x; points[0].y = yFrom; points[1].x = x + dist * 4; points[1].y = yFrom; points[2].x = x + dist * 12; points[2].y = yTo; points[3].x = x; points[3].y = yTo; PolyBezier(hdc, points, 4); MoveToEx(hdc, x - 4, points[3].y, NULL); LineTo(hdc, x + 4, yTo - 1); MoveToEx(hdc, x - 4, points[3].y, NULL); LineTo(hdc, x + 4, yTo + 1); ::SelectObject(hdc, oldPen); VERIFY(::DeleteObject(hPenJump)); } void DisasmView_DoDraw(HDC hdc) { ASSERT(g_pBoard != nullptr); // Create and select font HFONT hFont = CreateMonospacedFont(); HGDIOBJ hOldFont = SelectObject(hdc, hFont); int cxChar, cyLine; GetFontWidthAndHeight(hdc, &cxChar, &cyLine); COLORREF colorOld = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); SetBkMode(hdc, TRANSPARENT); CProcessor* pDisasmPU = g_pBoard->GetCPU(); // Draw disassembly for the current processor uint16_t prevPC = g_wEmulatorPrevCpuPC; int yFocus = DisasmView_DrawDisassemble(hdc, pDisasmPU, m_wDisasmBaseAddr, prevPC); SetTextColor(hdc, colorOld); SelectObject(hdc, hOldFont); VERIFY(::DeleteObject(hFont)); if (::GetFocus() == m_hwndDisasmViewer) { RECT rcFocus; GetClientRect(m_hwndDisasmViewer, &rcFocus); if (yFocus >= 0) { rcFocus.top = yFocus - 1; rcFocus.bottom = yFocus + cyLine; } DrawFocusRect(hdc, &rcFocus); } } void DisasmView_DrawBreakpoint(HDC hdc, int x, int y, int size) { COLORREF colorBreakpoint = Settings_GetColor(ColorDebugBreakpoint); HBRUSH hBreakBrush = CreateSolidBrush(colorBreakpoint); HGDIOBJ hOldBrush = SelectObject(hdc, hBreakBrush); HGDIOBJ hOldPen = SelectObject(hdc, GetStockObject(NULL_PEN)); Ellipse(hdc, x, y, x + size, y + size); ::SelectObject(hdc, hOldPen); ::SelectObject(hdc, hOldBrush); VERIFY(::DeleteObject(hBreakBrush)); } int DisasmView_DrawDisassemble(HDC hdc, const CProcessor* pProc, uint16_t current, uint16_t previous) { int result = -1; m_nDisasmCurrentLineIndex = -1; int cxChar, cyLine; GetFontWidthAndHeight(hdc, &cxChar, &cyLine); int x = 32 + 4 - cxChar * 4; int y = 2; m_cxDisasmBreakpointZone = cxChar * 5 / 2; m_cyDisasmLine = cyLine; COLORREF colorText = Settings_GetColor(ColorDebugText); COLORREF colorPrev = Settings_GetColor(ColorDebugPrevious); COLORREF colorValue = Settings_GetColor(ColorDebugValue); COLORREF colorValueRom = Settings_GetColor(ColorDebugValueRom); COLORREF colorSubtitle = Settings_GetColor(ColorDebugSubtitles); COLORREF colorJump = Settings_GetColor(ColorDebugJump); ::SetTextColor(hdc, colorText); uint16_t proccurrent = pProc->GetPC(); // Draw breakpoint zone COLORREF colorBreakptZone = Settings_GetColor(ColorDebugBreakptZone); HBRUSH hBrushBreakptZone = ::CreateSolidBrush(colorBreakptZone); HGDIOBJ hBrushOld = ::SelectObject(hdc, hBrushBreakptZone); ::PatBlt(hdc, 0, 0, m_cxDisasmBreakpointZone, cyLine * MAX_DISASMLINECOUNT, PATCOPY); ::SelectObject(hdc, hBrushOld); VERIFY(::DeleteObject(hBrushBreakptZone)); // Draw current line background if (!m_okDisasmSubtitles) //NOTE: Subtitles can move lines down { int yCurrent = (proccurrent - (current - 5)) * cyLine; COLORREF colorBackCurr = Settings_GetColor(ColorDebugBackCurrent); HBRUSH hBrushCurrent = ::CreateSolidBrush(colorBackCurr); HGDIOBJ oldBrush = ::SelectObject(hdc, hBrushCurrent); PatBlt(hdc, 0, yCurrent, 1000, cyLine, PATCOPY); ::SelectObject(hdc, oldBrush); VERIFY(::DeleteObject(hBrushCurrent)); } for (int lineindex = 0; lineindex < MAX_DISASMLINECOUNT; lineindex++) // Draw the lines { DisasmLineItem* pLineItem = m_pDisasmLineItems + lineindex; if (pLineItem->type == LINETYPE_NONE) break; uint16_t address = pLineItem->address; if ((pLineItem->type & LINETYPE_SUBTITLE) != 0 && (pLineItem->type & (LINETYPE_DATA | LINETYPE_INSTR)) == 0 && pLineItem->pSubItem != nullptr) // Subtitles - comment for a block { LPCTSTR strBlockSubtitle = pLineItem->pSubItem->comment; ::SetTextColor(hdc, colorSubtitle); TextOut(hdc, x + 21 * cxChar, y, strBlockSubtitle, static_cast(_tcslen(strBlockSubtitle))); ::SetTextColor(hdc, colorText); y += cyLine; continue; } if (Emulator_IsBreakpoint(address)) // Breakpoint { DisasmView_DrawBreakpoint(hdc, cxChar / 2, y, cyLine); } DrawOctalValue(hdc, x + 5 * cxChar, y, address); // Address // Value at the address uint16_t value = pLineItem->value; int memorytype = pLineItem->addrtype; ::SetTextColor(hdc, (memorytype == ADDRTYPE_ROM) ? colorValueRom : colorValue); DrawOctalValue(hdc, x + 13 * cxChar, y, value); ::SetTextColor(hdc, colorText); // Current position if (address == current) { //TextOut(hdc, x + 2 * cxChar, y, _T(" > "), 3); result = y; // Remember line for the focus rect m_nDisasmCurrentLineIndex = lineindex; } if (address == proccurrent) TextOut(hdc, x + 2 * cxChar, y, _T("PC>"), 3); else if (address == previous) { ::SetTextColor(hdc, colorPrev); TextOut(hdc, x + 2 * cxChar, y, _T(" > "), 3); } int posAfterArgs = 30; if ((pLineItem->type & (LINETYPE_DATA | LINETYPE_INSTR)) != 0) { LPCTSTR strInstr = pLineItem->strInstr; LPCTSTR strArg = pLineItem->strArg; ::SetTextColor(hdc, colorText); TextOut(hdc, x + 21 * cxChar, y, strInstr, static_cast(_tcslen(strInstr))); TextOut(hdc, x + 29 * cxChar, y, strArg, static_cast(_tcslen(strArg))); posAfterArgs += _tcslen(strArg); } if ((pLineItem->type & LINETYPE_SUBTITLE) != 0 && (pLineItem->type & (LINETYPE_DATA | LINETYPE_INSTR)) != 0 && pLineItem->pSubItem != nullptr) // Show subtitle comment for instruction or data { LPCTSTR strComment = pLineItem->pSubItem->comment; if (strComment != nullptr) { ::SetTextColor(hdc, colorSubtitle); TextOut(hdc, x + 52 * cxChar, y, strComment, static_cast(_tcslen(strComment))); ::SetTextColor(hdc, colorText); } } if (!m_okDisasmSubtitles) // We don't show jumps and hints with subtitles { bool isjump = (pLineItem->type & LINETYPE_JUMP) != 0; if (isjump) { int delta = pLineItem->jumpdelta; if (abs(delta) < 40) { COLORREF jumpcolor = colorJump; if (address == proccurrent) jumpcolor = Settings_GetColor(m_okDisasmJumpPredict ? ColorDebugJumpYes : ColorDebugJumpNo); DisasmView_DrawJump(hdc, y, delta, x + posAfterArgs * cxChar, cyLine, jumpcolor); } } if (address == proccurrent && *m_strDisasmHint != 0) // For current instruction, draw "Instruction Hints" { COLORREF hintcolor = Settings_GetColor(isjump ? ColorDebugJumpHint : ColorDebugHint); ::SetTextColor(hdc, hintcolor); TextOut(hdc, x + 52 * cxChar, y, m_strDisasmHint, static_cast(_tcslen(m_strDisasmHint))); if (*m_strDisasmHint2 != 0) TextOut(hdc, x + 52 * cxChar, y + cyLine, m_strDisasmHint2, static_cast(_tcslen(m_strDisasmHint2))); ::SetTextColor(hdc, colorText); } } y += cyLine; } return result; } //////////////////////////////////////////////////////////////////////