mirror of
https://github.com/libretro/Play-.git
synced 2025-01-22 08:41:07 +00:00
97c229ec98
Silence:"warning: '0' flag ignored with precision and ‘%X’ gnu_printf format"
876 lines
21 KiB
C++
876 lines
21 KiB
C++
#include <stdio.h>
|
|
#include <boost/bind.hpp>
|
|
#include "DisAsm.h"
|
|
#include "resource.h"
|
|
#include "win32/InputBox.h"
|
|
#include "win32/Font.h"
|
|
#include "win32/DefaultWndClass.h"
|
|
#include "win32/DpiUtils.h"
|
|
#include "win32/ClientDeviceContext.h"
|
|
#include "string_cast.h"
|
|
#include "string_format.h"
|
|
#include "lexical_cast_ex.h"
|
|
#include "WinUtils.h"
|
|
#include "../Ps2Const.h"
|
|
#include "DebugExpressionEvaluator.h"
|
|
|
|
#define ID_DISASM_GOTOPC 40001
|
|
#define ID_DISASM_GOTOADDRESS 40002
|
|
#define ID_DISASM_GOTOEA 40003
|
|
#define ID_DISASM_EDITCOMMENT 40004
|
|
#define ID_DISASM_FINDCALLERS 40005
|
|
#define ID_DISASM_GOTOPREV 40006
|
|
#define ID_DISASM_GOTONEXT 40007
|
|
|
|
CDisAsm::CDisAsm(HWND parentWnd, const RECT& rect, CVirtualMachine& virtualMachine, CMIPS* ctx)
|
|
: m_virtualMachine(virtualMachine)
|
|
, m_font(Framework::Win32::CreateFont(_T("Courier New"), 8))
|
|
, m_ctx(ctx)
|
|
, m_selected(0)
|
|
, m_selectionEnd(-1)
|
|
, m_address(0)
|
|
, m_instructionSize(4)
|
|
{
|
|
HistoryReset();
|
|
|
|
m_arrowBitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_ARROW));
|
|
m_arrowMaskBitmap = WinUtils::CreateMask(m_arrowBitmap, 0xFF00FF);
|
|
|
|
m_breakpointBitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BREAKPOINT));
|
|
m_breakpointMaskBitmap = WinUtils::CreateMask(m_breakpointBitmap, 0xFF00FF);
|
|
|
|
//Fill in render metrics
|
|
{
|
|
auto fontSize = GetFixedFontSize(m_font);
|
|
m_renderMetrics.leftBarSize = Framework::Win32::PointsToPixels(17);
|
|
m_renderMetrics.xmargin = Framework::Win32::PointsToPixels(18);
|
|
m_renderMetrics.yspace = Framework::Win32::PointsToPixels(3);
|
|
m_renderMetrics.ymargin = Framework::Win32::PointsToPixels(1);
|
|
m_renderMetrics.fontSizeX = fontSize.cx;
|
|
m_renderMetrics.fontSizeY = fontSize.cy;
|
|
m_renderMetrics.xtextStart = m_renderMetrics.xmargin + MulDiv(fontSize.cx, 3, 5);
|
|
}
|
|
|
|
Create(WS_EX_CLIENTEDGE, Framework::Win32::CDefaultWndClass::GetName(), _T(""), WS_VISIBLE | WS_VSCROLL | WS_CHILD, rect, parentWnd, NULL);
|
|
SetClassPtr();
|
|
|
|
m_virtualMachine.OnMachineStateChange.connect(boost::bind(&CDisAsm::OnMachineStateChange, this));
|
|
m_virtualMachine.OnRunningStateChange.connect(boost::bind(&CDisAsm::OnRunningStateChange, this));
|
|
|
|
SCROLLINFO si;
|
|
memset(&si, 0, sizeof(SCROLLINFO));
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.nMin = 0;
|
|
si.nMax = 0x4000;
|
|
si.nPos = 0x2000;
|
|
si.fMask = SIF_RANGE | SIF_POS;
|
|
SetScrollInfo(m_hWnd, SB_VERT, &si, FALSE);
|
|
}
|
|
|
|
CDisAsm::~CDisAsm()
|
|
{
|
|
DeleteObject(m_arrowBitmap);
|
|
DeleteObject(m_arrowMaskBitmap);
|
|
DeleteObject(m_breakpointBitmap);
|
|
DeleteObject(m_breakpointMaskBitmap);
|
|
}
|
|
|
|
void CDisAsm::SetAddress(uint32 address)
|
|
{
|
|
m_address = address;
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::SetCenterAtAddress(uint32 address)
|
|
{
|
|
unsigned int lineCount = GetLineCount();
|
|
m_address = address - (lineCount * m_instructionSize / 2);
|
|
m_address &= ~(m_instructionSize - 1);
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::SetSelectedAddress(uint32 address)
|
|
{
|
|
m_selectionEnd = -1;
|
|
m_selected = address;
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::OnMachineStateChange()
|
|
{
|
|
if(!IsAddressVisible(m_ctx->m_State.nPC))
|
|
{
|
|
m_address = m_ctx->m_State.nPC & ~(m_instructionSize - 1);
|
|
}
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::OnRunningStateChange()
|
|
{
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::GotoAddress()
|
|
{
|
|
if(m_virtualMachine.GetStatus() == CVirtualMachine::RUNNING)
|
|
{
|
|
MessageBeep(-1);
|
|
return;
|
|
}
|
|
|
|
Framework::Win32::CInputBox i(
|
|
_T("Goto Address"),
|
|
_T("Enter new address:"),
|
|
(_T("0x") + lexical_cast_hex<std::tstring>(m_address, 8)).c_str());
|
|
|
|
const TCHAR* sValue = i.GetValue(m_hWnd);
|
|
if(sValue != NULL)
|
|
{
|
|
try
|
|
{
|
|
uint32 nAddress = CDebugExpressionEvaluator::Evaluate(string_cast<std::string>(sValue).c_str(), m_ctx);
|
|
if(nAddress & (m_instructionSize - 1))
|
|
{
|
|
MessageBox(m_hWnd, _T("Invalid address"), NULL, 16);
|
|
return;
|
|
}
|
|
|
|
if(m_address != nAddress)
|
|
{
|
|
HistorySave(m_address);
|
|
}
|
|
|
|
m_address = nAddress;
|
|
Redraw();
|
|
}
|
|
catch(const std::exception& exception)
|
|
{
|
|
std::tstring message = std::tstring(_T("Error evaluating expression: ")) + string_cast<std::tstring>(exception.what());
|
|
MessageBox(m_hWnd, message.c_str(), NULL, 16);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDisAsm::GotoPC()
|
|
{
|
|
if(m_virtualMachine.GetStatus() == CVirtualMachine::RUNNING)
|
|
{
|
|
MessageBeep(-1);
|
|
return;
|
|
}
|
|
|
|
m_address = m_ctx->m_State.nPC;
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::GotoEA()
|
|
{
|
|
if(m_virtualMachine.GetStatus() == CVirtualMachine::RUNNING)
|
|
{
|
|
MessageBeep(-1);
|
|
return;
|
|
}
|
|
uint32 nOpcode = GetInstruction(m_selected);
|
|
if(m_ctx->m_pArch->IsInstructionBranch(m_ctx, m_selected, nOpcode) == MIPS_BRANCH_NORMAL)
|
|
{
|
|
uint32 nAddress = m_ctx->m_pArch->GetInstructionEffectiveAddress(m_ctx, m_selected, nOpcode);
|
|
|
|
if(m_address != nAddress)
|
|
{
|
|
HistorySave(m_address);
|
|
}
|
|
|
|
m_address = nAddress;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
void CDisAsm::EditComment()
|
|
{
|
|
if(m_virtualMachine.GetStatus() == CVirtualMachine::RUNNING)
|
|
{
|
|
MessageBeep(-1);
|
|
return;
|
|
}
|
|
|
|
const char* comment = m_ctx->m_Comments.Find(m_selected);
|
|
std::tstring commentConv;
|
|
|
|
if(comment != nullptr)
|
|
{
|
|
commentConv = string_cast<std::tstring>(comment);
|
|
}
|
|
else
|
|
{
|
|
commentConv = _T("");
|
|
}
|
|
|
|
Framework::Win32::CInputBox i(_T("Edit Comment"), _T("Enter new comment:"), commentConv.c_str());
|
|
const TCHAR* value = i.GetValue(m_hWnd);
|
|
|
|
if(value != nullptr)
|
|
{
|
|
m_ctx->m_Comments.InsertTag(m_selected, string_cast<std::string>(value).c_str());
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
void CDisAsm::FindCallers()
|
|
{
|
|
if(m_virtualMachine.GetStatus() == CVirtualMachine::RUNNING)
|
|
{
|
|
MessageBeep(-1);
|
|
return;
|
|
}
|
|
|
|
FindCallersRequested(m_selected);
|
|
}
|
|
|
|
unsigned int CDisAsm::GetLineCount()
|
|
{
|
|
auto clientRect = GetClientRect();
|
|
unsigned int lineStep = (m_renderMetrics.fontSizeY + m_renderMetrics.yspace);
|
|
unsigned int lines = (clientRect.Bottom() - (m_renderMetrics.ymargin * 2)) / lineStep;
|
|
lines++;
|
|
return lines;
|
|
}
|
|
|
|
bool CDisAsm::IsAddressVisible(uint32 nAddress)
|
|
{
|
|
uint32 nTop = m_address;
|
|
uint32 nBottom = nTop + ((GetLineCount() - 1) * m_instructionSize);
|
|
|
|
if(nAddress < nTop) return false;
|
|
if(nAddress > nBottom) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
CDisAsm::SelectionRangeType CDisAsm::GetSelectionRange()
|
|
{
|
|
if(m_selectionEnd == -1)
|
|
{
|
|
return SelectionRangeType(m_selected, m_selected);
|
|
}
|
|
|
|
if(m_selectionEnd > m_selected)
|
|
{
|
|
return SelectionRangeType(m_selected, m_selectionEnd);
|
|
}
|
|
else
|
|
{
|
|
return SelectionRangeType(m_selectionEnd, m_selected);
|
|
}
|
|
}
|
|
|
|
void CDisAsm::HistoryReset()
|
|
{
|
|
m_historyPosition = -1;
|
|
m_historySize = 0;
|
|
memset(m_history, 0, sizeof(uint32) * HISTORY_STACK_MAX);
|
|
}
|
|
|
|
void CDisAsm::HistorySave(uint32 nAddress)
|
|
{
|
|
if(m_historySize == HISTORY_STACK_MAX)
|
|
{
|
|
memmove(m_history + 1, m_history, HISTORY_STACK_MAX - 1);
|
|
m_historySize--;
|
|
}
|
|
|
|
m_history[m_historySize] = nAddress;
|
|
m_historyPosition = m_historySize;
|
|
m_historySize++;
|
|
}
|
|
|
|
void CDisAsm::HistoryGoBack()
|
|
{
|
|
if(m_historyPosition == -1) return;
|
|
|
|
uint32 address = HistoryGetPrevious();
|
|
m_history[m_historyPosition] = m_address;
|
|
m_address = address;
|
|
|
|
m_historyPosition--;
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::HistoryGoForward()
|
|
{
|
|
if(m_historyPosition == m_historySize) return;
|
|
|
|
uint32 address = HistoryGetNext();
|
|
m_historyPosition++;
|
|
m_history[m_historyPosition] = m_address;
|
|
m_address = address;
|
|
|
|
Redraw();
|
|
}
|
|
|
|
uint32 CDisAsm::HistoryGetPrevious()
|
|
{
|
|
return m_history[m_historyPosition];
|
|
}
|
|
|
|
uint32 CDisAsm::HistoryGetNext()
|
|
{
|
|
if(m_historyPosition == m_historySize) return 0;
|
|
return m_history[m_historyPosition + 1];
|
|
}
|
|
|
|
bool CDisAsm::HistoryHasPrevious()
|
|
{
|
|
return (m_historySize != 0) && (m_historyPosition != -1);
|
|
}
|
|
|
|
bool CDisAsm::HistoryHasNext()
|
|
{
|
|
return (m_historySize != 0) && (m_historyPosition != (m_historySize - 1));
|
|
}
|
|
|
|
void CDisAsm::UpdateMouseSelection(unsigned int nX, unsigned int nY)
|
|
{
|
|
if(nX <= m_renderMetrics.xmargin) return;
|
|
|
|
uint32 nNew = nY / (m_renderMetrics.fontSizeY + m_renderMetrics.yspace);
|
|
nNew = (m_address + (nNew * m_instructionSize));
|
|
|
|
if(GetKeyState(VK_SHIFT) & 0x8000)
|
|
{
|
|
m_selectionEnd = nNew;
|
|
}
|
|
else
|
|
{
|
|
m_selectionEnd = -1;
|
|
if(nNew == m_selected) return;
|
|
m_selected = nNew;
|
|
}
|
|
|
|
Redraw();
|
|
}
|
|
|
|
uint32 CDisAsm::GetAddressAtPosition(unsigned int nX, unsigned int nY)
|
|
{
|
|
uint32 address = nY / (m_renderMetrics.fontSizeY + m_renderMetrics.yspace);
|
|
address = (m_address + (address * m_instructionSize));
|
|
return address;
|
|
}
|
|
|
|
uint32 CDisAsm::GetInstruction(uint32 address)
|
|
{
|
|
//Address translation perhaps?
|
|
return m_ctx->m_pMemoryMap->GetInstruction(address);
|
|
}
|
|
|
|
void CDisAsm::ToggleBreakpoint(uint32 address)
|
|
{
|
|
if(m_virtualMachine.GetStatus() == CVirtualMachine::RUNNING)
|
|
{
|
|
MessageBeep(-1);
|
|
return;
|
|
}
|
|
m_ctx->ToggleBreakpoint(address);
|
|
Redraw();
|
|
}
|
|
|
|
void CDisAsm::UpdatePosition(int delta)
|
|
{
|
|
m_address += delta;
|
|
Redraw();
|
|
}
|
|
|
|
long CDisAsm::OnSetFocus()
|
|
{
|
|
if(m_focus) return TRUE;
|
|
m_focus = true;
|
|
Redraw();
|
|
return TRUE;
|
|
}
|
|
|
|
long CDisAsm::OnKillFocus()
|
|
{
|
|
m_focus = false;
|
|
Redraw();
|
|
return TRUE;
|
|
}
|
|
|
|
long CDisAsm::OnLeftButtonDown(int nX, int nY)
|
|
{
|
|
UpdateMouseSelection(nX, nY);
|
|
SetFocus();
|
|
return FALSE;
|
|
}
|
|
|
|
long CDisAsm::OnRightButtonUp(int nX, int nY)
|
|
{
|
|
POINT pt;
|
|
pt.x = nX;
|
|
pt.y = nY;
|
|
ClientToScreen(m_hWnd, &pt);
|
|
|
|
HMENU hMenu = CreatePopupMenu();
|
|
unsigned int position = BuildContextMenu(hMenu);
|
|
|
|
if(m_selected != MIPS_INVALID_PC)
|
|
{
|
|
uint32 nOpcode = GetInstruction(m_selected);
|
|
if(m_ctx->m_pArch->IsInstructionBranch(m_ctx, m_selected, nOpcode) == MIPS_BRANCH_NORMAL)
|
|
{
|
|
TCHAR sTemp[256];
|
|
uint32 nAddress = m_ctx->m_pArch->GetInstructionEffectiveAddress(m_ctx, m_selected, nOpcode);
|
|
_sntprintf(sTemp, countof(sTemp), _T("Go to 0x%08X"), nAddress);
|
|
InsertMenu(hMenu, position++, MF_BYPOSITION, ID_DISASM_GOTOEA, sTemp);
|
|
}
|
|
}
|
|
|
|
if(HistoryHasPrevious())
|
|
{
|
|
TCHAR sTemp[256];
|
|
_sntprintf(sTemp, countof(sTemp), _T("Go back (0x%08X)"), HistoryGetPrevious());
|
|
InsertMenu(hMenu, position++, MF_BYPOSITION, ID_DISASM_GOTOPREV, sTemp);
|
|
}
|
|
|
|
if(HistoryHasNext())
|
|
{
|
|
TCHAR sTemp[256];
|
|
_sntprintf(sTemp, countof(sTemp), _T("Go forward (0x%08X)"), HistoryGetNext());
|
|
InsertMenu(hMenu, position++, MF_BYPOSITION, ID_DISASM_GOTONEXT, sTemp);
|
|
}
|
|
|
|
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, m_hWnd, NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
long CDisAsm::OnMouseMove(WPARAM nButton, int nX, int nY)
|
|
{
|
|
if(!(nButton & MK_LBUTTON)) return TRUE;
|
|
if(m_focus)
|
|
{
|
|
UpdateMouseSelection(nX, nY);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
long CDisAsm::OnSize(unsigned int nType, unsigned int nX, unsigned int nY)
|
|
{
|
|
CCustomDrawn::OnSize(nType, nX, nY);
|
|
return TRUE;
|
|
}
|
|
|
|
long CDisAsm::OnMouseWheel(int x, int y, short z)
|
|
{
|
|
if(z < 0)
|
|
{
|
|
UpdatePosition(m_instructionSize * 3);
|
|
}
|
|
else
|
|
{
|
|
UpdatePosition(-m_instructionSize * 3);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
long CDisAsm::OnVScroll(unsigned int nType, unsigned int nTrackPos)
|
|
{
|
|
switch(nType)
|
|
{
|
|
case SB_LINEDOWN:
|
|
UpdatePosition(m_instructionSize);
|
|
break;
|
|
case SB_LINEUP:
|
|
UpdatePosition(-m_instructionSize);
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
UpdatePosition(m_instructionSize * 10);
|
|
break;
|
|
case SB_PAGEUP:
|
|
UpdatePosition(-m_instructionSize * 10);
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
long CDisAsm::OnKeyDown(WPARAM nKey, LPARAM)
|
|
{
|
|
switch(nKey)
|
|
{
|
|
case 'C':
|
|
if(GetAsyncKeyState(VK_CONTROL))
|
|
{
|
|
SendMessage(m_hWnd, WM_COPY, 0, 0);
|
|
}
|
|
break;
|
|
case VK_F9:
|
|
ToggleBreakpoint(m_selected);
|
|
break;
|
|
case VK_DOWN:
|
|
m_selected += m_instructionSize;
|
|
if(!IsAddressVisible(m_selected))
|
|
{
|
|
UpdatePosition(m_instructionSize);
|
|
}
|
|
else
|
|
{
|
|
Redraw();
|
|
}
|
|
break;
|
|
case VK_UP:
|
|
m_selected -= m_instructionSize;
|
|
if(!IsAddressVisible(m_selected))
|
|
{
|
|
UpdatePosition(-m_instructionSize);
|
|
}
|
|
else
|
|
{
|
|
Redraw();
|
|
}
|
|
break;
|
|
case VK_RIGHT:
|
|
if (m_selected != MIPS_INVALID_PC)
|
|
{
|
|
uint32 nOpcode = GetInstruction(m_selected);
|
|
if (m_ctx->m_pArch->IsInstructionBranch(m_ctx, m_selected, nOpcode) == MIPS_BRANCH_NORMAL)
|
|
{
|
|
m_address = m_selected; // Ensure history tracks where we came from
|
|
GotoEA();
|
|
SetSelectedAddress(m_address);
|
|
}
|
|
else if (HistoryHasNext()){
|
|
HistoryGoForward();
|
|
SetSelectedAddress(m_address);
|
|
}
|
|
}
|
|
break;
|
|
case VK_LEFT:
|
|
if (HistoryHasPrevious()){
|
|
HistoryGoBack();
|
|
SetSelectedAddress(m_address);
|
|
}
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
long CDisAsm::OnLeftButtonDblClk(int nX, int nY)
|
|
{
|
|
if(nX > m_renderMetrics.xmargin)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
ToggleBreakpoint(GetAddressAtPosition(nX, nY));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
long CDisAsm::OnCommand(unsigned short nID, unsigned short nMsg, HWND hFrom)
|
|
{
|
|
switch(nID)
|
|
{
|
|
case ID_DISASM_GOTOPC:
|
|
GotoPC();
|
|
return FALSE;
|
|
break;
|
|
case ID_DISASM_GOTOADDRESS:
|
|
GotoAddress();
|
|
return FALSE;
|
|
break;
|
|
case ID_DISASM_GOTOEA:
|
|
GotoEA();
|
|
return FALSE;
|
|
break;
|
|
case ID_DISASM_EDITCOMMENT:
|
|
EditComment();
|
|
return FALSE;
|
|
break;
|
|
case ID_DISASM_FINDCALLERS:
|
|
FindCallers();
|
|
return FALSE;
|
|
break;
|
|
case ID_DISASM_GOTOPREV:
|
|
HistoryGoBack();
|
|
break;
|
|
case ID_DISASM_GOTONEXT:
|
|
HistoryGoForward();
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
long CDisAsm::OnCopy()
|
|
{
|
|
std::tstring text;
|
|
|
|
auto selectionRange = GetSelectionRange();
|
|
for(uint32 address = selectionRange.first; address <= selectionRange.second; address += m_instructionSize)
|
|
{
|
|
if(address != selectionRange.first)
|
|
{
|
|
text += _T("\r\n");
|
|
}
|
|
text += GetInstructionDetailsText(address);
|
|
}
|
|
|
|
WinUtils::CopyStringToClipboard(text);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void DrawMaskedBitmap(HDC dstDc, Framework::Win32::CRect& dstRect,
|
|
HBITMAP srcBitmap, HBITMAP srcMaskBitmap, Framework::Win32::CRect& srcRect)
|
|
{
|
|
SetTextColor(dstDc, RGB(0x00, 0x00, 0x00));
|
|
|
|
{
|
|
HDC memDc = CreateCompatibleDC(dstDc);
|
|
SelectObject(memDc, srcMaskBitmap);
|
|
StretchBlt(
|
|
dstDc, dstRect.Left(), dstRect.Top(), dstRect.Width(), dstRect.Height(),
|
|
memDc, srcRect.Left(), srcRect.Top(), srcRect.Width(), srcRect.Height(), SRCAND);
|
|
DeleteDC(memDc);
|
|
}
|
|
|
|
{
|
|
HDC memDc = CreateCompatibleDC(dstDc);
|
|
SelectObject(memDc, srcBitmap);
|
|
StretchBlt(
|
|
dstDc, dstRect.Left(), dstRect.Top(), dstRect.Width(), dstRect.Height(),
|
|
memDc, srcRect.Left(), srcRect.Top(), srcRect.Width(), srcRect.Height(), SRCPAINT);
|
|
DeleteDC(memDc);
|
|
}
|
|
}
|
|
|
|
void CDisAsm::Paint(HDC hDC)
|
|
{
|
|
Framework::Win32::CDeviceContext deviceContext(hDC);
|
|
|
|
RECT rwin = GetClientRect();
|
|
|
|
BitBlt(hDC, 0, 0, rwin.right, rwin.bottom, NULL, 0, 0, WHITENESS);
|
|
|
|
deviceContext.SelectObject(m_font);
|
|
|
|
int lineStep = (m_renderMetrics.fontSizeY + m_renderMetrics.yspace);
|
|
int textOffset = (lineStep - m_renderMetrics.fontSizeY) / 2;
|
|
|
|
int lines = (rwin.bottom - (m_renderMetrics.ymargin * 2)) / lineStep;
|
|
lines++;
|
|
|
|
RECT rmarg;
|
|
SetRect(&rmarg, 0, 0, m_renderMetrics.leftBarSize, rwin.bottom);
|
|
FillRect(hDC, &rmarg, (HBRUSH)COLOR_WINDOW);
|
|
|
|
Framework::Win32::CPen ltGrayPen = CreatePen(PS_SOLID, Framework::Win32::PointsToPixels(2), RGB(0x40, 0x40, 0x40));
|
|
|
|
//Draw the margin border line
|
|
{
|
|
Framework::Win32::CPen pen = CreatePen(PS_SOLID, 0, RGB(0x80, 0x80, 0x80));
|
|
SelectObject(hDC, pen);
|
|
MoveToEx(hDC, m_renderMetrics.leftBarSize, 0, NULL);
|
|
LineTo(hDC, m_renderMetrics.leftBarSize, rwin.bottom);
|
|
}
|
|
|
|
SetBkMode(hDC, TRANSPARENT);
|
|
|
|
unsigned int y = m_renderMetrics.ymargin;
|
|
|
|
SelectionRangeType SelectionRange = GetSelectionRange();
|
|
|
|
for(int i = 0; i < lines; i++)
|
|
{
|
|
uint32 address = m_address + (i * m_instructionSize);
|
|
|
|
auto iconArea = Framework::Win32::CRect(0, y, m_renderMetrics.xmargin, y + lineStep);
|
|
|
|
//Draw breakpoint icon
|
|
if(m_ctx->m_breakpoints.find(address) != std::end(m_ctx->m_breakpoints))
|
|
{
|
|
auto breakpointSrcRect = Framework::Win32::MakeRectPositionSize(0, 0, 15, 15);
|
|
auto breakpointDstRect = Framework::Win32::PointsToPixels(breakpointSrcRect).CenterInside(iconArea);
|
|
DrawMaskedBitmap(hDC, breakpointDstRect, m_breakpointBitmap, m_breakpointMaskBitmap, breakpointSrcRect);
|
|
}
|
|
|
|
//Draw current instruction arrow
|
|
if(m_virtualMachine.GetStatus() != CVirtualMachine::RUNNING)
|
|
{
|
|
if(address == m_ctx->m_State.nPC)
|
|
{
|
|
auto arrowSrcRect = Framework::Win32::MakeRectPositionSize(0, 0, 13, 13);
|
|
auto arrowDstRect = Framework::Win32::PointsToPixels(arrowSrcRect).CenterInside(iconArea);
|
|
DrawMaskedBitmap(hDC, arrowDstRect, m_arrowBitmap, m_arrowMaskBitmap, arrowSrcRect);
|
|
}
|
|
}
|
|
|
|
bool selected = false;
|
|
|
|
if(
|
|
(address >= SelectionRange.first) &&
|
|
(address <= SelectionRange.second)
|
|
)
|
|
{
|
|
RECT rsel;
|
|
SetRect(&rsel, m_renderMetrics.xmargin, y, rwin.right, y + lineStep);
|
|
if(m_focus)
|
|
{
|
|
FillRect(hDC, &rsel, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
}
|
|
else
|
|
{
|
|
FillRect(hDC, &rsel, (HBRUSH)GetStockObject(GRAY_BRUSH));
|
|
}
|
|
SetTextColor(hDC, RGB(0xFF, 0xFF, 0xFF));
|
|
selected = true;
|
|
}
|
|
else
|
|
{
|
|
SetTextColor(hDC, RGB(0x00, 0x00, 0x00));
|
|
}
|
|
|
|
//Draw address
|
|
deviceContext.TextOut(m_renderMetrics.xtextStart, y + textOffset,
|
|
string_format(_T("%08X"), address).c_str());
|
|
|
|
//Draw function boundaries
|
|
const auto* sub = m_ctx->m_analysis->FindSubroutine(address);
|
|
if(sub != nullptr)
|
|
{
|
|
SelectObject(hDC, ltGrayPen);
|
|
if(address == sub->start)
|
|
{
|
|
MoveToEx(hDC, GetFuncBoundaryPosition(), y + lineStep, NULL);
|
|
LineTo(hDC, GetFuncBoundaryPosition(), y + (lineStep / 2) - 1);
|
|
LineTo(hDC, GetFuncBoundaryPosition() + m_renderMetrics.fontSizeX * 0.5f, y + (lineStep / 2));
|
|
}
|
|
else if(address == sub->end)
|
|
{
|
|
MoveToEx(hDC, GetFuncBoundaryPosition(), y, NULL);
|
|
LineTo(hDC, GetFuncBoundaryPosition(), y + (lineStep / 2));
|
|
LineTo(hDC, GetFuncBoundaryPosition() + m_renderMetrics.fontSizeX * 0.5f, y + (lineStep / 2));
|
|
}
|
|
else
|
|
{
|
|
MoveToEx(hDC, GetFuncBoundaryPosition(), y, NULL);
|
|
LineTo(hDC, GetFuncBoundaryPosition(), y + lineStep);
|
|
}
|
|
}
|
|
|
|
DrawInstructionDetails(deviceContext, address, y + textOffset);
|
|
DrawInstructionMetadata(deviceContext, address, y + textOffset, selected);
|
|
|
|
y += lineStep;
|
|
}
|
|
}
|
|
|
|
unsigned int CDisAsm::BuildContextMenu(HMENU menuHandle)
|
|
{
|
|
unsigned int position = 0;
|
|
InsertMenu(menuHandle, position++, MF_BYPOSITION, ID_DISASM_GOTOPC, _T("Goto PC"));
|
|
InsertMenu(menuHandle, position++, MF_BYPOSITION, ID_DISASM_GOTOADDRESS, _T("Goto Address..."));
|
|
InsertMenu(menuHandle, position++, MF_BYPOSITION, ID_DISASM_EDITCOMMENT, _T("Edit Comment..."));
|
|
InsertMenu(menuHandle, position++, MF_BYPOSITION, ID_DISASM_FINDCALLERS, _T("Find Callers"));
|
|
return position;
|
|
}
|
|
|
|
std::tstring CDisAsm::GetInstructionDetailsText(uint32 address)
|
|
{
|
|
uint32 opcode = GetInstruction(address);
|
|
|
|
std::tstring result;
|
|
|
|
result += lexical_cast_hex<std::tstring>(address, 8) + _T(" ");
|
|
result += lexical_cast_hex<std::tstring>(opcode, 8) + _T(" ");
|
|
|
|
char disasm[256];
|
|
m_ctx->m_pArch->GetInstructionMnemonic(m_ctx, address, opcode, disasm, countof(disasm));
|
|
|
|
result += string_cast<std::tstring>(disasm);
|
|
for(size_t j = strlen(disasm); j < 15; j++)
|
|
{
|
|
result += _T(" ");
|
|
}
|
|
|
|
m_ctx->m_pArch->GetInstructionOperands(m_ctx, address, opcode, disasm, countof(disasm));
|
|
result += string_cast<std::tstring>(disasm);
|
|
|
|
return result;
|
|
}
|
|
|
|
unsigned int CDisAsm::GetMetadataPosition() const
|
|
{
|
|
return m_renderMetrics.fontSizeX * 55;
|
|
}
|
|
|
|
unsigned int CDisAsm::GetFuncBoundaryPosition() const
|
|
{
|
|
unsigned int addressEnd = m_renderMetrics.xtextStart + (8 * m_renderMetrics.fontSizeX);
|
|
unsigned int instructionStart = 16 * m_renderMetrics.fontSizeX;
|
|
|
|
return ((instructionStart + addressEnd) / 2);
|
|
}
|
|
|
|
void CDisAsm::DrawInstructionDetails(Framework::Win32::CDeviceContext& deviceContext, uint32 address, int y)
|
|
{
|
|
uint32 data = GetInstruction(address);
|
|
deviceContext.TextOut(m_renderMetrics.fontSizeX * 15, y, lexical_cast_hex<std::tstring>(data, 8).c_str());
|
|
|
|
{
|
|
char disAsm[256];
|
|
m_ctx->m_pArch->GetInstructionMnemonic(m_ctx, address, data, disAsm, 256);
|
|
deviceContext.TextOut(m_renderMetrics.fontSizeX * 25, y, string_cast<std::tstring>(disAsm).c_str());
|
|
}
|
|
|
|
{
|
|
char disAsm[256];
|
|
m_ctx->m_pArch->GetInstructionOperands(m_ctx, address, data, disAsm, 256);
|
|
deviceContext.TextOut(m_renderMetrics.fontSizeX * 35, y, string_cast<std::tstring>(disAsm).c_str());
|
|
}
|
|
}
|
|
|
|
void CDisAsm::DrawInstructionMetadata(Framework::Win32::CDeviceContext& deviceContext, uint32 address, int y, bool selected)
|
|
{
|
|
bool commentDrawn = false;
|
|
unsigned int metadataPosition = GetMetadataPosition();
|
|
|
|
//Draw function name
|
|
{
|
|
const char* tag = m_ctx->m_Functions.Find(address);
|
|
if(tag != nullptr)
|
|
{
|
|
std::tstring disAsm = _T("@") + string_cast<std::tstring>(tag);
|
|
deviceContext.TextOut(metadataPosition, y, disAsm.c_str(), selected ? RGB(0x40, 0x80, 0xFF) : RGB(0x00, 0x00, 0x80));
|
|
commentDrawn = true;
|
|
}
|
|
}
|
|
|
|
//Draw target function call if applicable
|
|
if(!commentDrawn)
|
|
{
|
|
uint32 opcode = GetInstruction(address);
|
|
if(m_ctx->m_pArch->IsInstructionBranch(m_ctx, address, opcode) == MIPS_BRANCH_NORMAL)
|
|
{
|
|
uint32 effAddr = m_ctx->m_pArch->GetInstructionEffectiveAddress(m_ctx, address, opcode);
|
|
const char* tag = m_ctx->m_Functions.Find(effAddr);
|
|
if(tag != nullptr)
|
|
{
|
|
std::tstring disAsm = _T("-> ") + string_cast<std::tstring>(tag);
|
|
deviceContext.TextOut(metadataPosition, y, disAsm.c_str(), selected ? RGB(0x40, 0x80, 0xFF) : RGB(0x00, 0x00, 0x80));
|
|
commentDrawn = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Draw comment
|
|
if(!commentDrawn)
|
|
{
|
|
const char* tag = m_ctx->m_Comments.Find(address);
|
|
if(tag != nullptr)
|
|
{
|
|
std::tstring disAsm = _T(";") + string_cast<std::tstring>(tag);
|
|
deviceContext.TextOut(metadataPosition, y, disAsm.c_str(), selected ? RGB(0x00, 0xF0, 0x00) : RGB(0x00, 0x80, 0x00));
|
|
commentDrawn = true;
|
|
}
|
|
}
|
|
}
|