diff --git a/Source/Project64/Project64.vcxproj b/Source/Project64/Project64.vcxproj index be3f631b9..b04cdb2fa 100644 --- a/Source/Project64/Project64.vcxproj +++ b/Source/Project64/Project64.vcxproj @@ -59,6 +59,7 @@ + @@ -156,6 +157,7 @@ + diff --git a/Source/Project64/Project64.vcxproj.filters b/Source/Project64/Project64.vcxproj.filters index cafd83e27..e8f1f77d8 100644 --- a/Source/Project64/Project64.vcxproj.filters +++ b/Source/Project64/Project64.vcxproj.filters @@ -333,6 +333,9 @@ Source Files\User Interface Source\Debugger Source + + Source Files\User Interface Source\Debugger Source + @@ -596,6 +599,9 @@ Header Files\User Interface Headers\Debugger Headers\ScriptAPI + + Header Files\User Interface Headers\Debugger Headers + diff --git a/Source/Project64/UserInterface/Debugger/Debugger-Commands.cpp b/Source/Project64/UserInterface/Debugger/Debugger-Commands.cpp index 2922c61b2..d5c398640 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger-Commands.cpp +++ b/Source/Project64/UserInterface/Debugger/Debugger-Commands.cpp @@ -659,6 +659,133 @@ void CDebugCommandsView::ShowAddress(uint32_t address, bool top, bool bUserInput m_CommandList.SetRedraw(TRUE); } +void CDebugCommandsView::CopyCommands(uint32_t address, uint32_t count) +{ + const int maximumMnemonicLength = 7 + 1; // The + 1 gets us a space after the mnemonic + std::string buffer; + std::string newline = "\r\n"; + std::string noteBuffer = " "; // Used to distance the note from the args visually + bool any = false; + for (int i = 0; i < count; i++) + { + uint32_t opAddr = address + i * 4; + + COpInfo OpInfo; + R4300iOpcode & OpCode = OpInfo.m_OpCode; + + if (!m_Debugger->DebugLoad_VAddr(opAddr, OpCode.Value)) + { + // Not sure what to do with this + continue; + } + + if (any) buffer.append(newline); + any = true; + + char addrStr[10]; + sprintf(addrStr, "%08X ", opAddr); + + buffer.append(addrStr); + + R4300iInstruction Instruction(opAddr, OpCode.Value); + std::string cmdArgs = Instruction.Param(); + + CSymbol jalSymbol; + + // Show subroutine symbol name for JAL target + if (OpCode.op == R4300i_JAL) + { + uint32_t targetAddr = (m_StartAddress & 0xF0000000) | (OpCode.target << 2); + + if (m_Debugger->SymbolTable()->GetSymbolByAddress(targetAddr, &jalSymbol)) + { + cmdArgs = jalSymbol.m_Name; + } + } + + // Detect reads and writes to mapped registers, cart header data, etc. + const char * annotation = nullptr; + bool bLoadStoreAnnotation = false; + + CSymbol memSymbol; + + if (OpInfo.IsLoadStoreCommand()) + { + for (int offset = -4; offset > -24; offset -= 4) + { + R4300iOpcode OpCodeTest; + + if (!m_Debugger->DebugLoad_VAddr(opAddr + offset, OpCodeTest.Value)) + { + break; + } + + if (OpCodeTest.op != R4300i_LUI) + { + continue; + } + + if (OpCodeTest.rt != OpCode.rs) + { + continue; + } + + uint32_t memAddr = (OpCodeTest.immediate << 16) + (short)OpCode.offset; + + if (m_Debugger->SymbolTable()->GetSymbolByAddress(memAddr, &memSymbol)) + { + annotation = memSymbol.m_Name; + } + else + { + annotation = GetDataAddressNotes(memAddr); + } + break; + } + } + + if (annotation == nullptr) + { + annotation = GetCodeAddressNotes(opAddr); + } + else + { + bLoadStoreAnnotation = true; + } + + std::string instruction = Instruction.Name(); + while (instruction.length() < maximumMnemonicLength) + { + instruction.append(" "); + } + + buffer.append(instruction); + buffer.append(cmdArgs); + + // Show routine symbol name for this address + CSymbol pcSymbol; + if (m_Debugger->SymbolTable()->GetSymbolByAddress(opAddr, &pcSymbol)) + { + buffer.append(noteBuffer); + buffer.append(pcSymbol.m_Name); + } + else if (annotation != nullptr) + { + const char * annotationFormat = bLoadStoreAnnotation ? "// (%s)" : "// %s"; + buffer.append(noteBuffer); + buffer.append(stdstr_f(annotationFormat, annotation)); + } + } + + HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, buffer.length() + 1); + strncpy((char *)GlobalLock(hMem), buffer.c_str(), buffer.length()); + GlobalUnlock(hMem); + OpenClipboard(); + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + CloseClipboard(); +} + // Highlight command list items and draw branch arrows LRESULT CDebugCommandsView::OnCustomDrawList(NMHDR * pNMHDR) { @@ -1199,6 +1326,12 @@ LRESULT CDebugCommandsView::OnPopupmenuInsertNOP(WORD /*wNotifyCode*/, WORD /*wI return FALSE; } +LRESULT CDebugCommandsView::OnPopupmenuCopyCommands(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hwnd*/, BOOL & /*bHandled*/) +{ + m_CopyCommandsDlg.DoModal(m_Debugger, m_SelectedAddress); + return FALSE; +} + LRESULT CDebugCommandsView::OnPopupmenuRestore(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hwnd*/, BOOL & /*bHandled*/) { RestoreOp(m_SelectedAddress); diff --git a/Source/Project64/UserInterface/Debugger/Debugger-Commands.h b/Source/Project64/UserInterface/Debugger/Debugger-Commands.h index d2a8adb5d..4e2b9eec2 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger-Commands.h +++ b/Source/Project64/UserInterface/Debugger/Debugger-Commands.h @@ -77,6 +77,7 @@ public: virtual ~CDebugCommandsView(void); void ShowAddress(uint32_t address, bool top, bool bUserInput = false); + void CopyCommands(uint32_t address, uint32_t count); void ShowPIRegTab(); void Reset(); @@ -110,6 +111,7 @@ private: COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnCancel); COMMAND_HANDLER(ID_POPUPMENU_EDIT, BN_CLICKED, OnPopupmenuEdit); COMMAND_HANDLER(ID_POPUPMENU_INSERTNOP, BN_CLICKED, OnPopupmenuInsertNOP); + COMMAND_HANDLER(ID_POPUPMENU_COPY_COMMANDS, BN_CLICKED, OnPopupmenuCopyCommands); COMMAND_HANDLER(ID_POPUPMENU_RESTORE, BN_CLICKED, OnPopupmenuRestore); COMMAND_HANDLER(ID_POPUPMENU_RESTOREALL, BN_CLICKED, OnPopupmenuRestoreAll); COMMAND_HANDLER(ID_POPUPMENU_ADDSYMBOL, BN_CLICKED, OnPopupmenuAddSymbol); @@ -205,6 +207,7 @@ private: LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); LRESULT OnPopupmenuEdit(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); LRESULT OnPopupmenuInsertNOP(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); + LRESULT OnPopupmenuCopyCommands(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); LRESULT OnPopupmenuRestore(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); LRESULT OnPopupmenuRestoreAll(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); LRESULT OnPopupmenuAddSymbol(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL & bHandled); @@ -265,6 +268,7 @@ private: CAddBreakpointDlg m_AddBreakpointDlg; CAddSymbolDlg m_AddSymbolDlg; + CCopyCommandsDlg m_CopyCommandsDlg; uint32_t m_StartAddress; CEditNumber32 m_PCEdit; diff --git a/Source/Project64/UserInterface/Debugger/Debugger-CopyCommands.cpp b/Source/Project64/UserInterface/Debugger/Debugger-CopyCommands.cpp new file mode 100644 index 000000000..9f619e224 --- /dev/null +++ b/Source/Project64/UserInterface/Debugger/Debugger-CopyCommands.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" + +#include "DebuggerUI.h" +#include "Symbols.h" + +LRESULT CCopyCommandsDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL & /*bHandled*/) +{ + CenterWindow(); + + m_AddressEdit.Attach(GetDlgItem(IDC_ADDR_EDIT)); + m_AddressEdit.SetDisplayType(CEditNumber32::DisplayHex); + m_CountEdit.Attach(GetDlgItem(IDC_COUNT_EDIT)); + m_CountEdit.SetDisplayType(CEditNumber32::DisplayDec); + + m_AddressEdit.SetWindowText(L""); + m_AddressEdit.SetFocus(); + + if (m_bHaveAddress) + { + m_AddressEdit.SetValue(m_InitAddress, DisplayMode::ZeroExtend); + m_CountEdit.SetFocus(); + } + + m_CountEdit.SetValue(m_bHaveCount ? m_InitCount : 1, DisplayMode::None); + + return FALSE; +} + +LRESULT CCopyCommandsDlg::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL & /*bHandled*/) +{ + switch (wID) + { + case IDCANCEL: + EndDialog(0); + break; + case IDOK: + int addrLen = m_AddressEdit.GetWindowTextLength(); + if (!addrLen) + { + MessageBox(L"Address required", L"Error", MB_OK); + return 0; + } + + int countLen = m_CountEdit.GetWindowTextLength(); + if (!countLen) + { + MessageBox(L"Count required", L"Error", MB_OK); + return 0; + } + + uint32_t address = m_AddressEdit.GetValue(); + uint32_t count = m_CountEdit.GetValue(); + + m_Debugger->CopyCommands(address, count); + + EndDialog(0); + break; + } + return 0; +} + +INT_PTR CCopyCommandsDlg::DoModal(CDebuggerUI * debugger) +{ + m_Debugger = debugger; + m_bHaveAddress = false; + m_bHaveCount = false; + return CDialogImpl::DoModal(); +} + +INT_PTR CCopyCommandsDlg::DoModal(CDebuggerUI * debugger, uint32_t initAddress) +{ + m_Debugger = debugger; + m_bHaveAddress = true; + m_bHaveCount = false; + m_InitAddress = initAddress; + return CDialogImpl::DoModal(); +} + +INT_PTR CCopyCommandsDlg::DoModal(CDebuggerUI * debugger, uint32_t initAddress, uint32_t initCount) +{ + m_Debugger = debugger; + m_bHaveAddress = true; + m_bHaveCount = true; + m_InitAddress = initAddress; + m_InitCount = initCount; + return CDialogImpl::DoModal(); +} diff --git a/Source/Project64/UserInterface/Debugger/Debugger-CopyCommands.h b/Source/Project64/UserInterface/Debugger/Debugger-CopyCommands.h new file mode 100644 index 000000000..ee59356b0 --- /dev/null +++ b/Source/Project64/UserInterface/Debugger/Debugger-CopyCommands.h @@ -0,0 +1,40 @@ +#pragma once + +class CCopyCommandsDlg : public CDialogImpl +{ +public: + enum + { + IDD = IDD_Debugger_CopyCommands + }; + + INT_PTR DoModal(CDebuggerUI * debugger); + INT_PTR DoModal(CDebuggerUI * debugger, uint32_t initAddress); + INT_PTR DoModal(CDebuggerUI * debugger, uint32_t initAddress, uint32_t initCount); + +private: + CDebuggerUI * m_Debugger; + + bool m_bHaveAddress; + bool m_bHaveCount; + uint32_t m_InitAddress; + uint32_t m_InitCount; + + CEditNumber32 m_AddressEdit; + CEditNumber32 m_CountEdit; + + LRESULT OnClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL & bHandled); + LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL & /*bHandled*/); + LRESULT OnDestroy(void) + { + return 0; + } + + BEGIN_MSG_MAP_EX(CCopyCommandsDlg) + { + MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog); + COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked); + MSG_WM_DESTROY(OnDestroy); + } + END_MSG_MAP() +}; diff --git a/Source/Project64/UserInterface/Debugger/Debugger.cpp b/Source/Project64/UserInterface/Debugger/Debugger.cpp index af8a59ae5..9dd92ab92 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger.cpp +++ b/Source/Project64/UserInterface/Debugger/Debugger.cpp @@ -786,6 +786,11 @@ void CDebuggerUI::WaitForStep(void) g_Settings->SaveBool(Debugger_WaitingForStep, false); } +void CDebuggerUI::CopyCommands(uint32_t address, uint32_t count) +{ + m_CommandsView->CopyCommands(address, count); +} + void CDebuggerUI::StartAutorunScripts(void) { if (m_ScriptSystem == nullptr) diff --git a/Source/Project64/UserInterface/Debugger/DebuggerUI.h b/Source/Project64/UserInterface/Debugger/DebuggerUI.h index f5a2b1ac0..6c6d69561 100644 --- a/Source/Project64/UserInterface/Debugger/DebuggerUI.h +++ b/Source/Project64/UserInterface/Debugger/DebuggerUI.h @@ -5,6 +5,7 @@ #include "DebugDialog.h" #include "Debugger-AddBreakpoint.h" #include "Debugger-AddSymbol.h" +#include "Debugger-CopyCommands.h" #include "Debugger-CPULogView.h" #include "Debugger-Commands.h" #include "Debugger-DMALogView.h" diff --git a/Source/Project64/UserInterface/Debugger/debugger.h b/Source/Project64/UserInterface/Debugger/debugger.h index ef8bd0105..f18b8e8cc 100644 --- a/Source/Project64/UserInterface/Debugger/debugger.h +++ b/Source/Project64/UserInterface/Debugger/debugger.h @@ -57,6 +57,7 @@ public: void OpenCPULogWindow(void); void Debug_RefreshCPULogWindow(void); void OpenExcBreakpointsWindow(void); + void CopyCommands(uint32_t address, uint32_t count); void StartAutorunScripts(); diff --git a/Source/Project64/UserInterface/UIResources.rc b/Source/Project64/UserInterface/UIResources.rc index 718b2a822..e7f6a893f 100644 --- a/Source/Project64/UserInterface/UIResources.rc +++ b/Source/Project64/UserInterface/UIResources.rc @@ -911,6 +911,18 @@ BEGIN LTEXT "Description",IDC_STATIC,6,30,42,8 END +IDD_Debugger_CopyCommands DIALOGEX 0, 0, 110, 55 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Copy Commands" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_ADDR_EDIT,5,14,45,13,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT IDC_COUNT_EDIT,53,14,49,12, ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,65,35,40,14 + LTEXT "Start Address",IDC_STATIC,5,4,31,10 + LTEXT "Count",IDC_STATIC,53,4,30,9 +END + IDD_Debugger_DMALog DIALOGEX 0, 0, 309, 215 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "DMA Log" @@ -2048,6 +2060,7 @@ BEGIN MENUITEM "Add symbol...", ID_POPUPMENU_ADDSYMBOL MENUITEM SEPARATOR MENUITEM "Replace with NOP", ID_POPUPMENU_INSERTNOP + MENUITEM "Copy commands...", ID_POPUPMENU_COPY_COMMANDS MENUITEM "Edit code", ID_POPUPMENU_EDIT MENUITEM "Restore code", ID_POPUPMENU_RESTORE MENUITEM "Restore all code", ID_POPUPMENU_RESTOREALL diff --git a/Source/Project64/UserInterface/resource.h b/Source/Project64/UserInterface/resource.h index c668de9ef..f1582e596 100644 --- a/Source/Project64/UserInterface/resource.h +++ b/Source/Project64/UserInterface/resource.h @@ -81,6 +81,7 @@ #define IDD_Settings_GameDiskDrive 217 #define IDD_Enhancement_Plugins 218 #define IDD_Debugger_ScriptsAutorun 225 +#define IDD_Debugger_CopyCommands 230 #define IDC_MENU_ITEM_TEXT 1000 #define IDC_CLOSE_BUTTON 1001 #define IDC_LIST2 1003 @@ -861,6 +862,7 @@ #define IDC_RDRAM_SIZE_KNOWN 1738 #define IDC_MEMORY_SIZE_UNKOWN_TEXT 1739 #define IDC_MEMORY_SIZE_KNOWN_TEXT 1740 +#define IDC_COUNT_EDIT 1741 #define ID_POPUPMENU_PLAYGAMEWITHDISK 40008 #define ID_POPUPMENU_ADDSYMBOL 40013 #define ID_POPUPMENU_VIEWDISASM 40017 @@ -871,6 +873,7 @@ #define ID_POPUP_RUN 40024 #define ID_POPUP_STOP 40025 #define ID_POPUPMENU_INSERTNOP 40026 +#define ID_POPUPMENU_COPY_COMMANDS 40200 #define ID_POPUPMENU_RESTORE 40027 #define ID_POPUPMENU_RESTOREALL 40029 #define ID_POPUPMENU_EDIT 40030