mirror of https://github.com/PCSX2/pcsx2.git
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
This commit is contained in:
parent
038d3bea87
commit
babd8868d9
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
#include "Breakpoints.h"
|
||||
#include "SymbolMap.h"
|
||||
#include "MIPSAnalyst.h"
|
||||
#include <cstdio>
|
||||
#include "../R5900.h"
|
||||
#include "../System.h"
|
||||
|
||||
std::vector<BreakPoint> CBreakPoints::breakPoints_;
|
||||
u32 CBreakPoints::breakSkipFirstAt_ = 0;
|
||||
u64 CBreakPoints::breakSkipFirstTicks_ = 0;
|
||||
std::vector<MemCheck> CBreakPoints::memChecks_;
|
||||
std::vector<MemCheck *> 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<MemCheck>::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<MemCheck> CBreakPoints::GetMemCheckRanges()
|
||||
{
|
||||
std::vector<MemCheck> 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<MemCheck> CBreakPoints::GetMemChecks()
|
||||
{
|
||||
return memChecks_;
|
||||
}
|
||||
|
||||
const std::vector<BreakPoint> 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();
|
||||
}
|
|
@ -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 <vector>
|
||||
|
||||
#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<MemCheck> GetMemCheckRanges();
|
||||
|
||||
static const std::vector<MemCheck> GetMemChecks();
|
||||
static const std::vector<BreakPoint> 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<BreakPoint> breakPoints_;
|
||||
static u32 breakSkipFirstAt_;
|
||||
static u64 breakSkipFirstTicks_;
|
||||
|
||||
static std::vector<MemCheck> memChecks_;
|
||||
static std::vector<MemCheck *> cleanupMemChecks_;
|
||||
};
|
||||
|
||||
|
|
@ -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!
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]); }
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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<BranchLine>& 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<BranchLine>& dest);
|
||||
private:
|
||||
void generateBranchLines();
|
||||
void load();
|
||||
void clear();
|
||||
void addOpcodeSequence(u32 start, u32 end);
|
||||
|
||||
DebugInterface* cpu;
|
||||
u32 address;
|
||||
u32 size;
|
||||
u32 hash;
|
||||
std::vector<BranchLine> lines;
|
||||
std::map<u32,DisassemblyEntry*> entries;
|
||||
std::vector<u32> 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<BranchLine>& 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<u32,DataEntry> lines;
|
||||
std::vector<u32> 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<BranchLine> 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<u32,DisassemblyEntry*> entries;
|
||||
DebugInterface* cpu;
|
||||
static int maxParamChars;
|
||||
};
|
||||
|
||||
bool isInInterval(u32 start, u32 size, u32 value);
|
|
@ -0,0 +1,594 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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<ExpressionOpcodeType> 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<u64> 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_SHR: // 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]<fArg[0]);
|
||||
else
|
||||
valueStack.push_back(arg[1]<arg[0]);
|
||||
break;
|
||||
case EXOP_EQUAL: // a == b
|
||||
valueStack.push_back(arg[1]==arg[0]);
|
||||
break;
|
||||
case EXOP_NOTEQUAL: // a != b
|
||||
valueStack.push_back(arg[1]!=arg[0]);
|
||||
break;
|
||||
case EXOP_BITAND: // a&b
|
||||
valueStack.push_back(arg[1]&arg[0]);
|
||||
break;
|
||||
case EXOP_XOR: // a^b
|
||||
valueStack.push_back(arg[1]^arg[0]);
|
||||
break;
|
||||
case EXOP_BITOR: // a|b
|
||||
valueStack.push_back(arg[1]|arg[0]);
|
||||
break;
|
||||
case EXOP_LOGAND: // a && b
|
||||
valueStack.push_back(arg[1]&&arg[0]);
|
||||
break;
|
||||
case EXOP_LOGOR: // a || b
|
||||
valueStack.push_back(arg[1]||arg[0]);
|
||||
break;
|
||||
case EXOP_TERTIF: // darf so nicht vorkommen
|
||||
return false;
|
||||
case EXOP_TERTELSE: // exp ? exp : exp, else muss zuerst kommen!
|
||||
if (exp[num++].second != EXOP_TERTIF)
|
||||
{
|
||||
sprintf(expressionError,"Invalid tertiary operator");
|
||||
return false;
|
||||
}
|
||||
valueStack.push_back(arg[2]?arg[1]:arg[0]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valueStack.size() != 1) return false;
|
||||
dest = valueStack[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseExpression(char* exp, IExpressionFunctions* funcs, u64& dest)
|
||||
{
|
||||
PostfixExpression postfix;
|
||||
if (initPostfixExpression(exp,funcs,postfix) == false) return false;
|
||||
return parsePostfixExpression(postfix,funcs,dest);
|
||||
}
|
||||
|
||||
const char* getExpressionError()
|
||||
{
|
||||
if (expressionError[0] == 0) strcpy(expressionError,"Invalid expression");
|
||||
return expressionError;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "Pcsx2Types.h"
|
||||
#include <vector>
|
||||
|
||||
typedef std::pair<u64,u64> ExpressionPair;
|
||||
typedef std::vector<ExpressionPair> 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();
|
|
@ -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<MIPSAnalyst::AnalyzedFunction> 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -0,0 +1,704 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "SymbolMap.h"
|
||||
#include <algorithm>
|
||||
|
||||
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<SymbolEntry> SymbolMap::GetAllSymbols(SymbolType symmask) {
|
||||
std::vector<SymbolEntry> 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<LoadedModuleInfo> SymbolMap::getAllModules() const {
|
||||
Threading::ScopedLock guard(lock_);
|
||||
|
||||
std::vector<LoadedModuleInfo> 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<int, u32> 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;
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#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<SymbolEntry> 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<LoadedModuleInfo> 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<u32, const FunctionEntry> activeFunctions;
|
||||
std::map<u32, const LabelEntry> activeLabels;
|
||||
std::map<u32, const DataEntry> activeData;
|
||||
|
||||
// This is indexed by the end address of the module.
|
||||
std::map<u32, const ModuleEntry> activeModuleEnds;
|
||||
|
||||
typedef std::pair<int, u32> SymbolKey;
|
||||
|
||||
// These are indexed by the module id and relative address in the module.
|
||||
std::map<SymbolKey, FunctionEntry> functions;
|
||||
std::map<SymbolKey, LabelEntry> labels;
|
||||
std::map<SymbolKey, DataEntry> data;
|
||||
std::vector<ModuleEntry> modules;
|
||||
|
||||
mutable Threading::MutexRecursive lock_;
|
||||
};
|
||||
|
||||
extern SymbolMap symbolMap;
|
||||
|
|
@ -23,6 +23,7 @@ using namespace std;
|
|||
|
||||
u32 ElfCRC;
|
||||
u32 ElfEntry;
|
||||
std::pair<u32,u32> 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<u32,u32> 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());
|
||||
|
|
|
@ -155,6 +155,7 @@ class ElfObject
|
|||
bool hasSectionHeaders();
|
||||
bool hasHeaders();
|
||||
|
||||
std::pair<u32,u32> getTextRange();
|
||||
u32 getCRC();
|
||||
};
|
||||
|
||||
|
@ -165,6 +166,7 @@ extern int GetPS2ElfName( wxString& dest );
|
|||
|
||||
extern u32 ElfCRC;
|
||||
extern u32 ElfEntry;
|
||||
extern std::pair<u32,u32> ElfTextRange;
|
||||
extern wxString LastELF;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_]; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <wx/stdpaths.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "DebugTools/Debug.h"
|
||||
#include "Dialogs/ModalPopups.h"
|
||||
|
||||
#include "Debugger/DisassemblyDialog.h"
|
||||
|
||||
#include <wx/cmdline.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/stdpaths.h>
|
||||
|
@ -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...
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "Dialogs/ConfigurationDialog.h"
|
||||
#include "Dialogs/LogOptionsDialog.h"
|
||||
|
||||
#include "Debugger/DisassemblyDialog.h"
|
||||
|
||||
#include "Utilities/IniInterface.h"
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
|
@ -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 )
|
||||
|
|
|
@ -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<int>(addressText->GetSize().x,sizeText->GetSize().x);
|
||||
minWidth = std::max<int>(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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
#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;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
|
||||
#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<u32,int>& 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<u32> jumpStack;
|
||||
|
||||
wxIcon bpEnabled,bpDisabled;
|
||||
wxMenu menu;
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
|
||||
#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;
|
||||
};
|
|
@ -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<int>(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<int>(3,maxBits/32-1);
|
||||
int actualX = size.x-4-(startIndex+1)*(8*charWidth+2);
|
||||
x = std::max<int>(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<int>(currentRows[category]-1,0));
|
||||
break;
|
||||
case WXK_DOWN:
|
||||
setCurrentRow(std::min<int>(currentRows[category]+1,cpu->getRegisterCount(category)));
|
||||
break;
|
||||
case WXK_TAB:
|
||||
category = (category+1) % cpu->getRegisterCategoryCount();
|
||||
Refresh();
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
|
||||
#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<u32>(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<int>(maxWidth,w);
|
||||
columnChars += strlen(cpu->getRegisterCategoryName(i))+1;
|
||||
maxRows = std::max<int>(maxRows,cpu->getRegisterCount(i));
|
||||
}
|
||||
|
||||
maxWidth = std::max<int>(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<ChangedReg*> changedCategories;
|
||||
std::vector<int> startPositions;
|
||||
std::vector<int> currentRows;
|
||||
|
||||
DebugInterface* cpu;
|
||||
int rowHeight,charWidth;
|
||||
u32 lastPc;
|
||||
int category;
|
||||
int maxBits;
|
||||
wxMenu menu;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
#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);
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
#include <wx/notebook.h>
|
||||
#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;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -160,6 +160,24 @@
|
|||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Active.png">
|
||||
<FileType>Document</FileType>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Inactive.png">
|
||||
<FileType>Document</FileType>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<None Include="..\..\Utilities\folderdesc.txt" />
|
||||
<None Include="..\..\Docs\License.txt" />
|
||||
<None Include="..\..\x86\aVUzerorec.S" />
|
||||
|
@ -402,11 +420,23 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\..\CDVD\BlockdumpFileReader.cpp" />
|
||||
<ClCompile Include="..\..\CDVD\OutputIsoFile.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\Breakpoints.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\DebugInterface.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\DisassemblyManager.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\ExpressionParser.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\MIPSAnalyst.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp" />
|
||||
<ClCompile Include="..\..\GameDatabase.cpp" />
|
||||
<ClCompile Include="..\..\Gif_Logger.cpp" />
|
||||
<ClCompile Include="..\..\Gif_Unit.cpp" />
|
||||
<ClCompile Include="..\..\gui\AppGameDatabase.cpp" />
|
||||
<ClCompile Include="..\..\gui\AppUserMode.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\BreakpointWindow.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlDisassemblyView.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\GameDatabaseDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\McdConfigDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Panels\GameDatabasePanel.cpp" />
|
||||
|
@ -674,9 +704,21 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\AsyncFileReader.h" />
|
||||
<ClInclude Include="..\..\DebugTools\Breakpoints.h" />
|
||||
<ClInclude Include="..\..\DebugTools\DebugInterface.h" />
|
||||
<ClInclude Include="..\..\DebugTools\DisassemblyManager.h" />
|
||||
<ClInclude Include="..\..\DebugTools\ExpressionParser.h" />
|
||||
<ClInclude Include="..\..\DebugTools\MIPSAnalyst.h" />
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h" />
|
||||
<ClInclude Include="..\..\GameDatabase.h" />
|
||||
<ClInclude Include="..\..\Gif_Unit.h" />
|
||||
<ClInclude Include="..\..\gui\AppGameDatabase.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\BreakpointWindow.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlDisassemblyView.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h" />
|
||||
<ClInclude Include="..\..\gui\Panels\MemoryCardPanels.h" />
|
||||
<ClInclude Include="..\..\IPU\IPUdma.h" />
|
||||
<ClInclude Include="..\..\Patch.h" />
|
||||
|
|
|
@ -148,6 +148,9 @@
|
|||
<Filter Include="AppHost\Cheats">
|
||||
<UniqueIdentifier>{b9da6fbf-4fd9-4cfb-89f7-34ac0f933ce9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="AppHost\Debugger">
|
||||
<UniqueIdentifier>{02398f88-aadb-4183-be7e-68f0cacdf620}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\Utilities\folderdesc.txt">
|
||||
|
@ -802,6 +805,42 @@
|
|||
<ClCompile Include="..\..\CDVD\BlockdumpFileReader.cpp">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlDisassemblyView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\DebugInterface.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\DisassemblyManager.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\MIPSAnalyst.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\Breakpoints.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\ExpressionParser.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\BreakpointWindow.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\Patch.h">
|
||||
|
@ -1171,6 +1210,42 @@
|
|||
<ClInclude Include="..\..\AsyncFileReader.h">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlDisassemblyView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\DebugInterface.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\DisassemblyManager.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\MIPSAnalyst.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\Breakpoints.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\ExpressionParser.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\BreakpointWindow.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\..\3rdparty\wxWidgets\include\wx\msw\wx.rc">
|
||||
|
@ -1246,5 +1321,11 @@
|
|||
<CustomBuild Include="..\..\gui\Resources\ConfigIcon_Appearance.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Inactive.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Active.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -407,11 +407,23 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\..\CDVD\BlockdumpFileReader.cpp" />
|
||||
<ClCompile Include="..\..\CDVD\OutputIsoFile.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\Breakpoints.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\DebugInterface.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\DisassemblyManager.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\ExpressionParser.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\MIPSAnalyst.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp" />
|
||||
<ClCompile Include="..\..\GameDatabase.cpp" />
|
||||
<ClCompile Include="..\..\Gif_Logger.cpp" />
|
||||
<ClCompile Include="..\..\Gif_Unit.cpp" />
|
||||
<ClCompile Include="..\..\gui\AppGameDatabase.cpp" />
|
||||
<ClCompile Include="..\..\gui\AppUserMode.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\BreakpointWindow.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlDisassemblyView.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\GameDatabaseDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\McdConfigDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Panels\GameDatabasePanel.cpp" />
|
||||
|
@ -679,9 +691,21 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\AsyncFileReader.h" />
|
||||
<ClInclude Include="..\..\DebugTools\Breakpoints.h" />
|
||||
<ClInclude Include="..\..\DebugTools\DebugInterface.h" />
|
||||
<ClInclude Include="..\..\DebugTools\DisassemblyManager.h" />
|
||||
<ClInclude Include="..\..\DebugTools\ExpressionParser.h" />
|
||||
<ClInclude Include="..\..\DebugTools\MIPSAnalyst.h" />
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h" />
|
||||
<ClInclude Include="..\..\GameDatabase.h" />
|
||||
<ClInclude Include="..\..\Gif_Unit.h" />
|
||||
<ClInclude Include="..\..\gui\AppGameDatabase.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\BreakpointWindow.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlDisassemblyView.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h" />
|
||||
<ClInclude Include="..\..\gui\Panels\MemoryCardPanels.h" />
|
||||
<ClInclude Include="..\..\IPU\IPUdma.h" />
|
||||
<ClInclude Include="..\..\Patch.h" />
|
||||
|
@ -887,6 +911,24 @@
|
|||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Active.png">
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Inactive.png">
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
|
|
|
@ -148,6 +148,9 @@
|
|||
<Filter Include="AppHost\Cheats">
|
||||
<UniqueIdentifier>{b9da6fbf-4fd9-4cfb-89f7-34ac0f933ce9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="AppHost\Debugger">
|
||||
<UniqueIdentifier>{b49e5139-daa9-411b-995e-a53d98c583ba}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\Utilities\folderdesc.txt">
|
||||
|
@ -802,6 +805,42 @@
|
|||
<ClCompile Include="..\..\CDVD\BlockdumpFileReader.cpp">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\BreakpointWindow.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlDisassemblyView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\Breakpoints.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\DebugInterface.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\DisassemblyManager.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\ExpressionParser.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\MIPSAnalyst.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\Patch.h">
|
||||
|
@ -1171,6 +1210,42 @@
|
|||
<ClInclude Include="..\..\AsyncFileReader.h">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\BreakpointWindow.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlDisassemblyView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\Breakpoints.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\DebugInterface.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\DisassemblyManager.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\ExpressionParser.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\MIPSAnalyst.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\..\3rdparty\wxWidgets\include\wx\msw\wx.rc">
|
||||
|
@ -1246,5 +1321,11 @@
|
|||
<CustomBuild Include="..\..\gui\Resources\ConfigIcon_Appearance.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Inactive.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Active.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -407,11 +407,23 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\..\CDVD\BlockdumpFileReader.cpp" />
|
||||
<ClCompile Include="..\..\CDVD\OutputIsoFile.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\Breakpoints.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\DebugInterface.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\DisassemblyManager.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\ExpressionParser.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\MIPSAnalyst.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp" />
|
||||
<ClCompile Include="..\..\GameDatabase.cpp" />
|
||||
<ClCompile Include="..\..\Gif_Logger.cpp" />
|
||||
<ClCompile Include="..\..\Gif_Unit.cpp" />
|
||||
<ClCompile Include="..\..\gui\AppGameDatabase.cpp" />
|
||||
<ClCompile Include="..\..\gui\AppUserMode.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\BreakpointWindow.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlDisassemblyView.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp" />
|
||||
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\GameDatabaseDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\McdConfigDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Panels\GameDatabasePanel.cpp" />
|
||||
|
@ -679,9 +691,21 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\AsyncFileReader.h" />
|
||||
<ClInclude Include="..\..\DebugTools\Breakpoints.h" />
|
||||
<ClInclude Include="..\..\DebugTools\DebugInterface.h" />
|
||||
<ClInclude Include="..\..\DebugTools\DisassemblyManager.h" />
|
||||
<ClInclude Include="..\..\DebugTools\ExpressionParser.h" />
|
||||
<ClInclude Include="..\..\DebugTools\MIPSAnalyst.h" />
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h" />
|
||||
<ClInclude Include="..\..\GameDatabase.h" />
|
||||
<ClInclude Include="..\..\Gif_Unit.h" />
|
||||
<ClInclude Include="..\..\gui\AppGameDatabase.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\BreakpointWindow.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlDisassemblyView.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h" />
|
||||
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h" />
|
||||
<ClInclude Include="..\..\gui\Panels\MemoryCardPanels.h" />
|
||||
<ClInclude Include="..\..\IPU\IPUdma.h" />
|
||||
<ClInclude Include="..\..\Patch.h" />
|
||||
|
@ -887,6 +911,24 @@
|
|||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Active.png">
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Inactive.png">
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cmd.exe /c "%(RootDir)%(Directory)\bin2cpp.cmd" %(Filename)%(Extension)</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(RootDir)%(Directory)\%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
|
|
|
@ -148,6 +148,9 @@
|
|||
<Filter Include="AppHost\Cheats">
|
||||
<UniqueIdentifier>{b9da6fbf-4fd9-4cfb-89f7-34ac0f933ce9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="AppHost\Debugger">
|
||||
<UniqueIdentifier>{7a0a1104-f9ee-4cd1-aaba-f8267eba1658}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\Utilities\folderdesc.txt">
|
||||
|
@ -802,6 +805,42 @@
|
|||
<ClCompile Include="..\..\CDVD\BlockdumpFileReader.cpp">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\DebugInterface.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\DisassemblyManager.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlDisassemblyView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\MIPSAnalyst.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\Breakpoints.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DebugTools\ExpressionParser.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Debugger\BreakpointWindow.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\Patch.h">
|
||||
|
@ -1171,6 +1210,42 @@
|
|||
<ClInclude Include="..\..\AsyncFileReader.h">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\DebugInterface.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\DisassemblyManager.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlDisassemblyView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\MIPSAnalyst.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\Breakpoints.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DebugTools\ExpressionParser.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\gui\Debugger\BreakpointWindow.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\..\3rdparty\wxWidgets\include\wx\msw\wx.rc">
|
||||
|
@ -1246,5 +1321,11 @@
|
|||
<CustomBuild Include="..\..\gui\Resources\ConfigIcon_Appearance.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Inactive.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\gui\Resources\Breakpoint_Active.png">
|
||||
<Filter>AppHost\Resources</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -32,6 +32,8 @@
|
|||
#include "CDVD/CDVD.h"
|
||||
#include "Elfheader.h"
|
||||
|
||||
#include "../DebugTools/Breakpoints.h"
|
||||
|
||||
#if !PCSX2_SEH
|
||||
# include <csetjmp>
|
||||
#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.
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue