ppsspp/Windows/Debugger/Debugger_Disasm.cpp
Unknown W. Brackets cbc1ed4bfe Debugger: Lock memory during stack walk.
In case of shutdown during it.
2023-02-22 21:14:33 -08:00

918 lines
24 KiB
C++

#include "Core/Config.h"
#include "Core/MemMap.h"
#include "Windows/resource.h"
#include "Windows/InputBox.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/SymbolMap.h"
#include "Windows/Debugger/BreakpointWindow.h"
#include "Windows/Debugger/CtrlDisAsmView.h"
#include "Windows/Debugger/Debugger_MemoryDlg.h"
#include "Windows/Debugger/Debugger_Disasm.h"
#include "Windows/Debugger/Debugger_VFPUDlg.h"
#include "Windows/Debugger/DebuggerShared.h"
// #include "Windows/W32Util/DarkMode.h"
#include "Windows/main.h"
#include "Windows/Debugger/CtrlRegisterList.h"
#include "Windows/Debugger/CtrlMemView.h"
#include "Windows/Debugger/Debugger_Lists.h"
#include "Windows/MainWindow.h"
#include "Core/Core.h"
#include "Core/HLE/HLE.h"
#include "Core/CoreTiming.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/CommonWindows.h"
#include "Common/StringUtils.h"
#include <windowsx.h>
#include <commctrl.h>
static FAR WNDPROC DefGotoEditProc;
LRESULT CALLBACK GotoEditProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_KEYDOWN:
if( wParam == VK_RETURN )
{
SendMessage(GetParent(hDlg),WM_DEB_GOTOADDRESSEDIT,0,0);
return 0;
}
break;
case WM_KEYUP:
if( wParam == VK_RETURN ) return 0;
break;
case WM_CHAR:
if( wParam == VK_RETURN ) return 0;
break;
case WM_GETDLGCODE:
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
{
if (wParam == VK_RETURN) return DLGC_WANTMESSAGE;
}
break;
};
return (LRESULT)CallWindowProc((WNDPROC)DefGotoEditProc,hDlg,message,wParam,lParam);
}
static FAR WNDPROC DefFuncListProc;
LRESULT CALLBACK FuncListProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_KEYDOWN:
if( wParam == VK_RETURN )
{
SendMessage(GetParent(hDlg),WM_COMMAND,MAKEWPARAM(IDC_FUNCTIONLIST,CBN_DBLCLK),0);
SetFocus(hDlg); // it's more natural to keep the focus when using keyboard controls
return 0;
}
break;
case WM_GETDLGCODE:
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
{
if (wParam == VK_RETURN) return DLGC_WANTMESSAGE;
}
break;
};
return (LRESULT)CallWindowProc((WNDPROC)DefFuncListProc,hDlg,message,wParam,lParam);
}
static constexpr UINT_PTR IDT_UPDATE = 0xC0DE0042;
static constexpr UINT UPDATE_DELAY = 1000 / 60;
CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Dialog((LPCSTR)IDD_DISASM, _hInstance, _hParent) {
cpu = _cpu;
lastTicks = PSP_IsInited() ? CoreTiming::GetTicks() : 0;
SetWindowText(m_hDlg, ConvertUTF8ToWString(_cpu->GetName()).c_str());
RECT windowRect;
GetWindowRect(m_hDlg,&windowRect);
int defaultWidth = windowRect.right-windowRect.left;
int defaultHeight = windowRect.bottom-windowRect.top;
minWidth = defaultWidth - 100;
minHeight = defaultHeight - 200;
int x = g_Config.iDisasmWindowX == -1 ? windowRect.left : g_Config.iDisasmWindowX;
int y = g_Config.iDisasmWindowY == -1 ? windowRect.top : g_Config.iDisasmWindowY;
int w = g_Config.iDisasmWindowW == -1 ? defaultWidth : g_Config.iDisasmWindowW;
int h = g_Config.iDisasmWindowH == -1 ? defaultHeight : g_Config.iDisasmWindowH;
// init status bar
statusBarWnd = CreateWindowEx(0, STATUSCLASSNAME, L"", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, m_hDlg, (HMENU)IDC_DISASMSTATUSBAR, _hInstance, NULL);
if (g_Config.bDisplayStatusBar == false) {
ShowWindow(statusBarWnd,SW_HIDE);
}
// set it to use two parts
RECT statusBarRect;
GetClientRect(statusBarWnd,&statusBarRect);
int parts[2];
parts[1] = statusBarRect.right-statusBarRect.left;
parts[0] = parts[1]*2./3.;
SendMessage(statusBarWnd, SB_SETPARTS, (WPARAM) 2, (LPARAM) parts);
// init other controls
CtrlDisAsmView *ptr = DisAsmView();
ptr->setDebugger(cpu);
ptr->gotoAddr(0x00000000);
CtrlRegisterList *rl = CtrlRegisterList::getFrom(GetDlgItem(m_hDlg,IDC_REGLIST));
rl->setCPU(cpu);
leftTabs = new TabControl(GetDlgItem(m_hDlg,IDC_LEFTTABS));
leftTabs->SetIgnoreBottomMargin(true);
leftTabs->AddTab(GetDlgItem(m_hDlg,IDC_REGLIST),L"Regs");
leftTabs->AddTab(GetDlgItem(m_hDlg,IDC_FUNCTIONLIST),L"Funcs");
leftTabs->ShowTab(0);
// subclass the goto edit box
HWND editWnd = GetDlgItem(m_hDlg,IDC_ADDRESS);
DefGotoEditProc = (WNDPROC)GetWindowLongPtr(editWnd,GWLP_WNDPROC);
SetWindowLongPtr(editWnd,GWLP_WNDPROC,(LONG_PTR)GotoEditProc);
// subclass the function list
HWND funcListWnd = GetDlgItem(m_hDlg,IDC_FUNCTIONLIST);
DefFuncListProc = (WNDPROC)GetWindowLongPtr(funcListWnd,GWLP_WNDPROC);
SetWindowLongPtr(funcListWnd,GWLP_WNDPROC,(LONG_PTR)FuncListProc);
// init bottom tabs
bottomTabs = new TabControl(GetDlgItem(m_hDlg,IDC_DEBUG_BOTTOMTABS));
HWND memHandle = GetDlgItem(m_hDlg,IDC_DEBUGMEMVIEW);
CtrlMemView *mem = CtrlMemView::getFrom(memHandle);
mem->setDebugger(_cpu);
bottomTabs->AddTab(memHandle,L"Memory");
breakpointList = new CtrlBreakpointList(GetDlgItem(m_hDlg,IDC_BREAKPOINTLIST),cpu,ptr);
breakpointList->reloadBreakpoints();
bottomTabs->AddTab(breakpointList->GetHandle(),L"Breakpoints");
threadList = new CtrlThreadList(GetDlgItem(m_hDlg,IDC_THREADLIST));
threadList->reloadThreads();
bottomTabs->AddTab(threadList->GetHandle(),L"Threads");
stackTraceView = new CtrlStackTraceView(GetDlgItem(m_hDlg,IDC_STACKFRAMES),cpu,ptr);
stackTraceView->loadStackTrace();
bottomTabs->AddTab(stackTraceView->GetHandle(),L"Stack frames");
moduleList = new CtrlModuleList(GetDlgItem(m_hDlg,IDC_MODULELIST),cpu);
moduleList->loadModules();
bottomTabs->AddTab(moduleList->GetHandle(),L"Modules");
bottomTabs->SetShowTabTitles(g_Config.bShowBottomTabTitles);
bottomTabs->ShowTab(memHandle);
// Actually resize the window to the proper size (after the above setup.)
// do it twice so that the window definitely receives a WM_SIZE message with
// the correct size (the default from the .rc tends to be off)
MoveWindow(m_hDlg,x,y,1,1,FALSE);
MoveWindow(m_hDlg,x,y,w,h,TRUE);
SetDebugMode(true, true);
}
CDisasm::~CDisasm()
{
DestroyWindow(statusBarWnd);
delete leftTabs;
delete bottomTabs;
delete breakpointList;
delete threadList;
delete stackTraceView;
delete moduleList;
}
void CDisasm::stepInto()
{
if (!PSP_IsInited() || !Core_IsStepping()) {
return;
}
CtrlDisAsmView *ptr = DisAsmView();
lastTicks = CoreTiming::GetTicks();
u32 currentPc = cpu->GetPC();
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);
u32 newAddress = currentPc+ptr->getInstructionSizeAt(currentPc);
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,currentPc);
if (info.isBranch)
{
ptr->scrollStepping(newAddress);
} else {
bool scroll = true;
if (currentMIPS->inDelaySlot)
{
MIPSAnalyst::MipsOpcodeInfo prevInfo = MIPSAnalyst::GetOpcodeInfo(cpu,currentPc-cpu->getInstructionSize(0));
if (!prevInfo.isConditional || prevInfo.conditionMet)
scroll = false;
}
if (scroll)
{
ptr->scrollStepping(newAddress);
}
}
for (u32 i = 0; i < (newAddress-currentPc)/4; i++)
{
Core_DoSingleStep();
Sleep(1);
}
ptr->gotoPC();
UpdateDialog();
if (vfpudlg)
vfpudlg->Update();
threadList->reloadThreads();
stackTraceView->loadStackTrace();
}
void CDisasm::stepOver()
{
if (!PSP_IsInited() || Core_IsActive()) {
return;
}
CtrlDisAsmView *ptr = DisAsmView();
lastTicks = CoreTiming::GetTicks();
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);
u32 currentPc = cpu->GetPC();
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,cpu->GetPC());
ptr->setDontRedraw(true);
u32 breakpointAddress = currentPc+ptr->getInstructionSizeAt(currentPc);
if (info.isBranch)
{
if (info.isConditional == false)
{
if (info.isLinkedBranch) // jal, jalr
{
// it's a function call with a delay slot - skip that too
breakpointAddress += cpu->getInstructionSize(0);
} else { // j, ...
// in case of absolute branches, set the breakpoint at the branch target
breakpointAddress = info.branchTarget;
}
} else { // beq, ...
if (info.conditionMet)
{
breakpointAddress = info.branchTarget;
} else {
breakpointAddress = currentPc+2*cpu->getInstructionSize(0);
ptr->scrollStepping(breakpointAddress);
}
}
} else {
ptr->scrollStepping(breakpointAddress);
}
CBreakPoints::AddBreakPoint(breakpointAddress,true);
Core_EnableStepping(false);
Sleep(1);
ptr->gotoAddr(breakpointAddress);
UpdateDialog();
}
void CDisasm::stepOut() {
auto memLock = Memory::Lock();
if (!PSP_IsInited())
return;
auto threads = GetThreadsInfo();
u32 entry = cpu->GetPC(), stackTop = 0;
for (size_t i = 0; i < threads.size(); i++)
{
if (threads[i].isCurrent)
{
entry = threads[i].entrypoint;
stackTop = threads[i].initialStack;
break;
}
}
auto frames = MIPSStackWalk::Walk(cpu->GetPC(),cpu->GetRegValue(0,31),cpu->GetRegValue(0,29),entry,stackTop);
if (frames.size() < 2) return;
u32 breakpointAddress = frames[1].pc;
lastTicks = CoreTiming::GetTicks();
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);
CtrlDisAsmView *ptr = DisAsmView();
ptr->setDontRedraw(true);
CBreakPoints::AddBreakPoint(breakpointAddress,true);
Core_EnableStepping(false);
Sleep(1);
ptr->gotoAddr(breakpointAddress);
UpdateDialog();
}
void CDisasm::runToLine()
{
if (!PSP_IsInited()) {
return;
}
CtrlDisAsmView *ptr = DisAsmView();
u32 pos = ptr->getSelection();
lastTicks = CoreTiming::GetTicks();
ptr->setDontRedraw(true);
CBreakPoints::AddBreakPoint(pos,true);
Core_EnableStepping(false);
}
BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
{
//if (!m_hDlg) return FALSE;
switch(message)
{
case WM_INITDIALOG:
// DarkModeInitDialog(m_hDlg);
return TRUE;
case WM_NOTIFY:
switch (wParam)
{
case IDC_LEFTTABS:
leftTabs->HandleNotify(lParam);
break;
case IDC_BREAKPOINTLIST:
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, breakpointList->HandleNotify(lParam));
return TRUE;
case IDC_THREADLIST:
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, threadList->HandleNotify(lParam));
return TRUE;
case IDC_STACKFRAMES:
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, stackTraceView->HandleNotify(lParam));
return TRUE;
case IDC_MODULELIST:
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, moduleList->HandleNotify(lParam));
return TRUE;
case IDC_DEBUG_BOTTOMTABS:
bottomTabs->HandleNotify(lParam);
break;
}
break;
case WM_COMMAND:
{
CtrlDisAsmView *ptr = DisAsmView();
switch (LOWORD(wParam)) {
case ID_TOGGLE_BREAK:
SendMessage(MainWindow::GetHWND(), WM_COMMAND, ID_TOGGLE_BREAK, 0);
break;
case ID_DEBUG_DISPLAYMEMVIEW:
bottomTabs->ShowTab(GetDlgItem(m_hDlg,IDC_DEBUGMEMVIEW));
break;
case ID_DEBUG_DISPLAYBREAKPOINTLIST:
bottomTabs->ShowTab(breakpointList->GetHandle());
break;
case ID_DEBUG_DISPLAYTHREADLIST:
bottomTabs->ShowTab(threadList->GetHandle());
break;
case ID_DEBUG_DISPLAYSTACKFRAMELIST:
bottomTabs->ShowTab(stackTraceView->GetHandle());
break;
case ID_DEBUG_DISPLAYREGISTERLIST:
leftTabs->ShowTab(0);
break;
case ID_DEBUG_DISPLAYFUNCTIONLIST:
leftTabs->ShowTab(1);
break;
case ID_DEBUG_ADDBREAKPOINT:
{
CtrlDisAsmView *view = DisAsmView();
keepStatusBarText = true;
view->LockPosition();
bool isRunning = Core_IsActive();
if (isRunning)
{
Core_EnableStepping(true, "cpu.breakpoint.add", 0);
Core_WaitInactive(200);
}
BreakpointWindow bpw(m_hDlg,cpu);
if (bpw.exec()) bpw.addBreakpoint();
if (isRunning)
Core_EnableStepping(false);
view->UnlockPosition();
keepStatusBarText = false;
}
break;
case ID_DEBUG_STEPOVER:
if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) stepOver();
break;
case ID_DEBUG_STEPINTO:
if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) stepInto();
break;
case ID_DEBUG_RUNTOLINE:
if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) runToLine();
break;
case ID_DEBUG_STEPOUT:
if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) stepOut();
break;
case ID_DEBUG_HIDEBOTTOMTABS:
{
RECT rect;
hideBottomTabs = !hideBottomTabs;
GetClientRect(m_hDlg,&rect);
UpdateSize(rect.right-rect.left,rect.bottom-rect.top);
}
break;
case ID_DEBUG_TOGGLEBOTTOMTABTITLES:
bottomTabs->SetShowTabTitles(!bottomTabs->GetShowTabTitles());
break;
case IDC_SHOWVFPU:
MainWindow::CreateVFPUWindow();
vfpudlg->Show(true);
break;
case IDC_FUNCTIONLIST:
switch (HIWORD(wParam))
{
case CBN_DBLCLK:
{
HWND lb = GetDlgItem(m_hDlg,LOWORD(wParam));
int n = ListBox_GetCurSel(lb);
if (n!=-1)
{
unsigned int addr = (unsigned int)ListBox_GetItemData(lb,n);
ptr->gotoAddr(addr);
SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
}
}
break;
case CBN_SELCHANGE:
{
HWND lb = GetDlgItem(m_hDlg,LOWORD(wParam));
int n = ListBox_GetCurSel(lb);
wchar_t buffer[512];
ListBox_GetText(lb,n,buffer);
SendMessage(statusBarWnd,SB_SETTEXT,1,(LPARAM) buffer);
}
};
break;
case IDC_GOTOINT:
switch (HIWORD(wParam))
{
case LBN_SELCHANGE:
{
HWND lb =GetDlgItem(m_hDlg,LOWORD(wParam));
int n = ComboBox_GetCurSel(lb);
unsigned int addr = (unsigned int)ComboBox_GetItemData(lb,n);
if (addr != 0xFFFFFFFF)
{
ptr->gotoAddr(addr);
SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
}
}
break;
};
break;
case IDC_STOPGO:
{
if (!PSP_IsInited()) {
break;
}
if (!Core_IsStepping()) // stop
{
ptr->setDontRedraw(false);
Core_EnableStepping(true, "ui.break", 0);
Sleep(1); //let cpu catch up
ptr->gotoPC();
UpdateDialog();
if (vfpudlg)
vfpudlg->Update();
} else { // go
lastTicks = CoreTiming::GetTicks();
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);
Core_EnableStepping(false);
}
}
break;
case IDC_STEP:
stepInto();
break;
case IDC_STEPOVER:
stepOver();
break;
case IDC_STEPOUT:
stepOut();
break;
case IDC_STEPHLE:
{
if (Core_IsActive())
break;
lastTicks = CoreTiming::GetTicks();
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);
hleDebugBreak();
Core_EnableStepping(false);
}
break;
case IDC_MEMCHECK:
SendMessage(m_hDlg,WM_COMMAND,ID_DEBUG_ADDBREAKPOINT,0);
break;
case IDC_GOTOPC:
{
ptr->gotoPC();
SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
UpdateDialog();
}
break;
case IDC_GOTOLR:
{
ptr->gotoAddr(cpu->GetLR());
SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
}
break;
default:
return FALSE;
}
return TRUE;
}
case WM_DEB_MAPLOADED:
NotifyMapLoaded();
break;
case WM_DEB_GOTOWPARAM:
{
CtrlDisAsmView *ptr = DisAsmView();
ptr->gotoAddr(wParam);
SetFocus(GetDlgItem(m_hDlg,IDC_DISASMVIEW));
break;
}
case WM_DEB_GOTOADDRESSEDIT:
{
if (!PSP_IsInited()) {
break;
}
wchar_t szBuffer[256];
CtrlDisAsmView *ptr = DisAsmView();
GetWindowText(GetDlgItem(m_hDlg,IDC_ADDRESS),szBuffer,256);
u32 addr;
if (parseExpression(ConvertWStringToUTF8(szBuffer).c_str(),cpu,addr) == false)
{
displayExpressionError(GetDlgItem(m_hDlg,IDC_ADDRESS));
} else {
ptr->gotoAddr(addr);
SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
}
UpdateDialog();
}
break;
case WM_DEB_SETDEBUGLPARAM:
SetDebugMode(lParam != 0, true);
return TRUE;
case WM_DEB_UPDATE:
Update();
return TRUE;
case WM_DEB_TABPRESSED:
bottomTabs->NextTab(true);
SetFocus(bottomTabs->CurrentTabHandle());
break;
case WM_DEB_SETSTATUSBARTEXT:
if (!keepStatusBarText)
{
if (wParam == 0)
{
// erase the second part if the first is set
SendMessage(statusBarWnd,SB_SETTEXT,0,(LPARAM)ConvertUTF8ToWString((const char *)lParam).c_str());
SendMessage(statusBarWnd,SB_SETTEXT,1,(LPARAM)L"");
} else if (wParam == 1)
{
SendMessage(statusBarWnd,SB_SETTEXT,1,(LPARAM)ConvertUTF8ToWString((const char *)lParam).c_str());
}
}
break;
case WM_DEB_GOTOHEXEDIT:
{
CtrlMemView *memory = CtrlMemView::getFrom(GetDlgItem(m_hDlg,IDC_DEBUGMEMVIEW));
memory->gotoAddr(wParam);
// display the memory viewer too
bottomTabs->ShowTab(GetDlgItem(m_hDlg,IDC_DEBUGMEMVIEW));
}
break;
case WM_SIZE:
{
UpdateSize(LOWORD(lParam), HIWORD(lParam));
SendMessage(statusBarWnd,WM_SIZE,0,10);
SavePosition();
return TRUE;
}
case WM_MOVE:
SavePosition();
break;
case WM_GETMINMAXINFO:
{
MINMAXINFO *m = (MINMAXINFO *)lParam;
// Reduce the minimum size slightly, so they can size it however they like.
m->ptMinTrackSize.x = minWidth;
//m->ptMaxTrackSize.x = m->ptMinTrackSize.x;
m->ptMinTrackSize.y = minHeight;
}
return TRUE;
case WM_CLOSE:
Show(false);
return TRUE;
case WM_ACTIVATE:
if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE)
{
g_activeWindow = WINDOW_CPUDEBUGGER;
}
break;
case WM_TIMER:
if (wParam == IDT_UPDATE) {
ProcessUpdateDialog();
updateDialogScheduled_ = false;
KillTimer(GetDlgHandle(), wParam);
}
break;
}
return 0; // DarkModeDlgProc(m_hDlg, message, wParam, lParam);
}
void CDisasm::updateThreadLabel(bool clear)
{
char label[512];
if (clear) {
snprintf(label, sizeof(label), "Thread: -");
} else {
snprintf(label, sizeof(label), "Thread: %s", threadList->getCurrentThreadName());
}
SetDlgItemText(m_hDlg, IDC_THREADNAME, ConvertUTF8ToWString(label).c_str());
}
void CDisasm::UpdateSize(WORD width, WORD height)
{
struct Position
{
int x,y;
int w,h;
};
RECT windowRect;
Position positions[3];
HWND disasm = GetDlgItem(m_hDlg, IDC_DISASMVIEW);
HWND leftTabs = GetDlgItem(m_hDlg,IDC_LEFTTABS);
HWND bottomTabs = GetDlgItem(m_hDlg, IDC_DEBUG_BOTTOMTABS);
// ignore the status bar
int topHeightOffset = 0;
if (g_Config.bDisplayStatusBar)
{
GetWindowRect(statusBarWnd,&windowRect);
topHeightOffset = (windowRect.bottom-windowRect.top);
}
CtrlDisAsmView *ptr = DisAsmView();
int disassemblyRowHeight = ptr->getRowHeight();
// disassembly
GetWindowRect(disasm,&windowRect);
MapWindowPoints(HWND_DESKTOP,m_hDlg,(LPPOINT)&windowRect,2);
positions[0].x = windowRect.left;
positions[0].y = windowRect.top;
// compute border height of the disassembly
int totalHeight = windowRect.bottom-windowRect.top;
GetClientRect(disasm,&windowRect);
int clientHeight = windowRect.bottom-windowRect.top;
int borderHeight = totalHeight-clientHeight;
// left tabs
GetWindowRect(leftTabs,&windowRect);
MapWindowPoints(HWND_DESKTOP,m_hDlg,(LPPOINT)&windowRect,2);
positions[1].x = windowRect.left;
positions[1].y = windowRect.top;
positions[1].w = positions[0].x-2*windowRect.left;
int borderMargin = positions[1].x;
float weight = hideBottomTabs ? 1.f : 390.f/500.f;
// don't use the part above the disassembly for the computations
int bottomHeightOffset = positions[0].y;
positions[0].w = width-borderMargin-positions[0].x;
positions[0].h = (height-bottomHeightOffset-topHeightOffset) * weight;
positions[0].h = ((positions[0].h-borderHeight)/disassemblyRowHeight)*disassemblyRowHeight+borderHeight;
positions[1].h = positions[0].h-(positions[1].y-positions[0].y);
// bottom tabs
positions[2].x = borderMargin;
positions[2].y = positions[0].y+positions[0].h+borderMargin;
positions[2].w = width-2*borderMargin;
positions[2].h = hideBottomTabs ? 0 : height-bottomHeightOffset-positions[2].y;
// now actually move all the windows
MoveWindow(disasm,positions[0].x,positions[0].y,positions[0].w,positions[0].h,TRUE);
MoveWindow(leftTabs,positions[1].x,positions[1].y,positions[1].w,positions[1].h,TRUE);
MoveWindow(bottomTabs,positions[2].x,positions[2].y,positions[2].w,positions[2].h,TRUE);
ShowWindow(bottomTabs,hideBottomTabs ? SW_HIDE : SW_NORMAL);
}
void CDisasm::SavePosition()
{
RECT rc;
if (GetWindowRect(m_hDlg, &rc))
{
g_Config.iDisasmWindowX = rc.left;
g_Config.iDisasmWindowY = rc.top;
g_Config.iDisasmWindowW = rc.right - rc.left;
g_Config.iDisasmWindowH = rc.bottom - rc.top;
}
}
void CDisasm::SetDebugMode(bool _bDebug, bool switchPC)
{
HWND hDlg = m_hDlg;
bool ingame = (GetUIState() == UISTATE_INGAME || GetUIState() == UISTATE_EXCEPTION) && PSP_IsInited();
// If we're stepping, update debugging windows.
// This is called potentially asynchronously, so state might've changed.
if (Core_IsStepping() && ingame) {
breakpointList->reloadBreakpoints();
threadList->reloadThreads();
stackTraceView->loadStackTrace();
moduleList->loadModules();
EnableWindow(GetDlgItem(hDlg, IDC_STOPGO), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_STEP), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_STEPOVER), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_STEPHLE), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_STEPOUT), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_GOTOPC), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_GOTOLR), TRUE);
CtrlDisAsmView *ptr = DisAsmView();
ptr->setDontRedraw(false);
if (switchPC)
ptr->gotoPC();
ptr->scanFunctions();
}
else
{
if (ingame)
EnableWindow(GetDlgItem(hDlg, IDC_STOPGO), TRUE);
else
EnableWindow(GetDlgItem(hDlg, IDC_STOPGO), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_STEP), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_STEPOVER), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_STEPHLE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_STEPOUT), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_GOTOPC), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_GOTOLR), FALSE);
CtrlRegisterList *reglist = CtrlRegisterList::getFrom(GetDlgItem(m_hDlg,IDC_REGLIST));
reglist->redraw();
}
UpdateDialog();
}
void CDisasm::Show(bool bShow, bool includeToTop) {
if (deferredSymbolFill_ && bShow) {
if (g_symbolMap) {
g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION);
deferredSymbolFill_ = false;
}
}
Dialog::Show(bShow, includeToTop);
}
void CDisasm::NotifyMapLoaded() {
if (m_bShowState != SW_HIDE && g_symbolMap) {
g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION);
} else {
deferredSymbolFill_ = true;
}
CtrlDisAsmView *ptr = DisAsmView();
ptr->clearFunctions();
ptr->redraw();
}
void CDisasm::Goto(u32 addr)
{
CtrlDisAsmView *ptr = DisAsmView();
ptr->gotoAddr(addr);
SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
ptr->redraw();
}
void CDisasm::UpdateDialog() {
if (!updateDialogScheduled_) {
SetTimer(GetDlgHandle(), IDT_UPDATE, UPDATE_DELAY, nullptr);
updateDialogScheduled_ = true;
}
// Since these update on a delay, it's okay to do them immediately.
CtrlDisAsmView *ptr = DisAsmView();
ptr->redraw();
CtrlRegisterList *rl = CtrlRegisterList::getFrom(GetDlgItem(m_hDlg, IDC_REGLIST));
rl->redraw();
// Repaint windows at the bottom. only the memory view needs to be forced to redraw.
// All others are updated manually
CtrlMemView *memview = CtrlMemView::getFrom(GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW));
memview->redraw();
// Update memory window too.
if (memoryWindow)
memoryWindow->Update();
}
void CDisasm::ProcessUpdateDialog() {
/*
HWND gotoInt = GetDlgItem(m_hDlg, IDC_GOTOINT);
ComboBox_ResetContent(gotoInt);
for (int i=0; i<numRegions; i++)
{
// TODO: wchar_t
int n = ComboBox_AddString(gotoInt,regions[i].name);
ComboBox_SetItemData(gotoInt,n,regions[i].start);
}
ComboBox_InsertString(gotoInt,0,"[Goto Rgn]");
ComboBox_SetItemData(gotoInt,0,0xFFFFFFFF);
ComboBox_SetCurSel(gotoInt,0);
*/
// Update Debug Counter
if (PSP_IsInited()) {
wchar_t tempTicks[24]{};
_snwprintf(tempTicks, 23, L"%lld", CoreTiming::GetTicks() - lastTicks);
SetDlgItemText(m_hDlg, IDC_DEBUG_COUNT, tempTicks);
}
bool ingame = (GetUIState() == UISTATE_INGAME || GetUIState() == UISTATE_EXCEPTION) && PSP_IsInited();
if (Core_IsStepping() || !ingame) {
SetDlgItemText(m_hDlg, IDC_STOPGO, L"Go");
} else {
SetDlgItemText(m_hDlg, IDC_STOPGO, L"Break");
}
updateThreadLabel(!ingame || !Core_IsStepping());
}
CtrlDisAsmView *CDisasm::DisAsmView() {
return CtrlDisAsmView::getFrom(GetDlgItem(m_hDlg, IDC_DISASMVIEW));
}