From babd8868d9e3f1f603775892d4185436fb2f49a8 Mon Sep 17 00:00:00 2001 From: gigaherz Date: Fri, 21 Feb 2014 14:29:13 +0000 Subject: [PATCH] Initial debugger work by Kingcom. Features: - Advanced disassembly view for R5900 and R3000 - Register list with change highlight - Editable memory view - Conditional execute breakpoints (r5900 only) - Step over - Scan for functions (incomplete), show macros - Enable C++11 for debug tools. - Expression parser - Disasm updates for thread safety Squashed from: https://github.com/PCSX2/pcsx2/pull/1 Thanks to Kingcom for coding it all and mziab for Linux patches git-svn-id: http://pcsx2.googlecode.com/svn/trunk@5905 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/CDVD/CDVD.cpp | 1 + pcsx2/CMakeLists.txt | 34 +- pcsx2/DebugTools/Breakpoints.cpp | 397 +++++++ pcsx2/DebugTools/Breakpoints.h | 166 +++ pcsx2/DebugTools/Debug.h | 6 + pcsx2/DebugTools/DebugInterface.cpp | 639 ++++++++++ pcsx2/DebugTools/DebugInterface.h | 100 ++ pcsx2/DebugTools/DisASM.h | 20 +- pcsx2/DebugTools/DisR5900asm.cpp | 62 +- pcsx2/DebugTools/DisassemblyManager.cpp | 1015 ++++++++++++++++ pcsx2/DebugTools/DisassemblyManager.h | 198 ++++ pcsx2/DebugTools/ExpressionParser.cpp | 594 ++++++++++ pcsx2/DebugTools/ExpressionParser.h | 28 + pcsx2/DebugTools/MIPSAnalyst.cpp | 395 +++++++ pcsx2/DebugTools/MIPSAnalyst.h | 56 + pcsx2/DebugTools/SymbolMap.cpp | 704 +++++++++++ pcsx2/DebugTools/SymbolMap.h | 137 +++ pcsx2/Elfheader.cpp | 15 + pcsx2/Elfheader.h | 2 + pcsx2/R5900OpcodeImpl.cpp | 12 +- pcsx2/R5900OpcodeTables.cpp | 30 +- pcsx2/R5900OpcodeTables.h | 32 +- pcsx2/System/SysCoreThread.cpp | 10 + pcsx2/System/SysThreadBase.cpp | 16 + pcsx2/System/SysThreads.h | 4 +- pcsx2/gui/App.h | 16 +- pcsx2/gui/AppCoreThread.cpp | 12 +- pcsx2/gui/AppCoreThread.h | 1 + pcsx2/gui/AppInit.cpp | 5 + pcsx2/gui/AppMain.cpp | 23 + pcsx2/gui/Debugger/BreakpointWindow.cpp | 332 ++++++ pcsx2/gui/Debugger/BreakpointWindow.h | 48 + pcsx2/gui/Debugger/CtrlDisassemblyView.cpp | 1053 +++++++++++++++++ pcsx2/gui/Debugger/CtrlDisassemblyView.h | 89 ++ pcsx2/gui/Debugger/CtrlMemView.h | 46 + pcsx2/gui/Debugger/CtrlRegisterList.cpp | 422 +++++++ pcsx2/gui/Debugger/CtrlRegisterList.h | 72 ++ pcsx2/gui/Debugger/DebugEvents.h | 14 + pcsx2/gui/Debugger/DisassemblyDialog.cpp | 287 +++++ pcsx2/gui/Debugger/DisassemblyDialog.h | 61 + pcsx2/gui/MainFrame.cpp | 2 +- pcsx2/gui/MainMenuClicks.cpp | 4 + pcsx2/windows/VCprojects/pcsx2.vcxproj | 42 + .../windows/VCprojects/pcsx2.vcxproj.filters | 81 ++ pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj | 42 + .../VCprojects/pcsx2_vs2012.vcxproj.filters | 81 ++ pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj | 42 + .../VCprojects/pcsx2_vs2013.vcxproj.filters | 81 ++ pcsx2/x86/ix86-32/iR5900-32.cpp | 41 + 49 files changed, 7481 insertions(+), 89 deletions(-) create mode 100644 pcsx2/DebugTools/Breakpoints.cpp create mode 100644 pcsx2/DebugTools/Breakpoints.h create mode 100644 pcsx2/DebugTools/DebugInterface.cpp create mode 100644 pcsx2/DebugTools/DebugInterface.h create mode 100644 pcsx2/DebugTools/DisassemblyManager.cpp create mode 100644 pcsx2/DebugTools/DisassemblyManager.h create mode 100644 pcsx2/DebugTools/ExpressionParser.cpp create mode 100644 pcsx2/DebugTools/ExpressionParser.h create mode 100644 pcsx2/DebugTools/MIPSAnalyst.cpp create mode 100644 pcsx2/DebugTools/MIPSAnalyst.h create mode 100644 pcsx2/DebugTools/SymbolMap.cpp create mode 100644 pcsx2/DebugTools/SymbolMap.h create mode 100644 pcsx2/gui/Debugger/BreakpointWindow.cpp create mode 100644 pcsx2/gui/Debugger/BreakpointWindow.h create mode 100644 pcsx2/gui/Debugger/CtrlDisassemblyView.cpp create mode 100644 pcsx2/gui/Debugger/CtrlDisassemblyView.h create mode 100644 pcsx2/gui/Debugger/CtrlMemView.h create mode 100644 pcsx2/gui/Debugger/CtrlRegisterList.cpp create mode 100644 pcsx2/gui/Debugger/CtrlRegisterList.h create mode 100644 pcsx2/gui/Debugger/DebugEvents.h create mode 100644 pcsx2/gui/Debugger/DisassemblyDialog.cpp create mode 100644 pcsx2/gui/Debugger/DisassemblyDialog.h diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index 9246e0b963..f2a68d90b7 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -361,6 +361,7 @@ static __fi void _reloadElfInfo(wxString elfpath) ElfCRC = elfptr->getCRC(); ElfEntry = elfptr->header.e_entry; + ElfTextRange = elfptr->getTextRange(); Console.WriteLn( Color_StrongBlue, L"ELF (%s) Game CRC = 0x%08X, EntryPoint = 0x%08X", elfpath.c_str(), ElfCRC, ElfEntry); // Note: Do not load game database info here. This code is generic and called from diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index cb16d294ae..d948597962 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -261,6 +261,12 @@ set(pcsx2CDVDHeaders # DebugTools sources set(pcsx2DebugToolsSources + DebugTools/DebugInterface.cpp + DebugTools/DisassemblyManager.cpp + DebugTools/ExpressionParser.cpp + DebugTools/MIPSAnalyst.cpp + DebugTools/Breakpoints.cpp + DebugTools/SymbolMap.cpp DebugTools/DisR3000A.cpp DebugTools/DisR5900asm.cpp DebugTools/DisR5900.cpp @@ -269,6 +275,12 @@ set(pcsx2DebugToolsSources # DebugTools headers set(pcsx2DebugToolsHeaders + DebugTools/DebugInterface.h + DebugTools/DisassemblyManager.h + DebugTools/ExpressionParser.h + DebugTools/MIPSAnalyst.h + DebugTools/Breakpoints.h + DebugTools/SymbolMap.h DebugTools/Debug.h DebugTools/DisASm.h DebugTools/DisVUmicro.h @@ -307,6 +319,12 @@ set(pcsx2GuiSources gui/Dialogs/PickUserModeDialog.cpp gui/Dialogs/StuckThreadDialog.cpp gui/Dialogs/SysConfigDialog.cpp + gui/Debugger/BreakpointWindow.cpp + gui/Debugger/CtrlDisassemblyView.cpp + gui/Debugger/CtrlRegisterList.cpp + gui/Debugger/CtrlMemView.cpp + gui/Debugger/DisassemblyDialog.cpp + gui/Debugger/DebugEvents.cpp gui/ExecutorThread.cpp gui/FrameForGS.cpp gui/GlobalCommands.cpp @@ -356,6 +374,12 @@ set(pcsx2GuiHeaders gui/Dialogs/ConfigurationDialog.h gui/Dialogs/LogOptionsDialog.h gui/Dialogs/ModalPopups.h + gui/Debugger/BreakpointWindow.h + gui/Debugger/CtrlDisassemblyView.h + gui/Debugger/CtrlRegisterList.h + gui/Debugger/CtrlMemView.h + gui/Debugger/DisassemblyDialog.h + gui/Debugger/DebugEvents.h gui/i18n.h gui/IsoDropTarget.h gui/MainFrame.h @@ -387,6 +411,8 @@ set(pcsx2GuiResources ${res_bin}/ConfigIcon_Speedhacks.h ${res_bin}/ConfigIcon_Video.h ${res_bin}/Dualshock.h + ${res_bin}/Breakpoint_Active.h + ${res_bin}/Breakpoint_Inactive.h ) # IPU sources @@ -586,6 +612,9 @@ set(pcsx2SSources # change language of .S-files to c++ set_source_files_properties(${pcsx2SSources} PROPERTIES LANGUAGE CXX) +# C++11 is used for debug tools, so we enable it +set_source_files_properties(${pcsx2DebugToolsSources} PROPERTIES COMPILE_FLAGS "-std=c++0x") + # common Sources set(Common ${pcsx2Sources} @@ -593,7 +622,6 @@ set(Common ${pcsx2CDVDSources} ${pcsx2CDVDHeaders} ${pcsx2DebugToolsSources} - ${pcsx2DebugToolsSources} ${pcsx2GuiSources} ${pcsx2GuiResources} ${pcsx2GuiHeaders} @@ -652,12 +680,12 @@ add_custom_command(OUTPUT "${res_bin}/Dualshock.h" COMMAND perl ${PROJECT_SOUR foreach(res_file IN ITEMS AppIcon16 AppIcon32 AppIcon64 BackgroundLogo ButtonIcon_Camera ConfigIcon_Appearance ConfigIcon_Cpu ConfigIcon_Gamefixes ConfigIcon_MemoryCard - ConfigIcon_Paths ConfigIcon_Plugins ConfigIcon_Speedhacks ConfigIcon_Video) + ConfigIcon_Paths ConfigIcon_Plugins ConfigIcon_Speedhacks ConfigIcon_Video Breakpoint_Active Breakpoint_Inactive) add_custom_command(OUTPUT "${res_bin}/${res_file}.h" COMMAND perl ${PROJECT_SOURCE_DIR}/linux_various/hex2h.pl "${res_src}/${res_file}.png" "${res_bin}/${res_file}" ) endforeach(res_file IN ITEMS AppIcon16 AppIcon32 AppIcon64 BackgroundLogo ButtonIcon_Camera ConfigIcon_Appearance ConfigIcon_Cpu ConfigIcon_Gamefixes ConfigIcon_MemoryCard - ConfigIcon_Paths ConfigIcon_Plugins ConfigIcon_Speedhacks ConfigIcon_Video) + ConfigIcon_Paths ConfigIcon_Plugins ConfigIcon_Speedhacks ConfigIcon_Video Breakpoint_Active Breakpoint_Inactive) # Suppress all the system-specific predefined macros outside the reserved namespace. # Needed when stringifying macros. diff --git a/pcsx2/DebugTools/Breakpoints.cpp b/pcsx2/DebugTools/Breakpoints.cpp new file mode 100644 index 0000000000..7ed37ff69d --- /dev/null +++ b/pcsx2/DebugTools/Breakpoints.cpp @@ -0,0 +1,397 @@ +#include "PrecompiledHeader.h" +#include "Breakpoints.h" +#include "SymbolMap.h" +#include "MIPSAnalyst.h" +#include +#include "../R5900.h" +#include "../System.h" + +std::vector CBreakPoints::breakPoints_; +u32 CBreakPoints::breakSkipFirstAt_ = 0; +u64 CBreakPoints::breakSkipFirstTicks_ = 0; +std::vector CBreakPoints::memChecks_; +std::vector CBreakPoints::cleanupMemChecks_; + +MemCheck::MemCheck() +{ + numHits = 0; +} + +void MemCheck::Log(u32 addr, bool write, int size, u32 pc) +{ +} + +void MemCheck::Action(u32 addr, bool write, int size, u32 pc) +{ + int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ; + if (cond & mask) + { + ++numHits; + + Log(addr, write, size, pc); + if (result & MEMCHECK_BREAK) + { + // Core_EnableStepping(true); + // host->SetDebugMode(true); + } + } +} + +void MemCheck::JitBefore(u32 addr, bool write, int size, u32 pc) +{ + int mask = MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE; + if (write && (cond & mask) == mask) + { + lastAddr = addr; + lastPC = pc; + lastSize = size; + + // We have to break to find out if it changed. + //Core_EnableStepping(true); + } + else + { + lastAddr = 0; + Action(addr, write, size, pc); + } +} + +void MemCheck::JitCleanup() +{ + if (lastAddr == 0 || lastPC == 0) + return; + /* + // Here's the tricky part: would this have changed memory? + // Note that it did not actually get written. + bool changed = MIPSAnalyst::OpWouldChangeMemory(lastPC, lastAddr); + if (changed) + { + ++numHits; + Log(lastAddr, true, lastSize, lastPC); + } + + // Resume if it should not have gone to stepping, or if it did not change. + if ((!(result & MEMCHECK_BREAK) || !changed) && coreState == CORE_STEPPING) + { + CBreakPoints::SetSkipFirst(lastPC); + Core_EnableStepping(false); + } + else + host->SetDebugMode(true);*/ +} + +size_t CBreakPoints::FindBreakpoint(u32 addr, bool matchTemp, bool temp) +{ + for (size_t i = 0; i < breakPoints_.size(); ++i) + { + if (breakPoints_[i].addr == addr && (!matchTemp || breakPoints_[i].temporary == temp)) + return i; + } + + return INVALID_BREAKPOINT; +} + +size_t CBreakPoints::FindMemCheck(u32 start, u32 end) +{ + for (size_t i = 0; i < memChecks_.size(); ++i) + { + if (memChecks_[i].start == start && memChecks_[i].end == end) + return i; + } + + return INVALID_MEMCHECK; +} + +bool CBreakPoints::IsAddressBreakPoint(u32 addr) +{ + size_t bp = FindBreakpoint(addr); + return bp != INVALID_BREAKPOINT && breakPoints_[bp].enabled; +} + +bool CBreakPoints::IsAddressBreakPoint(u32 addr, bool* enabled) +{ + size_t bp = FindBreakpoint(addr); + if (bp == INVALID_BREAKPOINT) return false; + if (enabled != NULL) *enabled = breakPoints_[bp].enabled; + return true; +} + +bool CBreakPoints::IsTempBreakPoint(u32 addr) +{ + size_t bp = FindBreakpoint(addr, true, true); + return bp != INVALID_BREAKPOINT; +} + +void CBreakPoints::AddBreakPoint(u32 addr, bool temp) +{ + size_t bp = FindBreakpoint(addr, true, temp); + if (bp == INVALID_BREAKPOINT) + { + BreakPoint pt; + pt.enabled = true; + pt.temporary = temp; + pt.addr = addr; + + breakPoints_.push_back(pt); + Update(addr); + } + else if (!breakPoints_[bp].enabled) + { + breakPoints_[bp].enabled = true; + breakPoints_[bp].hasCond = false; + Update(addr); + } +} + +void CBreakPoints::RemoveBreakPoint(u32 addr) +{ + size_t bp = FindBreakpoint(addr); + if (bp != INVALID_BREAKPOINT) + { + breakPoints_.erase(breakPoints_.begin() + bp); + + // Check again, there might've been an overlapping temp breakpoint. + bp = FindBreakpoint(addr); + if (bp != INVALID_BREAKPOINT) + breakPoints_.erase(breakPoints_.begin() + bp); + + Update(addr); + } +} + +void CBreakPoints::ChangeBreakPoint(u32 addr, bool status) +{ + size_t bp = FindBreakpoint(addr); + if (bp != INVALID_BREAKPOINT) + { + breakPoints_[bp].enabled = status; + Update(addr); + } +} + +void CBreakPoints::ClearAllBreakPoints() +{ + if (!breakPoints_.empty()) + { + breakPoints_.clear(); + Update(); + } +} + +void CBreakPoints::ClearTemporaryBreakPoints() +{ + if (breakPoints_.empty()) + return; + + for (int i = (int)breakPoints_.size()-1; i >= 0; --i) + { + if (breakPoints_[i].temporary) + { + Update(breakPoints_[i].addr); + breakPoints_.erase(breakPoints_.begin() + i); + } + } +} + +void CBreakPoints::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond) +{ + size_t bp = FindBreakpoint(addr, true, false); + if (bp != INVALID_BREAKPOINT) + { + breakPoints_[bp].hasCond = true; + breakPoints_[bp].cond = cond; + Update(); + } +} + +void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr) +{ + size_t bp = FindBreakpoint(addr, true, false); + if (bp != INVALID_BREAKPOINT) + { + breakPoints_[bp].hasCond = false; + Update(); + } +} + +BreakPointCond *CBreakPoints::GetBreakPointCondition(u32 addr) +{ + size_t bp = FindBreakpoint(addr, true, false); + if (bp != INVALID_BREAKPOINT && breakPoints_[bp].hasCond) + return &breakPoints_[bp].cond; + return NULL; +} + +void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, MemCheckResult result) +{ + // This will ruin any pending memchecks. + cleanupMemChecks_.clear(); + + size_t mc = FindMemCheck(start, end); + if (mc == INVALID_MEMCHECK) + { + MemCheck check; + check.start = start; + check.end = end; + check.cond = cond; + check.result = result; + + memChecks_.push_back(check); + Update(); + } + else + { + memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond); + memChecks_[mc].result = (MemCheckResult)(memChecks_[mc].result | result); + Update(); + } +} + +void CBreakPoints::RemoveMemCheck(u32 start, u32 end) +{ + // This will ruin any pending memchecks. + cleanupMemChecks_.clear(); + + size_t mc = FindMemCheck(start, end); + if (mc != INVALID_MEMCHECK) + { + memChecks_.erase(memChecks_.begin() + mc); + Update(); + } +} + +void CBreakPoints::ChangeMemCheck(u32 start, u32 end, MemCheckCondition cond, MemCheckResult result) +{ + size_t mc = FindMemCheck(start, end); + if (mc != INVALID_MEMCHECK) + { + memChecks_[mc].cond = cond; + memChecks_[mc].result = result; + Update(); + } +} + +void CBreakPoints::ClearAllMemChecks() +{ + // This will ruin any pending memchecks. + cleanupMemChecks_.clear(); + + if (!memChecks_.empty()) + { + memChecks_.clear(); + Update(); + } +} + +static inline u32 NotCached(u32 val) +{ + // Remove the cached part of the address. + return val & ~0x40000000; +} + +MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) +{ + std::vector::iterator iter; + for (iter = memChecks_.begin(); iter != memChecks_.end(); ++iter) + { + MemCheck &check = *iter; + if (check.end != 0) + { + if (NotCached(address + size) > NotCached(check.start) && NotCached(address) < NotCached(check.end)) + return ✓ + } + else + { + if (NotCached(check.start) == NotCached(address)) + return ✓ + } + } + + //none found + return 0; +} + +void CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc) +{ + auto check = GetMemCheck(address, size); + if (check) + check->Action(address, write, size, pc); +} + +void CBreakPoints::ExecMemCheckJitBefore(u32 address, bool write, int size, u32 pc) +{ + auto check = GetMemCheck(address, size); + if (check) { + check->JitBefore(address, write, size, pc); + cleanupMemChecks_.push_back(check); + } +} + +void CBreakPoints::ExecMemCheckJitCleanup() +{ + for (auto it = cleanupMemChecks_.begin(), end = cleanupMemChecks_.end(); it != end; ++it) { + auto check = *it; + check->JitCleanup(); + } + cleanupMemChecks_.clear(); +} + +void CBreakPoints::SetSkipFirst(u32 pc) +{ + breakSkipFirstAt_ = pc; +// breakSkipFirstTicks_ = CoreTiming::GetTicks(); +} +u32 CBreakPoints::CheckSkipFirst(u32 cmpPc) +{ + u32 pc = breakSkipFirstAt_; + if (pc == cmpPc) + return 1; + return 0; +} + +const std::vector CBreakPoints::GetMemCheckRanges() +{ + std::vector ranges = memChecks_; + for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it) + { + MemCheck check = *it; + // Toggle the cached part of the address. + check.start ^= 0x40000000; + if (check.end != 0) + check.end ^= 0x40000000; + ranges.push_back(check); + } + + return ranges; +} + +const std::vector CBreakPoints::GetMemChecks() +{ + return memChecks_; +} + +const std::vector CBreakPoints::GetBreakpoints() +{ + return breakPoints_; +} + +void CBreakPoints::Update(u32 addr) +{ + bool resume = false; + if (r5900Debug.isCpuPaused() == false) + { + r5900Debug.pauseCpu(); + resume = true; + } + + if (addr != 0) + Cpu->Clear(addr-4,8); + else + SysClearExecutionCache(); + + if (resume) + r5900Debug.resumeCpu(); + + // Redraw in order to show the breakpoint. + // host->UpdateDisassembly(); +} diff --git a/pcsx2/DebugTools/Breakpoints.h b/pcsx2/DebugTools/Breakpoints.h new file mode 100644 index 0000000000..a136396987 --- /dev/null +++ b/pcsx2/DebugTools/Breakpoints.h @@ -0,0 +1,166 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include + +#include "DebugInterface.h" +#include "Pcsx2Types.h" + +struct BreakPointCond +{ + DebugInterface *debug; + PostfixExpression expression; + char expressionString[128]; + + BreakPointCond() : debug(NULL) + { + expressionString[0] = '\0'; + } + + u32 Evaluate() + { + u64 result; + if (debug->parseExpression(expression,result) == false || result == 0) return 0; + return 1; + } +}; + +struct BreakPoint +{ + BreakPoint() : hasCond(false) {} + + u32 addr; + bool enabled; + bool temporary; + + bool hasCond; + BreakPointCond cond; + + bool operator == (const BreakPoint &other) const { + return addr == other.addr; + } + bool operator < (const BreakPoint &other) const { + return addr < other.addr; + } +}; + +enum MemCheckCondition +{ + MEMCHECK_READ = 0x01, + MEMCHECK_WRITE = 0x02, + MEMCHECK_WRITE_ONCHANGE = 0x04, + + MEMCHECK_READWRITE = 0x03, +}; + +enum MemCheckResult +{ + MEMCHECK_IGNORE = 0x00, + MEMCHECK_LOG = 0x01, + MEMCHECK_BREAK = 0x02, + + MEMCHECK_BOTH = 0x03, +}; + +struct MemCheck +{ + MemCheck(); + u32 start; + u32 end; + + MemCheckCondition cond; + MemCheckResult result; + + u32 numHits; + + u32 lastPC; + u32 lastAddr; + int lastSize; + + void Action(u32 addr, bool write, int size, u32 pc); + void JitBefore(u32 addr, bool write, int size, u32 pc); + void JitCleanup(); + + void Log(u32 addr, bool write, int size, u32 pc); + + bool operator == (const MemCheck &other) const { + return start == other.start && end == other.end; + } +}; + +// BreakPoints cannot overlap, only one is allowed per address. +// MemChecks can overlap, as long as their ends are different. +// WARNING: MemChecks are not used in the interpreter or HLE currently. +class CBreakPoints +{ +public: + static const size_t INVALID_BREAKPOINT = -1; + static const size_t INVALID_MEMCHECK = -1; + + static bool IsAddressBreakPoint(u32 addr); + static bool IsAddressBreakPoint(u32 addr, bool* enabled); + static bool IsTempBreakPoint(u32 addr); + static void AddBreakPoint(u32 addr, bool temp = false); + static void RemoveBreakPoint(u32 addr); + static void ChangeBreakPoint(u32 addr, bool enable); + static void ClearAllBreakPoints(); + static void ClearTemporaryBreakPoints(); + + // Makes a copy. Temporary breakpoints can't have conditions. + static void ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond); + static void ChangeBreakPointRemoveCond(u32 addr); + static BreakPointCond *GetBreakPointCondition(u32 addr); + + static void AddMemCheck(u32 start, u32 end, MemCheckCondition cond, MemCheckResult result); + static void RemoveMemCheck(u32 start, u32 end); + static void ChangeMemCheck(u32 start, u32 end, MemCheckCondition cond, MemCheckResult result); + static void ClearAllMemChecks(); + + static MemCheck *GetMemCheck(u32 address, int size); + static void ExecMemCheck(u32 address, bool write, int size, u32 pc); + + // Executes memchecks but used by the jit. Cleanup finalizes after jit is done. + static void ExecMemCheckJitBefore(u32 address, bool write, int size, u32 pc); + static void ExecMemCheckJitCleanup(); + + static void SetSkipFirst(u32 pc); + static u32 CheckSkipFirst(u32 pc); + + // Includes uncached addresses. + static const std::vector GetMemCheckRanges(); + + static const std::vector GetMemChecks(); + static const std::vector GetBreakpoints(); + + static void Update(u32 addr = 0); + +private: + static size_t FindBreakpoint(u32 addr, bool matchTemp = false, bool temp = false); + // Finds exactly, not using a range check. + static size_t FindMemCheck(u32 start, u32 end); + + static std::vector breakPoints_; + static u32 breakSkipFirstAt_; + static u64 breakSkipFirstTicks_; + + static std::vector memChecks_; + static std::vector cleanupMemChecks_; +}; + + diff --git a/pcsx2/DebugTools/Debug.h b/pcsx2/DebugTools/Debug.h index 501e1d0820..9c73d35771 100644 --- a/pcsx2/DebugTools/Debug.h +++ b/pcsx2/DebugTools/Debug.h @@ -16,6 +16,7 @@ #pragma once #include "Utilities/TraceLog.h" +#include "../Memory.h" extern FILE *emuLog; extern wxString emuLogName; @@ -29,6 +30,9 @@ extern const char * const CP2VFnames[]; extern const char * const disRNameCP2f[]; extern const char * const disRNameCP2i[]; +extern const char * const disRNameCP2f[]; +extern const char * const disRNameCP2i[]; + namespace R5900 { // [TODO] : These function names can be de-obfuscated with the help of a little namespace love. @@ -42,6 +46,8 @@ namespace R5900 void dFindSym( std::string& output, u32 addr ); extern const char * const disRNameGPR[]; + extern const char * const disRNameCP0[]; + extern const char * const disRNameCP1[]; // A helper class for getting a quick and efficient string representation of the // R5900's current instruction. This class is *not* thread safe! diff --git a/pcsx2/DebugTools/DebugInterface.cpp b/pcsx2/DebugTools/DebugInterface.cpp new file mode 100644 index 0000000000..36da5fa34d --- /dev/null +++ b/pcsx2/DebugTools/DebugInterface.cpp @@ -0,0 +1,639 @@ +#include "PrecompiledHeader.h" + +#include "DebugInterface.h" +#include "Memory.h" +#include "R5900.h" +#include "AppCoreThread.h" +#include "Debug.h" +#include "../VU.h" + +#include "../R3000A.h" +#include "../IopMem.h" +#include "SymbolMap.h" + +extern AppCoreThread CoreThread; + +R5900DebugInterface r5900Debug; +R3000DebugInterface r3000Debug; + +enum { EECAT_GPR, EECAT_CP0, EECAT_CP1, EECAT_CP2F, EECAT_CP2I, EECAT_COUNT }; +enum { IOPCAT_GPR, IOPCAT_COUNT }; + +#ifdef WIN32 +#define strcasecmp stricmp +#endif + +enum ReferenceIndexType { + REF_INDEX_PC = 32, + REF_INDEX_HI = 33, + REF_INDEX_LO = 34, + REF_INDEX_FPU = 0x1000, + REF_INDEX_FPU_INT = 0x2000, + REF_INDEX_VFPU = 0x4000, + REF_INDEX_VFPU_INT = 0x8000, + REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU, +}; + + +class MipsExpressionFunctions: public IExpressionFunctions +{ +public: + MipsExpressionFunctions(DebugInterface* cpu): cpu(cpu) { }; + + virtual bool parseReference(char* str, u64& referenceIndex) + { + for (int i = 0; i < 32; i++) + { + char reg[8]; + sprintf(reg, "r%d", i); + + if (strcasecmp(str, reg) == 0 || strcasecmp(str, cpu->getRegisterName(0, i)) == 0) + { + referenceIndex = i; + return true; + } + } + + if (strcasecmp(str, "pc") == 0) + { + referenceIndex = REF_INDEX_PC; + return true; + } + + if (strcasecmp(str, "hi") == 0) + { + referenceIndex = REF_INDEX_HI; + return true; + } + + if (strcasecmp(str, "lo") == 0) + { + referenceIndex = REF_INDEX_LO; + return true; + } + + return false; + } + + virtual bool parseSymbol(char* str, u64& symbolValue) + { + u32 value; + bool result = symbolMap.GetLabelValue(str,value); + symbolValue = value; + return result; + } + + virtual u64 getReferenceValue(u64 referenceIndex) + { + if (referenceIndex < 32) + return cpu->getRegister(0, referenceIndex)._u64[0]; + if (referenceIndex == REF_INDEX_PC) + return cpu->getPC(); + if (referenceIndex == REF_INDEX_HI) + return cpu->getHI()._u64[0]; + if (referenceIndex == REF_INDEX_LO) + return cpu->getLO()._u64[0]; + return -1; + } + + virtual ExpressionType getReferenceType(u64 referenceIndex) { + if (referenceIndex & REF_INDEX_IS_FLOAT) { + return EXPR_TYPE_FLOAT; + } + return EXPR_TYPE_UINT; + } + + virtual bool getMemoryValue(u32 address, int size, u64& dest, char* error) + { + switch (size) + { + case 1: case 2: case 4: case 8: + break; + default: + sprintf(error,"Invalid memory access size %d",size); + return false; + } + + if (address % size) + { + sprintf(error,"Invalid memory access (unaligned)"); + return false; + } + + switch (size) + { + case 1: + dest = cpu->read8(address); + break; + case 2: + dest = cpu->read16(address); + break; + case 4: + dest = cpu->read32(address); + break; + case 8: + dest = cpu->read64(address); + break; + } + + return true; + } + +private: + DebugInterface* cpu; +}; + +// +// DebugInterface +// + +bool DebugInterface::isAlive() +{ + return GetCoreThread().IsOpen(); +} + +bool DebugInterface::isCpuPaused() +{ + return GetCoreThread().IsPaused(); +} + +void DebugInterface::pauseCpu() +{ + SysCoreThread& core = GetCoreThread(); + if (!core.IsPaused()) + core.Pause(); +} + +void DebugInterface::resumeCpu() +{ + SysCoreThread& core = GetCoreThread(); + if (core.IsPaused()) + core.Resume(); +} + +bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest) +{ + MipsExpressionFunctions funcs(this); + return initPostfixExpression(exp,&funcs,dest); +} + +bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest) +{ + MipsExpressionFunctions funcs(this); + return parsePostfixExpression(exp,&funcs,dest); +} + + +// +// R5900DebugInterface +// + +u32 R5900DebugInterface::read8(u32 address) +{ + if (!isValidAddress(address)) + return -1; + return memRead8(address); +} + +u32 R5900DebugInterface::read16(u32 address) +{ + if (!isValidAddress(address)) + return -1; + return memRead16(address); +} + +u32 R5900DebugInterface::read32(u32 address) +{ + if (!isValidAddress(address)) + return -1; + return memRead32(address); +} + +u64 R5900DebugInterface::read64(u32 address) +{ + if (!isValidAddress(address)) + return -1; + + u64 result; + memRead64(address,result); + return result; +} + +u128 R5900DebugInterface::read128(u32 address) +{ + if (!isValidAddress(address)) + return u128::From32(-1); + + u128 result; + memRead128(address,result); + return result; +} + +void R5900DebugInterface::write8(u32 address, u8 value) +{ + if (!isValidAddress(address)) + return; + + memWrite8(address,value); +} + +int R5900DebugInterface::getRegisterCategoryCount() +{ + return EECAT_COUNT; +} + +const char* R5900DebugInterface::getRegisterCategoryName(int cat) +{ + switch (cat) + { + case EECAT_GPR: + return "GPR"; + case EECAT_CP0: + return "CP0"; + case EECAT_CP1: + return "CP1"; + case EECAT_CP2F: + return "CP2f"; + case EECAT_CP2I: + return "CP2i"; + default: + return "Invalid"; + } +} + +int R5900DebugInterface::getRegisterSize(int cat) +{ + switch (cat) + { + case EECAT_GPR: + case EECAT_CP2F: + return 128; + case EECAT_CP0: + case EECAT_CP1: + case EECAT_CP2I: + return 32; + default: + return 0; + } +} + +int R5900DebugInterface::getRegisterCount(int cat) +{ + switch (cat) + { + case EECAT_GPR: + return 35; // 32 + pc + hi + lo + case EECAT_CP0: + case EECAT_CP1: + case EECAT_CP2F: + case EECAT_CP2I: + return 32; + default: + return 0; + } +} + +DebugInterface::RegisterType R5900DebugInterface::getRegisterType(int cat) +{ + switch (cat) + { + case EECAT_GPR: + case EECAT_CP0: + case EECAT_CP2F: + case EECAT_CP2I: + default: + return NORMAL; + case EECAT_CP1: + return SPECIAL; + } +} + +const char* R5900DebugInterface::getRegisterName(int cat, int num) +{ + switch (cat) + { + case EECAT_GPR: + switch (num) + { + case 32: // pc + return "pc"; + case 33: // hi + return "hi"; + case 34: // lo + return "lo"; + default: + return R5900::disRNameGPR[num]; + } + case EECAT_CP0: + return R5900::disRNameCP0[num]; + case EECAT_CP1: + return R5900::disRNameCP1[num]; + case EECAT_CP2F: + return disRNameCP2f[num]; + case EECAT_CP2I: + return disRNameCP2i[num]; + default: + return "Invalid"; + } +} + +u128 R5900DebugInterface::getRegister(int cat, int num) +{ + u128 result; + switch (cat) + { + case EECAT_GPR: + switch (num) + { + case 32: // pc + result = u128::From32(cpuRegs.pc); + break; + case 33: // hi + result = cpuRegs.HI.UQ; + break; + case 34: // lo + result = cpuRegs.LO.UQ; + break; + default: + result = cpuRegs.GPR.r[num].UQ; + break; + } + break; + case EECAT_CP0: + result = u128::From32(cpuRegs.CP0.r[num]); + break; + case EECAT_CP1: + result = u128::From32(fpuRegs.fpr[num].UL); + break; + case EECAT_CP2F: + result = VU1.VF[num].UQ; + break; + case EECAT_CP2I: + result = u128::From32(VU1.VI[num].UL); + break; + default: + result.From32(0); + break; + } + + return result; +} + +wxString R5900DebugInterface::getRegisterString(int cat, int num) +{ + switch (cat) + { + case EECAT_GPR: + case EECAT_CP0: + return getRegister(cat,num).ToString(); + case EECAT_CP1: + { + char str[64]; + sprintf(str,"%f",fpuRegs.fpr[num].f); + return wxString(str,wxConvUTF8); + } + default: + return L""; + } +} + + +u128 R5900DebugInterface::getHI() +{ + return cpuRegs.HI.UQ; +} + +u128 R5900DebugInterface::getLO() +{ + return cpuRegs.LO.UQ; +} + +u32 R5900DebugInterface::getPC() +{ + return cpuRegs.pc; +} + +void R5900DebugInterface::setPc(u32 newPc) +{ + cpuRegs.pc = newPc; +} + +std::string R5900DebugInterface::disasm(u32 address) +{ + std::string out; + + u32 op = read32(address); + R5900::disR5900Fasm(out,op,address); + return out; +} + +bool R5900DebugInterface::isValidAddress(u32 addr) +{ + if (addr < 0x100000) + return false; + if (addr >= 0x10000000 && addr < 0x10010000) + return true; + if (addr >= 0x12000000 && addr < 0x12001100) + return true; + if (addr >= 0x70000000 && addr < 0x70004000) + return true; + + return !(addr & 0x40000000) && vtlb_GetPhyPtr(addr & 0x1FFFFFFF) != NULL; +} + + +// +// R3000DebugInterface +// + + +u32 R3000DebugInterface::read8(u32 address) +{ + if (!isValidAddress(address)) + return -1; + return iopMemRead8(address); +} + +u32 R3000DebugInterface::read16(u32 address) +{ + if (!isValidAddress(address)) + return -1; + return iopMemRead16(address); +} + +u32 R3000DebugInterface::read32(u32 address) +{ + if (!isValidAddress(address)) + return -1; + return iopMemRead32(address); +} + +u64 R3000DebugInterface::read64(u32 address) +{ + return 0; +} + +u128 R3000DebugInterface::read128(u32 address) +{ + return u128::From32(0); +} + +void R3000DebugInterface::write8(u32 address, u8 value) +{ + if (!isValidAddress(address)) + return; + + iopMemWrite8(address,value); +} + +int R3000DebugInterface::getRegisterCategoryCount() +{ + return IOPCAT_COUNT; +} + +const char* R3000DebugInterface::getRegisterCategoryName(int cat) +{ + switch (cat) + { + case IOPCAT_GPR: + return "GPR"; + default: + return "Invalid"; + } +} + +int R3000DebugInterface::getRegisterSize(int cat) +{ + switch (cat) + { + case IOPCAT_GPR: + return 32; + default: + return 0; + } +} + +int R3000DebugInterface::getRegisterCount(int cat) +{ + switch (cat) + { + case IOPCAT_GPR: + return 35; // 32 + pc + hi + lo + default: + return 0; + } +} + +DebugInterface::RegisterType R3000DebugInterface::getRegisterType(int cat) +{ + switch (cat) + { + case IOPCAT_GPR: + default: + return DebugInterface::NORMAL; + } +} + +const char* R3000DebugInterface::getRegisterName(int cat, int num) +{ + switch (cat) + { + case IOPCAT_GPR: + switch (num) + { + case 32: // pc + return "pc"; + case 33: // hi + return "hi"; + case 34: // lo + return "lo"; + default: + return R5900::disRNameGPR[num]; + } + default: + return "Invalid"; + } +} + +u128 R3000DebugInterface::getRegister(int cat, int num) +{ + u32 value; + + switch (cat) + { + case IOPCAT_GPR: + switch (num) + { + case 32: // pc + value = psxRegs.pc; + break; + case 33: // hi + value = psxRegs.GPR.n.hi; + break; + case 34: // lo + value = psxRegs.GPR.n.lo; + break; + default: + value = psxRegs.GPR.r[num]; + break; + } + break; + default: + value = -1; + break; + } + + return u128::From32(value); +} + +wxString R3000DebugInterface::getRegisterString(int cat, int num) +{ + switch (cat) + { + case IOPCAT_GPR: + return getRegister(cat,num).ToString(); + default: + return L"Invalid"; + } +} + +u128 R3000DebugInterface::getHI() +{ + return u128::From32(psxRegs.GPR.n.hi); +} + +u128 R3000DebugInterface::getLO() +{ + return u128::From32(psxRegs.GPR.n.lo); +} + +u32 R3000DebugInterface::getPC() +{ + return psxRegs.pc; +} + +void R3000DebugInterface::setPc(u32 newPc) +{ + psxRegs.pc = newPc; +} + +std::string R3000DebugInterface::disasm(u32 address) +{ + std::string out; + + u32 op = read32(address); + R5900::disR5900Fasm(out,op,address); + return out; +} + +bool R3000DebugInterface::isValidAddress(u32 addr) +{ + if (addr >= 0x10000000 && addr < 0x10010000) + return true; + if (addr >= 0x12000000 && addr < 0x12001100) + return true; + if (addr >= 0x70000000 && addr < 0x70004000) + return true; + + return !(addr & 0x40000000) && vtlb_GetPhyPtr(addr & 0x1FFFFFFF) != NULL; +} diff --git a/pcsx2/DebugTools/DebugInterface.h b/pcsx2/DebugTools/DebugInterface.h new file mode 100644 index 0000000000..7694960233 --- /dev/null +++ b/pcsx2/DebugTools/DebugInterface.h @@ -0,0 +1,100 @@ +#pragma once +#include "MemoryTypes.h" +#include "ExpressionParser.h" + +class DebugInterface +{ +public: + enum RegisterType { NORMAL, SPECIAL }; + + virtual u32 read8(u32 address) = 0; + virtual u32 read16(u32 address) = 0; + virtual u32 read32(u32 address) = 0; + virtual u64 read64(u32 address) = 0; + virtual u128 read128(u32 address) = 0; + virtual void write8(u32 address, u8 value) = 0; + + // register stuff + virtual int getRegisterCategoryCount() = 0; + virtual const char* getRegisterCategoryName(int cat) = 0; + virtual int getRegisterSize(int cat) = 0; + virtual int getRegisterCount(int cat) = 0; + virtual RegisterType getRegisterType(int cat) = 0; + virtual const char* getRegisterName(int cat, int num) = 0; + virtual u128 getRegister(int cat, int num) = 0; + virtual wxString getRegisterString(int cat, int num) = 0; + virtual u128 getHI() = 0; + virtual u128 getLO() = 0; + virtual u32 getPC() = 0; + virtual void setPc(u32 newPc) = 0; + + virtual std::string disasm(u32 address) = 0; + virtual bool isValidAddress(u32 address) = 0; + + bool initExpression(const char* exp, PostfixExpression& dest); + bool parseExpression(PostfixExpression& exp, u64& dest); + bool isAlive(); + bool isCpuPaused(); + void pauseCpu(); + void resumeCpu(); +}; + +class R5900DebugInterface: public DebugInterface +{ +public: + virtual u32 read8(u32 address); + virtual u32 read16(u32 address); + virtual u32 read32(u32 address); + virtual u64 read64(u32 address); + virtual u128 read128(u32 address); + virtual void write8(u32 address, u8 value); + + // register stuff + virtual int getRegisterCategoryCount(); + virtual const char* getRegisterCategoryName(int cat); + virtual int getRegisterSize(int cat); + virtual int getRegisterCount(int cat); + virtual RegisterType getRegisterType(int cat); + virtual const char* getRegisterName(int cat, int num); + virtual u128 getRegister(int cat, int num); + virtual wxString getRegisterString(int cat, int num); + virtual u128 getHI(); + virtual u128 getLO(); + virtual u32 getPC(); + virtual void setPc(u32 newPc); + + virtual std::string disasm(u32 address); + virtual bool isValidAddress(u32 address); +}; + + +class R3000DebugInterface: public DebugInterface +{ +public: + virtual u32 read8(u32 address); + virtual u32 read16(u32 address); + virtual u32 read32(u32 address); + virtual u64 read64(u32 address); + virtual u128 read128(u32 address); + virtual void write8(u32 address, u8 value); + + // register stuff + virtual int getRegisterCategoryCount(); + virtual const char* getRegisterCategoryName(int cat); + virtual int getRegisterSize(int cat); + virtual int getRegisterCount(int cat); + virtual RegisterType getRegisterType(int cat); + virtual const char* getRegisterName(int cat, int num); + virtual u128 getRegister(int cat, int num); + virtual wxString getRegisterString(int cat, int num); + virtual u128 getHI(); + virtual u128 getLO(); + virtual u32 getPC(); + virtual void setPc(u32 newPc); + + virtual std::string disasm(u32 address); + virtual bool isValidAddress(u32 address); +}; + +extern R5900DebugInterface r5900Debug; +extern R3000DebugInterface r3000Debug; \ No newline at end of file diff --git a/pcsx2/DebugTools/DisASM.h b/pcsx2/DebugTools/DisASM.h index 83c95ec0cf..dd0e5e2d2c 100644 --- a/pcsx2/DebugTools/DisASM.h +++ b/pcsx2/DebugTools/DisASM.h @@ -24,19 +24,19 @@ #define DECODE_FD (DECODE_SA) ///******** -#define DECODE_FUNCTION ((cpuRegs.code) & 0x3F) -#define DECODE_RD ((cpuRegs.code >> 11) & 0x1F) // The rd part of the instruction register -#define DECODE_RT ((cpuRegs.code >> 16) & 0x1F) // The rt part of the instruction register -#define DECODE_RS ((cpuRegs.code >> 21) & 0x1F) // The rs part of the instruction register -#define DECODE_SA ((cpuRegs.code >> 6) & 0x1F) // The sa part of the instruction register -#define DECODE_IMMED ( cpuRegs.code & 0xFFFF) // The immediate part of the instruction register +#define DECODE_FUNCTION ((disasmOpcode) & 0x3F) +#define DECODE_RD ((disasmOpcode >> 11) & 0x1F) // The rd part of the instruction register +#define DECODE_RT ((disasmOpcode >> 16) & 0x1F) // The rt part of the instruction register +#define DECODE_RS ((disasmOpcode >> 21) & 0x1F) // The rs part of the instruction register +#define DECODE_SA ((disasmOpcode >> 6) & 0x1F) // The sa part of the instruction register +#define DECODE_IMMED ( disasmOpcode & 0xFFFF) // The immediate part of the instruction register #define DECODE_OFFSET ((((short)DECODE_IMMED * 4) + opcode_addr + 4)) -#define DECODE_JUMP (opcode_addr & 0xf0000000)|((cpuRegs.code&0x3ffffff)<<2) +#define DECODE_JUMP (opcode_addr & 0xf0000000)|((disasmOpcode&0x3ffffff)<<2) #define DECODE_SYSCALL ((opcode_addr & 0x03FFFFFF) >> 6) #define DECODE_BREAK (DECODE_SYSCALL) -#define DECODE_C0BC ((cpuRegs.code >> 16) & 0x03) -#define DECODE_C1BC ((cpuRegs.code >> 16) & 0x03) -#define DECODE_C2BC ((cpuRegs.code >> 16) & 0x03) +#define DECODE_C0BC ((disasmOpcode >> 16) & 0x03) +#define DECODE_C1BC ((disasmOpcode >> 16) & 0x03) +#define DECODE_C2BC ((disasmOpcode >> 16) & 0x03) //IOP diff --git a/pcsx2/DebugTools/DisR5900asm.cpp b/pcsx2/DebugTools/DisR5900asm.cpp index 3210cfd9c3..ecef2735ef 100644 --- a/pcsx2/DebugTools/DisR5900asm.cpp +++ b/pcsx2/DebugTools/DisR5900asm.cpp @@ -26,6 +26,7 @@ #include "R5900OpcodeTables.h" unsigned long opcode_addr; +u32 disasmOpcode; using namespace std; @@ -41,19 +42,19 @@ namespace R5900 #define DECODE_FD (DECODE_SA) /// ******** -#define DECODE_FUNCTION ((cpuRegs.code) & 0x3F) -#define DECODE_RD ((cpuRegs.code >> 11) & 0x1F) // The rd part of the instruction register -#define DECODE_RT ((cpuRegs.code >> 16) & 0x1F) // The rt part of the instruction register -#define DECODE_RS ((cpuRegs.code >> 21) & 0x1F) // The rs part of the instruction register -#define DECODE_SA ((cpuRegs.code >> 6) & 0x1F) // The sa part of the instruction register -#define DECODE_IMMED ( cpuRegs.code & 0xFFFF) // The immediate part of the instruction register +#define DECODE_FUNCTION ((disasmOpcode) & 0x3F) +#define DECODE_RD ((disasmOpcode >> 11) & 0x1F) // The rd part of the instruction register +#define DECODE_RT ((disasmOpcode >> 16) & 0x1F) // The rt part of the instruction register +#define DECODE_RS ((disasmOpcode >> 21) & 0x1F) // The rs part of the instruction register +#define DECODE_SA ((disasmOpcode >> 6) & 0x1F) // The sa part of the instruction register +#define DECODE_IMMED ( disasmOpcode & 0xFFFF) // The immediate part of the instruction register #define DECODE_OFFSET ((((short)DECODE_IMMED * 4) + opcode_addr + 4)) -#define DECODE_JUMP (opcode_addr & 0xf0000000)|((cpuRegs.code&0x3ffffff)<<2) +#define DECODE_JUMP (opcode_addr & 0xf0000000)|((disasmOpcode&0x3ffffff)<<2) #define DECODE_SYSCALL ((opcode_addr & 0x03FFFFFF) >> 6) #define DECODE_BREAK (DECODE_SYSCALL) -#define DECODE_C0BC ((cpuRegs.code >> 16) & 0x03) -#define DECODE_C1BC ((cpuRegs.code >> 16) & 0x03) -#define DECODE_C2BC ((cpuRegs.code >> 16) & 0x03) +#define DECODE_C0BC ((disasmOpcode >> 16) & 0x03) +#define DECODE_C1BC ((disasmOpcode >> 16) & 0x03) +#define DECODE_C2BC ((disasmOpcode >> 16) & 0x03) */ /*************************CPUS REGISTERS**************************/ static const char * const GPR_REG[32] = { @@ -609,15 +610,10 @@ void disR5900Fasm( string& output, u32 code, u32 pc ) string dbuf; char obuf[48]; - const u32 scode = cpuRegs.code; opcode_addr = pc; - cpuRegs.code = code; + disasmOpcode = code; - sprintf(obuf, "%08X:\t", pc ); - output.assign( obuf ); - GetCurrentInstruction().disasm( output ); - - cpuRegs.code = scode; + GetInstruction(code).disasm( output ); } //************************************************************* @@ -632,7 +628,7 @@ void P_COP2_SPECIAL( string& output ) } void P_COP2_SPECIAL2( string& output ) { - COP2SPECIAL2PrintTable[(cpuRegs.code & 0x3) | ((cpuRegs.code >> 4) & 0x7c)]( output ); + COP2SPECIAL2PrintTable[(disasmOpcode & 0x3) | ((disasmOpcode >> 4) & 0x7c)]( output ); } //**************************UNKNOWN**************************** @@ -649,15 +645,7 @@ void P_COP2_Unknown( string& output ) void label_decode( string& output, u32 addr ) { string buf; - ssprintf(buf, "0x%08X", addr); - const char* label = disR5900GetSym( addr ); - - if( label != NULL ) - { - output += label; - output += ' '; - } - + ssprintf(buf, "->$0x%08X", addr); output += buf; } @@ -757,7 +745,7 @@ void LQC2( string& output ) { _sap("lqc2\t%s, 0x%04X(%s)") COP2_REG_FP[DECOD void SLL( string& output ) { - if (cpuRegs.code == 0x00000000) + if (disasmOpcode == 0x00000000) output += "nop"; else _sap("sll\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); @@ -768,16 +756,16 @@ void SRA( string& output ) { _sap("sra\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], void SLLV( string& output ) { _sap("sllv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } void SRLV( string& output ) { _sap("srlv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]);} void SRAV( string& output ) { _sap("srav\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } -void JR( string& output ) { _sap("jr\t%s") GPR_REG[DECODE_RS]); } +void JR( string& output ) { _sap("jr\t->%s") GPR_REG[DECODE_RS]); } void JALR( string& output ) { int rd = DECODE_RD; if (rd == 31) - _sap("jalr\t%s") GPR_REG[DECODE_RS]); + _sap("jalr\t->%s") GPR_REG[DECODE_RS]); else - _sap("jalr\t%s, %s") GPR_REG[rd], GPR_REG[DECODE_RS]); + _sap("jalr\t%s, ->%s") GPR_REG[rd], GPR_REG[DECODE_RS]); } @@ -1032,10 +1020,10 @@ void P_BC2T( string& output ){ output += "bc2t\t"; offset_decode(output) void P_BC2FL( string& output ){ output += "bc2fl\t"; offset_decode(output); } void P_BC2TL( string& output ){ output += "bc2tl\t"; offset_decode(output); } //******************************SPECIAL 1 VUO TABLE**************************************** -#define _X ((cpuRegs.code>>24) & 1) -#define _Y ((cpuRegs.code>>23) & 1) -#define _Z ((cpuRegs.code>>22) & 1) -#define _W ((cpuRegs.code>>21) & 1) +#define _X ((disasmOpcode>>24) & 1) +#define _Y ((disasmOpcode>>23) & 1) +#define _Z ((disasmOpcode>>22) & 1) +#define _W ((disasmOpcode>>21) & 1) const char *dest_string(void) { @@ -1054,13 +1042,13 @@ const char *dest_string(void) char dest_fsf() { const char arr[4] = { 'x', 'y', 'z', 'w' }; - return arr[(cpuRegs.code>>21)&3]; + return arr[(disasmOpcode>>21)&3]; } char dest_ftf() { const char arr[4] = { 'x', 'y', 'z', 'w' }; - return arr[(cpuRegs.code>>23)&3]; + return arr[(disasmOpcode>>23)&3]; } void P_VADDx( string& output ){_sap("vaddx.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } diff --git a/pcsx2/DebugTools/DisassemblyManager.cpp b/pcsx2/DebugTools/DisassemblyManager.cpp new file mode 100644 index 0000000000..b243d1205c --- /dev/null +++ b/pcsx2/DebugTools/DisassemblyManager.cpp @@ -0,0 +1,1015 @@ +#include "PrecompiledHeader.h" + +#include +#include +#include + +#include "DisassemblyManager.h" +#include "Memory.h" +#include "Debug.h" +#include "MIPSAnalyst.h" + +int DisassemblyManager::maxParamChars = 29; + +bool isInInterval(u32 start, u32 size, u32 value) +{ + return start <= value && value <= (start+size-1); +} + +static u32 computeHash(u32 address, u32 size) +{ + u32 end = address+size; + u32 hash = 0xBACD7814; + while (address < end) + { + hash += memRead32(address); + address += 4; + } + return hash; +} + + +void parseDisasm(const char* disasm, char* opcode, char* arguments, bool insertSymbols) +{ + if (*disasm == '(') + { + while (*disasm != ')' && *disasm != 0) + disasm++; + if (*disasm == ')') + disasm++; + } + + // copy opcode + while (*disasm != 0 && *disasm != '\t') + { + *opcode++ = *disasm++; + } + *opcode = 0; + + if (*disasm++ == 0) + { + *arguments = 0; + return; + } + + const char* jumpAddress = strstr(disasm,"->$"); + const char* jumpRegister = strstr(disasm,"->"); + while (*disasm != 0) + { + // parse symbol + if (disasm == jumpAddress) + { + u32 branchTarget; + sscanf(disasm+3,"0x%08x",&branchTarget); + + const std::string addressSymbol = symbolMap.GetLabelString(branchTarget); + if (!addressSymbol.empty() && insertSymbols) + { + arguments += sprintf(arguments,"%s",addressSymbol.c_str()); + } else { + arguments += sprintf(arguments,"0x%08X",branchTarget); + } + + disasm += 3+2+8; + continue; + } + + if (disasm == jumpRegister) + disasm += 2; + + if (*disasm == ' ') + { + disasm++; + continue; + } + *arguments++ = *disasm++; + } + + *arguments = 0; +} + +std::map::iterator findDisassemblyEntry(std::map& entries, u32 address, bool exact) +{ + if (exact) + return entries.find(address); + + if (entries.size() == 0) + return entries.end(); + + // find first elem that's >= address + auto it = entries.lower_bound(address); + if (it != entries.end()) + { + // it may be an exact match + if (isInInterval(it->second->getLineAddress(0),it->second->getTotalSize(),address)) + return it; + + // otherwise it may point to the next + if (it != entries.begin()) + { + it--; + if (isInInterval(it->second->getLineAddress(0),it->second->getTotalSize(),address)) + return it; + } + } + + // check last entry manually + auto rit = entries.rbegin(); + if (isInInterval(rit->second->getLineAddress(0),rit->second->getTotalSize(),address)) + { + return (++rit).base(); + } + + // no match otherwise + return entries.end(); +} + +void DisassemblyManager::analyze(u32 address, u32 size = 1024) +{ + if (cpu->isAlive() == false) + return; + + u32 end = address+size; + + address &= ~3; + u32 start = address; + + while (address < end && start <= address) + { + auto it = findDisassemblyEntry(entries,address,false); + if (it != entries.end()) + { + DisassemblyEntry* entry = it->second; + entry->recheck(); + address = entry->getLineAddress(0)+entry->getTotalSize(); + continue; + } + + SymbolInfo info; + if (!symbolMap.GetSymbolInfo(&info,address,ST_ALL)) + { + if (address % 4) + { + u32 next = std::min((address+3) & ~3,symbolMap.GetNextSymbolAddress(address,ST_ALL)); + DisassemblyData* data = new DisassemblyData(cpu,address,next-address,DATATYPE_BYTE); + entries[address] = data; + address = next; + continue; + } + + u32 next = symbolMap.GetNextSymbolAddress(address,ST_ALL); + + if ((next % 4) && next != -1) + { + u32 alignedNext = next & ~3; + + if (alignedNext != address) + { + DisassemblyOpcode* opcode = new DisassemblyOpcode(cpu,address,(alignedNext-address)/4); + entries[address] = opcode; + } + + DisassemblyData* data = new DisassemblyData(cpu,address,next-alignedNext,DATATYPE_BYTE); + entries[alignedNext] = data; + } else { + DisassemblyOpcode* opcode = new DisassemblyOpcode(cpu,address,(next-address)/4); + entries[address] = opcode; + } + + address = next; + continue; + } + + switch (info.type) + { + case ST_FUNCTION: + { + DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address,info.size); + entries[info.address] = function; + address = info.address+info.size; + } + break; + case ST_DATA: + { + DisassemblyData* data = new DisassemblyData(cpu,info.address,info.size,symbolMap.GetDataType(info.address)); + entries[info.address] = data; + address = info.address+info.size; + } + break; + } + } + +} + +std::vector DisassemblyManager::getBranchLines(u32 start, u32 size) +{ + std::vector result; + + auto it = findDisassemblyEntry(entries,start,false); + if (it != entries.end()) + { + do + { + it->second->getBranchLines(start,size,result); + it++; + } while (it != entries.end() && start+size > it->second->getLineAddress(0)); + } + + return result; +} + +void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest) +{ + auto it = findDisassemblyEntry(entries,address,false); + if (it == entries.end()) + { + analyze(address); + it = findDisassemblyEntry(entries,address,false); + + if (it == entries.end()) + { + if (address % 4) + dest.totalSize = ((address+3) & ~3)-address; + else + dest.totalSize = 4; + dest.name = "ERROR"; + dest.params = "Disassembly failure"; + return; + } + } + + DisassemblyEntry* entry = it->second; + if (entry->disassemble(address,dest,insertSymbols)) + return; + + if (address % 4) + dest.totalSize = ((address+3) & ~3)-address; + else + dest.totalSize = 4; + dest.name = "ERROR"; + dest.params = "Disassembly failure"; +} + +u32 DisassemblyManager::getStartAddress(u32 address) +{ + auto it = findDisassemblyEntry(entries,address,false); + if (it == entries.end()) + { + analyze(address); + it = findDisassemblyEntry(entries,address,false); + if (it == entries.end()) + return address; + } + + DisassemblyEntry* entry = it->second; + int line = entry->getLineNum(address,true); + return entry->getLineAddress(line); +} + +u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n) +{ + while (cpu->isValidAddress(address)) + { + auto it = findDisassemblyEntry(entries,address,false); + + while (it != entries.end()) + { + DisassemblyEntry* entry = it->second; + int oldLineNum = entry->getLineNum(address,true); + int oldNumLines = entry->getNumLines(); + if (n <= oldLineNum) + { + return entry->getLineAddress(oldLineNum-n); + } + + address = entry->getLineAddress(0)-1; + n -= oldLineNum+1; + it = findDisassemblyEntry(entries,address,false); + } + + analyze(address-127,128); + } + + return address-n*4; +} + +u32 DisassemblyManager::getNthNextAddress(u32 address, int n) +{ + while (cpu->isValidAddress(address)) + { + auto it = findDisassemblyEntry(entries,address,false); + + while (it != entries.end()) + { + DisassemblyEntry* entry = it->second; + int oldLineNum = entry->getLineNum(address,true); + int oldNumLines = entry->getNumLines(); + if (oldLineNum+n < oldNumLines) + { + return entry->getLineAddress(oldLineNum+n); + } + + address = entry->getLineAddress(0)+entry->getTotalSize(); + n -= (oldNumLines-oldLineNum); + it = findDisassemblyEntry(entries,address,false); + } + + analyze(address); + } + + return address+n*4; +} + +void DisassemblyManager::clear() +{ + for (auto it = entries.begin(); it != entries.end(); it++) + { + delete it->second; + } + entries.clear(); +} + +DisassemblyFunction::DisassemblyFunction(DebugInterface* _cpu, u32 _address, u32 _size): address(_address), size(_size) +{ + cpu = _cpu; + hash = computeHash(address,size); + load(); +} + +void DisassemblyFunction::recheck() +{ + u32 newHash = computeHash(address,size); + if (hash != newHash) + { + hash = newHash; + clear(); + load(); + } +} + +int DisassemblyFunction::getNumLines() +{ + return (int) lineAddresses.size(); +} + +int DisassemblyFunction::getLineNum(u32 address, bool findStart) +{ + if (findStart) + { + int last = (int)lineAddresses.size() - 1; + for (int i = 0; i < last; i++) + { + u32 next = lineAddresses[i + 1]; + if (lineAddresses[i] <= address && next > address) + return i; + } + if (lineAddresses[last] <= address && this->address + this->size > address) + return last; + } + else + { + int last = (int)lineAddresses.size() - 1; + for (int i = 0; i < last; i++) + { + u32 next = lineAddresses[i + 1]; + if (lineAddresses[i] == address) + return i; + } + if (lineAddresses[last] == address) + return last; + } + + return 0; +} + +u32 DisassemblyFunction::getLineAddress(int line) +{ + return lineAddresses[line]; +} + +bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +{ + auto it = findDisassemblyEntry(entries,address,false); + if (it == entries.end()) + return false; + + return it->second->disassemble(address,dest,insertSymbols); +} + +void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector& dest) +{ + u32 end = start+size; + + for (size_t i = 0; i < lines.size(); i++) + { + BranchLine& line = lines[i]; + + u32 first = line.first; + u32 second = line.second; + + // skip branches that are entirely before or entirely after the window + if ((first < start && second < start) || + (first > end && second > end)) + continue; + + dest.push_back(line); + } +} + +#define NUM_LANES 16 + +void DisassemblyFunction::generateBranchLines() +{ + struct LaneInfo + { + bool used; + u32 end; + }; + + LaneInfo lanes[NUM_LANES]; + for (int i = 0; i < NUM_LANES; i++) + lanes[i].used = false; + + u32 end = address+size; + + for (u32 funcPos = address; funcPos < end; funcPos += 4) + { + MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos); + + bool inFunction = (opInfo.branchTarget >= address && opInfo.branchTarget < end); + if (opInfo.isBranch && !opInfo.isBranchToRegister && !opInfo.isLinkedBranch && inFunction) + { + BranchLine line; + if (opInfo.branchTarget < funcPos) + { + line.first = opInfo.branchTarget; + line.second = funcPos; + line.type = LINE_UP; + } else { + line.first = funcPos; + line.second = opInfo.branchTarget; + line.type = LINE_DOWN; + } + + lines.push_back(line); + } + } + + std::sort(lines.begin(),lines.end()); + for (size_t i = 0; i < lines.size(); i++) + { + for (int l = 0; l < NUM_LANES; l++) + { + if (lines[i].first > lanes[l].end) + lanes[l].used = false; + } + + int lane = -1; + for (int l = 0; l < NUM_LANES; l++) + { + if (lanes[l].used == false) + { + lane = l; + break; + } + } + + if (lane == -1) + { + // error + continue; + } + + lanes[lane].end = lines[i].second; + lanes[lane].used = true; + lines[i].laneIndex = lane; + } +} + +void DisassemblyFunction::addOpcodeSequence(u32 start, u32 end) +{ + DisassemblyOpcode* opcode = new DisassemblyOpcode(cpu,start,(end-start)/4); + entries[start] = opcode; + for (u32 pos = start; pos < end; pos += 4) + { + lineAddresses.push_back(pos); + } +} + +void DisassemblyFunction::load() +{ + generateBranchLines(); + + // gather all branch targets + std::set branchTargets; + for (size_t i = 0; i < lines.size(); i++) + { + switch (lines[i].type) + { + case LINE_DOWN: + branchTargets.insert(lines[i].second); + break; + case LINE_UP: + branchTargets.insert(lines[i].first); + break; + } + } + + u32 funcPos = address; + u32 funcEnd = address+size; + + u32 nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); + u32 opcodeSequenceStart = funcPos; + while (funcPos < funcEnd) + { + if (funcPos == nextData) + { + if (opcodeSequenceStart != funcPos) + addOpcodeSequence(opcodeSequenceStart,funcPos); + + DisassemblyData* data = new DisassemblyData(cpu,funcPos,symbolMap.GetDataSize(funcPos),symbolMap.GetDataType(funcPos)); + entries[funcPos] = data; + lineAddresses.push_back(funcPos); + funcPos += data->getTotalSize(); + + nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); + opcodeSequenceStart = funcPos; + continue; + } + + // force align + if (funcPos % 4) + { + u32 nextPos = (funcPos+3) & ~3; + + DisassemblyComment* comment = new DisassemblyComment(cpu,funcPos,nextPos-funcPos,".align","4"); + entries[funcPos] = comment; + lineAddresses.push_back(funcPos); + + funcPos = nextPos; + opcodeSequenceStart = funcPos; + continue; + } + + MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos); + u32 opAddress = funcPos; + funcPos += 4; + + // skip branches and their delay slots + if (opInfo.isBranch) + { + funcPos += 4; + continue; + } + + // lui + if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData) + { + u32 next = cpu->read32(funcPos); + + u32 immediate = ((opInfo.encodedOpcode & 0xFFFF) << 16) + (s16)(next & 0xFFFF); + u32 immediateOr = ((opInfo.encodedOpcode & 0xFFFF) << 16) | (u16)(next & 0xFFFF); + int rt = MIPS_GET_RT(opInfo.encodedOpcode); + + int nextRs = MIPS_GET_RS(next); + int nextRt = MIPS_GET_RT(next); + + // both rs and rt of the second op have to match rt of the first, + // otherwise there may be hidden consequences if the macro is displayed. + // also, don't create a macro if something branches into the middle of it + if (nextRs == rt && nextRt == rt && branchTargets.find(funcPos) == branchTargets.end()) + { + DisassemblyMacro* macro = NULL; + switch (MIPS_GET_OP(next)) + { + case 0x09: // addiu + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroLi(immediate,rt); + funcPos += 4; + break; + case 0x0D: // ori + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroLi(immediateOr,rt); + funcPos += 4; + break; + case 0x20: // lb + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("lb",immediate,rt,1); + funcPos += 4; + break; + case 0x21: // lh + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("lh",immediate,rt,2); + funcPos += 4; + break; + case 0x23: // lw + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("lw",immediate,rt,4); + funcPos += 4; + break; + case 0x24: // lbu + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("lbu",immediate,rt,1); + funcPos += 4; + break; + case 0x25: // lhu + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("lhu",immediate,rt,2); + funcPos += 4; + break; + case 0x28: // sb + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("sb",immediate,rt,1); + funcPos += 4; + break; + case 0x29: // sh + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("sh",immediate,rt,2); + funcPos += 4; + case 0x2B: // sw + macro = new DisassemblyMacro(cpu,opAddress); + macro->setMacroMemory("sw",immediate,rt,4); + funcPos += 4; + break; + } + + if (macro != NULL) + { + if (opcodeSequenceStart != opAddress) + addOpcodeSequence(opcodeSequenceStart,opAddress); + + entries[opAddress] = macro; + for (int i = 0; i < macro->getNumLines(); i++) + { + lineAddresses.push_back(macro->getLineAddress(i)); + } + + opcodeSequenceStart = funcPos; + continue; + } + } + } + + // just a normal opcode + } + + if (opcodeSequenceStart != funcPos) + addOpcodeSequence(opcodeSequenceStart,funcPos); +} + +void DisassemblyFunction::clear() +{ + for (auto it = entries.begin(); it != entries.end(); it++) + { + delete it->second; + } + + entries.clear(); + lines.clear(); + lineAddresses.clear(); + hash = 0; +} + +bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +{ + char opcode[64],arguments[256]; + + std::string dis = cpu->disasm(address); + parseDisasm(dis.c_str(),opcode,arguments,insertSymbols); + dest.type = DISTYPE_OPCODE; + dest.name = opcode; + dest.params = arguments; + dest.totalSize = 4; + dest.info = MIPSAnalyst::GetOpcodeInfo(cpu,address); + return true; +} + +void DisassemblyOpcode::getBranchLines(u32 start, u32 size, std::vector& dest) +{ + if (start < address) + { + size = start+size-address; + start = address; + } + + if (start+size > address+num*4) + size = address+num*4-start; + + int lane = 0; + for (u32 pos = start; pos < start+size; pos += 4) + { + MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,pos); + if (info.isBranch && !info.isBranchToRegister && !info.isLinkedBranch) + { + BranchLine line; + line.laneIndex = lane++; + + if (info.branchTarget < pos) + { + line.first = info.branchTarget; + line.second = pos; + line.type = LINE_UP; + } else { + line.first = pos; + line.second = info.branchTarget; + line.type = LINE_DOWN; + } + + dest.push_back(line); + } + } +} + + +void DisassemblyMacro::setMacroLi(u32 _immediate, u8 _rt) +{ + type = MACRO_LI; + name = "li"; + immediate = _immediate; + rt = _rt; + numOpcodes = 2; +} + +void DisassemblyMacro::setMacroMemory(std::string _name, u32 _immediate, u8 _rt, int _dataSize) +{ + type = MACRO_MEMORYIMM; + name = _name; + immediate = _immediate; + rt = _rt; + dataSize = _dataSize; + numOpcodes = 2; +} + +bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +{ + char buffer[64]; + dest.type = DISTYPE_MACRO; + dest.info = MIPSAnalyst::GetOpcodeInfo(cpu,address); + + std::string addressSymbol; + switch (type) + { + case MACRO_LI: + dest.name = name; + + addressSymbol = symbolMap.GetLabelString(immediate); + if (!addressSymbol.empty() && insertSymbols) + { + sprintf(buffer,"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str()); + } else { + sprintf(buffer,"%s,0x%08X",cpu->getRegisterName(0,rt),immediate); + } + + dest.params = buffer; + + dest.info.hasRelevantAddress = true; + dest.info.releventAddress = immediate; + break; + case MACRO_MEMORYIMM: + dest.name = name; + + addressSymbol = symbolMap.GetLabelString(immediate); + if (!addressSymbol.empty() && insertSymbols) + { + sprintf(buffer,"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str()); + } else { + sprintf(buffer,"%s,0x%08X",cpu->getRegisterName(0,rt),immediate); + } + + dest.params = buffer; + + dest.info.isDataAccess = true; + dest.info.dataAddress = immediate; + dest.info.dataSize = dataSize; + + dest.info.hasRelevantAddress = true; + dest.info.releventAddress = immediate; + break; + default: + return false; + } + + dest.totalSize = getTotalSize(); + return true; +} + + +DisassemblyData::DisassemblyData(DebugInterface* _cpu, u32 _address, u32 _size, DataType _type): address(_address), size(_size), type(_type) +{ + cpu = _cpu; + hash = computeHash(address,size); + createLines(); +} + +void DisassemblyData::recheck() +{ + u32 newHash = computeHash(address,size); + if (newHash != hash) + { + hash = newHash; + createLines(); + } +} + +bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +{ + dest.type = DISTYPE_DATA; + + switch (type) + { + case DATATYPE_BYTE: + dest.name = ".byte"; + break; + case DATATYPE_HALFWORD: + dest.name = ".half"; + break; + case DATATYPE_WORD: + dest.name = ".word"; + break; + case DATATYPE_ASCII: + dest.name = ".ascii"; + break; + default: + return false; + } + + auto it = lines.find(address); + if (it == lines.end()) + return false; + + dest.params = it->second.text; + dest.totalSize = it->second.size; + return true; +} + +int DisassemblyData::getLineNum(u32 address, bool findStart) +{ + auto it = lines.upper_bound(address); + if (it != lines.end()) + { + if (it == lines.begin()) + return 0; + it--; + return it->second.lineNum; + } + + return lines.rbegin()->second.lineNum; +} + +void DisassemblyData::createLines() +{ + lines.clear(); + lineAddresses.clear(); + + u32 pos = address; + u32 end = address+size; + u32 maxChars = DisassemblyManager::getMaxParamChars(); + + std::string currentLine; + u32 currentLineStart = pos; + + int lineCount = 0; + if (type == DATATYPE_ASCII) + { + bool inString = false; + while (pos < end) + { + u8 b = memRead8(pos++); + if (b >= 0x20 && b <= 0x7F) + { + if (currentLine.size()+1 >= maxChars) + { + if (inString == true) + currentLine += "\""; + + DataEntry entry = {currentLine,pos-1-currentLineStart,lineCount++}; + lines[currentLineStart] = entry; + lineAddresses.push_back(currentLineStart); + + currentLine = ""; + currentLineStart = pos-1; + inString = false; + } + + if (inString == false) + currentLine += "\""; + currentLine += (char)b; + inString = true; + } else { + char buffer[64]; + if (pos == end && b == 0) + strcpy(buffer,"0"); + else + sprintf(buffer,"0x%02X",b); + + if (currentLine.size()+strlen(buffer) >= maxChars) + { + if (inString == true) + currentLine += "\""; + + DataEntry entry = {currentLine,pos-1-currentLineStart,lineCount++}; + lines[currentLineStart] = entry; + lineAddresses.push_back(currentLineStart); + + currentLine = ""; + currentLineStart = pos-1; + inString = false; + } + + bool comma = false; + if (currentLine.size() != 0) + comma = true; + + if (inString) + currentLine += "\""; + + if (comma) + currentLine += ","; + + currentLine += buffer; + inString = false; + } + } + + if (inString == true) + currentLine += "\""; + + if (currentLine.size() != 0) + { + DataEntry entry = {currentLine,pos-currentLineStart,lineCount++}; + lines[currentLineStart] = entry; + lineAddresses.push_back(currentLineStart); + } + } else { + while (pos < end) + { + char buffer[64]; + u32 value; + + u32 currentPos = pos; + + switch (type) + { + case DATATYPE_BYTE: + value = memRead8(pos); + sprintf(buffer,"0x%02X",value); + pos++; + break; + case DATATYPE_HALFWORD: + value = memRead16(pos); + sprintf(buffer,"0x%04X",value); + pos += 2; + break; + case DATATYPE_WORD: + { + value = memRead32(pos); + const std::string label = symbolMap.GetLabelString(value); + if (!label.empty()) + sprintf(buffer,"%s",label.c_str()); + else + sprintf(buffer,"0x%08X",value); + pos += 4; + } + break; + } + + size_t len = strlen(buffer); + if (currentLine.size() != 0 && currentLine.size()+len >= maxChars) + { + DataEntry entry = {currentLine,currentPos-currentLineStart,lineCount++}; + lines[currentLineStart] = entry; + lineAddresses.push_back(currentLineStart); + + currentLine = ""; + currentLineStart = currentPos; + } + + if (currentLine.size() != 0) + currentLine += ","; + currentLine += buffer; + } + + if (currentLine.size() != 0) + { + DataEntry entry = {currentLine,pos-currentLineStart,lineCount++}; + lines[currentLineStart] = entry; + lineAddresses.push_back(currentLineStart); + } + } +} + + +DisassemblyComment::DisassemblyComment(DebugInterface* _cpu, u32 _address, u32 _size, std::string _name, std::string _param) + : cpu(_cpu), address(_address), size(_size), name(_name), param(_param) +{ + +} + +bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +{ + dest.type = DISTYPE_OTHER; + dest.name = name; + dest.params = param; + dest.totalSize = size; + return true; +} diff --git a/pcsx2/DebugTools/DisassemblyManager.h b/pcsx2/DebugTools/DisassemblyManager.h new file mode 100644 index 0000000000..d629e3c687 --- /dev/null +++ b/pcsx2/DebugTools/DisassemblyManager.h @@ -0,0 +1,198 @@ +#pragma once + +#include "SymbolMap.h" +#include "Utilities/Threading.h" +#include "Pcsx2Types.h" +#include "DebugInterface.h" +#include "MIPSAnalyst.h" + +enum DisassemblyLineType { DISTYPE_OPCODE, DISTYPE_MACRO, DISTYPE_DATA, DISTYPE_OTHER }; + +struct DisassemblyLineInfo +{ + DisassemblyLineType type; + MIPSAnalyst::MipsOpcodeInfo info; + std::string name; + std::string params; + u32 totalSize; +}; + +enum LineType { LINE_UP, LINE_DOWN, LINE_RIGHT }; + +struct BranchLine +{ + u32 first; + u32 second; + LineType type; + int laneIndex; + + bool operator<(const BranchLine& other) const + { + return first < other.first; + } +}; + +class DisassemblyEntry +{ +public: + virtual ~DisassemblyEntry() { }; + virtual void recheck() = 0; + virtual int getNumLines() = 0; + virtual int getLineNum(u32 address, bool findStart) = 0; + virtual u32 getLineAddress(int line) = 0; + virtual u32 getTotalSize() = 0; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) = 0; + virtual void getBranchLines(u32 start, u32 size, std::vector& dest) { }; +}; + +class DisassemblyFunction: public DisassemblyEntry +{ +public: + DisassemblyFunction(DebugInterface* _cpu, u32 _address, u32 _size); + virtual void recheck(); + virtual int getNumLines(); + virtual int getLineNum(u32 address, bool findStart); + virtual u32 getLineAddress(int line); + virtual u32 getTotalSize() { return size; }; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols); + virtual void getBranchLines(u32 start, u32 size, std::vector& dest); +private: + void generateBranchLines(); + void load(); + void clear(); + void addOpcodeSequence(u32 start, u32 end); + + DebugInterface* cpu; + u32 address; + u32 size; + u32 hash; + std::vector lines; + std::map entries; + std::vector lineAddresses; +}; + +class DisassemblyOpcode: public DisassemblyEntry +{ +public: + DisassemblyOpcode(DebugInterface* _cpu, u32 _address, int _num): cpu(_cpu), address(_address), num(_num) { }; + virtual ~DisassemblyOpcode() { }; + virtual void recheck() { }; + virtual int getNumLines() { return num; }; + virtual int getLineNum(u32 address, bool findStart) { return (address-this->address)/4; }; + virtual u32 getLineAddress(int line) { return address+line*4; }; + virtual u32 getTotalSize() { return num*4; }; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols); + virtual void getBranchLines(u32 start, u32 size, std::vector& dest); +private: + DebugInterface* cpu; + u32 address; + int num; +}; + + +class DisassemblyMacro: public DisassemblyEntry +{ +public: + DisassemblyMacro(DebugInterface* _cpu, u32 _address): cpu(_cpu), address(_address) { }; + virtual ~DisassemblyMacro() { }; + + void setMacroLi(u32 _immediate, u8 _rt); + void setMacroMemory(std::string _name, u32 _immediate, u8 _rt, int _dataSize); + + virtual void recheck() { }; + virtual int getNumLines() { return 1; }; + virtual int getLineNum(u32 address, bool findStart) { return 0; }; + virtual u32 getLineAddress(int line) { return address; }; + virtual u32 getTotalSize() { return numOpcodes*4; }; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) ; +private: + enum MacroType { MACRO_LI, MACRO_MEMORYIMM }; + + DebugInterface* cpu; + MacroType type; + std::string name; + u32 immediate; + u32 address; + u32 numOpcodes; + u8 rt; + int dataSize; +}; + + +class DisassemblyData: public DisassemblyEntry +{ +public: + DisassemblyData(DebugInterface* _cpu, u32 _address, u32 _size, DataType _type); + virtual ~DisassemblyData() { }; + + virtual void recheck(); + virtual int getNumLines() { return (int)lines.size(); }; + virtual int getLineNum(u32 address, bool findStart); + virtual u32 getLineAddress(int line) { return lineAddresses[line]; }; + virtual u32 getTotalSize() { return size; }; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols); +private: + void createLines(); + + struct DataEntry + { + std::string text; + u32 size; + int lineNum; + }; + + DebugInterface* cpu; + u32 address; + u32 size; + u32 hash; + DataType type; + std::map lines; + std::vector lineAddresses; +}; + +class DisassemblyComment: public DisassemblyEntry +{ +public: + DisassemblyComment(DebugInterface* _cpu, u32 _address, u32 _size, std::string name, std::string param); + virtual ~DisassemblyComment() { }; + + virtual void recheck() { }; + virtual int getNumLines() { return 1; }; + virtual int getLineNum(u32 address, bool findStart) { return 0; }; + virtual u32 getLineAddress(int line) { return address; }; + virtual u32 getTotalSize() { return size; }; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols); +private: + DebugInterface* cpu; + u32 address; + u32 size; + std::string name; + std::string param; +}; + +class DebugInterface; + +class DisassemblyManager +{ +public: + void clear(); + + void setCpu(DebugInterface* _cpu) { cpu = _cpu; }; + void setMaxParamChars(int num) { maxParamChars = num; clear(); }; + void getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest); + void analyze(u32 address, u32 size); + std::vector getBranchLines(u32 start, u32 size); + + u32 getStartAddress(u32 address); + u32 getNthPreviousAddress(u32 address, int n = 1); + u32 getNthNextAddress(u32 address, int n = 1); + + static int getMaxParamChars() { return maxParamChars; }; +private: + DisassemblyEntry* getEntry(u32 address); + std::map entries; + DebugInterface* cpu; + static int maxParamChars; +}; + +bool isInInterval(u32 start, u32 size, u32 value); diff --git a/pcsx2/DebugTools/ExpressionParser.cpp b/pcsx2/DebugTools/ExpressionParser.cpp new file mode 100644 index 0000000000..06225cedf9 --- /dev/null +++ b/pcsx2/DebugTools/ExpressionParser.cpp @@ -0,0 +1,594 @@ +#include "PrecompiledHeader.h" +#include "ExpressionParser.h" +#include +#include +#include + +typedef enum { + EXOP_BRACKETL, EXOP_BRACKETR, EXOP_MEML, EXOP_MEMR, EXOP_MEMSIZE, EXOP_SIGNPLUS, EXOP_SIGNMINUS, + EXOP_BITNOT, EXOP_LOGNOT, EXOP_MUL, EXOP_DIV, EXOP_MOD, EXOP_ADD, EXOP_SUB, + EXOP_SHL, EXOP_SHR, EXOP_GREATEREQUAL, EXOP_GREATER, EXOP_LOWEREQUAL, EXOP_LOWER, + EXOP_EQUAL, EXOP_NOTEQUAL, EXOP_BITAND, EXOP_XOR, EXOP_BITOR, EXOP_LOGAND, + EXOP_LOGOR, EXOP_TERTIF, EXOP_TERTELSE, EXOP_NUMBER, EXOP_MEM, EXOP_NONE, EXOP_COUNT +} ExpressionOpcodeType; + +typedef enum { EXCOMM_CONST, EXCOMM_CONST_FLOAT, EXCOMM_REF, EXCOMM_OP } ExpressionCommand; + +static char expressionError[256]; + +typedef struct { + char Name[4]; + unsigned char Priority; + unsigned char len; + unsigned char args; + bool sign; +} ExpressionOpcode; + +const ExpressionOpcode ExpressionOpcodes[] = { + { "(", 25, 1, 0, false }, // EXOP_BRACKETL + { ")", 25, 1, 0, false }, // EXOP_BRACKETR + { "[", 4, 1, 0, false }, // EXOP_MEML + { "]", 4, 1, 0, false }, // EXOP_MEMR + { ",", 5, 1, 2, false }, // EXOP_MEMSIZE + { "+", 22, 1, 1, true }, // EXOP_SIGNPLUS + { "-", 22, 1, 1, true }, // EXOP_SIGNMINUS + { "~", 22, 1, 1, false }, // EXOP_BITNOT + { "!", 22, 1, 1, false }, // EXOP_LOGNOT + { "*", 21, 1, 2, false }, // EXOP_MUL + { "/", 21, 1, 2, false }, // EXOP_DIV + { "%", 21, 1, 2, false }, // EXOP_MOD + { "+", 20, 1, 2, false }, // EXOP_ADD + { "-", 20, 1, 2, false }, // EXOP_SUB + { "<<", 19, 2, 2, false }, // EXOP_SHL + { ">>", 19, 2, 2, false }, // EXOP_SHR + { ">=", 18, 2, 2, false }, // EXOP_GREATEREQUAL + { ">", 18, 1, 2, false }, // EXOP_GREATER + { "<=", 18, 2, 2, false }, // EXOP_LOWEREQUAL + { "<", 18, 1, 2, false }, // EXOP_LOWER + { "==", 17, 2, 2, false }, // EXOP_EQUAL + { "!=", 17, 2, 2, false }, // EXOP_NOTEQUAL + { "&", 16, 1, 2, false }, // EXOP_BITAND + { "^", 15, 1, 2, false }, // EXOP_XOR + { "|", 14, 1, 2, false }, // EXOP_BITOR + { "&&", 13, 2, 2, false }, // EXOP_LOGAND + { "||", 12, 2, 2, false }, // EXOP_LOGOR + { "?", 10, 1, 0, false }, // EXOP_TERTIF + { ":", 11, 1, 3, false }, // EXOP_TERTELSE + { "", 0, 0, 0, false }, // EXOP_NUMBER + { "[]", 0, 0, 1, false }, // EXOP_MEM + { "", 0, 0, 0, false } // EXOP_NONE +}; + +bool parseNumber(char* str, int defaultrad, int len, u64& result) +{ + u64 val = 0; + int r = 0; + if (len == 0) len = (int) strlen(str); + + if (str[0] == '0' && tolower(str[1]) == 'x') + { + r = 16; + str+=2; + len-=2; + } else if (str[0] == '$') + { + r = 16; + str++; + len--; + } else if (str[0] == '0' && tolower(str[1]) == 'o') + { + r = 8; + str+=2; + len-=2; + } else { + if (!(str[0] >= '0' && str[0] <= '9')) return false; + + if (tolower(str[len-1]) == 'b' && defaultrad != 16) + { + r = 2; + len--; + } else if (tolower(str[len-1]) == 'o') + { + r = 8; + len--; + } else if (tolower(str[len-1]) == 'h') + { + r = 16; + len--; + } else { + r = defaultrad; + } + } + + switch (r) + { + case 2: // bin + while (len--) + { + if (*str != '0' && *str != '1') return false; + val = val << 1; + if (*str++ == '1') + { + val++; + } + } + break; + case 8: // oct + while (len--) + { + if (*str < '0' || *str > '7') return false; + val = val << 3; + val+=(*str++-'0'); + } + break; + case 10: // dec + while (len--) + { + if (*str < '0' || *str > '9') return false; + val = val * 10; + val += (*str++ - '0'); + } + break; + case 16: // hex + while (len--) + { + char c = tolower(*str++); + if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) return false; + val = val << 4; + + if (c >= 'a') val += c-'a'+10; + else val += c-'0'; + } + break; + default: + return false; + } + + result = val; + return true; +} + +// Parse only a float, and return as float bits. +static bool parseFloat(const char *str, int len, u64 &result) +{ + bool foundDecimal = false; + for (int i = 0; i < len; ++i) + { + if (str[i] == '.') + { + if (foundDecimal) + return false; + foundDecimal = true; + continue; + } + if (str[i] < '0' || str[i] > '9') + return false; + } + + result = 0; + float f = (float)atof(str); + memcpy(&result, &f, sizeof(float)); + return foundDecimal; +} + +ExpressionOpcodeType getExpressionOpcode(const char* str, int& ReturnLen, ExpressionOpcodeType LastOpcode) +{ + int longestlen = 0; + ExpressionOpcodeType result = EXOP_NONE; + + for (int i = 0; i < EXOP_NUMBER; i++) + { + if (ExpressionOpcodes[i].sign == true && + (LastOpcode == EXOP_NUMBER || LastOpcode == EXOP_BRACKETR)) continue; + + int len = ExpressionOpcodes[i].len; + if (len > longestlen) + { + if (strncmp(ExpressionOpcodes[i].Name,str,len) == 0) + { + result = (ExpressionOpcodeType) i; + longestlen = len; + } + } + } + + ReturnLen = longestlen; + return result; +} + +bool isAlphaNum(char c) +{ + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + c == '@' || c == '_' || c == '$' || c == '.') + { + return true; + } else { + return false; + } +} + +bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest) +{ + expressionError[0] = 0; + + int infixPos = 0; + int infixLen = (int)strlen(infix); + ExpressionOpcodeType lastOpcode = EXOP_NONE; + std::vector opcodeStack; + dest.clear(); + + while (infixPos < infixLen) + { + char first = tolower(infix[infixPos]); + char subStr[256]; + int subPos = 0; + + if (first == ' ' || first == '\t') + { + infixPos++; + continue; + } + + if (first >= '0' && first <= '9') + { + while (isAlphaNum(infix[infixPos])) + { + subStr[subPos++] = infix[infixPos++]; + } + subStr[subPos] = 0; + + u64 value; + bool isFloat = false; + if (parseFloat(subStr,subPos,value) == true) + isFloat = true; + else if (parseNumber(subStr,16,subPos,value) == false) + { + sprintf(expressionError,"Invalid number \"%s\"",subStr); + return false; + } + + dest.push_back(ExpressionPair(isFloat?EXCOMM_CONST_FLOAT:EXCOMM_CONST,value)); + lastOpcode = EXOP_NUMBER; + } else if ((first >= 'a' && first <= 'z') || first == '@') + { + while (isAlphaNum(infix[infixPos])) + { + subStr[subPos++] = infix[infixPos++]; + } + subStr[subPos] = 0; + + u64 value; + if (funcs->parseReference(subStr,value) == true) + { + dest.push_back(ExpressionPair(EXCOMM_REF,value)); + lastOpcode = EXOP_NUMBER; + continue; + } + + if (funcs->parseSymbol(subStr,value) == true) + { + dest.push_back(ExpressionPair(EXCOMM_CONST,value)); + lastOpcode = EXOP_NUMBER; + continue; + } + + sprintf(expressionError,"Invalid symbol \"%s\"",subStr); + return false; + } else { + int len; + ExpressionOpcodeType type = getExpressionOpcode(&infix[infixPos],len,lastOpcode); + if (type == EXOP_NONE) + { + sprintf(expressionError,"Invalid operator at \"%s\"",&infix[infixPos]); + return false; + } + + switch (type) + { + case EXOP_BRACKETL: + case EXOP_MEML: + opcodeStack.push_back(type); + break; + case EXOP_BRACKETR: + while (true) + { + if (opcodeStack.empty()) + { + sprintf(expressionError,"Closing parenthesis without opening one"); + return false; + } + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + if (t == EXOP_BRACKETL) break; + dest.push_back(ExpressionPair(EXCOMM_OP,t)); + } + break; + case EXOP_MEMR: + while (true) + { + if (opcodeStack.empty()) + { + sprintf(expressionError,"Closing bracket without opening one"); + return false; + } + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + if (t == EXOP_MEML) + { + dest.push_back(ExpressionPair(EXCOMM_OP,EXOP_MEM)); + break; + } + dest.push_back(ExpressionPair(EXCOMM_OP,t)); + } + type = EXOP_NUMBER; + break; + default: + if (opcodeStack.empty() == false) + { + int CurrentPriority = ExpressionOpcodes[type].Priority; + while (!opcodeStack.empty()) + { + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + + if (t == EXOP_BRACKETL || t == EXOP_MEML) + { + opcodeStack.push_back(t); + break; + } + + if (ExpressionOpcodes[t].Priority >= CurrentPriority) + { + dest.push_back(ExpressionPair(EXCOMM_OP,t)); + } else { + opcodeStack.push_back(t); + break; + } + } + } + opcodeStack.push_back(type); + break; + } + infixPos += len; + lastOpcode = type; + } + } + + while (!opcodeStack.empty()) + { + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + + if (t == EXOP_BRACKETL) // opening bracket without closing one + { + sprintf(expressionError,"Parenthesis not closed"); + return false; + } + dest.push_back(ExpressionPair(EXCOMM_OP,t)); + } + +#if 0 // only for testing + char test[1024]; + int testPos = 0; + for (int i = 0; i < dest.size(); i++) + { + switch (dest[i].first) + { + case EXCOMM_CONST: + case EXCOMM_CONST_FLOAT: + testPos += sprintf(&test[testPos],"0x%04X ",dest[i].second); + break; + case EXCOMM_REF: + testPos += sprintf(&test[testPos],"r%d ",dest[i].second); + break; + case EXCOMM_OP: + testPos += sprintf(&test[testPos],"%s ",ExpressionOpcodes[dest[i].second].Name); + break; + }; + } +#endif + + return true; +} + +bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest) +{ + size_t num = 0; + u64 opcode; + std::vector valueStack; + u64 arg[5]; + float fArg[5]; + bool useFloat = false; + + while (num < exp.size()) + { + switch (exp[num].first) + { + case EXCOMM_CONST: // konstante zahl + valueStack.push_back(exp[num++].second); + break; + case EXCOMM_CONST_FLOAT: + useFloat = true; + valueStack.push_back(exp[num++].second); + break; + case EXCOMM_REF: + useFloat = useFloat || funcs->getReferenceType(exp[num].second) == EXPR_TYPE_FLOAT; + opcode = funcs->getReferenceValue(exp[num++].second); + valueStack.push_back(opcode); + break; + case EXCOMM_OP: // opcode + opcode = exp[num++].second; + if (valueStack.size() < ExpressionOpcodes[opcode].args) + { + sprintf(expressionError,"Not enough arguments"); + return false; + } + for (int l = 0; l < ExpressionOpcodes[opcode].args; l++) + { + arg[l] = valueStack[valueStack.size()-1]; + fArg[l] = arg[l]; + valueStack.pop_back(); + } + + switch (opcode) + { + case EXOP_MEMSIZE: // must be followed by EXOP_MEM + if (exp[num++].second != EXOP_MEM) + { + sprintf(expressionError,"Invalid memsize operator"); + return false; + } + + u64 val; + if(funcs->getMemoryValue(arg[1],arg[0],val,expressionError) == false) + { + return false; + } + valueStack.push_back(val); + break; + case EXOP_MEM: + { + u64 val; + if (funcs->getMemoryValue(arg[0],4,val,expressionError) == false) + { + return false; + } + valueStack.push_back(val); + } + break; + case EXOP_SIGNPLUS: // keine aktion nötig + break; + case EXOP_SIGNMINUS: // -0 + if (useFloat) + valueStack.push_back(0.0-fArg[0]); + else + valueStack.push_back(0-arg[0]); + break; + case EXOP_BITNOT: // ~b + valueStack.push_back(~arg[0]); + break; + case EXOP_LOGNOT: // !b + valueStack.push_back(!arg[0]); + break; + case EXOP_MUL: // a*b + if (useFloat) + valueStack.push_back(fArg[1]*fArg[0]); + else + valueStack.push_back(arg[1]*arg[0]); + break; + case EXOP_DIV: // a/b + if (arg[0] == 0) + { + sprintf(expressionError,"Division by zero"); + return false; + } + if (useFloat) + valueStack.push_back(fArg[1]/fArg[0]); + else + valueStack.push_back(arg[1]/arg[0]); + break; + case EXOP_MOD: // a%b + if (arg[0] == 0) + { + sprintf(expressionError,"Modulo by zero"); + return false; + } + valueStack.push_back(arg[1]%arg[0]); + break; + case EXOP_ADD: // a+b + if (useFloat) + valueStack.push_back(fArg[1]+fArg[0]); + else + valueStack.push_back(arg[1]+arg[0]); + break; + case EXOP_SUB: // a-b + if (useFloat) + valueStack.push_back(fArg[1]-fArg[0]); + else + valueStack.push_back(arg[1]-arg[0]); + break; + case EXOP_SHL: // a<>b + valueStack.push_back(arg[1]>>arg[0]); + break; + case EXOP_GREATEREQUAL: // a >= b + if (useFloat) + valueStack.push_back(fArg[1]>=fArg[0]); + else + valueStack.push_back(arg[1]>=arg[0]); + break; + case EXOP_GREATER: // a > b + if (useFloat) + valueStack.push_back(fArg[1]>fArg[0]); + else + valueStack.push_back(arg[1]>arg[0]); + break; + case EXOP_LOWEREQUAL: // a <= b + if (useFloat) + valueStack.push_back(fArg[1]<=fArg[0]); + else + valueStack.push_back(arg[1]<=arg[0]); + break; + case EXOP_LOWER: // a < b + if (useFloat) + valueStack.push_back(fArg[1] + +typedef std::pair ExpressionPair; +typedef std::vector PostfixExpression; + +enum ExpressionType +{ + EXPR_TYPE_UINT = 0, + EXPR_TYPE_FLOAT = 2, +}; + +class IExpressionFunctions +{ +public: + virtual bool parseReference(char* str, u64& referenceIndex) = 0; + virtual bool parseSymbol(char* str, u64& symbolValue) = 0; + virtual u64 getReferenceValue(u64 referenceIndex) = 0; + virtual ExpressionType getReferenceType(u64 referenceIndex) = 0; + virtual bool getMemoryValue(u32 address, int size, u64& dest, char* error) = 0; +}; + +bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest); +bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest); +bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest); +const char* getExpressionError(); diff --git a/pcsx2/DebugTools/MIPSAnalyst.cpp b/pcsx2/DebugTools/MIPSAnalyst.cpp new file mode 100644 index 0000000000..97b221d513 --- /dev/null +++ b/pcsx2/DebugTools/MIPSAnalyst.cpp @@ -0,0 +1,395 @@ +#include "PrecompiledHeader.h" +#include "MIPSAnalyst.h" +#include "Debug.h" +#include "DebugInterface.h" +#include "SymbolMap.h" +#include "DebugInterface.h" + +static std::vector functions; + +#define MIPS_MAKE_J(addr) (0x08000000 | ((addr)>>2)) +#define MIPS_MAKE_JAL(addr) (0x0C000000 | ((addr)>>2)) +#define MIPS_MAKE_JR_RA() (0x03e00008) +#define MIPS_MAKE_NOP() (0) + +namespace MIPSAnalyst +{ + + static const char *DefaultFunctionName(char buffer[256], u32 startAddr) { + sprintf(buffer, "z_un_%08x", startAddr); + return buffer; + } + + void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols) { + AnalyzedFunction currentFunction = {startAddr}; + + u32 furthestBranch = 0; + bool looking = false; + bool end = false; + bool isStraightLeaf = true; + + u32 addr; + for (addr = startAddr; addr <= endAddr; addr += 4) { + // Use pre-existing symbol map info if available. May be more reliable. + SymbolInfo syminfo; + if (symbolMap.GetSymbolInfo(&syminfo, addr, ST_FUNCTION)) { + addr = syminfo.address + syminfo.size - 4; + + // We still need to insert the func for hashing purposes. + currentFunction.start = syminfo.address; + currentFunction.end = syminfo.address + syminfo.size - 4; + functions.push_back(currentFunction); + currentFunction.start = addr + 4; + furthestBranch = 0; + looking = false; + end = false; + continue; + } + + u32 op = r5900Debug.read32(addr); +/* + MIPSOpcode op = Memory::Read_Instruction(addr); + u32 target = GetBranchTargetNoRA(addr); + if (target != INVALIDTARGET) { + isStraightLeaf = false; + if (target > furthestBranch) { + furthestBranch = target; + } + } else if ((op & 0xFC000000) == 0x08000000) { + u32 sureTarget = GetJumpTarget(addr); + // Check for a tail call. Might not even have a jr ra. + if (sureTarget != INVALIDTARGET && sureTarget < currentFunction.start) { + if (furthestBranch > addr) { + looking = true; + addr += 4; + } else { + end = true; + } + } else if (sureTarget != INVALIDTARGET && sureTarget > addr && sureTarget > furthestBranch) { + // A jump later. Probably tail, but let's check if it jumps back. + u32 knownEnd = furthestBranch == 0 ? addr : furthestBranch; + u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd); + if (jumpback != INVALIDTARGET && jumpback > addr && jumpback > knownEnd) { + furthestBranch = jumpback; + } else { + if (furthestBranch > addr) { + looking = true; + addr += 4; + } else { + end = true; + } + } + } + }*/ + if (op == MIPS_MAKE_JR_RA()) { + // If a branch goes to the jr ra, it's still ending here. + if (furthestBranch > addr) { + looking = true; + addr += 4; + } else { + end = true; + } + } + + /* if (looking) { + if (addr >= furthestBranch) { + u32 sureTarget = GetSureBranchTarget(addr); + // Regular j only, jals are to new funcs. + if (sureTarget == INVALIDTARGET && ((op & 0xFC000000) == 0x08000000)) { + sureTarget = GetJumpTarget(addr); + } + + if (sureTarget != INVALIDTARGET && sureTarget < addr) { + end = true; + } else if (sureTarget != INVALIDTARGET) { + // Okay, we have a downward jump. Might be an else or a tail call... + // If there's a jump back upward in spitting distance of it, it's an else. + u32 knownEnd = furthestBranch == 0 ? addr : furthestBranch; + u32 jumpback = ScanAheadForJumpback(sureTarget, currentFunction.start, knownEnd); + if (jumpback != INVALIDTARGET && jumpback > addr && jumpback > knownEnd) { + furthestBranch = jumpback; + } + } + } + } + */ + if (end) { + currentFunction.end = addr + 4; + currentFunction.isStraightLeaf = isStraightLeaf; + functions.push_back(currentFunction); + furthestBranch = 0; + addr += 4; + looking = false; + end = false; + isStraightLeaf = true; + currentFunction.start = addr+4; + } + } + + currentFunction.end = addr + 4; + functions.push_back(currentFunction); + + for (auto iter = functions.begin(); iter != functions.end(); iter++) { + iter->size = iter->end - iter->start + 4; + if (insertSymbols) { + char temp[256]; + symbolMap.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4); + } + } + } + + + enum BranchType { NONE, JUMP, BRANCH }; + struct BranchInfo + { + BranchType type; + bool link; + bool likely; + bool toRegister; + }; + + bool getBranchInfo(MipsOpcodeInfo& info) + { + BranchType type = NONE; + bool link = false; + bool likely = false; + bool toRegister = false; + bool met = false; + + u32 op = info.encodedOpcode; + + u32 opNum = MIPS_GET_OP(op); + u32 rsNum = MIPS_GET_RS(op); + u32 rtNum = MIPS_GET_RT(op); + u32 rs = info.cpu->getRegister(0,rsNum); + u32 rt = info.cpu->getRegister(0,rtNum); + + switch (MIPS_GET_OP(op)) + { + case 0x00: // special + switch (MIPS_GET_FUNC(op)) + { + case 0x08: // jr + type = JUMP; + toRegister = true; + break; + case 0x09: // jalr + type = JUMP; + toRegister = true; + link = true; + break; + } + break; + case 0x01: // regimm + switch (MIPS_GET_RT(op)) + { + case 0x00: // bltz + case 0x02: // bltzl + case 0x10: // bltzal + case 0x12: // bltzall + type = BRANCH; + met = (((s32)rs) < 0); + likely = (rt & 2) != 0; + link = rt >= 0x10; + break; + + case 0x01: // bgez + case 0x03: // bgezl + case 0x11: // bgezal + case 0x13: // bgezall + type = BRANCH; + met = (((s32)rs) >= 0); + likely = (rt & 2) != 0; + link = rt >= 0x10; + break; + } + break; + case 0x02: // j + type = JUMP; + break; + case 0x03: // jal + type = JUMP; + link = true; + break; + + case 0x04: // beq + case 0x14: // beql + type = BRANCH; + met = (rt == rs); + if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) // always true + info.isConditional = false; + likely = opNum >= 0x10; + break; + + case 0x05: // bne + case 0x15: // bnel + type = BRANCH; + met = (rt != rs); + if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) // always false + info.isConditional = false; + likely = opNum >= 0x10; + break; + + case 0x06: // blez + case 0x16: // blezl + type = BRANCH; + met = (((s32)rs) <= 0); + likely = opNum >= 0x10; + break; + + case 0x07: // bgtz + case 0x17: // bgtzl + type = BRANCH; + met = (((s32)rs) > 0); + likely = opNum >= 0x10; + break; + } + + if (type == NONE) + return false; + + info.isBranch = true; + info.isLinkedBranch = link; + info.isLikelyBranch = true; + info.isBranchToRegister = toRegister; + info.isConditional = type == BRANCH; + info.conditionMet = met; + + switch (type) + { + case JUMP: + if (toRegister) + { + info.branchRegisterNum = (int)MIPS_GET_RS(op); + info.branchTarget = info.cpu->getRegister(0,info.branchRegisterNum)._u32[0]; + } else { + info.branchTarget = (info.opcodeAddress & 0xF0000000) | ((op&0x03FFFFFF) << 2); + } + break; + case BRANCH: + info.branchTarget = info.opcodeAddress + 4 + ((signed short)(op&0xFFFF)<<2); + break; + } + + return true; + } + + bool getDataAccessInfo(MipsOpcodeInfo& info) + { + int size = 0; + + u32 op = info.encodedOpcode; + int off = 0; + switch (MIPS_GET_OP(op)) + { + case 0x20: // lb + case 0x24: // lbu + case 0x28: // sb + size = 1; + break; + case 0x21: // lh + case 0x25: // lhu + case 0x29: // sh + size = 2; + break; + case 0x23: // lw + case 0x26: // lwr + case 0x2B: // sw + case 0x2E: // swr + size = 4; + break; + case 0x22: // lwl + case 0x2A: // swl + size = 4; + off = -3; + break; + case 0x37: // ld + case 0x1B: // ldr + case 0x3F: // sd + case 0x2D: // sdr + size = 8; + break; + case 0x1A: // ldl + case 0x2C: // sdl + size = 8; + off = -7; + break; + } + + if (size == 0) + return false; + + info.isDataAccess = true; + info.dataSize = size; + + u32 rs = info.cpu->getRegister(0, (int)MIPS_GET_RS(op)); + s16 imm16 = op & 0xFFFF; + info.dataAddress = rs + imm16 + off; + + info.hasRelevantAddress = true; + info.releventAddress = info.dataAddress; + return true; + } + + MipsOpcodeInfo GetOpcodeInfo(DebugInterface* cpu, u32 address) { + MipsOpcodeInfo info; + memset(&info, 0, sizeof(info)); + + if (cpu->isValidAddress(address) == false) { + return info; + } + + info.cpu = cpu; + info.opcodeAddress = address; + info.encodedOpcode = cpu->read32(address); + u32 op = info.encodedOpcode; + + if (getBranchInfo(info) == true) + return info; + + if (getDataAccessInfo(info) == true) + return info; + + // gather relevant address for alu operations + // that's usually the value of the dest register + switch (MIPS_GET_OP(op)) { + case 0: // special + switch (MIPS_GET_FUNC(op)) { + case 0x20: // add + case 0x21: // addu + info.hasRelevantAddress = true; + info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]+cpu->getRegister(0,MIPS_GET_RT(op))._u32[0]; + break; + case 0x22: // sub + case 0x23: // subu + info.hasRelevantAddress = true; + info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]-cpu->getRegister(0,MIPS_GET_RT(op))._u32[0]; + break; + } + break; + case 0x08: // addi + case 0x09: // adiu + info.hasRelevantAddress = true; + info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]+((s16)(op & 0xFFFF)); + break; + } + + // TODO: rest +/* // movn, movz + if (opInfo & IS_CONDMOVE) { + info.isConditional = true; + + u32 rt = cpu->GetRegValue(0, (int)MIPS_GET_RT(op)); + switch (opInfo & CONDTYPE_MASK) { + case CONDTYPE_EQ: + info.conditionMet = (rt == 0); + break; + case CONDTYPE_NE: + info.conditionMet = (rt != 0); + break; + } + }*/ + + return info; + } +} diff --git a/pcsx2/DebugTools/MIPSAnalyst.h b/pcsx2/DebugTools/MIPSAnalyst.h new file mode 100644 index 0000000000..ba636a1b18 --- /dev/null +++ b/pcsx2/DebugTools/MIPSAnalyst.h @@ -0,0 +1,56 @@ +#pragma once + +class DebugInterface; + + +#define MIPS_GET_OP(op) ((op>>26) & 0x3F) +#define MIPS_GET_FUNC(op) (op & 0x3F) +#define MIPS_GET_SA(op) ((op>>6) & 0x1F) + +#define MIPS_GET_RS(op) ((op>>21) & 0x1F) +#define MIPS_GET_RT(op) ((op>>16) & 0x1F) +#define MIPS_GET_RD(op) ((op>>11) & 0x1F) + +namespace MIPSAnalyst +{ + struct AnalyzedFunction { + u32 start; + u32 end; + u64 hash; + u32 size; + bool isStraightLeaf; + bool hasHash; + bool usesVFPU; + char name[64]; + }; + + void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols); + + typedef struct { + DebugInterface* cpu; + u32 opcodeAddress; + u32 encodedOpcode; + + // shared between branches and conditional moves + bool isConditional; + bool conditionMet; + + // branches + u32 branchTarget; + bool isBranch; + bool isLinkedBranch; + bool isLikelyBranch; + bool isBranchToRegister; + int branchRegisterNum; + + // data access + bool isDataAccess; + int dataSize; + u32 dataAddress; + + bool hasRelevantAddress; + u32 releventAddress; + } MipsOpcodeInfo; + + MipsOpcodeInfo GetOpcodeInfo(DebugInterface* cpu, u32 address); +}; diff --git a/pcsx2/DebugTools/SymbolMap.cpp b/pcsx2/DebugTools/SymbolMap.cpp new file mode 100644 index 0000000000..61c893ae13 --- /dev/null +++ b/pcsx2/DebugTools/SymbolMap.cpp @@ -0,0 +1,704 @@ +#include "PrecompiledHeader.h" + +#include "SymbolMap.h" +#include + +SymbolMap symbolMap; + +#ifdef WIN32 +#define strcasecmp stricmp +#endif + +#define ARRAY_SIZE(x) (sizeof((x))/sizeof(*(x))) + +void SymbolMap::SortSymbols() { + Threading::ScopedLock guard(lock_); + AssignFunctionIndices(); +} + +void SymbolMap::Clear() { + Threading::ScopedLock guard(lock_); + functions.clear(); + labels.clear(); + data.clear(); + activeFunctions.clear(); + activeLabels.clear(); + activeData.clear(); + activeModuleEnds.clear(); + modules.clear(); +} + + +bool SymbolMap::LoadNocashSym(const char *filename) { + Threading::ScopedLock guard(lock_); + FILE *f = fopen(filename, "r"); + if (!f) + return false; + + while (!feof(f)) { + char line[256], value[256] = {0}; + char *p = fgets(line, 256, f); + if (p == NULL) + break; + + u32 address; + if (sscanf(line, "%08X %s", &address, value) != 2) + continue; + if (address == 0 && strcmp(value, "0") == 0) + continue; + + if (value[0] == '.') { + // data directives + char* s = strchr(value, ':'); + if (s != NULL) { + *s = 0; + + u32 size = 0; + if (sscanf(s + 1, "%04X", &size) != 1) + continue; + + if (strcasecmp(value, ".byt") == 0) { + AddData(address, size, DATATYPE_BYTE, 0); + } else if (strcasecmp(value, ".wrd") == 0) { + AddData(address, size, DATATYPE_HALFWORD, 0); + } else if (strcasecmp(value, ".dbl") == 0) { + AddData(address, size, DATATYPE_WORD, 0); + } else if (strcasecmp(value, ".asc") == 0) { + AddData(address, size, DATATYPE_ASCII, 0); + } + } + } else { // labels + int size = 1; + char* seperator = strchr(value, ','); + if (seperator != NULL) { + *seperator = 0; + sscanf(seperator+1,"%08X",&size); + } + + if (size != 1) { + AddFunction(value, address,size, 0); + } else { + AddLabel(value, address, 0); + } + } + } + + fclose(f); + return true; +} + +SymbolType SymbolMap::GetSymbolType(u32 address) const { + Threading::ScopedLock guard(lock_); + if (activeFunctions.find(address) != activeFunctions.end()) + return ST_FUNCTION; + if (activeData.find(address) != activeData.end()) + return ST_DATA; + return ST_NONE; +} + +bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) const { + u32 functionAddress = INVALID_ADDRESS; + u32 dataAddress = INVALID_ADDRESS; + + if (symmask & ST_FUNCTION) + functionAddress = GetFunctionStart(address); + + if (symmask & ST_DATA) + dataAddress = GetDataStart(address); + + if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS) { + if (functionAddress != INVALID_ADDRESS) { + if (info != NULL) { + info->type = ST_FUNCTION; + info->address = functionAddress; + info->size = GetFunctionSize(functionAddress); + } + + return true; + } + + if (dataAddress != INVALID_ADDRESS) { + if (info != NULL) { + info->type = ST_DATA; + info->address = dataAddress; + info->size = GetDataSize(dataAddress); + } + + return true; + } + + return false; + } + + // if both exist, return the function + if (info != NULL) { + info->type = ST_FUNCTION; + info->address = functionAddress; + info->size = GetFunctionSize(functionAddress); + } + + return true; +} + +u32 SymbolMap::GetNextSymbolAddress(u32 address, SymbolType symmask) { + Threading::ScopedLock guard(lock_); + const auto functionEntry = symmask & ST_FUNCTION ? activeFunctions.upper_bound(address) : activeFunctions.end(); + const auto dataEntry = symmask & ST_DATA ? activeData.upper_bound(address) : activeData.end(); + + if (functionEntry == activeFunctions.end() && dataEntry == activeData.end()) + return INVALID_ADDRESS; + + u32 funcAddress = (functionEntry != activeFunctions.end()) ? functionEntry->first : 0xFFFFFFFF; + u32 dataAddress = (dataEntry != activeData.end()) ? dataEntry->first : 0xFFFFFFFF; + + if (funcAddress <= dataAddress) + return funcAddress; + else + return dataAddress; +} + +std::string SymbolMap::GetDescription(unsigned int address) const { + Threading::ScopedLock guard(lock_); + const char* labelName = NULL; + + u32 funcStart = GetFunctionStart(address); + if (funcStart != INVALID_ADDRESS) { + labelName = GetLabelName(funcStart); + } else { + u32 dataStart = GetDataStart(address); + if (dataStart != INVALID_ADDRESS) + labelName = GetLabelName(dataStart); + } + + if (labelName != NULL) + return labelName; + + char descriptionTemp[256]; + sprintf(descriptionTemp, "(%08x)", address); + return descriptionTemp; +} + +std::vector SymbolMap::GetAllSymbols(SymbolType symmask) { + std::vector result; + + if (symmask & ST_FUNCTION) { + Threading::ScopedLock guard(lock_); + for (auto it = activeFunctions.begin(); it != activeFunctions.end(); it++) { + SymbolEntry entry; + entry.address = it->first; + entry.size = GetFunctionSize(entry.address); + const char* name = GetLabelName(entry.address); + if (name != NULL) + entry.name = name; + result.push_back(entry); + } + } + + if (symmask & ST_DATA) { + Threading::ScopedLock guard(lock_); + for (auto it = activeData.begin(); it != activeData.end(); it++) { + SymbolEntry entry; + entry.address = it->first; + entry.size = GetDataSize(entry.address); + const char* name = GetLabelName(entry.address); + if (name != NULL) + entry.name = name; + result.push_back(entry); + } + } + + return result; +} + +void SymbolMap::AddModule(const char *name, u32 address, u32 size) { + Threading::ScopedLock guard(lock_); + + for (auto it = modules.begin(), end = modules.end(); it != end; ++it) { + if (!strcmp(it->name, name)) { + // Just reactivate that one. + it->start = address; + it->size = size; + activeModuleEnds.insert(std::make_pair(it->start + it->size, *it)); + UpdateActiveSymbols(); + return; + } + } + + ModuleEntry mod; + strncpy(mod.name, name, ARRAY_SIZE(mod.name)); + mod.start = address; + mod.size = size; + mod.index = (int)modules.size() + 1; + + modules.push_back(mod); + activeModuleEnds.insert(std::make_pair(mod.start + mod.size, mod)); + UpdateActiveSymbols(); +} + +void SymbolMap::UnloadModule(u32 address, u32 size) { + Threading::ScopedLock guard(lock_); + activeModuleEnds.erase(address + size); + UpdateActiveSymbols(); +} + +u32 SymbolMap::GetModuleRelativeAddr(u32 address, int moduleIndex) const { + Threading::ScopedLock guard(lock_); + if (moduleIndex == -1) { + moduleIndex = GetModuleIndex(address); + } + + for (auto it = modules.begin(), end = modules.end(); it != end; ++it) { + if (it->index == moduleIndex) { + return address - it->start; + } + } + return address; +} + +u32 SymbolMap::GetModuleAbsoluteAddr(u32 relative, int moduleIndex) const { + Threading::ScopedLock guard(lock_); + for (auto it = modules.begin(), end = modules.end(); it != end; ++it) { + if (it->index == moduleIndex) { + return it->start + relative; + } + } + return relative; +} + +int SymbolMap::GetModuleIndex(u32 address) const { + Threading::ScopedLock guard(lock_); + auto iter = activeModuleEnds.upper_bound(address); + if (iter == activeModuleEnds.end()) + return -1; + return iter->second.index; +} + +bool SymbolMap::IsModuleActive(int moduleIndex) const { + if (moduleIndex == 0) { + return true; + } + + Threading::ScopedLock guard(lock_); + for (auto it = activeModuleEnds.begin(), end = activeModuleEnds.end(); it != end; ++it) { + if (it->second.index == moduleIndex) { + return true; + } + } + return false; +} + +std::vector SymbolMap::getAllModules() const { + Threading::ScopedLock guard(lock_); + + std::vector result; + for (size_t i = 0; i < modules.size(); i++) { + LoadedModuleInfo m; + m.name = modules[i].name; + m.address = modules[i].start; + m.size = modules[i].size; + + u32 key = modules[i].start + modules[i].size; + m.active = activeModuleEnds.find(key) != activeModuleEnds.end(); + + result.push_back(m); + } + + return result; +} + +void SymbolMap::AddFunction(const char* name, u32 address, u32 size, int moduleIndex) { + Threading::ScopedLock guard(lock_); + + if (moduleIndex == -1) { + moduleIndex = GetModuleIndex(address); + } + + // Is there an existing one? + u32 relAddress = GetModuleRelativeAddr(address, moduleIndex); + auto symbolKey = std::make_pair(moduleIndex, relAddress); + auto existing = functions.find(symbolKey); + if (existing == functions.end()) { + // Fall back: maybe it's got moduleIndex = 0. + existing = functions.find(std::make_pair(0, address)); + } + + if (existing != functions.end()) { + existing->second.size = size; + if (existing->second.module != moduleIndex) { + FunctionEntry func = existing->second; + func.start = relAddress; + func.module = moduleIndex; + functions.erase(existing); + functions[symbolKey] = func; + } + + // Refresh the active item if it exists. + auto active = activeFunctions.find(address); + if (active != activeFunctions.end() && active->second.module == moduleIndex) { + activeFunctions.erase(active); + activeFunctions.insert(std::make_pair(address, existing->second)); + } + } else { + FunctionEntry func; + func.start = relAddress; + func.size = size; + func.index = (int)functions.size(); + func.module = moduleIndex; + functions[symbolKey] = func; + + if (IsModuleActive(moduleIndex)) { + activeFunctions.insert(std::make_pair(address, func)); + } + } + + AddLabel(name, address, moduleIndex); +} + +u32 SymbolMap::GetFunctionStart(u32 address) const { + Threading::ScopedLock guard(lock_); + auto it = activeFunctions.upper_bound(address); + if (it == activeFunctions.end()) { + // check last element + auto rit = activeFunctions.rbegin(); + if (rit != activeFunctions.rend()) { + u32 start = rit->first; + u32 size = rit->second.size; + if (start <= address && start+size > address) + return start; + } + // otherwise there's no function that contains this address + return INVALID_ADDRESS; + } + + if (it != activeFunctions.begin()) { + it--; + u32 start = it->first; + u32 size = it->second.size; + if (start <= address && start+size > address) + return start; + } + + return INVALID_ADDRESS; +} + +u32 SymbolMap::GetFunctionSize(u32 startAddress) const { + Threading::ScopedLock guard(lock_); + auto it = activeFunctions.find(startAddress); + if (it == activeFunctions.end()) + return INVALID_ADDRESS; + + return it->second.size; +} + +int SymbolMap::GetFunctionNum(u32 address) const { + Threading::ScopedLock guard(lock_); + u32 start = GetFunctionStart(address); + if (start == INVALID_ADDRESS) + return INVALID_ADDRESS; + + auto it = activeFunctions.find(start); + if (it == activeFunctions.end()) + return INVALID_ADDRESS; + + return it->second.index; +} + +void SymbolMap::AssignFunctionIndices() { + Threading::ScopedLock guard(lock_); + int index = 0; + for (auto mod = activeModuleEnds.begin(), modend = activeModuleEnds.end(); mod != modend; ++mod) { + int moduleIndex = mod->second.index; + auto begin = functions.lower_bound(std::make_pair(moduleIndex, 0)); + auto end = functions.upper_bound(std::make_pair(moduleIndex, 0xFFFFFFFF)); + for (auto it = begin; it != end; ++it) { + it->second.index = index++; + } + } +} + +void SymbolMap::UpdateActiveSymbols() { + // return; (slow in debug mode) + Threading::ScopedLock guard(lock_); + std::map activeModuleIndexes; + for (auto it = activeModuleEnds.begin(), end = activeModuleEnds.end(); it != end; ++it) { + activeModuleIndexes[it->second.index] = it->second.start; + } + + activeFunctions.clear(); + activeLabels.clear(); + activeData.clear(); + + for (auto it = functions.begin(), end = functions.end(); it != end; ++it) { + const auto mod = activeModuleIndexes.find(it->second.module); + if (it->second.module <= 0) { + activeFunctions.insert(std::make_pair(it->second.start, it->second)); + } else if (mod != activeModuleIndexes.end()) { + activeFunctions.insert(std::make_pair(mod->second + it->second.start, it->second)); + } + } + + for (auto it = labels.begin(), end = labels.end(); it != end; ++it) { + const auto mod = activeModuleIndexes.find(it->second.module); + if (it->second.module <= 0) { + activeLabels.insert(std::make_pair(it->second.addr, it->second)); + } else if (mod != activeModuleIndexes.end()) { + activeLabels.insert(std::make_pair(mod->second + it->second.addr, it->second)); + } + } + + for (auto it = data.begin(), end = data.end(); it != end; ++it) { + const auto mod = activeModuleIndexes.find(it->second.module); + if (it->second.module <= 0) { + activeData.insert(std::make_pair(it->second.start, it->second)); + } else if (mod != activeModuleIndexes.end()) { + activeData.insert(std::make_pair(mod->second + it->second.start, it->second)); + } + } + + AssignFunctionIndices(); +} + +bool SymbolMap::SetFunctionSize(u32 startAddress, u32 newSize) { + Threading::ScopedLock guard(lock_); + + auto funcInfo = activeFunctions.find(startAddress); + if (funcInfo != activeFunctions.end()) { + auto symbolKey = std::make_pair(funcInfo->second.module, funcInfo->second.start); + auto func = functions.find(symbolKey); + if (func != functions.end()) { + func->second.size = newSize; + UpdateActiveSymbols(); + } + } + + // TODO: check for overlaps + return true; +} + +bool SymbolMap::RemoveFunction(u32 startAddress, bool removeName) { + Threading::ScopedLock guard(lock_); + + auto it = activeFunctions.find(startAddress); + if (it == activeFunctions.end()) + return false; + + auto symbolKey = std::make_pair(it->second.module, it->second.start); + auto it2 = functions.find(symbolKey); + if (it2 != functions.end()) { + functions.erase(it2); + } + activeFunctions.erase(it); + + if (removeName) { + auto labelIt = activeLabels.find(startAddress); + if (labelIt != activeLabels.end()) { + symbolKey = std::make_pair(labelIt->second.module, labelIt->second.addr); + auto labelIt2 = labels.find(symbolKey); + if (labelIt2 != labels.end()) { + labels.erase(labelIt2); + } + activeLabels.erase(labelIt); + } + } + + return true; +} + +void SymbolMap::AddLabel(const char* name, u32 address, int moduleIndex) { + Threading::ScopedLock guard(lock_); + + if (moduleIndex == -1) { + moduleIndex = GetModuleIndex(address); + } + + // Is there an existing one? + u32 relAddress = GetModuleRelativeAddr(address, moduleIndex); + auto symbolKey = std::make_pair(moduleIndex, relAddress); + auto existing = labels.find(symbolKey); + if (existing == labels.end()) { + // Fall back: maybe it's got moduleIndex = 0. + existing = labels.find(std::make_pair(0, address)); + } + + if (existing != labels.end()) { + // We leave an existing label alone, rather than overwriting. + // But we'll still upgrade it to the correct module / relative address. + if (existing->second.module != moduleIndex) { + LabelEntry label = existing->second; + label.addr = relAddress; + label.module = moduleIndex; + labels.erase(existing); + labels[symbolKey] = label; + + // Refresh the active item if it exists. + auto active = activeLabels.find(address); + if (active != activeLabels.end() && active->second.module == moduleIndex) { + activeLabels.erase(active); + activeLabels.insert(std::make_pair(address, existing->second)); + } + } + } else { + LabelEntry label; + label.addr = relAddress; + label.module = moduleIndex; + strncpy(label.name, name, 128); + label.name[127] = 0; + + labels[symbolKey] = label; + if (IsModuleActive(moduleIndex)) { + activeLabels.insert(std::make_pair(address, label)); + } + } +} + +void SymbolMap::SetLabelName(const char* name, u32 address, bool updateImmediately) { + Threading::ScopedLock guard(lock_); + auto labelInfo = activeLabels.find(address); + if (labelInfo == activeLabels.end()) { + AddLabel(name, address); + } else { + auto symbolKey = std::make_pair(labelInfo->second.module, labelInfo->second.addr); + auto label = labels.find(symbolKey); + if (label != labels.end()) { + strcpy(label->second.name,name); + label->second.name[127] = 0; + + // Allow the caller to skip this as it causes extreme startup slowdown + // when this gets called for every function identified by the function replacement code. + if (updateImmediately) { + UpdateActiveSymbols(); + } + } + } +} + +const char *SymbolMap::GetLabelName(u32 address) const { + Threading::ScopedLock guard(lock_); + auto it = activeLabels.find(address); + if (it == activeLabels.end()) + return NULL; + + return it->second.name; +} + +const char *SymbolMap::GetLabelNameRel(u32 relAddress, int moduleIndex) const { + Threading::ScopedLock guard(lock_); + auto it = labels.find(std::make_pair(moduleIndex, relAddress)); + if (it == labels.end()) + return NULL; + + return it->second.name; +} + +std::string SymbolMap::GetLabelString(u32 address) const { + Threading::ScopedLock guard(lock_); + const char *label = GetLabelName(address); + if (label == NULL) + return ""; + return label; +} + +bool SymbolMap::GetLabelValue(const char* name, u32& dest) { + Threading::ScopedLock guard(lock_); + for (auto it = activeLabels.begin(); it != activeLabels.end(); it++) { + if (strcasecmp(name, it->second.name) == 0) { + dest = it->first; + return true; + } + } + + return false; +} + +void SymbolMap::AddData(u32 address, u32 size, DataType type, int moduleIndex) { + Threading::ScopedLock guard(lock_); + + if (moduleIndex == -1) { + moduleIndex = GetModuleIndex(address); + } + + // Is there an existing one? + u32 relAddress = GetModuleRelativeAddr(address, moduleIndex); + auto symbolKey = std::make_pair(moduleIndex, relAddress); + auto existing = data.find(symbolKey); + if (existing == data.end()) { + // Fall back: maybe it's got moduleIndex = 0. + existing = data.find(std::make_pair(0, address)); + } + + if (existing != data.end()) { + existing->second.size = size; + existing->second.type = type; + if (existing->second.module != moduleIndex) { + DataEntry entry = existing->second; + entry.module = moduleIndex; + entry.start = relAddress; + data.erase(existing); + data[symbolKey] = entry; + } + + // Refresh the active item if it exists. + auto active = activeData.find(address); + if (active != activeData.end() && active->second.module == moduleIndex) { + activeData.erase(active); + activeData.insert(std::make_pair(address, existing->second)); + } + } else { + DataEntry entry; + entry.start = relAddress; + entry.size = size; + entry.type = type; + entry.module = moduleIndex; + + data[symbolKey] = entry; + if (IsModuleActive(moduleIndex)) { + activeData.insert(std::make_pair(address, entry)); + } + } +} + +u32 SymbolMap::GetDataStart(u32 address) const { + Threading::ScopedLock guard(lock_); + auto it = activeData.upper_bound(address); + if (it == activeData.end()) + { + // check last element + auto rit = activeData.rbegin(); + + if (rit != activeData.rend()) + { + u32 start = rit->first; + u32 size = rit->second.size; + if (start <= address && start+size > address) + return start; + } + // otherwise there's no data that contains this address + return INVALID_ADDRESS; + } + + if (it != activeData.begin()) { + it--; + u32 start = it->first; + u32 size = it->second.size; + if (start <= address && start+size > address) + return start; + } + + return INVALID_ADDRESS; +} + +u32 SymbolMap::GetDataSize(u32 startAddress) const { + Threading::ScopedLock guard(lock_); + auto it = activeData.find(startAddress); + if (it == activeData.end()) + return INVALID_ADDRESS; + return it->second.size; +} + +DataType SymbolMap::GetDataType(u32 startAddress) const { + Threading::ScopedLock guard(lock_); + auto it = activeData.find(startAddress); + if (it == activeData.end()) + return DATATYPE_NONE; + return it->second.type; +} diff --git a/pcsx2/DebugTools/SymbolMap.h b/pcsx2/DebugTools/SymbolMap.h new file mode 100644 index 0000000000..79ab2cf410 --- /dev/null +++ b/pcsx2/DebugTools/SymbolMap.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include + +#include "Utilities/Threading.h" +#include "Pcsx2Types.h" + +enum SymbolType { + ST_NONE = 0, + ST_FUNCTION = 1, + ST_DATA = 2, + ST_ALL = 3, +}; + +struct SymbolInfo { + SymbolType type; + u32 address; + u32 size; +}; + +struct SymbolEntry { + std::string name; + u32 address; + u32 size; +}; + +struct LoadedModuleInfo { + std::string name; + u32 address; + u32 size; + bool active; +}; + +enum DataType { + DATATYPE_NONE, DATATYPE_BYTE, DATATYPE_HALFWORD, DATATYPE_WORD, DATATYPE_ASCII +}; + +class SymbolMap { +public: + SymbolMap() {} + void Clear(); + void SortSymbols(); + + bool LoadNocashSym(const char *ilename); + + SymbolType GetSymbolType(u32 address) const; + bool GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask = ST_FUNCTION) const; + u32 GetNextSymbolAddress(u32 address, SymbolType symmask); + std::string GetDescription(unsigned int address) const; + std::vector GetAllSymbols(SymbolType symmask); + + void AddModule(const char *name, u32 address, u32 size); + void UnloadModule(u32 address, u32 size); + u32 GetModuleRelativeAddr(u32 address, int moduleIndex = -1) const; + u32 GetModuleAbsoluteAddr(u32 relative, int moduleIndex) const; + int GetModuleIndex(u32 address) const; + bool IsModuleActive(int moduleIndex) const; + std::vector getAllModules() const; + + void AddFunction(const char* name, u32 address, u32 size, int moduleIndex = -1); + u32 GetFunctionStart(u32 address) const; + int GetFunctionNum(u32 address) const; + u32 GetFunctionSize(u32 startAddress) const; + bool SetFunctionSize(u32 startAddress, u32 newSize); + bool RemoveFunction(u32 startAddress, bool removeName); + + void AddLabel(const char* name, u32 address, int moduleIndex = -1); + std::string GetLabelString(u32 address) const; + void SetLabelName(const char* name, u32 address, bool updateImmediately = true); + bool GetLabelValue(const char* name, u32& dest); + + void AddData(u32 address, u32 size, DataType type, int moduleIndex = -1); + u32 GetDataStart(u32 address) const; + u32 GetDataSize(u32 startAddress) const; + DataType GetDataType(u32 startAddress) const; + + static const u32 INVALID_ADDRESS = (u32)-1; + + void UpdateActiveSymbols(); + +private: + void AssignFunctionIndices(); + const char *GetLabelName(u32 address) const; + const char *GetLabelNameRel(u32 relAddress, int moduleIndex) const; + + struct FunctionEntry { + u32 start; + u32 size; + int index; + int module; + }; + + struct LabelEntry { + u32 addr; + int module; + char name[128]; + }; + + struct DataEntry { + DataType type; + u32 start; + u32 size; + int module; + }; + + struct ModuleEntry { + // Note: this index is +1, 0 matches any for backwards-compat. + int index; + u32 start; + u32 size; + char name[128]; + }; + + // These are flattened, read-only copies of the actual data in active modules only. + std::map activeFunctions; + std::map activeLabels; + std::map activeData; + + // This is indexed by the end address of the module. + std::map activeModuleEnds; + + typedef std::pair SymbolKey; + + // These are indexed by the module id and relative address in the module. + std::map functions; + std::map labels; + std::map data; + std::vector modules; + + mutable Threading::MutexRecursive lock_; +}; + +extern SymbolMap symbolMap; + diff --git a/pcsx2/Elfheader.cpp b/pcsx2/Elfheader.cpp index dfdbfc31ff..c68e99b551 100644 --- a/pcsx2/Elfheader.cpp +++ b/pcsx2/Elfheader.cpp @@ -23,6 +23,7 @@ using namespace std; u32 ElfCRC; u32 ElfEntry; +std::pair ElfTextRange; wxString LastELF; #if 0 @@ -238,6 +239,20 @@ bool ElfObject::hasProgramHeaders() { return (proghead != NULL); } bool ElfObject::hasSectionHeaders() { return (secthead != NULL); } bool ElfObject::hasHeaders() { return (hasProgramHeaders() && hasSectionHeaders()); } +std::pair ElfObject::getTextRange() +{ + for (int i = 0; i < header.e_phnum; i++) + { + u32 start = proghead[i].p_vaddr; + u32 size = proghead[i].p_memsz; + + if (start <= header.e_entry && (start+size) > header.e_entry) + return std::make_pair(start,size); + } + + return std::make_pair(0,0); +} + void ElfObject::readIso(IsoFile file) { int rsize = file.read(data.GetPtr(), data.GetSizeInBytes()); diff --git a/pcsx2/Elfheader.h b/pcsx2/Elfheader.h index 591d5f3fdf..2603a06d9b 100644 --- a/pcsx2/Elfheader.h +++ b/pcsx2/Elfheader.h @@ -155,6 +155,7 @@ class ElfObject bool hasSectionHeaders(); bool hasHeaders(); + std::pair getTextRange(); u32 getCRC(); }; @@ -165,6 +166,7 @@ extern int GetPS2ElfName( wxString& dest ); extern u32 ElfCRC; extern u32 ElfEntry; +extern std::pair ElfTextRange; extern wxString LastELF; #endif diff --git a/pcsx2/R5900OpcodeImpl.cpp b/pcsx2/R5900OpcodeImpl.cpp index 213df7e063..60a3394de1 100644 --- a/pcsx2/R5900OpcodeImpl.cpp +++ b/pcsx2/R5900OpcodeImpl.cpp @@ -71,7 +71,17 @@ const R5900::OPCODE& R5900::GetCurrentInstruction() const OPCODE* opcode = &R5900::OpcodeTables::tbl_Standard[_Opcode_]; while( opcode->getsubclass != NULL ) - opcode = &opcode->getsubclass(); + opcode = &opcode->getsubclass(cpuRegs.code); + + return *opcode; +} + +const R5900::OPCODE& R5900::GetInstruction(u32 op) +{ + const OPCODE* opcode = &R5900::OpcodeTables::tbl_Standard[op >> 26]; + + while( opcode->getsubclass != NULL ) + opcode = &opcode->getsubclass(op); return *opcode; } diff --git a/pcsx2/R5900OpcodeTables.cpp b/pcsx2/R5900OpcodeTables.cpp index 8e762ad8c2..d7f0ef4350 100644 --- a/pcsx2/R5900OpcodeTables.cpp +++ b/pcsx2/R5900OpcodeTables.cpp @@ -619,23 +619,23 @@ namespace R5900 { using namespace OpcodeTables; - const OPCODE& Class_SPECIAL() { return tbl_Special[_Funct_]; } - const OPCODE& Class_REGIMM() { return tbl_RegImm[_Rt_]; } + const OPCODE& Class_SPECIAL(u32 op) { return tbl_Special[op & 0x3F]; } + const OPCODE& Class_REGIMM(u32 op) { return tbl_RegImm[(op >> 16) & 0x1F]; } + + const OPCODE& Class_MMI(u32 op) { return tbl_MMI[op & 0x3F]; } + const OPCODE& Class_MMI0(u32 op) { return tbl_MMI0[(op >> 6) & 0x1F]; } + const OPCODE& Class_MMI1(u32 op) { return tbl_MMI1[(op >> 6) & 0x1F]; } + const OPCODE& Class_MMI2(u32 op) { return tbl_MMI2[(op >> 6) & 0x1F]; } + const OPCODE& Class_MMI3(u32 op) { return tbl_MMI3[(op >> 6) & 0x1F]; } - const OPCODE& Class_MMI() { return tbl_MMI[_Funct_]; } - const OPCODE& Class_MMI0() { return tbl_MMI0[_Sa_]; } - const OPCODE& Class_MMI1() { return tbl_MMI1[_Sa_]; } - const OPCODE& Class_MMI2() { return tbl_MMI2[_Sa_]; } - const OPCODE& Class_MMI3() { return tbl_MMI3[_Sa_]; } + const OPCODE& Class_COP0(u32 op) { return tbl_COP0[(op >> 21) & 0x1F]; } + const OPCODE& Class_COP0_BC0(u32 op) { return tbl_COP0_BC0[(cpuRegs.code >> 16) & 0x03]; } + const OPCODE& Class_COP0_C0(u32 op) { return tbl_COP0_C0[op & 0x3F]; } - const OPCODE& Class_COP0() { return tbl_COP0[_Rs_]; } - const OPCODE& Class_COP0_BC0() { return tbl_COP0_BC0[(cpuRegs.code >> 16) & 0x03]; } - const OPCODE& Class_COP0_C0() { return tbl_COP0_C0[_Funct_]; } - - const OPCODE& Class_COP1() { return tbl_COP1[_Rs_]; } - const OPCODE& Class_COP1_BC1() { return tbl_COP1_BC1[_Rt_]; } - const OPCODE& Class_COP1_S() { return tbl_COP1_S[_Funct_]; } - const OPCODE& Class_COP1_W() { return tbl_COP1_W[_Funct_]; } + const OPCODE& Class_COP1(u32 op) { return tbl_COP1[(op >> 21) & 0x1F]; } + const OPCODE& Class_COP1_BC1(u32 op) { return tbl_COP1_BC1[(op >> 16) & 0x1F]; } + const OPCODE& Class_COP1_S(u32 op) { return tbl_COP1_S[op & 0x3F]; } + const OPCODE& Class_COP1_W(u32 op) { return tbl_COP1_W[op & 0x3F]; } // These are for future use when the COP2 tables are completed. //const OPCODE& Class_COP2() { return tbl_COP2[_Rs_]; } diff --git a/pcsx2/R5900OpcodeTables.h b/pcsx2/R5900OpcodeTables.h index 74b58c27ed..385991a410 100644 --- a/pcsx2/R5900OpcodeTables.h +++ b/pcsx2/R5900OpcodeTables.h @@ -79,7 +79,7 @@ namespace R5900 // Number of cycles this instruction normally uses. u8 cycles; - const OPCODE& (*getsubclass)(); + const OPCODE& (*getsubclass)(u32 op); // Process the instruction using the interpreter. // The action is performed immediately on the EE's cpu state. @@ -96,7 +96,7 @@ namespace R5900 // Returns the current real instruction, as per the current cpuRegs settings. const OPCODE& GetCurrentInstruction(); - + const OPCODE& GetInstruction(u32 op); namespace OpcodeTables { using ::R5900::OPCODE; @@ -126,22 +126,22 @@ namespace R5900 { using ::R5900::OPCODE; - const OPCODE& Class_SPECIAL(); - const OPCODE& Class_REGIMM(); - const OPCODE& Class_MMI(); - const OPCODE& Class_MMI0(); - const OPCODE& Class_MMI1(); - const OPCODE& Class_MMI2(); - const OPCODE& Class_MMI3(); + const OPCODE& Class_SPECIAL(u32 op); + const OPCODE& Class_REGIMM(u32 op); + const OPCODE& Class_MMI(u32 op); + const OPCODE& Class_MMI0(u32 op); + const OPCODE& Class_MMI1(u32 op); + const OPCODE& Class_MMI2(u32 op); + const OPCODE& Class_MMI3(u32 op); - const OPCODE& Class_COP0(); - const OPCODE& Class_COP0_BC0(); - const OPCODE& Class_COP0_C0(); + const OPCODE& Class_COP0(u32 op); + const OPCODE& Class_COP0_BC0(u32 op); + const OPCODE& Class_COP0_C0(u32 op); - const OPCODE& Class_COP1(); - const OPCODE& Class_COP1_BC1(); - const OPCODE& Class_COP1_S(); - const OPCODE& Class_COP1_W(); + const OPCODE& Class_COP1(u32 op); + const OPCODE& Class_COP1_BC1(u32 op); + const OPCODE& Class_COP1_S(u32 op); + const OPCODE& Class_COP1_W(u32 op); } namespace OpcodeDisasm diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index a968bb27a2..e6f8293ea4 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -25,6 +25,9 @@ #include "SysThreads.h" #include "MTVU.h" +#include "../DebugTools/MIPSAnalyst.h" +#include "../DebugTools/SymbolMap.h" + #include "Utilities/PageFaultSource.h" #include "Utilities/TlsVariable.inl" @@ -127,6 +130,7 @@ void SysCoreThread::Reset() ResetQuick(); GetVmMemory().DecommitAll(); SysClearExecutionCache(); + sApp.PostAppMethod( &Pcsx2App::leaveDebugMode ); } @@ -226,6 +230,12 @@ void SysCoreThread::GameStartingInThread() { GetMTGS().SendGameCRC(ElfCRC); +#ifdef PCSX2_DEVBUILD + MIPSAnalyst::ScanForFunctions(ElfTextRange.first,ElfTextRange.first+ElfTextRange.second,true); + symbolMap.UpdateActiveSymbols(); + sApp.PostAppMethod(&Pcsx2App::resetDebugger); +#endif + if (EmuConfig.EnablePatches) ApplyPatch(0); if (EmuConfig.EnableCheats) ApplyCheat(0); } diff --git a/pcsx2/System/SysThreadBase.cpp b/pcsx2/System/SysThreadBase.cpp index 20699630ce..694a6a3a78 100644 --- a/pcsx2/System/SysThreadBase.cpp +++ b/pcsx2/System/SysThreadBase.cpp @@ -142,12 +142,28 @@ void SysThreadBase::Pause() pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." ); + OnPause(); m_sem_event.Post(); } m_RunningLock.Wait(); } +void SysThreadBase::PauseSelf() +{ + if( !IsSelf() || !IsRunning() ) return; + + { + ScopedLock locker( m_ExecModeMutex ); + + if( m_ExecMode == ExecMode_Opened ) + m_ExecMode = ExecMode_Pausing; + + OnPause(); + m_sem_event.Post(); + } +} + // Resumes the core execution state, or does nothing is the core is already running. If // settings were changed, resets will be performed as needed and emulation state resumed from // memory savestates. diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h index 3ca161c2ae..853ad4d735 100644 --- a/pcsx2/System/SysThreads.h +++ b/pcsx2/System/SysThreads.h @@ -113,7 +113,8 @@ public: virtual void Suspend( bool isBlocking = true ); virtual void Resume(); virtual void Pause(); - + virtual void PauseSelf(); + protected: virtual void OnStart(); @@ -121,6 +122,7 @@ protected: // the core emulation thread. You should overload this rather than Resume(), since // Resume() has a lot of checks and balances to prevent re-entrance and race conditions. virtual void OnResumeReady() {} + virtual void OnPause() {} virtual bool StateCheckInThread(); virtual void OnCleanupInThread(); diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 9ec953a847..8595b04d0c 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -26,6 +26,8 @@ #include "AppCoreThread.h" #include "RecentIsoList.h" +class DisassemblyDialog; + #include "System.h" #include "System/SysThreads.h" @@ -503,6 +505,7 @@ protected: wxWindowID m_id_MainFrame; wxWindowID m_id_GsFrame; wxWindowID m_id_ProgramLogBox; + wxWindowID m_id_Disassembler; wxKeyEvent m_kevt; @@ -521,11 +524,16 @@ public: SysMainMemory& GetVmReserve(); - GSFrame& GetGsFrame() const; - MainEmuFrame& GetMainFrame() const; + GSFrame& GetGsFrame() const; + MainEmuFrame& GetMainFrame() const; - GSFrame* GetGsFramePtr() const { return (GSFrame*)wxWindow::FindWindowById( m_id_GsFrame ); } - MainEmuFrame* GetMainFramePtr() const { return (MainEmuFrame*)wxWindow::FindWindowById( m_id_MainFrame ); } + GSFrame* GetGsFramePtr() const { return (GSFrame*)wxWindow::FindWindowById( m_id_GsFrame ); } + MainEmuFrame* GetMainFramePtr() const { return (MainEmuFrame*)wxWindow::FindWindowById( m_id_MainFrame ); } + DisassemblyDialog* GetDisassemblyPtr() const { return (DisassemblyDialog*)wxWindow::FindWindowById( m_id_Disassembler ); } + + void enterDebugMode(); + void leaveDebugMode(); + void resetDebugger(); bool HasMainFrame() const { return GetMainFramePtr() != NULL; } diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index c2c17f862e..3f1fccd548 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -20,6 +20,8 @@ #include +#include "Debugger/DisassemblyDialog.h" + #include "Utilities/TlsVariable.inl" #include "ps2/BiosTools.h" @@ -107,7 +109,7 @@ void AppCoreThread::Reset() GetSysExecutorThread().PostEvent( SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Reset) ); return; } - + _parent::Reset(); } @@ -197,9 +199,17 @@ void AppCoreThread::OnResumeReady() { wxGetApp().SysApplySettings(); wxGetApp().PostMethod( AppSaveSettings ); + + sApp.PostAppMethod( &Pcsx2App::leaveDebugMode ); _parent::OnResumeReady(); } +void AppCoreThread::OnPause() +{ + sApp.PostAppMethod( &Pcsx2App::enterDebugMode ); + _parent::OnPause(); +} + // Load Game Settings found in database // (game fixes, round modes, clamp modes, etc...) // Returns number of gamefixes set diff --git a/pcsx2/gui/AppCoreThread.h b/pcsx2/gui/AppCoreThread.h index 3ed3ae90a8..895fb635e4 100644 --- a/pcsx2/gui/AppCoreThread.h +++ b/pcsx2/gui/AppCoreThread.h @@ -146,6 +146,7 @@ protected: virtual void DoCpuExecute(); virtual void OnResumeReady(); + virtual void OnPause(); virtual void OnResumeInThread( bool IsSuspended ); virtual void OnSuspendInThread(); virtual void OnCleanupInThread(); diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index 5f62067968..ad9bab57ac 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -23,6 +23,8 @@ #include "DebugTools/Debug.h" #include "Dialogs/ModalPopups.h" +#include "Debugger/DisassemblyDialog.h" + #include #include #include @@ -86,6 +88,9 @@ void Pcsx2App::OpenMainFrame() MainEmuFrame* mainFrame = new MainEmuFrame( NULL, pxGetAppName() ); m_id_MainFrame = mainFrame->GetId(); + DisassemblyDialog* disassembly = new DisassemblyDialog( mainFrame ); + m_id_Disassembler = disassembly->GetId(); + PostIdleAppMethod( &Pcsx2App::OpenProgramLog ); SetTopWindow( mainFrame ); // not really needed... diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 19f2676b36..218d77ad0e 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -28,6 +28,8 @@ #include "Dialogs/ConfigurationDialog.h" #include "Dialogs/LogOptionsDialog.h" +#include "Debugger/DisassemblyDialog.h" + #include "Utilities/IniInterface.h" #include @@ -751,6 +753,27 @@ GSFrame& Pcsx2App::GetGsFrame() const return *gsFrame; } +void Pcsx2App::enterDebugMode() +{ + DisassemblyDialog* dlg = GetDisassemblyPtr(); + if (dlg) + dlg->setDebugMode(true); +} + +void Pcsx2App::leaveDebugMode() +{ + DisassemblyDialog* dlg = GetDisassemblyPtr(); + if (dlg) + dlg->setDebugMode(false); +} + +void Pcsx2App::resetDebugger() +{ + DisassemblyDialog* dlg = GetDisassemblyPtr(); + if (dlg) + dlg->reset(); +} + // NOTE: Plugins are *not* applied by this function. Changes to plugins need to handled // manually. The PluginSelectorPanel does this, for example. void AppApplySettings( const AppConfig* oldconf ) diff --git a/pcsx2/gui/Debugger/BreakpointWindow.cpp b/pcsx2/gui/Debugger/BreakpointWindow.cpp new file mode 100644 index 0000000000..a8c00d9807 --- /dev/null +++ b/pcsx2/gui/Debugger/BreakpointWindow.cpp @@ -0,0 +1,332 @@ +#include "PrecompiledHeader.h" +#include "BreakpointWindow.h" + + +BEGIN_EVENT_TABLE(BreakpointWindow, wxDialog) + EVT_RADIOBUTTON(wxID_ANY, BreakpointWindow::onRadioChange) + EVT_BUTTON(wxID_OK, BreakpointWindow::onButtonOk) +END_EVENT_TABLE() + + +BreakpointWindow::BreakpointWindow( wxWindow* parent, DebugInterface* _cpu ) + : wxDialog(parent,wxID_ANY,L"Breakpoint"), cpu(_cpu) +{ + wxBoxSizer* topLevelSizer = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* upperPart = new wxBoxSizer(wxHORIZONTAL); + + wxFlexGridSizer* leftSizer = new wxFlexGridSizer(2,2,7,7); + + // address + wxStaticText* addressText = new wxStaticText(this,wxID_ANY,L"Address"); + editAddress = new wxTextCtrl(this,wxID_ANY,L""); + + leftSizer->Add(addressText,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); + leftSizer->Add(editAddress,1); + + // size + wxStaticText* sizeText = new wxStaticText(this,wxID_ANY,L"Size"); + editSize = new wxTextCtrl(this,wxID_ANY,L""); + leftSizer->Add(sizeText,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); + leftSizer->Add(editSize,1); + + // right part + wxFlexGridSizer* rightSizer = new wxFlexGridSizer(3,2,7,7); + + radioMemory = new wxRadioButton(this,wxID_ANY,L"Memory",wxDefaultPosition,wxDefaultSize,wxRB_GROUP); + radioExecute = new wxRadioButton(this,wxID_ANY,L"Execute"); + rightSizer->Add(radioMemory,1); + rightSizer->Add(radioExecute,1,wxLEFT,8); + + checkRead = new wxCheckBox(this,wxID_ANY,L"Read"); + checkWrite = new wxCheckBox(this,wxID_ANY,L"Write"); + checkOnChange = new wxCheckBox(this,wxID_ANY,L"On change"); + rightSizer->Add(checkRead,1); + rightSizer->Add(new wxStaticText(this,wxID_ANY,L""),1); + rightSizer->Add(checkWrite,1); + rightSizer->Add(checkOnChange,1,wxLEFT,8); + + upperPart->Add(leftSizer,1,wxLEFT|wxTOP|wxRIGHT,8); + upperPart->Add(rightSizer,1,wxLEFT|wxTOP|wxRIGHT,8); + + // bottom part + wxBoxSizer* conditionSizer = new wxBoxSizer(wxHORIZONTAL); + wxStaticText* conditionText = new wxStaticText(this,wxID_ANY,L"Condition"); + editCondition = new wxTextCtrl(this,wxID_ANY,L""); + conditionSizer->Add(conditionText,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); + conditionSizer->AddSpacer(7); + conditionSizer->Add(editCondition,1,wxEXPAND); + + wxBoxSizer* bottomRowSizer = new wxBoxSizer(wxHORIZONTAL); + checkEnabled = new wxCheckBox(this,wxID_ANY,L"Enabled"); + checkLog = new wxCheckBox(this,wxID_ANY,L"Log"); + buttonOk = new wxButton(this,wxID_OK,L"OK"); + buttonCancel = new wxButton(this,wxID_CANCEL,L"Cancel"); + bottomRowSizer->AddStretchSpacer(); + bottomRowSizer->Add(checkEnabled,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); + bottomRowSizer->AddSpacer(8); + bottomRowSizer->Add(checkLog,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); + bottomRowSizer->AddSpacer(8); + bottomRowSizer->Add(buttonOk,1); + bottomRowSizer->AddSpacer(4); + bottomRowSizer->Add(buttonCancel,1); + + // align labels + int minWidth = std::max(addressText->GetSize().x,sizeText->GetSize().x); + minWidth = std::max(minWidth,conditionText->GetSize().x); + addressText->SetMinSize(wxSize(minWidth,addressText->GetSize().y)); + sizeText->SetMinSize(wxSize(minWidth,sizeText->GetSize().y)); + conditionText->SetMinSize(wxSize(minWidth,conditionText->GetSize().y)); + + // set tab order + radioMemory->MoveAfterInTabOrder(editAddress); + radioExecute->MoveAfterInTabOrder(radioMemory); + editSize->MoveAfterInTabOrder(radioExecute); + checkRead->MoveAfterInTabOrder(editSize); + checkWrite->MoveAfterInTabOrder(checkRead); + checkOnChange->MoveAfterInTabOrder(checkWrite); + editCondition->MoveAfterInTabOrder(checkOnChange); + checkEnabled->MoveAfterInTabOrder(editCondition); + checkLog->MoveAfterInTabOrder(checkEnabled); + buttonOk->MoveAfterInTabOrder(checkLog); + buttonCancel->MoveAfterInTabOrder(buttonOk); + + topLevelSizer->Add(upperPart,0); + topLevelSizer->AddSpacer(5); + topLevelSizer->Add(conditionSizer,0,wxLEFT|wxTOP|wxRIGHT|wxEXPAND,8); + topLevelSizer->AddSpacer(5); + topLevelSizer->Add(bottomRowSizer,0,wxLEFT|wxTOP|wxRIGHT|wxBOTTOM,8); + SetSizer(topLevelSizer); + topLevelSizer->Fit(this); + + buttonOk->SetDefault(); + + // default values + memory = true; + onChange = false; + read = write = true; + enabled = log = true; + address = -1; + size = 1; + condition[0] = 0; + setDefaultValues(); +} + +void BreakpointWindow::setDefaultValues() +{ + radioExecute->SetValue(!memory); + radioMemory->SetValue(memory); + checkRead->SetValue(read); + checkWrite->SetValue(write); + checkOnChange->SetValue(onChange); + checkEnabled->SetValue(enabled); + checkLog->SetValue(log); + + checkRead->Enable(memory); + checkWrite->Enable(memory); + checkOnChange->Enable(memory); + checkRead->Enable(memory); + editSize->Enable(memory); + editCondition->Enable(!memory); + checkLog->Enable(memory); + + wchar_t str[64]; + if (address != -1) + { + swprintf(str,64,L"0x%08X",address); + editAddress->SetLabel(str); + } + + swprintf(str,64,L"0x%08X",size); + editSize->SetLabel(str); + editCondition->SetLabel(wxString(condition,wxConvUTF8)); +} + +void BreakpointWindow::loadFromMemcheck(MemCheck& memcheck) +{ + memory = true; + + read = (memcheck.cond & MEMCHECK_READ) != 0; + write = (memcheck.cond & MEMCHECK_WRITE) != 0; + onChange = (memcheck.cond & MEMCHECK_WRITE_ONCHANGE) != 0; + + switch (memcheck.result) + { + case MEMCHECK_BOTH: + log = enabled = true; + break; + case MEMCHECK_LOG: + log = true; + enabled = false; + break; + case MEMCHECK_BREAK: + log = false; + enabled = true; + break; + case MEMCHECK_IGNORE: + log = enabled = false; + break; + } + + address = memcheck.start; + size = memcheck.end-address; + + setDefaultValues(); +} + +void BreakpointWindow::loadFromBreakpoint(BreakPoint& breakpoint) +{ + memory = false; + + enabled = breakpoint.enabled; + address = breakpoint.addr; + size = 1; + + if (breakpoint.hasCond) + { + strcpy(condition,breakpoint.cond.expressionString); + } else { + condition[0] = 0; + } + + setDefaultValues(); +} + +void BreakpointWindow::initBreakpoint(u32 _address) +{ + memory = false; + enabled = true; + address = _address; + size = 1; + condition[0] = 0; + + setDefaultValues(); +} + +void BreakpointWindow::onRadioChange(wxCommandEvent& evt) +{ + memory = radioMemory->GetValue(); + + checkRead->Enable(memory); + checkWrite->Enable(memory); + checkOnChange->Enable(memory); + checkRead->Enable(memory); + editSize->Enable(memory); + editCondition->Enable(!memory); + checkLog->Enable(memory); +} + +bool BreakpointWindow::fetchDialogData() +{ + wchar_t errorMessage[512]; + PostfixExpression exp; + + memory = radioMemory->GetValue(); + read = checkRead->GetValue(); + write = checkWrite->GetValue(); + enabled = checkEnabled->GetValue(); + log = checkLog->GetValue(); + onChange = checkOnChange->GetValue(); + + // parse address + wxCharBuffer addressText = editAddress->GetLabel().ToUTF8(); + if (cpu->initExpression(addressText,exp) == false) + { + swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetLabel().wchar_str()); + wxMessageBox(errorMessage,L"Error",wxICON_ERROR); + return false; + } + + u64 value; + if (cpu->parseExpression(exp,value) == false) + { + swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetLabel().wchar_str()); + wxMessageBox(errorMessage,L"Error",wxICON_ERROR); + return false; + } + address = value; + + if (memory) + { + // parse size + wxCharBuffer sizeText = editSize->GetLabel().ToUTF8(); + if (cpu->initExpression(sizeText,exp) == false) + { + swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetLabel().wchar_str()); + wxMessageBox(errorMessage,L"Error",wxICON_ERROR); + return false; + } + + if (cpu->parseExpression(exp,value) == false) + { + swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetLabel().wchar_str()); + wxMessageBox(errorMessage,L"Error",wxICON_ERROR); + return false; + } + size = value; + } + + // condition + wxCharBuffer conditionText = editCondition->GetLabel().ToUTF8(); + strncpy(condition,conditionText,sizeof(condition)); + condition[sizeof(condition)-1] = 0; + + compiledCondition.clear(); + if (condition[0] != 0) + { + if (cpu->initExpression(condition,compiledCondition) == false) + { + swprintf(errorMessage,512,L"Invalid expression \"%s\".",editCondition->GetLabel().wchar_str()); + wxMessageBox(errorMessage,L"Error",wxICON_ERROR); + return false; + } + } + + return true; +} + +void BreakpointWindow::onButtonOk(wxCommandEvent& evt) +{ + if (fetchDialogData() == true) + evt.Skip(); +} + +void BreakpointWindow::addBreakpoint() +{ + if (memory) + { + // add memcheck + int cond = 0; + if (read) + cond |= MEMCHECK_READ; + if (write) + cond |= MEMCHECK_WRITE; + if (onChange) + cond |= MEMCHECK_WRITE_ONCHANGE; + + MemCheckResult result; + if (log && enabled) result = MEMCHECK_BOTH; + else if (log) result = MEMCHECK_LOG; + else if (enabled) result = MEMCHECK_BREAK; + else result = MEMCHECK_IGNORE; + + CBreakPoints::AddMemCheck(address, address + size, (MemCheckCondition)cond, result); + } else { + // add breakpoint + CBreakPoints::AddBreakPoint(address,false); + + if (condition[0] != 0) + { + BreakPointCond cond; + cond.debug = cpu; + strcpy(cond.expressionString,condition); + cond.expression = compiledCondition; + CBreakPoints::ChangeBreakPointAddCond(address,cond); + } + + if (enabled == false) + { + CBreakPoints::ChangeBreakPoint(address,false); + } + } +} diff --git a/pcsx2/gui/Debugger/BreakpointWindow.h b/pcsx2/gui/Debugger/BreakpointWindow.h new file mode 100644 index 0000000000..bbd43a040a --- /dev/null +++ b/pcsx2/gui/Debugger/BreakpointWindow.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include "DebugTools/DebugInterface.h" +#include "DebugTools/Breakpoints.h" + +class BreakpointWindow : public wxDialog +{ +public: + BreakpointWindow( wxWindow* parent, DebugInterface* _cpu ); + void loadFromMemcheck(MemCheck& memcheck); + void loadFromBreakpoint(BreakPoint& breakpoint); + void initBreakpoint(u32 _address); + void addBreakpoint(); + + DECLARE_EVENT_TABLE() +protected: + void onRadioChange(wxCommandEvent& evt); + void onButtonOk(wxCommandEvent& evt); +private: + void setDefaultValues(); + bool fetchDialogData(); + + DebugInterface* cpu; + + wxTextCtrl* editAddress; + wxTextCtrl* editSize; + wxRadioButton* radioMemory; + wxRadioButton* radioExecute; + wxCheckBox* checkRead; + wxCheckBox* checkWrite; + wxCheckBox* checkOnChange; + wxTextCtrl* editCondition; + wxCheckBox* checkEnabled; + wxCheckBox* checkLog; + wxButton* buttonOk; + wxButton* buttonCancel; + + bool memory; + bool read; + bool write; + bool enabled; + bool log; + bool onChange; + u32 address; + u32 size; + char condition[128]; + PostfixExpression compiledCondition; +}; \ No newline at end of file diff --git a/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp b/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp new file mode 100644 index 0000000000..48117e057a --- /dev/null +++ b/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp @@ -0,0 +1,1053 @@ +#include "PrecompiledHeader.h" +#include "CtrlDisassemblyView.h" +#include "DebugTools/Breakpoints.h" +#include "DebugTools/Debug.h" + +#include "DebugEvents.h" +#include "BreakpointWindow.h" + +#include +#include +#include + +#include "Resources/Breakpoint_Active.h" +#include "Resources/Breakpoint_Inactive.h" + +BEGIN_EVENT_TABLE(CtrlDisassemblyView, wxWindow) + EVT_PAINT(CtrlDisassemblyView::paintEvent) + EVT_MOUSEWHEEL(CtrlDisassemblyView::mouseEvent) + EVT_LEFT_DOWN(CtrlDisassemblyView::mouseEvent) + EVT_LEFT_DCLICK(CtrlDisassemblyView::mouseEvent) + EVT_RIGHT_DOWN(CtrlDisassemblyView::mouseEvent) + EVT_RIGHT_UP(CtrlDisassemblyView::mouseEvent) + EVT_MOTION(CtrlDisassemblyView::mouseEvent) + EVT_KEY_DOWN(CtrlDisassemblyView::keydownEvent) + EVT_CHAR(CtrlDisassemblyView::keydownEvent) + EVT_SCROLLWIN_LINEUP(CtrlDisassemblyView::scrollbarEvent) + EVT_SCROLLWIN_LINEDOWN(CtrlDisassemblyView::scrollbarEvent) + EVT_SCROLLWIN_PAGEUP(CtrlDisassemblyView::scrollbarEvent) + EVT_SCROLLWIN_PAGEDOWN(CtrlDisassemblyView::scrollbarEvent) + EVT_SIZE(CtrlDisassemblyView::sizeEvent) + EVT_SET_FOCUS(CtrlDisassemblyView::focusEvent) + EVT_KILL_FOCUS(CtrlDisassemblyView::focusEvent) +END_EVENT_TABLE() + +enum DisassemblyMenuIdentifiers +{ + ID_DISASM_COPYADDRESS = 1, + ID_DISASM_COPYINSTRUCTIONHEX, + ID_DISASM_COPYINSTRUCTIONDISASM, + ID_DISASM_DISASSEMBLETOFILE, + ID_DISASM_ASSEMBLE, + ID_DISASM_RUNTOHERE, + ID_DISASM_SETPCTOHERE, + ID_DISASM_TOGGLEBREAKPOINT, + ID_DISASM_FOLLOWBRANCH, + ID_DISASM_GOTOINMEMORYVIEW, + ID_DISASM_KILLFUNCTION, + ID_DISASM_RENAMEFUNCTION, + ID_DISASM_REMOVEFUNCTION, + ID_DISASM_ADDFUNCTION +}; + + + +inline wxIcon _wxGetIconFromMemory(const unsigned char *data, int length) { + wxMemoryInputStream is(data, length); + wxBitmap b = wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1); + wxIcon icon; + icon.CopyFromBitmap(b); + return icon; +} + +CtrlDisassemblyView::CtrlDisassemblyView(wxWindow* parent, DebugInterface* _cpu) + : wxWindow(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxWANTS_CHARS|wxBORDER), cpu(_cpu) +{ + manager.setCpu(cpu); + windowStart = 0x20100000; + rowHeight = 14; + charWidth = 8; + displaySymbols = true; + visibleRows = 1; + + bpEnabled = _wxGetIconFromMemory(res_Breakpoint_Active::Data,res_Breakpoint_Active::Length); + bpDisabled = _wxGetIconFromMemory(res_Breakpoint_Inactive::Data,res_Breakpoint_Inactive::Length); + + menu.Append(ID_DISASM_COPYADDRESS, L"Copy Address"); + menu.Append(ID_DISASM_COPYINSTRUCTIONHEX, L"Copy Instruction (Hex)"); + menu.Append(ID_DISASM_COPYINSTRUCTIONDISASM, L"Copy Instruction (Disasm)"); + menu.Append(ID_DISASM_DISASSEMBLETOFILE, L"Disassemble to File"); + menu.AppendSeparator(); + menu.Append(ID_DISASM_ASSEMBLE, L"Assemble Opcode"); + menu.Enable(ID_DISASM_ASSEMBLE,false); + menu.AppendSeparator(); + menu.Append(ID_DISASM_RUNTOHERE, L"Run to Cursor"); + menu.Append(ID_DISASM_SETPCTOHERE, L"Jump to Cursor"); + menu.Append(ID_DISASM_TOGGLEBREAKPOINT, L"Toggle Breakpoint"); + menu.Append(ID_DISASM_FOLLOWBRANCH, L"Follow Branch"); + menu.AppendSeparator(); + menu.Append(ID_DISASM_GOTOINMEMORYVIEW, L"Go to in Memory View"); + menu.AppendSeparator(); + menu.Append(ID_DISASM_ADDFUNCTION, L"Add Function Here"); + menu.Append(ID_DISASM_RENAMEFUNCTION, L"Rename Function"); + menu.Append(ID_DISASM_REMOVEFUNCTION, L"Remove Function"); + menu.Connect(wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&CtrlDisassemblyView::onPopupClick, NULL, this); + + SetScrollbar(wxVERTICAL,100,1,201,true); + SetDoubleBuffered(true); + calculatePixelPositions(); + setCurAddress(windowStart); +} + +#ifdef WIN32 +WXLRESULT CtrlDisassemblyView::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +{ + switch (nMsg) + { + case 0x0104: // WM_SYSKEYDOWN, make f10 usable + postEvent(debEVT_STEPOVER,0); + return 0; + } + + return wxWindow::MSWWindowProc(nMsg,wParam,lParam); +} +#endif + +void CtrlDisassemblyView::scanFunctions() +{ + if (cpu->isAlive() == false) + return; + + manager.analyze(windowStart,manager.getNthNextAddress(windowStart,visibleRows)-windowStart); +} + +void CtrlDisassemblyView::postEvent(wxEventType type, wxString text) +{ + wxCommandEvent event( type, GetId() ); + event.SetEventObject(this); + event.SetString(text); + wxPostEvent(this,event); +} + +void CtrlDisassemblyView::postEvent(wxEventType type, int value) +{ + wxCommandEvent event( type, GetId() ); + event.SetEventObject(this); + event.SetInt(value); + wxPostEvent(this,event); +} + +void CtrlDisassemblyView::paintEvent(wxPaintEvent & evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void CtrlDisassemblyView::redraw() +{ + wxClientDC dc(this); + render(dc); +} + +bool CtrlDisassemblyView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) +{ + if (displaySymbols) + { + const std::string addressSymbol = symbolMap.GetLabelString(address); + if (!addressSymbol.empty()) + { + for (int k = 0; addressSymbol[k] != 0; k++) + { + // abbreviate long names + if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0) + { + *dest++ = '+'; + break; + } + *dest++ = addressSymbol[k]; + } + *dest++ = ':'; + *dest = 0; + return true; + } else { + sprintf(dest," %08X",address); + return false; + } + } else { + if (showData) + sprintf(dest,"%08X %08X",address,cpu->read32(address)); + else + sprintf(dest,"%08X",address); + return false; + } +} + +wxColor scaleColor(wxColor color, float factor) +{ + unsigned char r = color.Red(); + unsigned char g = color.Green(); + unsigned char b = color.Blue(); + unsigned char a = color.Alpha(); + + r = min(255,max((int)(r*factor),0)); + g = min(255,max((int)(g*factor),0)); + b = min(255,max((int)(b*factor),0)); + + return wxColor(r,g,b,a); +} + +void CtrlDisassemblyView::drawBranchLine(wxDC& dc, std::map& addressPositions, BranchLine& line) +{ + u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows); + + int winBottom = GetSize().GetHeight(); + + int topY; + int bottomY; + if (line.first < windowStart) + { + topY = -1; + } else if (line.first >= windowEnd) + { + topY = GetSize().GetHeight()+1; + } else { + topY = addressPositions[line.first] + rowHeight/2; + } + + if (line.second < windowStart) + { + bottomY = -1; + } else if (line.second >= windowEnd) + { + bottomY = GetSize().GetHeight()+1; + } else { + bottomY = addressPositions[line.second] + rowHeight/2; + } + + if ((topY < 0 && bottomY < 0) || (topY > winBottom && bottomY > winBottom)) + { + return; + } + + // highlight line in a different color if it affects the currently selected opcode + wxColor color; + if (line.first == curAddress || line.second == curAddress) + { + color = wxColor(0xFF257AFA); + } else { + color = wxColor(0xFFFF3020); + } + + wxPen pen = wxPen(color); + dc.SetBrush(wxBrush(color)); + dc.SetPen(wxPen(color)); + + int x = pixelPositions.arrowsStart+line.laneIndex*8; + + if (topY < 0) // first is not visible, but second is + { + dc.DrawLine(x-2,bottomY,x+2,bottomY); + dc.DrawLine(x+2,bottomY,x+2,0); + + if (line.type == LINE_DOWN) + { + dc.DrawLine(x,bottomY-4,x-4,bottomY); + dc.DrawLine(x-4,bottomY,x+1,bottomY+5); + } + } else if (bottomY > winBottom) // second is not visible, but first is + { + dc.DrawLine(x-2,topY,x+2,topY); + dc.DrawLine(x+2,topY,x+2,winBottom); + + if (line.type == LINE_UP) + { + dc.DrawLine(x,topY-4,x-4,topY); + dc.DrawLine(x-4,topY,x+1,topY+5); + } + } else { // both are visible + if (line.type == LINE_UP) + { + dc.DrawLine(x-2,bottomY,x+2,bottomY); + dc.DrawLine(x+2,bottomY,x+2,topY); + dc.DrawLine(x+2,topY,x-4,topY); + + dc.DrawLine(x,topY-4,x-4,topY); + dc.DrawLine(x-4,topY,x+1,topY+5); + } else { + dc.DrawLine(x-2,topY,x+2,topY); + dc.DrawLine(x+2,topY,x+2,bottomY); + dc.DrawLine(x+2,bottomY,x-4,bottomY); + + dc.DrawLine(x,bottomY-4,x-4,bottomY); + dc.DrawLine(x-4,bottomY,x+1,bottomY+5); + } + } +} + +int getBackgroundColor(unsigned int address) +{ + int colors[6] = {0xFFe0FFFF,0xFFFFe0e0,0xFFe8e8FF,0xFFFFe0FF,0xFFe0FFe0,0xFFFFFFe0}; + int n=symbolMap.GetFunctionNum(address); + if (n==-1) return 0xFFFFFFFF; + return colors[n%6]; +} + +void CtrlDisassemblyView::render(wxDC& dc) +{ + // init stuff + int totalWidth, totalHeight; + GetSize(&totalWidth,&totalHeight); + visibleRows = totalHeight / rowHeight; + + // clear background + wxColor white = wxColor(0xFFFFFFFF); + + dc.SetBrush(wxBrush(white)); + dc.SetPen(wxPen(white)); + + int width,height; + dc.GetSize(&width,&height); + dc.DrawRectangle(0,0,width,height); + + if (!cpu->isAlive()) + return; + + #ifdef WIN32 + wxFont font = wxFont(wxSize(charWidth,rowHeight-2),wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,L"Lucida Console"); + wxFont boldFont = wxFont(wxSize(charWidth,rowHeight-2),wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,L"Lucida Console"); + #else + wxFont font = wxFont(8,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,L"Lucida Console"); + font.SetPixelSize(wxSize(charWidth,rowHeight-2)); + wxFont boldFont = wxFont(8,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD,false,L"Lucida Console"); + boldFont.SetPixelSize(wxSize(charWidth,rowHeight-2)); + #endif + + bool hasFocus = wxWindow::FindFocus() == this; + + std::map addressPositions; + + unsigned int address = windowStart; + DisassemblyLineInfo line; + for (int i = 0; i < visibleRows+1; i++) + { + manager.getLine(address,displaySymbols,line); + + int rowY1 = rowHeight*i; + int rowY2 = rowHeight*(i+1); + + addressPositions[address] = rowY1; + + wxColor backgroundColor = wxColor(getBackgroundColor(address)); + wxColor textColor = wxColor(0xFF000000); + + if (isInInterval(address,line.totalSize,cpu->getPC())) + { + backgroundColor = scaleColor(backgroundColor,1.05f); + } + + if (address >= selectRangeStart && address < selectRangeEnd) + { + if (hasFocus) + { + backgroundColor = address == curAddress ? 0xFFFF8822 : 0xFFFF9933; + textColor = 0xFFFFFFFF; + } else { + backgroundColor = 0xFFC0C0C0; + } + } + + // display whether the condition of a branch is met + if (line.info.isConditional && address == cpu->getPC()) + { + line.params += line.info.conditionMet ? " ; true" : " ; false"; + } + + // draw background + dc.SetBrush(wxBrush(backgroundColor)); + dc.SetPen(wxPen(backgroundColor)); + dc.DrawRectangle(0,rowY1,totalWidth,rowHeight); + + // display address/symbol + bool enabled; + if (CBreakPoints::IsAddressBreakPoint(address,&enabled)) + { + if (enabled) + textColor = 0x0000FF; + int yOffset = max(-1,(rowHeight-14+1)/2); + dc.DrawIcon(enabled ? bpEnabled : bpDisabled,2,rowY1+1+yOffset); + } + + dc.SetTextForeground(textColor); + + char addressText[64]; + getDisasmAddressText(address,addressText,true,line.type == DISTYPE_OPCODE); + + dc.SetFont(font); + dc.DrawText(wxString(addressText,wxConvUTF8),pixelPositions.addressStart,rowY1+2); + dc.DrawText(wxString(line.params.c_str(),wxConvUTF8),pixelPositions.argumentsStart,rowY1+2); + + if (isInInterval(address,line.totalSize,cpu->getPC())) + dc.DrawText(L"â– ",pixelPositions.opcodeStart-8,rowY1); + + dc.SetFont(boldFont); + dc.DrawText(wxString(line.name.c_str(),wxConvUTF8),pixelPositions.opcodeStart,rowY1+2); + + address += line.totalSize; + } + + std::vector branchLines = manager.getBranchLines(windowStart,address-windowStart); + for (size_t i = 0; i < branchLines.size(); i++) + { + drawBranchLine(dc,addressPositions,branchLines[i]); + } + +} + +void CtrlDisassemblyView::gotoAddress(u32 addr) +{ + u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows); + u32 newAddress = manager.getStartAddress(addr); + + if (newAddress < windowStart || newAddress >= windowEnd) + { + windowStart = manager.getNthPreviousAddress(newAddress,visibleRows/2); + } + + setCurAddress(addr); + scanFunctions(); + redraw(); +} + +void CtrlDisassemblyView::scrollAddressIntoView() +{ + u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows); + + if (curAddress < windowStart) + windowStart = curAddress; + else if (curAddress >= windowEnd) + windowStart = manager.getNthPreviousAddress(curAddress,visibleRows-1); + + scanFunctions(); +} + +void CtrlDisassemblyView::calculatePixelPositions() +{ + pixelPositions.addressStart = 16; + pixelPositions.opcodeStart = pixelPositions.addressStart + 18*charWidth; + pixelPositions.argumentsStart = pixelPositions.opcodeStart + 9*charWidth; + pixelPositions.arrowsStart = pixelPositions.argumentsStart + 30*charWidth; +} + + +void CtrlDisassemblyView::followBranch() +{ + DisassemblyLineInfo line; + manager.getLine(curAddress,true,line); + + if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) + { + if (line.info.isBranch) + { + jumpStack.push_back(curAddress); + gotoAddress(line.info.branchTarget); + } else if (line.info.hasRelevantAddress) + { + // well, not exactly a branch, but we can do something anyway + postEvent(debEVT_GOTOINMEMORYVIEW,line.info.releventAddress); + } + } else if (line.type == DISTYPE_DATA) + { + // jump to the start of the current line + postEvent(debEVT_GOTOINMEMORYVIEW,curAddress); + } +} + +void CtrlDisassemblyView::onPopupClick(wxCommandEvent& evt) +{ + switch (evt.GetId()) + { + case ID_DISASM_FOLLOWBRANCH: + followBranch(); + break; + case ID_DISASM_COPYADDRESS: + if (wxTheClipboard->Open()) + { + wchar_t text[64]; + swprintf(text,64,L"%08X",curAddress); + + wxTheClipboard->SetData(new wxTextDataObject(text)); + wxTheClipboard->Close(); + } + break; + case ID_DISASM_GOTOINMEMORYVIEW: + postEvent(debEVT_GOTOINMEMORYVIEW,curAddress); + break; + case ID_DISASM_COPYINSTRUCTIONHEX: + copyInstructions(selectRangeStart,selectRangeEnd,false); + break; + case ID_DISASM_COPYINSTRUCTIONDISASM: + copyInstructions(selectRangeStart,selectRangeEnd,true); + break; + case ID_DISASM_SETPCTOHERE: + cpu->setPc(curAddress); + redraw(); + break; + case ID_DISASM_RUNTOHERE: + postEvent(debEVT_RUNTOPOS,curAddress); + break; + case ID_DISASM_DISASSEMBLETOFILE: + disassembleToFile(); + break; + case ID_DISASM_RENAMEFUNCTION: + { + u32 funcBegin = symbolMap.GetFunctionStart(curAddress); + if (funcBegin != -1) + { + wxString newName = wxGetTextFromUser(L"Enter the new function name",L"New function name", + wxString(symbolMap.GetLabelString(funcBegin).c_str(),wxConvUTF8),this); + + if (!newName.empty()) + { + const wxCharBuffer converted = newName.ToUTF8(); + symbolMap.SetLabelName(converted,funcBegin); + postEvent(debEVT_MAPLOADED,0); + redraw(); + } + } + else + { + wxMessageBox(L"No symbol selected",L"Error",wxICON_ERROR); + } + break; + } + case ID_DISASM_REMOVEFUNCTION: + { + u32 funcBegin = symbolMap.GetFunctionStart(curAddress); + if (funcBegin != -1) + { + u32 prevBegin = symbolMap.GetFunctionStart(funcBegin-1); + if (prevBegin != -1) + { + u32 expandedSize = symbolMap.GetFunctionSize(prevBegin)+symbolMap.GetFunctionSize(funcBegin); + symbolMap.SetFunctionSize(prevBegin,expandedSize); + } + + symbolMap.RemoveFunction(funcBegin,true); + symbolMap.SortSymbols(); + symbolMap.UpdateActiveSymbols(); + manager.clear(); + + postEvent(debEVT_MAPLOADED,0); + } + else + { + postEvent(debEVT_SETSTATUSBARTEXT,L"WARNING: unable to find function symbol here"); + } + + redraw(); + break; + } + case ID_DISASM_ADDFUNCTION: + { + u32 prevBegin = symbolMap.GetFunctionStart(curAddress); + if (prevBegin != -1) + { + if (prevBegin == curAddress) + { + postEvent(debEVT_SETSTATUSBARTEXT,L"WARNING: There's already a function entry point at this adress"); + } + else + { + char symname[128]; + u32 prevSize = symbolMap.GetFunctionSize(prevBegin); + u32 newSize = curAddress-prevBegin; + symbolMap.SetFunctionSize(prevBegin,newSize); + + newSize = prevSize-newSize; + sprintf(symname,"u_un_%08X",curAddress); + symbolMap.AddFunction(symname,curAddress,newSize); + symbolMap.SortSymbols(); + symbolMap.UpdateActiveSymbols(); + manager.clear(); + + postEvent(debEVT_MAPLOADED,0); + } + } + else + { + char symname[128]; + int newSize = selectRangeEnd - selectRangeStart; + sprintf(symname, "u_un_%08X", selectRangeStart); + symbolMap.AddFunction(symname, selectRangeStart, newSize); + symbolMap.SortSymbols(); + symbolMap.UpdateActiveSymbols(); + + postEvent(debEVT_MAPLOADED,0); + } + + redraw(); + break; + } + default: + wxMessageBox( L"Unimplemented.", L"Unimplemented.", wxICON_INFORMATION); + break; + } +} + +void CtrlDisassemblyView::keydownEvent(wxKeyEvent& evt) +{ + u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows); + + if (evt.ControlDown()) + { + switch (evt.GetKeyCode()) + { + case 'd': + case 'D': + toggleBreakpoint(true); + break; + case 'e': + case 'E': + editBreakpoint(); + break; + case 'b': + case 'B': + { + BreakpointWindow bpw(this,cpu); + if (bpw.ShowModal() == wxID_OK) + { + bpw.addBreakpoint(); + postEvent(debEVT_UPDATE,0); + } + } + break; + case 'g': + case 'G': + { + u64 addr; + if (executeExpressionWindow(this,cpu,addr) == false) + return; + gotoAddress(addr); + } + default: + evt.Skip(); + break; + } + } else { + switch (evt.GetKeyCode()) + { + case WXK_LEFT: + if (jumpStack.empty()) + { + gotoAddress(cpu->getPC()); + } else { + u32 addr = jumpStack[jumpStack.size()-1]; + jumpStack.pop_back(); + gotoAddress(addr); + } + break; + case WXK_RIGHT: + followBranch(); + break; + case WXK_UP: + setCurAddress(manager.getNthPreviousAddress(curAddress,1), wxGetKeyState(WXK_SHIFT)); + scrollAddressIntoView(); + scanFunctions(); + break; + case WXK_DOWN: + setCurAddress(manager.getNthNextAddress(curAddress,1), wxGetKeyState(WXK_SHIFT)); + scrollAddressIntoView(); + scanFunctions(); + break; + case WXK_TAB: + displaySymbols = !displaySymbols; + break; + case WXK_PAGEUP: + if (curAddress != windowStart && curAddressIsVisible()) { + setCurAddress(windowStart, wxGetKeyState(WXK_SHIFT)); + scrollAddressIntoView(); + } else { + setCurAddress(manager.getNthPreviousAddress(windowStart,visibleRows), wxGetKeyState(WXK_SHIFT)); + scrollAddressIntoView(); + } + scanFunctions(); + break; + case WXK_PAGEDOWN: + if (manager.getNthNextAddress(curAddress,1) != windowEnd && curAddressIsVisible()) { + setCurAddress(manager.getNthPreviousAddress(windowEnd,1), wxGetKeyState(WXK_SHIFT)); + scrollAddressIntoView(); + } else { + setCurAddress(manager.getNthNextAddress(windowEnd,visibleRows-1), wxGetKeyState(WXK_SHIFT)); + scrollAddressIntoView(); + } + scanFunctions(); + break; + case WXK_F10: + postEvent(debEVT_STEPOVER,0); + break; + default: + evt.Skip(); + break; + } + } + + redraw(); +} + +void CtrlDisassemblyView::scrollbarEvent(wxScrollWinEvent& evt) +{ + int type = evt.GetEventType(); + if (type == wxEVT_SCROLLWIN_LINEUP) + { + windowStart = manager.getNthPreviousAddress(windowStart,1); + scanFunctions(); + } else if (type == wxEVT_SCROLLWIN_LINEDOWN) + { + windowStart = manager.getNthNextAddress(windowStart,1); + scanFunctions(); + } else if (type == wxEVT_SCROLLWIN_PAGEUP) + { + windowStart = manager.getNthPreviousAddress(windowStart,visibleRows); + scanFunctions(); + } else if (type == wxEVT_SCROLLWIN_PAGEDOWN) + { + windowStart = manager.getNthNextAddress(windowStart,visibleRows); + scanFunctions(); + } + + redraw(); +} + +void CtrlDisassemblyView::toggleBreakpoint(bool toggleEnabled) +{ + bool enabled; + if (CBreakPoints::IsAddressBreakPoint(curAddress,&enabled)) + { + if (!enabled) + { + // enable disabled breakpoints + CBreakPoints::ChangeBreakPoint(curAddress,true); + } else if (!toggleEnabled && CBreakPoints::GetBreakPointCondition(curAddress) != NULL) + { + // don't just delete a breakpoint with a custom condition + CBreakPoints::RemoveBreakPoint(curAddress); + } else if (toggleEnabled) + { + // disable breakpoint + CBreakPoints::ChangeBreakPoint(curAddress,false); + } else { + // otherwise just remove breakpoint + CBreakPoints::RemoveBreakPoint(curAddress); + } + } else { + CBreakPoints::AddBreakPoint(curAddress); + } +} + + +void CtrlDisassemblyView::updateStatusBarText() +{ + char text[512]; + DisassemblyLineInfo line; + manager.getLine(curAddress,true,line); + + text[0] = 0; + if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) + { + if (line.info.isDataAccess) + { + if (!cpu->isValidAddress(line.info.dataAddress)) + { + sprintf(text,"Invalid address %08X",line.info.dataAddress); + } else { + switch (line.info.dataSize) + { + case 1: + sprintf(text,"[%08X] = %02X",line.info.dataAddress,cpu->read8(line.info.dataAddress)); + break; + case 2: + sprintf(text,"[%08X] = %04X",line.info.dataAddress,cpu->read16(line.info.dataAddress)); + break; + case 4: + { + u32 data = cpu->read32(line.info.dataAddress); + const std::string addressSymbol = symbolMap.GetLabelString(data); + if (!addressSymbol.empty()) + { + sprintf(text,"[%08X] = %s (%08X)",line.info.dataAddress,addressSymbol.c_str(),data); + } else { + sprintf(text,"[%08X] = %08X",line.info.dataAddress,data); + } + break; + } + case 8: + { + u64 data = cpu->read64(line.info.dataAddress); + sprintf(text,"[%08X] = %016llX",line.info.dataAddress,data); + break; + } + case 16: + { + u128 data = cpu->read128(line.info.dataAddress); + sprintf(text,"[%08X] = %016llX%016llX",line.info.dataAddress,data._u64[1],data._u64[0]); + break; + } + } + } + } + + if (line.info.isBranch) + { + const std::string addressSymbol = symbolMap.GetLabelString(line.info.branchTarget); + if (addressSymbol.empty()) + { + sprintf(text,"%08X",line.info.branchTarget); + } else { + sprintf(text,"%08X = %s",line.info.branchTarget,addressSymbol.c_str()); + } + } + } else if (line.type == DISTYPE_DATA) + { + u32 start = symbolMap.GetDataStart(curAddress); + if (start == -1) + start = curAddress; + + u32 diff = curAddress-start; + const std::string label = symbolMap.GetLabelString(start); + + if (!label.empty()) + { + if (diff != 0) + sprintf(text,"%08X (%s) + %08X",start,label.c_str(),diff); + else + sprintf(text,"%08X (%s)",start,label.c_str()); + } else { + if (diff != 0) + sprintf(text,"%08X + %08X",start,diff); + else + sprintf(text,"%08X",start); + } + } + + postEvent(debEVT_SETSTATUSBARTEXT,wxString(text,wxConvUTF8)); +} + +void CtrlDisassemblyView::mouseEvent(wxMouseEvent& evt) +{ + // left button + wxEventType type = evt.GetEventType(); + bool hasFocus = wxWindow::FindFocus() == this; + + if (type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_DCLICK || type == wxEVT_RIGHT_DOWN ) + { + u32 newAddress = yToAddress(evt.GetY()); + bool extend = wxGetKeyState(WXK_SHIFT); + + if (type == wxEVT_RIGHT_DOWN) + { + // Maintain the current selection if right clicking into it. + if (newAddress >= selectRangeStart && newAddress < selectRangeEnd) + extend = true; + } else { + if (curAddress == newAddress && hasFocus) + toggleBreakpoint(false); + } + + setCurAddress(newAddress,extend); + SetFocus(); + SetFocusFromKbd(); + } else if (evt.GetEventType() == wxEVT_RIGHT_UP) + { + PopupMenu(&menu,evt.GetPosition()); + return; + } else if (evt.GetEventType() == wxEVT_MOUSEWHEEL) + { + if (evt.GetWheelRotation() > 0) + { + windowStart = manager.getNthPreviousAddress(windowStart,3); + scanFunctions(); + } else if (evt.GetWheelRotation() < 0) { + windowStart = manager.getNthNextAddress(windowStart,3); + scanFunctions(); + } + } else if (evt.GetEventType() == wxEVT_MOTION) + { + if (evt.ButtonIsDown(wxMOUSE_BTN_LEFT)) + { + int newAddress = yToAddress(evt.GetY()); + setCurAddress(newAddress,wxGetKeyState(WXK_SHIFT)); + } else + return; + } else { + evt.Skip(); + return; + } + + redraw(); +} + +void CtrlDisassemblyView::sizeEvent(wxSizeEvent& evt) +{ + wxSize s = evt.GetSize(); + visibleRows = s.GetWidth()/rowHeight; +} + +u32 CtrlDisassemblyView::yToAddress(int y) +{ + int line = y/rowHeight; + return manager.getNthNextAddress(windowStart,line); +} + +bool CtrlDisassemblyView::curAddressIsVisible() +{ + u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows); + return curAddress >= windowStart && curAddress < windowEnd; +} + +void CtrlDisassemblyView::scrollStepping(u32 newPc) +{ + u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows); + + newPc = manager.getStartAddress(newPc); + if (newPc >= windowEnd || newPc >= manager.getNthPreviousAddress(windowEnd,1)) + { + windowStart = manager.getNthPreviousAddress(newPc,visibleRows-2); + } +} + +std::string CtrlDisassemblyView::disassembleRange(u32 start, u32 size) +{ + std::string result; + + // gather all branch targets without labels + std::set branchAddresses; + for (u32 i = 0; i < size; i += 4) + { + MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,start+i); + + if (info.isBranch && symbolMap.GetLabelString(info.branchTarget).empty()) + { + if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) + { + branchAddresses.insert(info.branchTarget); + } + } + } + + u32 disAddress = start; + bool previousLabel = true; + DisassemblyLineInfo line; + while (disAddress < start+size) + { + char addressText[64],buffer[512]; + + manager.getLine(disAddress,displaySymbols,line); + bool isLabel = getDisasmAddressText(disAddress,addressText,false,line.type == DISTYPE_OPCODE); + + if (isLabel) + { + if (!previousLabel) result += "\r\n"; + sprintf(buffer,"%s\r\n\r\n",addressText); + result += buffer; + } else if (branchAddresses.find(disAddress) != branchAddresses.end()) + { + if (!previousLabel) result += "\r\n"; + sprintf(buffer,"pos_%08X:\r\n\r\n",disAddress); + result += buffer; + } + + if (line.info.isBranch && !line.info.isBranchToRegister + && symbolMap.GetLabelString(line.info.branchTarget).empty() + && branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) + { + sprintf(buffer,"pos_%08X",line.info.branchTarget); + line.params = line.params.substr(0,line.params.find("0x")) + buffer; + } + + sprintf(buffer,"\t%s\t%s\r\n",line.name.c_str(),line.params.c_str()); + result += buffer; + previousLabel = isLabel; + disAddress += line.totalSize; + } + + return result; +} + +void CtrlDisassemblyView::copyInstructions(u32 startAddr, u32 endAddr, bool withDisasm) +{ + if (!wxTheClipboard->Open()) + { + wxMessageBox( L"Could not open clipboard.", L"Error", wxICON_ERROR); + return; + } + + if (withDisasm == false) + { + int instructionSize = 4; + int count = (endAddr - startAddr) / instructionSize; + int space = count * 32; + char *temp = new char[space]; + + char *p = temp, *end = temp + space; + for (u32 pos = startAddr; pos < endAddr; pos += instructionSize) + { + p += sprintf(p, "%08X", cpu->read32(pos)); + + // Don't leave a trailing newline. + if (pos + instructionSize < endAddr) + p += sprintf(p,"\r\n"); + } + + wxTheClipboard->SetData(new wxTextDataObject(wxString(temp,wxConvUTF8))); + delete [] temp; + } else + { + std::string disassembly = disassembleRange(startAddr,endAddr-startAddr); + wxTheClipboard->SetData(new wxTextDataObject(wxString(disassembly.c_str(),wxConvUTF8))); + } + + wxTheClipboard->Close(); +} + +void CtrlDisassemblyView::disassembleToFile() +{ + wxFileDialog dlg(this,wxEmptyString,wxEmptyString,wxEmptyString,L"*.*",wxFD_SAVE); + + if (dlg.ShowModal() == wxID_CANCEL) + return; + + std::string disassembly = disassembleRange(selectRangeStart,selectRangeEnd-selectRangeStart); + wxFile output(dlg.GetPath(),wxFile::write); + output.Write(wxString(disassembly.c_str(),wxConvUTF8)); +} + +void CtrlDisassemblyView::editBreakpoint() +{ + BreakpointWindow win(this,cpu); + + bool exists = false; + if (CBreakPoints::IsAddressBreakPoint(curAddress)) + { + auto breakpoints = CBreakPoints::GetBreakpoints(); + for (size_t i = 0; i < breakpoints.size(); i++) + { + if (breakpoints[i].addr == curAddress) + { + win.loadFromBreakpoint(breakpoints[i]); + exists = true; + break; + } + } + } + + if (!exists) + win.initBreakpoint(curAddress); + + if (win.ShowModal() == wxID_OK) + { + if (exists) + CBreakPoints::RemoveBreakPoint(curAddress); + win.addBreakpoint(); + postEvent(debEVT_UPDATE,0); + } +} \ No newline at end of file diff --git a/pcsx2/gui/Debugger/CtrlDisassemblyView.h b/pcsx2/gui/Debugger/CtrlDisassemblyView.h new file mode 100644 index 0000000000..0e83180fca --- /dev/null +++ b/pcsx2/gui/Debugger/CtrlDisassemblyView.h @@ -0,0 +1,89 @@ +#pragma once +#include + +#include "DebugTools/DebugInterface.h" +#include "DebugTools/DisassemblyManager.h" + +class CtrlDisassemblyView: public wxWindow +{ +public: + CtrlDisassemblyView(wxWindow* parent, DebugInterface* _cpu); + + void mouseEvent(wxMouseEvent& evt); + void paintEvent(wxPaintEvent & evt); + void keydownEvent(wxKeyEvent& evt); + void scrollbarEvent(wxScrollWinEvent& evt); + void sizeEvent(wxSizeEvent& evt); + void focusEvent(wxFocusEvent& evt) { Refresh(); }; +#ifdef WIN32 + WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); +#endif + + void scanFunctions(); + void clearFunctions() { manager.clear(); }; + void redraw(); + + u32 getInstructionSizeAt(u32 address) + { + u32 start = manager.getStartAddress(address); + u32 next = manager.getNthNextAddress(start,1); + return next-address; + } + + void gotoAddress(u32 addr); + void gotoPc() { gotoAddress(cpu->getPC()); }; + void scrollStepping(u32 newPc); + DECLARE_EVENT_TABLE() +private: + void drawBranchLine(wxDC& dc, std::map& addressPositions, BranchLine& line); + void render(wxDC& dc); + void calculatePixelPositions(); + bool getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData); + u32 yToAddress(int y); + bool curAddressIsVisible(); + void followBranch(); + void toggleBreakpoint(bool toggleEnabled); + void updateStatusBarText(); + std::string disassembleRange(u32 start, u32 size); + void copyInstructions(u32 startAddr, u32 endAddr, bool withDisasm); + void disassembleToFile(); + void editBreakpoint(); + + void postEvent(wxEventType type, wxString text); + void postEvent(wxEventType type, int value); + + void onPopupClick(wxCommandEvent& evt); + + void setCurAddress(u32 newAddress, bool extend = false) + { + newAddress = manager.getStartAddress(newAddress); + u32 after = manager.getNthNextAddress(newAddress,1); + curAddress = newAddress; + selectRangeStart = extend ? std::min(selectRangeStart, newAddress) : newAddress; + selectRangeEnd = extend ? std::max(selectRangeEnd, after) : after; + updateStatusBarText(); + } + + void scrollAddressIntoView(); + struct { + int addressStart; + int opcodeStart; + int argumentsStart; + int arrowsStart; + } pixelPositions; + + DebugInterface* cpu; + DisassemblyManager manager; + u32 windowStart; + u32 curAddress; + u32 selectRangeStart; + u32 selectRangeEnd; + int visibleRows; + int rowHeight; + int charWidth; + bool displaySymbols; + std::vector jumpStack; + + wxIcon bpEnabled,bpDisabled; + wxMenu menu; +}; diff --git a/pcsx2/gui/Debugger/CtrlMemView.h b/pcsx2/gui/Debugger/CtrlMemView.h new file mode 100644 index 0000000000..e6c15b66a4 --- /dev/null +++ b/pcsx2/gui/Debugger/CtrlMemView.h @@ -0,0 +1,46 @@ +#pragma once +#include + +#include "DebugTools/DebugInterface.h" +#include "DebugTools/DisassemblyManager.h" + +class CtrlMemView: public wxWindow +{ +public: + CtrlMemView(wxWindow* parent, DebugInterface* _cpu); + + void paintEvent(wxPaintEvent & evt); + void mouseEvent(wxMouseEvent& evt); + void keydownEvent(wxKeyEvent& evt); + void charEvent(wxKeyEvent& evt); + void redraw(); + void gotoAddress(u32 address); + + DECLARE_EVENT_TABLE() +private: + void render(wxDC& dc); + void gotoPoint(int x, int y); + void updateStatusBarText(); + void postEvent(wxEventType type, wxString text); + void postEvent(wxEventType type, int value); + void scrollWindow(int lines); + void scrollCursor(int bytes); + void onPopupClick(wxCommandEvent& evt); + void focusEvent(wxFocusEvent& evt) { Refresh(); }; + + DebugInterface* cpu; + int rowHeight; + int charWidth; + u32 windowStart; + u32 curAddress; + int rowSize; + wxFont font,underlineFont; + + int addressStart; + int hexStart; + int asciiStart; + bool asciiSelected; + int selectedNibble; + + wxMenu menu; +}; \ No newline at end of file diff --git a/pcsx2/gui/Debugger/CtrlRegisterList.cpp b/pcsx2/gui/Debugger/CtrlRegisterList.cpp new file mode 100644 index 0000000000..ff8079f32b --- /dev/null +++ b/pcsx2/gui/Debugger/CtrlRegisterList.cpp @@ -0,0 +1,422 @@ +#include "PrecompiledHeader.h" +#include "CtrlRegisterList.h" +#include "DebugTools/Debug.h" + +#include "DebugEvents.h" + +BEGIN_EVENT_TABLE(CtrlRegisterList, wxWindow) + EVT_PAINT(CtrlRegisterList::paintEvent) + EVT_LEFT_DOWN(CtrlRegisterList::mouseEvent) + EVT_RIGHT_DOWN(CtrlRegisterList::mouseEvent) + EVT_RIGHT_UP(CtrlRegisterList::mouseEvent) + EVT_MOTION(CtrlRegisterList::mouseEvent) + EVT_KEY_DOWN(CtrlRegisterList::keydownEvent) +END_EVENT_TABLE() + +enum DisassemblyMenuIdentifiers +{ + ID_REGISTERLIST_DISPLAY32 = 1, + ID_REGISTERLIST_DISPLAY64, + ID_REGISTERLIST_DISPLAY128, +}; + + +CtrlRegisterList::CtrlRegisterList(wxWindow* parent, DebugInterface* _cpu) + : wxWindow(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxWANTS_CHARS|wxBORDER), cpu(_cpu) +{ + rowHeight = 14; + charWidth = 8; + category = 0; + maxBits = 128; + + for (int i = 0; i < cpu->getRegisterCategoryCount(); i++) + { + int count = cpu->getRegisterCount(i); + + ChangedReg* regs = new ChangedReg[count]; + memset(regs,0,sizeof(ChangedReg)*count); + changedCategories.push_back(regs); + + int maxLen = 0; + for (int k = 0; k < cpu->getRegisterCount(i); k++) + { + maxLen = std::max(maxLen,strlen(cpu->getRegisterName(i,k))); + } + + int x = 17+(maxLen+3)*charWidth; + startPositions.push_back(x); + currentRows.push_back(0); + } + + menu.AppendRadioItem(ID_REGISTERLIST_DISPLAY32, L"Display 32 bit"); + menu.AppendRadioItem(ID_REGISTERLIST_DISPLAY64, L"Display 64 bit"); + menu.AppendRadioItem(ID_REGISTERLIST_DISPLAY128, L"Display 128 bit"); + menu.Check(ID_REGISTERLIST_DISPLAY128,true); + menu.Connect(wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&CtrlRegisterList::onPopupClick, NULL, this); + + SetDoubleBuffered(true); + SetInitialBestSize(ClientToWindowSize(GetMinClientSize())); +} + +void CtrlRegisterList::postEvent(wxEventType type, wxString text) +{ + wxCommandEvent event( type, GetId() ); + event.SetEventObject(this); + event.SetString(text); + wxPostEvent(this,event); +} + +void CtrlRegisterList::postEvent(wxEventType type, int value) +{ + wxCommandEvent event( type, GetId() ); + event.SetEventObject(this); + event.SetInt(value); + wxPostEvent(this,event); +} + +void CtrlRegisterList::refreshChangedRegs() +{ + if (cpu->getPC() == lastPc) + return; + + for (size_t cat = 0; cat < changedCategories.size(); cat++) + { + ChangedReg* regs = changedCategories[cat]; + int size = cpu->getRegisterSize(category); + + for (int i = 0; i < cpu->getRegisterCount(cat); i++) + { + ChangedReg& reg = regs[i]; + memset(®.changed,0,sizeof(reg.changed)); + + u128 newValue = cpu->getRegister(cat,i); + + if (reg.oldValue != newValue) + { + bool changed = false; + + if (size >= 128 && (reg.oldValue._u32[3] != newValue._u32[3] || reg.oldValue._u32[2] != newValue._u32[2])) + { + changed = true; + reg.changed[3] = true; + reg.changed[2] = true; + } + + if (size >= 64 && (reg.oldValue._u32[1] != newValue._u32[1] || changed)) + { + changed = true; + reg.changed[1] = true; + } + + if (reg.oldValue._u32[0] != newValue._u32[0] || changed) + { + reg.changed[0] = true; + } + + reg.oldValue = newValue; + } + } + } + + lastPc = cpu->getPC(); +} + +void CtrlRegisterList::paintEvent(wxPaintEvent & evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void CtrlRegisterList::redraw() +{ + wxClientDC dc(this); + render(dc); +} + +void drawU32Text(wxDC& dc, u32 value, int x, int y) +{ + char str[32]; + sprintf(str,"%08X",value); + dc.DrawText(wxString(str,wxConvUTF8),x,y); +} + +void CtrlRegisterList::render(wxDC& dc) +{ + #ifdef WIN32 + wxFont font = wxFont(wxSize(charWidth,rowHeight-2),wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,L"Lucida Console"); + #else + wxFont font = wxFont(8,wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL,false,L"Lucida Console"); + font.SetPixelSize(wxSize(charWidth,rowHeight-2)); + #endif + dc.SetFont(font); + + // clear background + wxColor white = wxColor(0xFFFFFFFF); + + dc.SetBrush(wxBrush(white)); + dc.SetPen(wxPen(white)); + + wxSize size = GetSize(); + dc.DrawRectangle(0,0,size.x,size.y); + + refreshChangedRegs(); + + wxColor colorChanged = wxColor(0xFF0000FF); + wxColor colorUnchanged = wxColor(0xFF004000); + wxColor colorNormal = wxColor(0xFF600000); + + // draw categories + int piece = size.x/cpu->getRegisterCategoryCount(); + for (int i = 0; i < cpu->getRegisterCategoryCount(); i++) + { + const char* name = cpu->getRegisterCategoryName(i); + + int x = i*piece; + + if (i == category) + { + dc.SetBrush(wxBrush(wxColor(0xFF70FF70))); + dc.SetPen(wxPen(wxColor(0xFF000000))); + } else { + dc.SetBrush(wxBrush(wxColor(0xFFFFEFE8))); + dc.SetPen(wxPen(wxColor(0xFF000000))); + } + + dc.DrawRectangle(x-1,-1,piece+1,rowHeight+1); + + // center text + x += (piece-strlen(name)*charWidth)/2; + dc.DrawText(wxString(name,wxConvUTF8),x,1); + } + + int nameStart = 17; + int valueStart = startPositions[category]; + + ChangedReg* changedRegs = changedCategories[category]; + int registerBits = cpu->getRegisterSize(category); + DebugInterface::RegisterType type = cpu->getRegisterType(category); + + for (int i = 0; i < cpu->getRegisterCount(category); i++) + { + int x = valueStart; + int y = rowHeight*(i+1); + + if (currentRows[category] == i) + { + dc.SetBrush(wxBrush(wxColor(0xFFFFEFE8))); + dc.SetPen(wxPen(wxColor(0xFFFFEFE8))); + dc.DrawRectangle(0,y,size.x,rowHeight); + } + + const char* name = cpu->getRegisterName(category,i); + dc.SetTextForeground(colorNormal); + dc.DrawText(wxString(name,wxConvUTF8),nameStart,y+2); + + u128 value = cpu->getRegister(category,i); + ChangedReg& changed = changedRegs[i]; + + switch (type) + { + case DebugInterface::NORMAL: // display them in 32 bit parts + switch (registerBits) + { + case 128: + { + int startIndex = std::min(3,maxBits/32-1); + int actualX = size.x-4-(startIndex+1)*(8*charWidth+2); + x = std::max(actualX,x); + + if (startIndex != 3) + { + bool c = false; + for (int i = 3; i > startIndex; i--) + c = c || changed.changed[i]; + + if (c) + { + dc.SetTextForeground(colorChanged); + dc.DrawText(L"+",x-charWidth,y+2); + } + } + + for (int i = startIndex; i >= 0; i--) + { + if (changed.changed[i]) + dc.SetTextForeground(colorChanged); + else + dc.SetTextForeground(colorUnchanged); + + drawU32Text(dc,value._u32[i],x,y+2); + x += charWidth*8+2; + } + break; + } + case 64: + { + if (maxBits < 64 && changed.changed[1]) + { + dc.SetTextForeground(colorChanged); + dc.DrawText(L"+",x-charWidth,y+2); + } + + for (int i = 1; i >= 0; i--) + { + if (changed.changed[i]) + dc.SetTextForeground(colorChanged); + else + dc.SetTextForeground(colorUnchanged); + + drawU32Text(dc,value._u32[i],x,y+2); + x += charWidth*8+2; + } + break; + } + case 32: + { + if (changed.changed[0]) + dc.SetTextForeground(colorChanged); + else + dc.SetTextForeground(colorUnchanged); + + drawU32Text(dc,value._u32[0],x,y+2); + break; + } + } + break; + case DebugInterface::SPECIAL: // let debug interface format them and just display them + { + if (changed.changed[0] || changed.changed[1] || changed.changed[2] || changed.changed[3]) + dc.SetTextForeground(colorChanged); + else + dc.SetTextForeground(colorUnchanged); + + dc.DrawText(cpu->getRegisterString(category,i),x,y+2); + break; + } + } + } +} + +void CtrlRegisterList::onPopupClick(wxCommandEvent& evt) +{ + switch (evt.GetId()) + { + case ID_REGISTERLIST_DISPLAY32: + maxBits = 32; + SetBestSize(ClientToWindowSize(GetMinClientSize())); + postEvent(debEVT_UPDATELAYOUT,0); + Refresh(); + break; + case ID_REGISTERLIST_DISPLAY64: + maxBits = 64; + SetBestSize(ClientToWindowSize(GetMinClientSize())); + postEvent(debEVT_UPDATELAYOUT,0); + Refresh(); + break; + case ID_REGISTERLIST_DISPLAY128: + maxBits = 128; + SetBestSize(ClientToWindowSize(GetMinClientSize())); + postEvent(debEVT_UPDATELAYOUT,0); + Refresh(); + break; + default: + wxMessageBox( L"Unimplemented.", L"Unimplemented.", wxICON_INFORMATION); + break; + } +} + +void CtrlRegisterList::setCurrentRow(int row) +{ + char str[256]; + u128 value ; + wxString text; + + const char* name = cpu->getRegisterName(category,row); + + switch (cpu->getRegisterType(category)) + { + case DebugInterface::NORMAL: + value = cpu->getRegister(category,row); + switch (cpu->getRegisterSize(category)) + { + case 128: + sprintf(str,"%s = 0x%08X%08X%08X%08X",name,value._u32[3],value._u32[2],value._u32[1],value._u32[0]); + break; + case 64: + sprintf(str,"%s = 0x%08X%08X",name,value._u32[1],value._u32[0]); + break; + case 32: + sprintf(str,"%s = 0x%08X",name,value._u32[0]); + break; + } + text = wxString(str,wxConvUTF8); + break; + case DebugInterface::SPECIAL: + text = wxString(name,wxConvUTF8) + L" = " + cpu->getRegisterString(category,row); + break; + } + + currentRows[category] = row; + postEvent(debEVT_SETSTATUSBARTEXT,text); + Refresh(); +} + +void CtrlRegisterList::mouseEvent(wxMouseEvent& evt) +{ + if (evt.GetEventType() == wxEVT_RIGHT_UP) + { + int y = evt.GetPosition().y; + + if (y >= rowHeight) + { + int row = (y-rowHeight)/rowHeight; + if (row != currentRows[category] && row < cpu->getRegisterCount(category)) + setCurrentRow(row); + } + + PopupMenu(&menu,evt.GetPosition()); + return; + } + + if (evt.ButtonIsDown(wxMOUSE_BTN_LEFT) || evt.ButtonIsDown(wxMOUSE_BTN_RIGHT)) + { + int x = evt.GetPosition().x; + int y = evt.GetPosition().y; + + if (y < rowHeight) + { + int piece = GetSize().x/cpu->getRegisterCategoryCount(); + int cat = x/piece; + + if (cat != category) + { + category = cat; + Refresh(); + } + } else { + int row = (y-rowHeight)/rowHeight; + if (row != currentRows[category] && row < cpu->getRegisterCount(category)) + setCurrentRow(row); + } + + SetFocus(); + SetFocusFromKbd(); + } +} + +void CtrlRegisterList::keydownEvent(wxKeyEvent& evt) +{ + switch (evt.GetKeyCode()) + { + case WXK_UP: + setCurrentRow(std::max(currentRows[category]-1,0)); + break; + case WXK_DOWN: + setCurrentRow(std::min(currentRows[category]+1,cpu->getRegisterCount(category))); + break; + case WXK_TAB: + category = (category+1) % cpu->getRegisterCategoryCount(); + Refresh(); + break; + } +} diff --git a/pcsx2/gui/Debugger/CtrlRegisterList.h b/pcsx2/gui/Debugger/CtrlRegisterList.h new file mode 100644 index 0000000000..df447ba5b3 --- /dev/null +++ b/pcsx2/gui/Debugger/CtrlRegisterList.h @@ -0,0 +1,72 @@ +#pragma once +#include + +#include "DebugTools/DebugInterface.h" +#include "DebugTools/DisassemblyManager.h" + +class CtrlRegisterList: public wxWindow +{ +public: + CtrlRegisterList(wxWindow* parent, DebugInterface* _cpu); + + void paintEvent(wxPaintEvent & evt); + void mouseEvent(wxMouseEvent& evt); + void keydownEvent(wxKeyEvent& evt); + void onPopupClick(wxCommandEvent& evt); + void redraw(); + DECLARE_EVENT_TABLE() + + virtual wxSize GetMinClientSize() const + { + int columnChars = 0; + int maxWidth = 0; + int maxRows = 0; + + for (int i = 0; i < cpu->getRegisterCategoryCount(); i++) + { + int bits = std::min(maxBits,cpu->getRegisterSize(i)); + int start = startPositions[i]; + + int w = start+(bits/4) * charWidth; + if (bits > 32) + w += (bits/32)*2-2; + + maxWidth = std::max(maxWidth,w); + columnChars += strlen(cpu->getRegisterCategoryName(i))+1; + maxRows = std::max(maxRows,cpu->getRegisterCount(i)); + } + + maxWidth = std::max(columnChars*charWidth,maxWidth+4); + + return wxSize(maxWidth,(maxRows+1)*rowHeight); + } + + virtual wxSize DoGetBestClientSize() const + { + return GetMinClientSize(); + } +private: + void render(wxDC& dc); + void refreshChangedRegs(); + void setCurrentRow(int row); + + void postEvent(wxEventType type, wxString text); + void postEvent(wxEventType type, int value); + + struct ChangedReg + { + u128 oldValue; + bool changed[4]; + }; + + std::vector changedCategories; + std::vector startPositions; + std::vector currentRows; + + DebugInterface* cpu; + int rowHeight,charWidth; + u32 lastPc; + int category; + int maxBits; + wxMenu menu; +}; \ No newline at end of file diff --git a/pcsx2/gui/Debugger/DebugEvents.h b/pcsx2/gui/Debugger/DebugEvents.h new file mode 100644 index 0000000000..b5e2dae30c --- /dev/null +++ b/pcsx2/gui/Debugger/DebugEvents.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include "DebugTools/DebugInterface.h" + +DECLARE_LOCAL_EVENT_TYPE( debEVT_SETSTATUSBARTEXT, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_UPDATELAYOUT, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_GOTOINMEMORYVIEW, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_GOTOINDISASM, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_RUNTOPOS, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_MAPLOADED, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_STEPOVER, wxNewEventType() ) +DECLARE_LOCAL_EVENT_TYPE( debEVT_UPDATE, wxNewEventType() ) + +bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest); diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.cpp b/pcsx2/gui/Debugger/DisassemblyDialog.cpp new file mode 100644 index 0000000000..a01998c783 --- /dev/null +++ b/pcsx2/gui/Debugger/DisassemblyDialog.cpp @@ -0,0 +1,287 @@ +#include "PrecompiledHeader.h" + +#include "DisassemblyDialog.h" +#include "DebugTools/DebugInterface.h" +#include "DebugTools/DisassemblyManager.h" +#include "DebugTools/Breakpoints.h" +#include "BreakpointWindow.h" + +BEGIN_EVENT_TABLE(DisassemblyDialog, wxFrame) + EVT_COMMAND( wxID_ANY, debEVT_SETSTATUSBARTEXT, DisassemblyDialog::onDebuggerEvent ) + EVT_COMMAND( wxID_ANY, debEVT_UPDATELAYOUT, DisassemblyDialog::onDebuggerEvent ) + EVT_COMMAND( wxID_ANY, debEVT_GOTOINMEMORYVIEW, DisassemblyDialog::onDebuggerEvent ) + EVT_COMMAND( wxID_ANY, debEVT_RUNTOPOS, DisassemblyDialog::onDebuggerEvent ) + EVT_COMMAND( wxID_ANY, debEVT_GOTOINDISASM, DisassemblyDialog::onDebuggerEvent ) + EVT_COMMAND( wxID_ANY, debEVT_STEPOVER, DisassemblyDialog::onDebuggerEvent ) + EVT_COMMAND( wxID_ANY, debEVT_UPDATE, DisassemblyDialog::onDebuggerEvent ) +END_EVENT_TABLE() + +CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu) + : wxPanel(parent), cpu(_cpu) +{ + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + SetSizer(mainSizer); + + bottomTabs = new wxNotebook(this,wxID_ANY); + disassembly = new CtrlDisassemblyView(this,cpu); + registerList = new CtrlRegisterList(this,cpu); + memory = new CtrlMemView(bottomTabs,cpu); + + // create register list and disassembly section + wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer* registerSizer = new wxBoxSizer(wxVERTICAL); + registerSizer->Add(registerList,1); + + middleSizer->Add(registerSizer,0,wxEXPAND|wxRIGHT,2); + middleSizer->Add(disassembly,2,wxEXPAND); + mainSizer->Add(middleSizer,3,wxEXPAND|wxBOTTOM,3); + + // create bottom section + bottomTabs->AddPage(memory,L"Memory"); + mainSizer->Add(bottomTabs,1,wxEXPAND); + + mainSizer->Layout(); +} + +DisassemblyDialog::DisassemblyDialog(wxWindow* parent): + wxFrame( parent, wxID_ANY, L"Debugger", wxDefaultPosition,wxDefaultSize,wxRESIZE_BORDER|wxCLOSE_BOX|wxCAPTION|wxSYSTEM_MENU ), + currentCpu(NULL) +{ + + topSizer = new wxBoxSizer( wxVERTICAL ); + wxPanel *panel = new wxPanel(this, wxID_ANY, + wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _("panel")); + panel->SetSizer(topSizer); + + // create top row + wxBoxSizer* topRowSizer = new wxBoxSizer(wxHORIZONTAL); + + breakRunButton = new wxButton(panel, wxID_ANY, L"Run"); + Connect(breakRunButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DisassemblyDialog::onBreakRunClicked)); + topRowSizer->Add(breakRunButton,0,wxRIGHT,8); + + stepIntoButton = new wxButton( panel, wxID_ANY, L"Step Into" ); + stepIntoButton->Enable(false); + topRowSizer->Add(stepIntoButton,0,wxBOTTOM,2); + + stepOverButton = new wxButton( panel, wxID_ANY, L"Step Over" ); + stepOverButton->Enable(false); + Connect( stepOverButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DisassemblyDialog::onStepOverClicked ) ); + topRowSizer->Add(stepOverButton); + + stepOutButton = new wxButton( panel, wxID_ANY, L"Step Out" ); + stepOutButton->Enable(false); + topRowSizer->Add(stepOutButton,0,wxRIGHT,8); + + breakpointButton = new wxButton( panel, wxID_ANY, L"Breakpoint" ); + Connect( breakpointButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DisassemblyDialog::onBreakpointClick ) ); + topRowSizer->Add(breakpointButton); + + topSizer->Add(topRowSizer,0,wxLEFT|wxRIGHT|wxTOP,3); + + // create middle part of the window + wxNotebook* middleBook = new wxNotebook(panel,wxID_ANY); + middleBook->SetBackgroundColour(wxColour(0xFFF0F0F0)); + eeTab = new CpuTabPage(middleBook,&r5900Debug); + iopTab = new CpuTabPage(middleBook,&r3000Debug); + middleBook->AddPage(eeTab,L"R5900"); + middleBook->AddPage(iopTab,L"R3000"); + Connect(middleBook->GetId(),wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED,wxCommandEventHandler( DisassemblyDialog::onPageChanging)); + topSizer->Add(middleBook,3,wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM,3); + currentCpu = eeTab; + + CreateStatusBar(1); + + SetMinSize(wxSize(1000,600)); + panel->GetSizer()->Fit(this); + + setDebugMode(true); +} + +void DisassemblyDialog::onBreakRunClicked(wxCommandEvent& evt) +{ + if (r5900Debug.isCpuPaused()) + { + if (CBreakPoints::IsAddressBreakPoint(r5900Debug.getPC())) + CBreakPoints::SetSkipFirst(r5900Debug.getPC()); + r5900Debug.resumeCpu(); + } else + r5900Debug.pauseCpu(); +} + +void DisassemblyDialog::onStepOverClicked(wxCommandEvent& evt) +{ + stepOver(); +} + +void DisassemblyDialog::onPageChanging(wxCommandEvent& evt) +{ + wxNotebook* notebook = (wxNotebook*)wxWindow::FindWindowById(evt.GetId()); + wxWindow* currentPage = notebook->GetCurrentPage(); + + if (currentPage == eeTab) + currentCpu = eeTab; + else if (currentPage == iopTab) + currentCpu = iopTab; + else + currentCpu = NULL; + + if (currentCpu != NULL) + { + currentCpu->getDisassembly()->SetFocus(); + currentCpu->Refresh(); + } +} + +void DisassemblyDialog::stepOver() +{ + if (!r5900Debug.isAlive() || !r5900Debug.isCpuPaused() || currentCpu == NULL) + return; + + + // todo: breakpoints for iop + if (currentCpu != eeTab) + return; + + CtrlDisassemblyView* disassembly = currentCpu->getDisassembly(); + + // If the current PC is on a breakpoint, the user doesn't want to do nothing. + CBreakPoints::SetSkipFirst(r5900Debug.getPC()); + u32 currentPc = r5900Debug.getPC(); + + MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(&r5900Debug,r5900Debug.getPC()); + u32 breakpointAddress = currentPc+disassembly->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 += 4; + } 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*4; + disassembly->scrollStepping(breakpointAddress); + } + } + } else { + disassembly->scrollStepping(breakpointAddress); + } + + CBreakPoints::AddBreakPoint(breakpointAddress,true); + r5900Debug.resumeCpu(); +} + +void DisassemblyDialog::onBreakpointClick(wxCommandEvent& evt) +{ + if (currentCpu == NULL) + return; + + BreakpointWindow bpw(this,currentCpu->getCpu()); + if (bpw.ShowModal() == wxID_OK) + { + bpw.addBreakpoint(); + update(); + } +} + +void DisassemblyDialog::onDebuggerEvent(wxCommandEvent& evt) +{ + wxEventType type = evt.GetEventType(); + if (type == debEVT_SETSTATUSBARTEXT) + { + GetStatusBar()->SetLabel(evt.GetString()); + } else if (type == debEVT_UPDATELAYOUT) + { + if (currentCpu != NULL) + currentCpu->GetSizer()->Layout(); + topSizer->Layout(); + update(); + } else if (type == debEVT_GOTOINMEMORYVIEW) + { + if (currentCpu != NULL) + currentCpu->getMemoryView()->gotoAddress(evt.GetInt()); + } else if (type == debEVT_RUNTOPOS) + { + // todo: breakpoints for iop + if (currentCpu != eeTab) + return; + CBreakPoints::AddBreakPoint(evt.GetInt(),true); + currentCpu->getCpu()->resumeCpu(); + } else if (type == debEVT_GOTOINDISASM) + { + if (currentCpu != NULL) + { + currentCpu->getDisassembly()->gotoAddress(r5900Debug.getPC()); + currentCpu->getDisassembly()->SetFocus(); + update(); + } + } else if (type == debEVT_STEPOVER) + { + if (currentCpu != NULL) + stepOver(); + } else if (type == debEVT_UPDATE) + { + update(); + } +} + +void DisassemblyDialog::update() +{ + if (currentCpu != NULL) + { + stepOverButton->Enable(true); + breakpointButton->Enable(true); + currentCpu->Refresh(); + } else { + stepOverButton->Enable(false); + breakpointButton->Enable(false); + } +} + +void DisassemblyDialog::reset() +{ + eeTab->getDisassembly()->clearFunctions(); + iopTab->getDisassembly()->clearFunctions(); +}; + +void DisassemblyDialog::setDebugMode(bool debugMode) +{ + bool running = r5900Debug.isAlive(); + + eeTab->Enable(running); + iopTab->Enable(running); + + if (running) + { + if (debugMode) + { + CBreakPoints::ClearTemporaryBreakPoints(); + breakRunButton->SetLabel(L"Run"); + + stepOverButton->Enable(true); + + eeTab->getDisassembly()->gotoPc(); + iopTab->getDisassembly()->gotoPc(); + if (currentCpu != NULL) + currentCpu->getDisassembly()->SetFocus(); + } else { + breakRunButton->SetLabel(L"Break"); + + stepIntoButton->Enable(false); + stepOverButton->Enable(false); + stepOutButton->Enable(false); + } + } + + update(); +} \ No newline at end of file diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.h b/pcsx2/gui/Debugger/DisassemblyDialog.h new file mode 100644 index 0000000000..60b6adff2b --- /dev/null +++ b/pcsx2/gui/Debugger/DisassemblyDialog.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include "App.h" + +#include "CtrlDisassemblyView.h" +#include "CtrlRegisterList.h" +#include "CtrlMemView.h" +#include "DebugEvents.h" + +class CpuTabPage: public wxPanel +{ +public: + CpuTabPage(wxWindow* parent, DebugInterface* _cpu); + DebugInterface* getCpu() { return cpu; }; + CtrlDisassemblyView* getDisassembly() { return disassembly; }; + CtrlRegisterList* getRegisterList() { return registerList; }; + CtrlMemView* getMemoryView() { return memory; }; + wxNotebook* getBottomTabs() { return bottomTabs; }; +private: + DebugInterface* cpu; + CtrlDisassemblyView* disassembly; + CtrlRegisterList* registerList; + CtrlMemView* memory; + wxNotebook* bottomTabs; +}; + +class DisassemblyDialog : public wxFrame +{ +public: + DisassemblyDialog( wxWindow* parent=NULL ); + virtual ~DisassemblyDialog() throw() {} + + static wxString GetNameStatic() { return L"DisassemblyDialog"; } + wxString GetDialogName() const { return GetNameStatic(); } + + void update(); + void reset(); + void setDebugMode(bool debugMode); + + DECLARE_EVENT_TABLE() +protected: + void onBreakRunClicked(wxCommandEvent& evt); + void onStepOverClicked(wxCommandEvent& evt); + void onDebuggerEvent(wxCommandEvent& evt); + void onPageChanging(wxCommandEvent& evt); + void onBreakpointClick(wxCommandEvent& evt); + void stepOver(); +private: + CpuTabPage* eeTab; + CpuTabPage* iopTab; + CpuTabPage* currentCpu; + + wxBoxSizer* topSizer; + wxStatusBar* statusBar; + wxButton* breakRunButton; + wxButton* stepIntoButton; + wxButton* stepOverButton; + wxButton* stepOutButton; + wxButton* breakpointButton; +}; \ No newline at end of file diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index f128074a44..f32a4af20a 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -508,7 +508,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title) m_menuMisc.Append( MenuId_ChangeLang, L"Change Language" ); // Always in English #ifdef PCSX2_DEVBUILD - //m_menuDebug.Append(MenuId_Debug_Open, _("Open Debug Window..."), wxEmptyString); + m_menuDebug.Append(MenuId_Debug_Open, _("Open Debug Window..."), wxEmptyString); //m_menuDebug.Append(MenuId_Debug_MemoryDump, _("Memory Dump..."), wxEmptyString); m_menuDebug.Append(MenuId_Debug_Logging, _("Logging..."), wxEmptyString); #endif diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 14399bed9b..702f7281c2 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -24,6 +24,7 @@ #include "Dialogs/ModalPopups.h" #include "Dialogs/ConfigurationDialog.h" #include "Dialogs/LogOptionsDialog.h" +#include "Debugger/DisassemblyDialog.h" #include "Utilities/IniInterface.h" @@ -554,6 +555,9 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event) void MainEmuFrame::Menu_Debug_Open_Click(wxCommandEvent &event) { + DisassemblyDialog* dlg = wxGetApp().GetDisassemblyPtr(); + if (dlg) + dlg->Show(); } void MainEmuFrame::Menu_Debug_MemoryDump_Click(wxCommandEvent &event) diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index 02cf66bf39..8ed6c29b8d 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -160,6 +160,24 @@ %(RootDir)%(Directory)\%(Filename).h %(RootDir)%(Directory)\%(Filename).h + + Document + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + + + Document + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + @@ -402,11 +420,23 @@ + + + + + + + + + + + + @@ -674,9 +704,21 @@ + + + + + + + + + + + + diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index 7b4a7b2fbe..7a59ee5f6b 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -148,6 +148,9 @@ {b9da6fbf-4fd9-4cfb-89f7-34ac0f933ce9} + + {02398f88-aadb-4183-be7e-68f0cacdf620} + @@ -802,6 +805,42 @@ System\ISO + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + AppHost\Debugger + @@ -1171,6 +1210,42 @@ System\ISO + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + AppHost\Debugger + @@ -1246,5 +1321,11 @@ AppHost\Resources + + AppHost\Resources + + + AppHost\Resources + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj index 093212a5fe..bf8d192a04 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj @@ -407,11 +407,23 @@ + + + + + + + + + + + + @@ -679,9 +691,21 @@ + + + + + + + + + + + + @@ -887,6 +911,24 @@ false + + + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + + + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters index 7b4a7b2fbe..6bb02146cd 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters @@ -148,6 +148,9 @@ {b9da6fbf-4fd9-4cfb-89f7-34ac0f933ce9} + + {b49e5139-daa9-411b-995e-a53d98c583ba} + @@ -802,6 +805,42 @@ System\ISO + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + @@ -1171,6 +1210,42 @@ System\ISO + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + @@ -1246,5 +1321,11 @@ AppHost\Resources + + AppHost\Resources + + + AppHost\Resources + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj index e48ce32513..89b9744226 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj @@ -407,11 +407,23 @@ + + + + + + + + + + + + @@ -679,9 +691,21 @@ + + + + + + + + + + + + @@ -887,6 +911,24 @@ false + + + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + + + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension) + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + %(RootDir)%(Directory)\%(Filename).h + + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters index 5e09cf7d3b..cdcc925374 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters @@ -148,6 +148,9 @@ {b9da6fbf-4fd9-4cfb-89f7-34ac0f933ce9} + + {7a0a1104-f9ee-4cd1-aaba-f8267eba1658} + @@ -802,6 +805,42 @@ System\ISO + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + System\Ps2\Debug + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + AppHost\Debugger + @@ -1171,6 +1210,42 @@ System\ISO + + System\Ps2\Debug + + + System\Ps2\Debug + + + System\Ps2\Debug + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + System\Ps2\Debug + + + AppHost\Debugger + + + AppHost\Debugger + + + AppHost\Debugger + + + System\Ps2\Debug + + + AppHost\Debugger + @@ -1246,5 +1321,11 @@ AppHost\Resources + + AppHost\Resources + + + AppHost\Resources + \ No newline at end of file diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index e4f55c235f..2f7dd856fc 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -32,6 +32,8 @@ #include "CDVD/CDVD.h" #include "Elfheader.h" +#include "../DebugTools/Breakpoints.h" + #if !PCSX2_SEH # include #endif @@ -1254,11 +1256,36 @@ int cop2flags(u32 code) } #endif + +void dynarecCheckBreakpoint() +{ + u32 pc = cpuRegs.pc; + if (CBreakPoints::CheckSkipFirst(pc)) + { + CBreakPoints::SetSkipFirst(0xFFFFFFFF); + return; + } + + auto cond = CBreakPoints::GetBreakPointCondition(pc); + if (cond && !cond->Evaluate()) + return; + + GetCoreThread().PauseSelf(); + recExitExecution(); +} + void recompileNextInstruction(int delayslot) { static u8 s_bFlushReg = 1; int i, count; + // add breakpoint + if (CBreakPoints::IsAddressBreakPoint(pc)) + { + iFlushCall(FLUSH_EVERYTHING); + xCALL(&dynarecCheckBreakpoint); + } + s_pCode = (int *)PSM( pc ); pxAssert(s_pCode); @@ -1595,8 +1622,22 @@ static void __fastcall recRecompile( const u32 startpc ) s_nEndBlock = 0xffffffff; s_branchTo = -1; + // compile breakpoints as individual blocks + if (CBreakPoints::IsAddressBreakPoint(i)) + { + s_nEndBlock = i + 4; + goto StartRecomp; + } + while(1) { BASEBLOCK* pblock = PC_GETBLOCK(i); + + // stop before breakpoints + if (CBreakPoints::IsAddressBreakPoint(i)) + { + s_nEndBlock = i; + break; + } if(i != startpc) // Block size truncation checks. {