project64/Source/Project64/UserInterface/Debugger/Debugger-CPULogView.cpp

452 lines
12 KiB
C++

/****************************************************************************
* *
* Project64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#include "stdafx.h"
#include "DebuggerUI.h"
#include "CPULog.h"
#include "Debugger-CPULogView.h"
#include <Project64-core/N64System/Mips/OpCodeName.h>
CDebugCPULogView* CDebugCPULogView::_this = NULL;
HHOOK CDebugCPULogView::hWinMessageHook = NULL;
CDebugCPULogView::CDebugCPULogView(CDebuggerUI* debugger) :
CDebugDialog<CDebugCPULogView>(debugger),
m_CPULogCopy(NULL),
m_LogStartIndex(0),
m_RowHeight(13)
{
}
CDebugCPULogView::~CDebugCPULogView()
{
if (m_CPULogCopy != NULL)
{
delete m_CPULogCopy;
}
}
LRESULT CDebugCPULogView::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
DlgResize_Init(false, true);
DlgToolTip_Init();
DlgSavePos_Init(DebuggerUI_CPULogPos);
m_CPUListView.Attach(GetDlgItem(IDC_CPU_LIST));
m_StateInfoEdit.Attach(GetDlgItem(IDC_STATEINFO_EDIT));
m_EnabledChk.Attach(GetDlgItem(IDC_CHK_ENABLE));
m_BuffSizeEdit.Attach(GetDlgItem(IDC_BUFFSIZE_EDIT));
m_Scrollbar.Attach(GetDlgItem(IDC_SCRL_BAR));
m_ExportBtn.Attach(GetDlgItem(IDC_BTN_EXPORT));
m_CPUListView.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
m_CPUListView.ModifyStyle(LVS_OWNERDRAWFIXED, 0, 0);
m_CPUListView.AddColumn(L"PC", 0);
m_CPUListView.AddColumn(L"Command", 1);
m_CPUListView.AddColumn(L"Parameters", 2);
m_CPUListView.SetColumnWidth(0, 65);
m_CPUListView.SetColumnWidth(1, 60);
m_CPUListView.SetColumnWidth(2, 120);
bool bLoggingEnabled = g_Settings->LoadBool(Debugger_CPULoggingEnabled);
uint32_t bufferSize = g_Settings->LoadDword(Debugger_CPULogBufferSize);
m_EnabledChk.SetCheck(bLoggingEnabled);
m_BuffSizeEdit.SetDisplayType(CEditNumber32::DisplayDec);
m_BuffSizeEdit.SetValue(bufferSize);
m_BuffSizeEdit.EnableWindow(!bLoggingEnabled);
RefreshList(true);
m_ExportBtn.EnableWindow(false);
if (!bLoggingEnabled && m_CPULogCopy != NULL)
{
m_ExportBtn.EnableWindow(true);
}
_this = this;
DWORD dwThreadID = ::GetCurrentThreadId();
hWinMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)HookProc, NULL, dwThreadID);
LoadWindowPos();
WindowCreated();
return TRUE;
}
LRESULT CDebugCPULogView::OnDestroy(void)
{
UnhookWindowsHookEx(hWinMessageHook);
m_CPUListView.Detach();
m_StateInfoEdit.Detach();
m_EnabledChk.Detach();
m_BuffSizeEdit.Detach();
m_Scrollbar.Detach();
m_ExportBtn.Detach();
if (m_CPULogCopy != NULL)
{
delete m_CPULogCopy;
m_CPULogCopy = NULL;
}
return 0;
}
LRESULT CALLBACK CDebugCPULogView::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *pMsg = (MSG*)lParam;
switch (pMsg->message)
{
case WM_MOUSEWHEEL:
_this->InterceptMouseWheel(pMsg->wParam, pMsg->lParam);
break;
}
if (nCode < 0)
{
return CallNextHookEx(hWinMessageHook, nCode, wParam, lParam);
}
return 0;
}
LRESULT CDebugCPULogView::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND, BOOL& /*bHandled*/)
{
switch (wID)
{
case IDOK:
EndDialog(0);
break;
case IDCANCEL:
EndDialog(0);
break;
case IDC_CHK_ENABLE:
ToggleLoggingEnabled();
break;
case IDC_BTN_EXPORT:
Export();
break;
}
return FALSE;
}
LRESULT CDebugCPULogView::OnActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return FALSE;
}
LRESULT CDebugCPULogView::OnListItemChanged(NMHDR* pNMHDR)
{
NMITEMACTIVATE* pIA = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
int nItem = pIA->iItem;
ShowRegStates(m_LogStartIndex + nItem);
return FALSE;
}
LRESULT CDebugCPULogView::OnListDblClicked(NMHDR* pNMHDR)
{
NMITEMACTIVATE* pIA = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
int nItem = pIA->iItem;
CPUState* state = m_CPULogCopy->GetEntry(m_LogStartIndex + nItem);
if (state == NULL)
{
return FALSE;
}
m_Debugger->Debug_ShowCommandsLocation(state->pc, true);
return FALSE;
}
LRESULT CDebugCPULogView::OnMeasureItem(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
if (wParam == IDC_CPU_LIST)
{
CClientDC dc(m_hWnd);
dc.SelectFont(GetFont());
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
m_RowHeight = tm.tmHeight + tm.tmExternalLeading;
MEASUREITEMSTRUCT* lpMeasureItem = (MEASUREITEMSTRUCT*)lParam;
lpMeasureItem->itemHeight = m_RowHeight;
}
return FALSE;
}
LRESULT CDebugCPULogView::OnScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
WORD type = LOWORD(wParam);
HWND hScrollbar = (HWND)lParam;
WORD scrlId = (WORD)::GetDlgCtrlID(hScrollbar);
SCROLLINFO scrollInfo;
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_ALL;
::GetScrollInfo(hScrollbar, SB_CTL, &scrollInfo);
int newPos;
switch (type)
{
case SB_LINEUP: newPos = max(scrollInfo.nMin, scrollInfo.nPos - 1); break;
case SB_LINEDOWN: newPos = min(scrollInfo.nMax, scrollInfo.nPos + 1); break;
case SB_THUMBTRACK: newPos = scrollInfo.nTrackPos; break;
default: return 0;
}
m_LogStartIndex = newPos;
::SetScrollPos(hScrollbar, SB_CTL, newPos, TRUE);
if (scrlId == IDC_SCRL_BAR)
{
RefreshList(false);
}
return 0;
}
void CDebugCPULogView::InterceptMouseWheel(WPARAM wParam, LPARAM /*lParam*/)
{
int nScroll = -((short)HIWORD(wParam) / WHEEL_DELTA);
if (MouseHovering(IDC_CPU_LIST) || MouseHovering(IDC_SCRL_BAR))
{
int scrlMin, scrlMax;
m_Scrollbar.GetScrollRange(&scrlMin, &scrlMax);
int scrollPos = m_Scrollbar.GetScrollPos();
int newPos = scrollPos + nScroll;
if (newPos < scrlMin)
{
newPos = scrlMin;
}
else if (newPos > scrlMax)
{
newPos = scrlMax;
}
m_LogStartIndex = newPos;
m_Scrollbar.SetScrollPos(newPos, true);
RefreshList(false);
}
}
void CDebugCPULogView::OnExitSizeMove(void)
{
RefreshList(false);
SaveWindowPos(true);
}
void CDebugCPULogView::ToggleLoggingEnabled(void)
{
bool bEnableLogging = (m_EnabledChk.GetCheck() == BST_CHECKED);
m_BuffSizeEdit.EnableWindow(!bEnableLogging);
g_Settings->SaveBool(Debugger_CPULoggingEnabled, bEnableLogging);
if (bEnableLogging)
{
uint32_t newSize = m_BuffSizeEdit.GetValue();
g_Settings->SaveDword(Debugger_CPULogBufferSize, newSize);
m_Debugger->CPULog()->Reset();
m_ExportBtn.EnableWindow(false);
}
else
{
RefreshList(true);
m_ExportBtn.EnableWindow(m_CPULogCopy != NULL);
}
}
void CDebugCPULogView::RefreshList(bool bUpdateBuffer)
{
if (bUpdateBuffer)
{
if (m_CPULogCopy != NULL)
{
delete m_CPULogCopy;
}
m_CPULogCopy = m_Debugger->CPULog()->Clone();
}
if (m_CPULogCopy == NULL)
{
return;
}
size_t count = m_CPULogCopy->GetCount();
size_t numVisibleRows = GetNumVisibleRows(m_CPUListView);
bool bCanDisplayAll = (numVisibleRows >= count);
int scrollRangeMax = bCanDisplayAll ? 0 : count - numVisibleRows;
m_Scrollbar.SetScrollRange(0, scrollRangeMax, false);
m_Scrollbar.EnableWindow(!bCanDisplayAll);
if (bUpdateBuffer)
{
m_LogStartIndex = scrollRangeMax;
m_Scrollbar.SetScrollPos(m_LogStartIndex, true);
ShowRegStates(count - 1);
}
size_t start = m_Scrollbar.GetScrollPos();
size_t end = start + numVisibleRows;
if (end > count)
{
end = count;
}
m_CPUListView.SetRedraw(FALSE);
m_CPUListView.DeleteAllItems();
int nItem = 0;
for (size_t i = start; i < end; i++)
{
CPUState* state = m_CPULogCopy->GetEntry(i);
if (state == NULL)
{
break;
}
char szPC[9];
sprintf(szPC, "%08X", state->pc);
char *tokctx;
char* szCommand = (char*)R4300iOpcodeName(state->opcode.Hex, state->pc);
char* szCmdName = strtok_s((char*)szCommand, "\t", &tokctx);
char* szCmdArgs = strtok_s(NULL, "\t", &tokctx);
m_CPUListView.AddItem(nItem, 0, stdstr(szPC).ToUTF16().c_str());
m_CPUListView.AddItem(nItem, 1, stdstr(szCmdName).ToUTF16().c_str());
m_CPUListView.AddItem(nItem, 2, stdstr(szCmdArgs).ToUTF16().c_str());
nItem++;
}
m_CPUListView.SetRedraw(TRUE);
}
void CDebugCPULogView::ShowRegStates(size_t stateIndex)
{
CPUState* state = m_CPULogCopy->GetEntry(stateIndex);
if (state == NULL)
{
return;
}
char szRegStates[2048];
char* out = szRegStates;
out += sprintf(out, "PC: %08X\r\n\r\n", state->pc);
for (int i = 0; i < 16; i++)
{
int regl = i, regr = i + 16;
out += sprintf(out, "%s: %08X %08X %s: %08X %08X\r\n",
CRegName::GPR[regl], state->gpr[regl].UW[1], state->gpr[regl].UW[0],
CRegName::GPR[regr], state->gpr[regr].UW[1], state->gpr[regr].UW[0]);
}
out += sprintf(out, "HI: %08X %08X LO: %08X %08X\r\n\r\n",
state->gprHi.UW[1], state->gprHi.UW[0],
state->gprLo.UW[1], state->gprLo.UW[0]);
for (int i = 0; i < 16; i++)
{
int regl = i, regr = i + 16;
out += sprintf(out, "%-3s: %08X %-3s: %08X\r\n",
CRegName::FPR[regl], *(uint32_t*)&state->fpr[regl],
CRegName::FPR[regr], *(uint32_t*)&state->fpr[regr]);
}
out += sprintf(out, "FPCR: %08X\r\n", state->fpcr);
m_StateInfoEdit.SetWindowText(stdstr(szRegStates).ToUTF16().c_str());
}
void CDebugCPULogView::Export(void)
{
if (m_CPULogCopy == NULL)
{
return;
}
OPENFILENAMEA openfilename;
char filePath[255];
memset(&filePath, 0, sizeof(filePath));
memset(&openfilename, 0, sizeof(openfilename));
sprintf(filePath, "CPULOG.txt");
openfilename.lStructSize = sizeof(openfilename);
openfilename.hwndOwner = (HWND)m_hWnd;
openfilename.lpstrFilter = "CPU Log (*.*)\0*.*;\0";
openfilename.lpstrFile = filePath;
openfilename.lpstrInitialDir = "Logs";
openfilename.nMaxFile = MAX_PATH;
openfilename.Flags = OFN_HIDEREADONLY;
if (GetSaveFileNameA(&openfilename))
{
m_CPULogCopy->DumpToFile(filePath);
}
}
// util
int CDebugCPULogView::GetNumVisibleRows(CListViewCtrl& list)
{
CHeaderCtrl header = list.GetHeader();
CRect listRect, headRect;
list.GetWindowRect(&listRect);
header.GetWindowRect(&headRect);
int innerHeight = listRect.Height() - headRect.Height();
return (innerHeight / m_RowHeight);
}
bool CDebugCPULogView::MouseHovering(WORD ctrlId, int xMargin, int yMargin)
{
CRect rect;
POINT pointerPos;
::GetWindowRect(GetDlgItem(ctrlId), &rect);
::GetCursorPos(&pointerPos);
return (
pointerPos.x >= rect.left - xMargin &&
pointerPos.x <= rect.right + xMargin &&
pointerPos.y >= rect.top - yMargin &&
pointerPos.y <= rect.bottom + yMargin);
}