Merge pull request #5 from Kingcom/master

More debugger enhancements and fixes
This commit is contained in:
David Quintana 2014-03-25 14:42:59 +01:00
commit 2fbac107ca
37 changed files with 1165 additions and 75 deletions

40
bin/docs/debugger.txt Normal file
View File

@ -0,0 +1,40 @@
disassembly view:
ctrg+g goto
ctrl+e edit breakpoint
ctrl+d enable/disable breakpoint
ctrl+b add breakpoint
right follow branch/position memory view to accessed address
left go back one branch level/goto pc
up move cursor up one line
down move cursor down one line
page up move visible area up one page
page down move visible area down one page
f10 step over
f11 step into
tab toggle display symbols
left click select line/toggle breakpoint if line is already highlighted
right click open context menu
memory view:
ctrg+g goto
ctrl+b add breakpoint
left move cursor back one byte/nibble
right move cursor ahead one byte/nibble
up move cursor up one line
down move cursor down one line
page up move cursor up one page
page down move cursor down one page
0-9,A-F overwrite hex nibble
any overwrite ansi byte
left click select byte/nibble
right click open context menu
breakpoint list:
up select previous item
down select next item
delete remove selected breakpoint
return edit selected breakpoint
space toggle enable state of selected breakpoint

View File

@ -33,6 +33,9 @@
#include "CDVDisoReader.h"
#include "Utilities/ScopedPtr.h"
#include "DebugTools/SymbolMap.h"
#include "AppConfig.h"
const wxChar* CDVD_SourceLabels[] =
{
L"Iso",
@ -287,6 +290,21 @@ static CDVD_SourceType m_CurrentSourceType = CDVDsrc_NoDisc;
void CDVDsys_SetFile( CDVD_SourceType srctype, const wxString& newfile )
{
m_SourceFilename[srctype] = newfile;
// look for symbol file
if (g_Conf->EmuOptions.Debugger.EnableDebugger && symbolMap.IsEmpty())
{
wxString symName;
int n = newfile.Last('.');
if (n == wxNOT_FOUND)
symName = newfile + L".sym";
else
symName = newfile.substr(0,n) + L".sym";
wxCharBuffer buf = symName.ToUTF8();
symbolMap.LoadNocashSym(buf);
symbolMap.UpdateActiveSymbols();
}
}
const wxString& CDVDsys_GetFile( CDVD_SourceType srctype )

View File

@ -326,6 +326,7 @@ set(pcsx2GuiSources
gui/Debugger/CtrlDisassemblyView.cpp
gui/Debugger/CtrlRegisterList.cpp
gui/Debugger/CtrlMemView.cpp
gui/Debugger/DebuggerLists.cpp
gui/Debugger/DisassemblyDialog.cpp
gui/Debugger/DebugEvents.cpp
gui/ExecutorThread.cpp
@ -381,6 +382,7 @@ set(pcsx2GuiHeaders
gui/Debugger/CtrlDisassemblyView.h
gui/Debugger/CtrlRegisterList.h
gui/Debugger/CtrlMemView.h
gui/Debugger/DebuggerLists.h
gui/Debugger/DisassemblyDialog.h
gui/Debugger/DebugEvents.h
gui/i18n.h

View File

@ -404,6 +404,31 @@ struct Pcsx2Config
}
};
struct DebugOptions
{
BITFIELD32()
bool
EnableDebugger :1,
ShowDebuggerOnStart :1;
BITFIELD_END
u8 FontWidth;
u8 FontHeight;
DebugOptions();
void LoadSave( IniInterface& conf );
bool operator ==( const DebugOptions& right ) const
{
return OpEqu( bitset ) && OpEqu( FontWidth ) && OpEqu( FontHeight );
}
bool operator !=( const DebugOptions& right ) const
{
return !this->operator ==( right );
}
};
BITFIELD32()
bool
CdvdVerboseReads :1, // enables cdvd read activity verbosely dumped to the console
@ -429,6 +454,7 @@ struct Pcsx2Config
SpeedhackOptions Speedhacks;
GamefixOptions Gamefixes;
ProfilerOptions Profiler;
DebugOptions Debugger;
TraceLogFilters Trace;

View File

@ -11,6 +11,9 @@ u32 CBreakPoints::breakSkipFirstAt_ = 0;
u64 CBreakPoints::breakSkipFirstTicks_ = 0;
std::vector<MemCheck> CBreakPoints::memChecks_;
std::vector<MemCheck *> CBreakPoints::cleanupMemChecks_;
bool CBreakPoints::breakpointTriggered_ = false;
int addressMask = 0x1FFFFFFF;
MemCheck::MemCheck()
{
@ -82,9 +85,11 @@ void MemCheck::JitCleanup()
size_t CBreakPoints::FindBreakpoint(u32 addr, bool matchTemp, bool temp)
{
addr &= addressMask;
for (size_t i = 0; i < breakPoints_.size(); ++i)
{
if (breakPoints_[i].addr == addr && (!matchTemp || breakPoints_[i].temporary == temp))
if ((breakPoints_[i].addr & addressMask) == addr && (!matchTemp || breakPoints_[i].temporary == temp))
return i;
}
@ -93,9 +98,11 @@ size_t CBreakPoints::FindBreakpoint(u32 addr, bool matchTemp, bool temp)
size_t CBreakPoints::FindMemCheck(u32 start, u32 end)
{
start &= addressMask;
end &= addressMask;
for (size_t i = 0; i < memChecks_.size(); ++i)
{
if (memChecks_[i].start == start && memChecks_[i].end == end)
if ((memChecks_[i].start & addressMask) == start && (memChecks_[i].end & addressMask) == end)
return i;
}
@ -291,6 +298,8 @@ static inline u32 NotCached(u32 val)
MemCheck *CBreakPoints::GetMemCheck(u32 address, int size)
{
address &= addressMask;
std::vector<MemCheck>::iterator iter;
for (iter = memChecks_.begin(); iter != memChecks_.end(); ++iter)
{
@ -338,14 +347,16 @@ void CBreakPoints::ExecMemCheckJitCleanup()
void CBreakPoints::SetSkipFirst(u32 pc)
{
breakSkipFirstAt_ = pc;
// breakSkipFirstTicks_ = CoreTiming::GetTicks();
breakSkipFirstAt_ = pc & addressMask;
breakSkipFirstTicks_ = r5900Debug.getCycles();
}
u32 CBreakPoints::CheckSkipFirst(u32 cmpPc)
{
cmpPc &= addressMask;
u32 pc = breakSkipFirstAt_;
if (pc == cmpPc)
return 1;
if (breakSkipFirstTicks_ == r5900Debug.getCycles())
return pc;
return 0;
}
@ -375,6 +386,10 @@ const std::vector<BreakPoint> CBreakPoints::GetBreakpoints()
return breakPoints_;
}
// including them earlier causes some ambiguities
#include "App.h"
#include "Debugger/DisassemblyDialog.h"
void CBreakPoints::Update(u32 addr)
{
bool resume = false;
@ -384,14 +399,13 @@ void CBreakPoints::Update(u32 addr)
resume = true;
}
if (addr != 0)
Cpu->Clear(addr-4,8);
else
// if (addr != 0)
// Cpu->Clear(addr-4,8);
// else
SysClearExecutionCache();
if (resume)
r5900Debug.resumeCpu();
// Redraw in order to show the breakpoint.
// host->UpdateDisassembly();
wxGetApp().GetDisassemblyPtr()->update();
}

View File

@ -150,6 +150,9 @@ public:
static void Update(u32 addr = 0);
static void SetBreakpointTriggered(bool b) { breakpointTriggered_ = b; };
static bool GetBreakpointTriggered() { return breakpointTriggered_; };
private:
static size_t FindBreakpoint(u32 addr, bool matchTemp = false, bool temp = false);
// Finds exactly, not using a range check.
@ -158,6 +161,7 @@ private:
static std::vector<BreakPoint> breakPoints_;
static u32 breakSkipFirstAt_;
static u64 breakSkipFirstTicks_;
static bool breakpointTriggered_;
static std::vector<MemCheck> memChecks_;
static std::vector<MemCheck *> cleanupMemChecks_;

View File

@ -418,6 +418,44 @@ void R5900DebugInterface::setPc(u32 newPc)
cpuRegs.pc = newPc;
}
void R5900DebugInterface::setRegister(int cat, int num, u128 newValue)
{
switch (cat)
{
case EECAT_GPR:
switch (num)
{
case 32: // pc
cpuRegs.pc = newValue._u32[0];
break;
case 33: // hi
cpuRegs.HI.UQ = newValue;
break;
case 34: // lo
cpuRegs.LO.UQ = newValue;
break;
default:
cpuRegs.GPR.r[num].UQ = newValue;
break;
}
break;
case EECAT_CP0:
cpuRegs.CP0.r[num] = newValue._u32[0];
break;
case EECAT_CP1:
fpuRegs.fpr[num].UL = newValue._u32[0];
break;
case EECAT_CP2F:
VU1.VF[num].UQ = newValue;
break;
case EECAT_CP2I:
VU1.VI[num].UL = newValue._u32[0];
break;
default:
break;
}
}
std::string R5900DebugInterface::disasm(u32 address)
{
std::string out;
@ -429,7 +467,7 @@ std::string R5900DebugInterface::disasm(u32 address)
bool R5900DebugInterface::isValidAddress(u32 addr)
{
if (addr < 0x100000)
if (addr < 0x80000)
return false;
if (addr >= 0x10000000 && addr < 0x10010000)
return true;
@ -442,6 +480,11 @@ bool R5900DebugInterface::isValidAddress(u32 addr)
}
u32 R5900DebugInterface::getCycles()
{
return cpuRegs.cycle;
}
//
// R3000DebugInterface
//
@ -617,6 +660,32 @@ void R3000DebugInterface::setPc(u32 newPc)
psxRegs.pc = newPc;
}
void R3000DebugInterface::setRegister(int cat, int num, u128 newValue)
{
switch (cat)
{
case IOPCAT_GPR:
switch (num)
{
case 32: // pc
psxRegs.pc = newValue._u32[0];
break;
case 33: // hi
psxRegs.GPR.n.hi = newValue._u32[0];
break;
case 34: // lo
psxRegs.GPR.n.lo = newValue._u32[0];
break;
default:
psxRegs.GPR.r[num] = newValue._u32[0];
break;
}
break;
default:
break;
}
}
std::string R3000DebugInterface::disasm(u32 address)
{
std::string out;
@ -637,3 +706,8 @@ bool R3000DebugInterface::isValidAddress(u32 addr)
return !(addr & 0x40000000) && vtlb_GetPhyPtr(addr & 0x1FFFFFFF) != NULL;
}
u32 R3000DebugInterface::getCycles()
{
return psxRegs.cycle;
}

View File

@ -27,9 +27,11 @@ public:
virtual u128 getLO() = 0;
virtual u32 getPC() = 0;
virtual void setPc(u32 newPc) = 0;
virtual void setRegister(int cat, int num, u128 newValue) = 0;
virtual std::string disasm(u32 address) = 0;
virtual bool isValidAddress(u32 address) = 0;
virtual u32 getCycles() = 0;
bool initExpression(const char* exp, PostfixExpression& dest);
bool parseExpression(PostfixExpression& exp, u64& dest);
@ -62,9 +64,11 @@ public:
virtual u128 getLO();
virtual u32 getPC();
virtual void setPc(u32 newPc);
virtual void setRegister(int cat, int num, u128 newValue);
virtual std::string disasm(u32 address);
virtual bool isValidAddress(u32 address);
virtual u32 getCycles();
};
@ -91,9 +95,11 @@ public:
virtual u128 getLO();
virtual u32 getPC();
virtual void setPc(u32 newPc);
virtual void setRegister(int cat, int num, u128 newValue);
virtual std::string disasm(u32 address);
virtual bool isValidAddress(u32 address);
virtual u32 getCycles();
};
extern R5900DebugInterface r5900Debug;

View File

@ -4,6 +4,7 @@
#include "DebugInterface.h"
#include "SymbolMap.h"
#include "DebugInterface.h"
#include "../R5900.h"
static std::vector<MIPSAnalyst::AnalyzedFunction> functions;
@ -114,6 +115,11 @@ namespace MIPSAnalyst
}
*/
if (end) {
// most functions are aligned to 8 or 16 bytes
// add the padding to this one
while (r5900Debug.read32(addr+8) == 0)
addr += 4;
currentFunction.end = addr + 4;
currentFunction.isStraightLeaf = isStraightLeaf;
functions.push_back(currentFunction);
@ -122,6 +128,7 @@ namespace MIPSAnalyst
looking = false;
end = false;
isStraightLeaf = true;
currentFunction.start = addr+4;
}
}
@ -355,6 +362,10 @@ namespace MIPSAnalyst
switch (MIPS_GET_OP(op)) {
case 0: // special
switch (MIPS_GET_FUNC(op)) {
case 0x0C: // syscall
info.isSyscall = true;
info.branchTarget = 0x80000000+0x180;
break;
case 0x20: // add
case 0x21: // addu
info.hasRelevantAddress = true;
@ -372,6 +383,27 @@ namespace MIPSAnalyst
info.hasRelevantAddress = true;
info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]+((s16)(op & 0xFFFF));
break;
case 0x10: // cop0
switch (MIPS_GET_RS(op))
{
case 0x10: // tlb
switch (MIPS_GET_FUNC(op))
{
case 0x18: // eret
info.isBranch = true;
info.isConditional = false;
// probably shouldn't be hard coded like this...
if (cpuRegs.CP0.n.Status.b.ERL) {
info.branchTarget = cpuRegs.CP0.n.ErrorEPC;
} else {
info.branchTarget = cpuRegs.CP0.n.EPC;
}
break;
}
break;
}
break;
}
// TODO: rest

View File

@ -37,6 +37,7 @@ namespace MIPSAnalyst
// branches
u32 branchTarget;
bool isSyscall;
bool isBranch;
bool isLinkedBranch;
bool isLikelyBranch;

View File

@ -80,7 +80,7 @@ public:
static const u32 INVALID_ADDRESS = (u32)-1;
void UpdateActiveSymbols();
bool IsEmpty() const { return activeFunctions.empty() && activeLabels.empty() && activeData.empty(); };
private:
void AssignFunctionIndices();
const char *GetLabelName(u32 address) const;

View File

@ -63,6 +63,7 @@ namespace PathDefs
extern wxDirName GetLangs();
extern wxDirName GetCheats();
extern wxDirName GetCheatsWS();
extern wxDirName GetDocs();
extern wxDirName Get( FoldersEnum_t folderidx );
@ -81,6 +82,7 @@ namespace PathDefs
extern const wxDirName& Langs();
extern const wxDirName& Cheats();
extern const wxDirName& CheatsWS();
extern const wxDirName& Docs();
}
}

View File

@ -371,6 +371,28 @@ void Pcsx2Config::GamefixOptions::LoadSave( IniInterface& ini )
IniBitBool( FMVinSoftwareHack );
}
Pcsx2Config::DebugOptions::DebugOptions()
{
EnableDebugger = false;
ShowDebuggerOnStart = false;
FontWidth = 8;
FontHeight = 12;
}
void Pcsx2Config::DebugOptions::LoadSave( IniInterface& ini )
{
ScopedIniGroup path( ini, L"Debugger" );
IniBitBool( EnableDebugger );
IniBitBool( ShowDebuggerOnStart );
IniBitfield(FontWidth);
IniBitfield(FontHeight);
}
Pcsx2Config::Pcsx2Config()
{
bitset = 0;
@ -405,6 +427,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
Gamefixes .LoadSave( ini );
Profiler .LoadSave( ini );
Debugger .LoadSave( ini );
Trace .LoadSave( ini );
ini.Flush();

View File

@ -230,11 +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.Debugger.EnableDebugger)
{
MIPSAnalyst::ScanForFunctions(ElfTextRange.first,ElfTextRange.first+ElfTextRange.second,true);
symbolMap.UpdateActiveSymbols();
sApp.PostAppMethod(&Pcsx2App::resetDebugger);
}
if (EmuConfig.EnablePatches) ApplyPatch(0);
if (EmuConfig.EnableCheats) ApplyCheat(0);

View File

@ -95,6 +95,12 @@ namespace PathDefs
static const wxDirName retval( L"themes" );
return retval;
}
const wxDirName& Docs()
{
static const wxDirName retval( L"docs" );
return retval;
}
};
// Specifies the root folder for the application install.
@ -190,6 +196,11 @@ namespace PathDefs
{
return GetDocuments() + Base::CheatsWS();
}
wxDirName GetDocs()
{
return AppRoot() + Base::Docs();
}
wxDirName GetSavestates()
{

View File

@ -87,9 +87,22 @@ void Pcsx2App::OpenMainFrame()
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, pxGetAppName() );
m_id_MainFrame = mainFrame->GetId();
#ifndef PCSX2_DEVBUILD
if (g_Conf->EmuOptions.Debugger.EnableDebugger)
#endif
{
DisassemblyDialog* disassembly = new DisassemblyDialog( mainFrame );
m_id_Disassembler = disassembly->GetId();
DisassemblyDialog* disassembly = new DisassemblyDialog( mainFrame );
m_id_Disassembler = disassembly->GetId();
if (g_Conf->EmuOptions.Debugger.ShowDebuggerOnStart)
disassembly->Show();
}
#ifndef PCSX2_DEVBUILD
else {
m_id_Disassembler = 0;
}
#endif
PostIdleAppMethod( &Pcsx2App::OpenProgramLog );

View File

@ -1043,6 +1043,7 @@ protected:
DbgCon.WriteLn( Color_Gray, "(SysExecute) received." );
CoreThread.ResetQuick();
symbolMap.Clear();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
if( m_UseCDVDsrc )

View File

@ -229,10 +229,10 @@ bool BreakpointWindow::fetchDialogData()
onChange = checkOnChange->GetValue();
// parse address
wxCharBuffer addressText = editAddress->GetLabel().ToUTF8();
wxCharBuffer addressText = editAddress->GetValue().ToUTF8();
if (cpu->initExpression(addressText,exp) == false)
{
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetLabel().wchar_str().data());
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetValue().wchar_str().data());
wxMessageBox(errorMessage,L"Error",wxICON_ERROR);
return false;
}
@ -240,7 +240,7 @@ bool BreakpointWindow::fetchDialogData()
u64 value;
if (cpu->parseExpression(exp,value) == false)
{
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetLabel().wchar_str().data());
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetValue().wchar_str().data());
wxMessageBox(errorMessage,L"Error",wxICON_ERROR);
return false;
}
@ -249,17 +249,17 @@ bool BreakpointWindow::fetchDialogData()
if (memory)
{
// parse size
wxCharBuffer sizeText = editSize->GetLabel().ToUTF8();
wxCharBuffer sizeText = editSize->GetValue().ToUTF8();
if (cpu->initExpression(sizeText,exp) == false)
{
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetLabel().wchar_str().data());
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetValue().wchar_str().data());
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().data());
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetValue().wchar_str().data());
wxMessageBox(errorMessage,L"Error",wxICON_ERROR);
return false;
}
@ -267,7 +267,7 @@ bool BreakpointWindow::fetchDialogData()
}
// condition
wxCharBuffer conditionText = editCondition->GetLabel().ToUTF8();
wxCharBuffer conditionText = editCondition->GetValue().ToUTF8();
strncpy(condition,conditionText,sizeof(condition));
condition[sizeof(condition)-1] = 0;
@ -276,7 +276,7 @@ bool BreakpointWindow::fetchDialogData()
{
if (cpu->initExpression(condition,compiledCondition) == false)
{
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editCondition->GetLabel().wchar_str().data());
swprintf(errorMessage,512,L"Invalid expression \"%s\".",editCondition->GetValue().wchar_str().data());
wxMessageBox(errorMessage,L"Error",wxICON_ERROR);
return false;
}

View File

@ -5,6 +5,7 @@
#include "DebugEvents.h"
#include "BreakpointWindow.h"
#include "AppConfig.h"
#include <wx/mstream.h>
#include <wx/clipbrd.h>
@ -65,8 +66,8 @@ CtrlDisassemblyView::CtrlDisassemblyView(wxWindow* parent, DebugInterface* _cpu)
{
manager.setCpu(cpu);
windowStart = 0x100000;
rowHeight = 14;
charWidth = 8;
rowHeight = g_Conf->EmuOptions.Debugger.FontHeight+2;
charWidth = g_Conf->EmuOptions.Debugger.FontWidth;
displaySymbols = true;
visibleRows = 1;
@ -105,8 +106,12 @@ WXLRESULT CtrlDisassemblyView::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPA
switch (nMsg)
{
case 0x0104: // WM_SYSKEYDOWN, make f10 usable
postEvent(debEVT_STEPOVER,0);
return 0;
if (wParam == 0x79) // f10
{
postEvent(debEVT_STEPOVER,0);
return 0;
}
break;
}
return wxWindow::MSWWindowProc(nMsg,wParam,lParam);
@ -684,7 +689,10 @@ void CtrlDisassemblyView::keydownEvent(wxKeyEvent& evt)
break;
case WXK_F10:
postEvent(debEVT_STEPOVER,0);
break;
return;
case WXK_F11:
postEvent(debEVT_STEPINTO,0);
return;
default:
evt.Skip();
break;
@ -1051,3 +1059,11 @@ void CtrlDisassemblyView::editBreakpoint()
postEvent(debEVT_UPDATE,0);
}
}
void CtrlDisassemblyView::getOpcodeText(u32 address, char* dest)
{
DisassemblyLineInfo line;
address = manager.getStartAddress(address);
manager.getLine(address,displaySymbols,line);
sprintf(dest,"%s %s",line.name.c_str(),line.params.c_str());
}

View File

@ -22,6 +22,7 @@ public:
void scanFunctions();
void clearFunctions() { manager.clear(); };
void redraw();
void getOpcodeText(u32 address, char* dest);
u32 getInstructionSizeAt(u32 address)
{

View File

@ -1,6 +1,7 @@
#include "PrecompiledHeader.h"
#include "CtrlMemView.h"
#include "DebugTools/Debug.h"
#include "AppConfig.h"
#include "BreakpointWindow.h"
#include "DebugEvents.h"
@ -35,8 +36,8 @@ enum MemoryViewMenuIdentifiers
CtrlMemView::CtrlMemView(wxWindow* parent, DebugInterface* _cpu)
: wxWindow(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxWANTS_CHARS), cpu(_cpu)
{
rowHeight = 12;
charWidth = 8;
rowHeight = g_Conf->EmuOptions.Debugger.FontHeight;
charWidth = g_Conf->EmuOptions.Debugger.FontWidth;
windowStart = 0x480000;
curAddress = windowStart;
rowSize = 16;

View File

@ -3,6 +3,7 @@
#include "DebugTools/Debug.h"
#include "DebugEvents.h"
#include "AppConfig.h"
BEGIN_EVENT_TABLE(CtrlRegisterList, wxWindow)
EVT_PAINT(CtrlRegisterList::paintEvent)
@ -18,14 +19,17 @@ enum DisassemblyMenuIdentifiers
ID_REGISTERLIST_DISPLAY32 = 1,
ID_REGISTERLIST_DISPLAY64,
ID_REGISTERLIST_DISPLAY128,
ID_REGISTERLIST_CHANGELOWER,
ID_REGISTERLIST_CHANGEUPPER,
ID_REGISTERLIST_CHANGEVALUE
};
CtrlRegisterList::CtrlRegisterList(wxWindow* parent, DebugInterface* _cpu)
: wxWindow(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxWANTS_CHARS|wxBORDER), cpu(_cpu)
{
rowHeight = 14;
charWidth = 8;
rowHeight = g_Conf->EmuOptions.Debugger.FontHeight+2;
charWidth = g_Conf->EmuOptions.Debugger.FontWidth;
category = 0;
maxBits = 128;
@ -48,12 +52,6 @@ CtrlRegisterList::CtrlRegisterList(wxWindow* parent, DebugInterface* _cpu)
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()));
}
@ -297,6 +295,46 @@ void CtrlRegisterList::render(wxDC& dc)
}
}
void CtrlRegisterList::changeValue(RegisterChangeMode mode)
{
wchar_t str[64];
u128 oldValue = cpu->getRegister(category,currentRows[category]);
switch (mode)
{
case LOWER64:
swprintf(str,64,L"0x%016llX",oldValue._u64[0]);
break;
case UPPER64:
swprintf(str,64,L"0x%016llX",oldValue._u64[1]);
break;
case CHANGE32:
swprintf(str,64,L"0x%08X",oldValue._u32[0]);
break;
}
u64 newValue;
if (executeExpressionWindow(this,cpu,newValue,str))
{
switch (mode)
{
case LOWER64:
oldValue._u64[0] = newValue;
break;
case UPPER64:
oldValue._u64[1] = newValue;
break;
case CHANGE32:
oldValue._u32[0] = newValue;
break;
}
cpu->setRegister(category,currentRows[category],oldValue);
}
}
void CtrlRegisterList::onPopupClick(wxCommandEvent& evt)
{
switch (evt.GetId())
@ -319,6 +357,21 @@ void CtrlRegisterList::onPopupClick(wxCommandEvent& evt)
postEvent(debEVT_UPDATELAYOUT,0);
Refresh();
break;
case ID_REGISTERLIST_CHANGELOWER:
changeValue(LOWER64);
Refresh();
break;
case ID_REGISTERLIST_CHANGEUPPER:
changeValue(UPPER64);
Refresh();
break;
case ID_REGISTERLIST_CHANGEVALUE:
if (cpu->getRegisterSize(category) == 32)
changeValue(CHANGE32);
else
changeValue(LOWER64);
Refresh();
break;
default:
wxMessageBox( L"Unimplemented.", L"Unimplemented.", wxICON_INFORMATION);
break;
@ -373,7 +426,37 @@ void CtrlRegisterList::mouseEvent(wxMouseEvent& evt)
if (row != currentRows[category] && row < cpu->getRegisterCount(category))
setCurrentRow(row);
}
wxMenu menu;
int bits = cpu->getRegisterSize(category);
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.AppendSeparator();
if (bits >= 64)
{
menu.Append(ID_REGISTERLIST_CHANGELOWER, L"Change lower 64 bit");
menu.Append(ID_REGISTERLIST_CHANGEUPPER, L"Change upper 64 bit");
} else {
menu.Append(ID_REGISTERLIST_CHANGEVALUE, L"Change value");
}
switch (maxBits)
{
case 128:
menu.Check(ID_REGISTERLIST_DISPLAY128,true);
break;
case 64:
menu.Check(ID_REGISTERLIST_DISPLAY64,true);
break;
case 32:
menu.Check(ID_REGISTERLIST_DISPLAY32,true);
break;
}
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&CtrlRegisterList::onPopupClick, NULL, this);
PopupMenu(&menu,evt.GetPosition());
return;
}

View File

@ -46,9 +46,12 @@ public:
return GetMinClientSize();
}
private:
enum RegisterChangeMode { LOWER64, UPPER64, CHANGE32 };
void render(wxDC& dc);
void refreshChangedRegs();
void setCurrentRow(int row);
void changeValue(RegisterChangeMode mode);
void postEvent(wxEventType type, wxString text);
void postEvent(wxEventType type, int value);
@ -68,5 +71,4 @@ private:
u32 lastPc;
int category;
int maxBits;
wxMenu menu;
};

View File

@ -8,6 +8,7 @@ DEFINE_LOCAL_EVENT_TYPE( debEVT_GOTOINDISASM )
DEFINE_LOCAL_EVENT_TYPE( debEVT_RUNTOPOS )
DEFINE_LOCAL_EVENT_TYPE( debEVT_MAPLOADED )
DEFINE_LOCAL_EVENT_TYPE( debEVT_STEPOVER )
DEFINE_LOCAL_EVENT_TYPE( debEVT_STEPINTO )
DEFINE_LOCAL_EVENT_TYPE( debEVT_UPDATE )
bool parseExpression(const char* exp, DebugInterface* cpu, u64& dest)
@ -22,9 +23,9 @@ void displayExpressionError(wxWindow* parent)
wxMessageBox(wxString(getExpressionError(),wxConvUTF8),L"Invalid expression",wxICON_ERROR);
}
bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest)
bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest, const wxString& defaultValue)
{
wxString result = wxGetTextFromUser(L"Enter expression",L"Expression",wxEmptyString,parent);
wxString result = wxGetTextFromUser(L"Enter expression",L"Expression",defaultValue,parent);
if (result.empty())
return false;

View File

@ -9,6 +9,7 @@ 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_STEPINTO, wxNewEventType() )
DECLARE_LOCAL_EVENT_TYPE( debEVT_UPDATE, wxNewEventType() )
bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest);
bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest, const wxString& defaultValue = wxEmptyString);

View File

@ -0,0 +1,334 @@
#include "PrecompiledHeader.h"
#include "DebuggerLists.h"
#include "BreakpointWindow.h"
#include "DebugEvents.h"
void insertListViewColumns(wxListCtrl* list, GenericListViewColumn* columns, int count)
{
int totalWidth = list->GetSize().x;
for (int i = 0; i < count; i++)
{
wxListItem column;
column.SetText(columns[i].name);
column.SetWidth(totalWidth * columns[i].size);
list->InsertColumn(i,column);
}
}
void resizeListViewColumns(wxListCtrl* list, GenericListViewColumn* columns, int count, int totalWidth)
{
for (int i = 0; i < count; i++)
{
list->SetColumnWidth(i,totalWidth*columns[i].size);
}
}
//
// BreakpointList
//
BEGIN_EVENT_TABLE(BreakpointList, wxWindow)
EVT_SIZE(BreakpointList::sizeEvent)
EVT_KEY_DOWN(BreakpointList::keydownEvent)
END_EVENT_TABLE()
enum { BPL_TYPE, BPL_OFFSET, BPL_SIZELABEL, BPL_OPCODE, BPL_CONDITION, BPL_HITS, BPL_ENABLED, BPL_COLUMNCOUNT };
GenericListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = {
{ L"Type", 0.12f },
{ L"Offset", 0.12f },
{ L"Size/Label", 0.18f },
{ L"Opcode", 0.28f },
{ L"Condition", 0.17f },
{ L"Hits", 0.05f },
{ L"Enabled", 0.08f }
};
BreakpointList::BreakpointList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly)
: wxListView(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxLC_VIRTUAL|wxLC_REPORT|wxLC_SINGLE_SEL|wxNO_BORDER), cpu(_cpu), disasm(_disassembly)
{
insertListViewColumns(this,breakpointColumns,BPL_COLUMNCOUNT);
}
void BreakpointList::sizeEvent(wxSizeEvent& evt)
{
resizeListViewColumns(this,breakpointColumns,BPL_COLUMNCOUNT,evt.GetSize().x);
}
void BreakpointList::update()
{
int newRows = getTotalBreakpointCount();
SetItemCount(newRows);
Refresh();
}
int BreakpointList::getTotalBreakpointCount()
{
int count = (int)CBreakPoints::GetMemChecks().size();
for (size_t i = 0; i < CBreakPoints::GetBreakpoints().size(); i++)
{
if (!displayedBreakPoints_[i].temporary) count++;
}
return count;
}
wxString BreakpointList::OnGetItemText(long item, long col) const
{
wchar_t dest[256];
bool isMemory;
int index = getBreakpointIndex(item,isMemory);
if (index == -1) return L"Invalid";
switch (col)
{
case BPL_TYPE:
{
if (isMemory) {
switch ((int)displayedMemChecks_[index].cond) {
case MEMCHECK_READ:
wcscpy(dest,L"Read");
break;
case MEMCHECK_WRITE:
wcscpy(dest,L"Write");
break;
case MEMCHECK_READWRITE:
wcscpy(dest,L"Read/Write");
break;
case MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE:
wcscpy(dest,L"Write Change");
break;
case MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE:
wcscpy(dest,L"Read/Write Change");
break;
}
} else {
wcscpy(dest,L"Execute");
}
}
break;
case BPL_OFFSET:
{
if (isMemory) {
swprintf(dest,256,L"0x%08X",displayedMemChecks_[index].start);
} else {
swprintf(dest,256,L"0x%08X",displayedBreakPoints_[index].addr);
}
}
break;
case BPL_SIZELABEL:
{
if (isMemory) {
auto mc = displayedMemChecks_[index];
if (mc.end == 0)
swprintf(dest,256,L"0x%08X",1);
else
swprintf(dest,256,L"0x%08X",mc.end-mc.start);
} else {
const std::string sym = symbolMap.GetLabelString(displayedBreakPoints_[index].addr);
if (!sym.empty())
{
swprintf(dest,256,L"%S",sym.c_str());
} else {
wcscpy(dest,L"-");
}
}
}
break;
case BPL_OPCODE:
{
if (isMemory) {
wcscpy(dest,L"-");
} else {
char temp[256];
disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp);
swprintf(dest,256,L"%S",temp);
}
}
break;
case BPL_CONDITION:
{
if (isMemory || displayedBreakPoints_[index].hasCond == false) {
wcscpy(dest,L"-");
} else {
swprintf(dest,256,L"%S",displayedBreakPoints_[index].cond.expressionString);
}
}
break;
case BPL_HITS:
{
if (isMemory) {
swprintf(dest,256,L"%d",displayedMemChecks_[index].numHits);
} else {
swprintf(dest,256,L"-");
}
}
break;
case BPL_ENABLED:
{
if (isMemory) {
swprintf(dest,256,L"%S",displayedMemChecks_[index].cond & MEMCHECK_BREAK ? "true" : "false");
} else {
swprintf(dest,256,L"%S",displayedBreakPoints_[index].enabled ? "true" : "false");
}
}
break;
default:
return L"Invalid";
}
return dest;
}
void BreakpointList::keydownEvent(wxKeyEvent& evt)
{
int sel = GetFirstSelected();
switch (evt.GetKeyCode())
{
case WXK_DELETE:
if (sel+1 == GetItemCount())
Select(sel-1);
removeBreakpoint(sel);
break;
case WXK_UP:
if (sel > 0)
Select(sel-1);
break;
case WXK_DOWN:
if (sel+1 < GetItemCount())
Select(sel+1);
break;
case WXK_RETURN:
editBreakpoint(sel);
break;
case WXK_SPACE:
toggleEnabled(sel);
break;
}
}
int BreakpointList::getBreakpointIndex(int itemIndex, bool& isMemory) const
{
// memory breakpoints first
if (itemIndex < (int)displayedMemChecks_.size())
{
isMemory = true;
return itemIndex;
}
itemIndex -= (int)displayedMemChecks_.size();
size_t i = 0;
while (i < displayedBreakPoints_.size())
{
if (displayedBreakPoints_[i].temporary)
{
i++;
continue;
}
// the index is 0 when there are no more breakpoints to skip
if (itemIndex == 0)
{
isMemory = false;
return (int)i;
}
i++;
itemIndex--;
}
return -1;
}
void BreakpointList::reloadBreakpoints()
{
// Update the items we're displaying from the debugger.
displayedBreakPoints_ = CBreakPoints::GetBreakpoints();
displayedMemChecks_= CBreakPoints::GetMemChecks();
update();
}
void BreakpointList::editBreakpoint(int itemIndex)
{
bool isMemory;
int index = getBreakpointIndex(itemIndex, isMemory);
if (index == -1) return;
BreakpointWindow win(this,cpu);
if (isMemory)
{
auto mem = displayedMemChecks_[index];
win.loadFromMemcheck(mem);
if (win.ShowModal() == wxID_OK)
{
CBreakPoints::RemoveMemCheck(mem.start,mem.end);
win.addBreakpoint();
}
} else {
auto bp = displayedBreakPoints_[index];
win.loadFromBreakpoint(bp);
if (win.ShowModal() == wxID_OK)
{
CBreakPoints::RemoveBreakPoint(bp.addr);
win.addBreakpoint();
}
}
}
void BreakpointList::toggleEnabled(int itemIndex)
{
bool isMemory;
int index = getBreakpointIndex(itemIndex, isMemory);
if (index == -1) return;
if (isMemory) {
MemCheck mcPrev = displayedMemChecks_[index];
CBreakPoints::ChangeMemCheck(mcPrev.start, mcPrev.end, mcPrev.cond, MemCheckResult(mcPrev.result ^ MEMCHECK_BREAK));
} else {
BreakPoint bpPrev = displayedBreakPoints_[index];
CBreakPoints::ChangeBreakPoint(bpPrev.addr, !bpPrev.enabled);
}
}
void BreakpointList::gotoBreakpointAddress(int itemIndex)
{
bool isMemory;
int index = getBreakpointIndex(itemIndex,isMemory);
if (index == -1) return;
if (isMemory)
{
u32 address = displayedMemChecks_[index].start;
postEvent(debEVT_GOTOINMEMORYVIEW,address);
} else {
u32 address = displayedBreakPoints_[index].addr;
postEvent(debEVT_GOTOINDISASM,address);
}
}
void BreakpointList::removeBreakpoint(int itemIndex)
{
bool isMemory;
int index = getBreakpointIndex(itemIndex,isMemory);
if (index == -1) return;
if (isMemory)
{
auto mc = displayedMemChecks_[index];
CBreakPoints::RemoveMemCheck(mc.start, mc.end);
} else {
u32 address = displayedBreakPoints_[index].addr;
CBreakPoints::RemoveBreakPoint(address);
}
}
void BreakpointList::postEvent(wxEventType type, int value)
{
wxCommandEvent event( type, GetId() );
event.SetEventObject(this);
event.SetInt(value);
wxPostEvent(this,event);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <wx/listctrl.h>
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "CtrlDisassemblyView.h"
struct GenericListViewColumn
{
wchar_t *name;
float size;
int flags;
};
class BreakpointList: public wxListView
{
public:
BreakpointList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly);
void reloadBreakpoints();
void update();
DECLARE_EVENT_TABLE()
protected:
wxString OnGetItemText(long item, long col) const;
void sizeEvent(wxSizeEvent& evt);
void keydownEvent(wxKeyEvent& evt);
private:
int getBreakpointIndex(int itemIndex, bool& isMemory) const;
int getTotalBreakpointCount();
void editBreakpoint(int itemIndex);
void toggleEnabled(int itemIndex);
void gotoBreakpointAddress(int itemIndex);
void removeBreakpoint(int itemIndex);
void postEvent(wxEventType type, int value);
std::vector<BreakPoint> displayedBreakPoints_;
std::vector<MemCheck> displayedMemChecks_;
DebugInterface* cpu;
CtrlDisassemblyView* disasm;
};

View File

@ -5,6 +5,11 @@
#include "DebugTools/DisassemblyManager.h"
#include "DebugTools/Breakpoints.h"
#include "BreakpointWindow.h"
#include "PathDefs.h"
#ifdef WIN32
#include <Windows.h>
#endif
BEGIN_EVENT_TABLE(DisassemblyDialog, wxFrame)
EVT_COMMAND( wxID_ANY, debEVT_SETSTATUSBARTEXT, DisassemblyDialog::onDebuggerEvent )
@ -13,9 +18,39 @@ BEGIN_EVENT_TABLE(DisassemblyDialog, wxFrame)
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_STEPINTO, DisassemblyDialog::onDebuggerEvent )
EVT_COMMAND( wxID_ANY, debEVT_UPDATE, DisassemblyDialog::onDebuggerEvent )
EVT_CLOSE( DisassemblyDialog::onClose )
END_EVENT_TABLE()
DebuggerHelpDialog::DebuggerHelpDialog(wxWindow* parent)
: wxDialog(parent,wxID_ANY,L"Help")
{
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxTextCtrl* textControl = new wxTextCtrl(this,wxID_ANY,L"",wxDefaultPosition,wxDefaultSize,
wxTE_MULTILINE|wxTE_READONLY);
textControl->SetMinSize(wxSize(400,300));
auto fileName = PathDefs::GetDocs().ToString()+L"/debugger.txt";
wxTextFile file(fileName);
if (file.Open())
{
wxString text = file.GetFirstLine();
while (!file.Eof())
{
text += file.GetNextLine()+L"\r\n";
}
textControl->SetLabel(text);
textControl->SetSelection(0,0);
}
sizer->Add(textControl,1,wxEXPAND);
SetSizerAndFit(sizer);
}
CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
: wxPanel(parent), cpu(_cpu)
{
@ -39,11 +74,34 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
// create bottom section
bottomTabs->AddPage(memory,L"Memory");
breakpointList = new BreakpointList(bottomTabs,cpu,disassembly);
bottomTabs->AddPage(breakpointList,L"Breakpoints");
mainSizer->Add(bottomTabs,1,wxEXPAND);
mainSizer->Layout();
}
void CpuTabPage::setBottomTabPage(wxWindow* win)
{
for (size_t i = 0; i < bottomTabs->GetPageCount(); i++)
{
if (bottomTabs->GetPage(i) == win)
{
bottomTabs->SetSelection(i);
break;
}
}
}
void CpuTabPage::update()
{
breakpointList->reloadBreakpoints();
Refresh();
}
DisassemblyDialog::DisassemblyDialog(wxWindow* parent):
wxFrame( parent, wxID_ANY, L"Debugger", wxDefaultPosition,wxDefaultSize,wxRESIZE_BORDER|wxCLOSE_BOX|wxCAPTION|wxSYSTEM_MENU ),
currentCpu(NULL)
@ -63,6 +121,7 @@ DisassemblyDialog::DisassemblyDialog(wxWindow* parent):
stepIntoButton = new wxButton( panel, wxID_ANY, L"Step Into" );
stepIntoButton->Enable(false);
Connect( stepIntoButton->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DisassemblyDialog::onStepIntoClicked ) );
topRowSizer->Add(stepIntoButton,0,wxBOTTOM,2);
stepOverButton = new wxButton( panel, wxID_ANY, L"Step Over" );
@ -99,12 +158,44 @@ DisassemblyDialog::DisassemblyDialog(wxWindow* parent):
setDebugMode(true);
}
#ifdef WIN32
WXLRESULT DisassemblyDialog::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
switch (nMsg)
{
case WM_SHOWWINDOW:
{
WXHWND hwnd = GetHWND();
u32 style = GetWindowLong((HWND)hwnd,GWL_STYLE);
style &= ~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX);
SetWindowLong((HWND)hwnd,GWL_STYLE,style);
u32 exStyle = GetWindowLong((HWND)hwnd,GWL_EXSTYLE);
exStyle |= (WS_EX_CONTEXTHELP);
SetWindowLong((HWND)hwnd,GWL_EXSTYLE,exStyle);
}
break;
case WM_SYSCOMMAND:
if (wParam == SC_CONTEXTHELP)
{
DebuggerHelpDialog help(this);
help.ShowModal();
return 0;
}
break;
}
return wxFrame::MSWWindowProc(nMsg,wParam,lParam);
}
#endif
void DisassemblyDialog::onBreakRunClicked(wxCommandEvent& evt)
{
if (r5900Debug.isCpuPaused())
{
if (CBreakPoints::IsAddressBreakPoint(r5900Debug.getPC()))
CBreakPoints::SetSkipFirst(r5900Debug.getPC());
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(r5900Debug.getPC());
r5900Debug.resumeCpu();
} else
r5900Debug.pauseCpu();
@ -115,6 +206,11 @@ void DisassemblyDialog::onStepOverClicked(wxCommandEvent& evt)
stepOver();
}
void DisassemblyDialog::onStepIntoClicked(wxCommandEvent& evt)
{
stepInto();
}
void DisassemblyDialog::onPageChanging(wxCommandEvent& evt)
{
wxNotebook* notebook = (wxNotebook*)wxWindow::FindWindowById(evt.GetId());
@ -130,7 +226,7 @@ void DisassemblyDialog::onPageChanging(wxCommandEvent& evt)
if (currentCpu != NULL)
{
currentCpu->getDisassembly()->SetFocus();
currentCpu->Refresh();
currentCpu->update();
}
}
@ -181,6 +277,48 @@ void DisassemblyDialog::stepOver()
r5900Debug.resumeCpu();
}
void DisassemblyDialog::stepInto()
{
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)
{
breakpointAddress = info.branchTarget;
} else {
if (info.conditionMet)
{
breakpointAddress = info.branchTarget;
} else {
breakpointAddress = currentPc+2*4;
disassembly->scrollStepping(breakpointAddress);
}
}
}
if (info.isSyscall)
breakpointAddress = info.branchTarget;
CBreakPoints::AddBreakPoint(breakpointAddress,true);
r5900Debug.resumeCpu();
}
void DisassemblyDialog::onBreakpointClick(wxCommandEvent& evt)
{
if (currentCpu == NULL)
@ -209,7 +347,11 @@ void DisassemblyDialog::onDebuggerEvent(wxCommandEvent& evt)
} else if (type == debEVT_GOTOINMEMORYVIEW)
{
if (currentCpu != NULL)
{
currentCpu->showMemoryView();
currentCpu->getMemoryView()->gotoAddress(evt.GetInt());
currentCpu->getDisassembly()->SetFocus();
}
} else if (type == debEVT_RUNTOPOS)
{
// todo: breakpoints for iop
@ -229,19 +371,28 @@ void DisassemblyDialog::onDebuggerEvent(wxCommandEvent& evt)
{
if (currentCpu != NULL)
stepOver();
} else if (type == debEVT_STEPINTO)
{
if (currentCpu != NULL)
stepInto();
} else if (type == debEVT_UPDATE)
{
update();
}
}
void DisassemblyDialog::onClose(wxCloseEvent& evt)
{
Hide();
}
void DisassemblyDialog::update()
{
if (currentCpu != NULL)
{
stepOverButton->Enable(true);
breakpointButton->Enable(true);
currentCpu->Refresh();
currentCpu->update();
} else {
stepOverButton->Enable(false);
breakpointButton->Enable(false);
@ -269,13 +420,17 @@ void DisassemblyDialog::setDebugMode(bool debugMode)
breakRunButton->SetLabel(L"Run");
stepOverButton->Enable(true);
stepIntoButton->Enable(true);
eeTab->getDisassembly()->gotoPc();
iopTab->getDisassembly()->gotoPc();
// Defocuses main window even when not debugging, causing savestate hotkeys to fail
/*if (currentCpu != NULL)
currentCpu->getDisassembly()->SetFocus();*/
if (CBreakPoints::GetBreakpointTriggered())
{
if (currentCpu != NULL)
currentCpu->getDisassembly()->SetFocus();
CBreakPoints::SetBreakpointTriggered(false);
}
} else {
breakRunButton->SetLabel(L"Break");

View File

@ -7,6 +7,13 @@
#include "CtrlRegisterList.h"
#include "CtrlMemView.h"
#include "DebugEvents.h"
#include "DebuggerLists.h"
class DebuggerHelpDialog: public wxDialog
{
public:
DebuggerHelpDialog(wxWindow* parent);
};
class CpuTabPage: public wxPanel
{
@ -17,12 +24,16 @@ public:
CtrlRegisterList* getRegisterList() { return registerList; };
CtrlMemView* getMemoryView() { return memory; };
wxNotebook* getBottomTabs() { return bottomTabs; };
void update();
void showMemoryView() { setBottomTabPage(memory); };
private:
void setBottomTabPage(wxWindow* win);
DebugInterface* cpu;
CtrlDisassemblyView* disassembly;
CtrlRegisterList* registerList;
CtrlMemView* memory;
wxNotebook* bottomTabs;
BreakpointList* breakpointList;
};
class DisassemblyDialog : public wxFrame
@ -37,15 +48,22 @@ public:
void update();
void reset();
void setDebugMode(bool debugMode);
#ifdef WIN32
WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam);
#endif
DECLARE_EVENT_TABLE()
protected:
void onBreakRunClicked(wxCommandEvent& evt);
void onStepOverClicked(wxCommandEvent& evt);
void onStepIntoClicked(wxCommandEvent& evt);
void onDebuggerEvent(wxCommandEvent& evt);
void onPageChanging(wxCommandEvent& evt);
void onBreakpointClick(wxCommandEvent& evt);
void onClose(wxCloseEvent& evt);
void stepOver();
void stepInto();
private:
CpuTabPage* eeTab;
CpuTabPage* iopTab;

View File

@ -339,9 +339,14 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
m_menubar.Append( &m_menuCDVD, _("CD&VD") );
m_menubar.Append( &m_menuConfig, _("&Config") );
m_menubar.Append( &m_menuMisc, _("&Misc") );
#ifdef PCSX2_DEVBUILD
m_menubar.Append( &m_menuDebug, _("&Debug") );
#ifndef PCSX2_DEVBUILD
if (g_Conf->EmuOptions.Debugger.EnableDebugger)
#endif
{
m_menubar.Append( &m_menuDebug, _("&Debug") );
}
SetMenuBar( &m_menubar );
// ------------------------------------------------------------------------
@ -507,11 +512,15 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
m_menuMisc.AppendSeparator();
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_MemoryDump, _("Memory Dump..."), wxEmptyString);
m_menuDebug.Append(MenuId_Debug_Logging, _("Logging..."), wxEmptyString);
#ifndef PCSX2_DEVBUILD
if (g_Conf->EmuOptions.Debugger.EnableDebugger)
#endif
{
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);
}
m_MenuItem_Console.Check( g_Conf->ProgLogBox.Visible );
ConnectMenus();

View File

@ -178,6 +178,8 @@
<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="..\..\gui\Debugger\DebuggerLists.cpp" />
<None Include="..\..\gui\Debugger\DebuggerLists.h" />
<None Include="..\..\Utilities\folderdesc.txt" />
<None Include="..\..\Docs\License.txt" />
<None Include="..\..\x86\aVUzerorec.S" />

View File

@ -207,6 +207,12 @@
<None Include="..\..\gui\Dialogs\BaseConfigurationDialog.inl">
<Filter>AppHost\Dialogs</Filter>
</None>
<None Include="..\..\gui\Debugger\DebuggerLists.cpp">
<Filter>AppHost\Debugger</Filter>
</None>
<None Include="..\..\gui\Debugger\DebuggerLists.h">
<Filter>AppHost\Debugger</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\Patch.cpp">

View File

@ -424,6 +424,7 @@
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp" />
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp" />
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp" />
<ClCompile Include="..\..\gui\Debugger\DebuggerLists.cpp" />
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp" />
<ClCompile Include="..\..\gui\Dialogs\GameDatabaseDialog.cpp" />
<ClCompile Include="..\..\gui\Dialogs\McdConfigDialog.cpp" />
@ -708,6 +709,7 @@
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h" />
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h" />
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h" />
<ClInclude Include="..\..\gui\Debugger\DebuggerLists.h" />
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h" />
<ClInclude Include="..\..\gui\Panels\MemoryCardPanels.h" />
<ClInclude Include="..\..\IPU\IPUdma.h" />

View File

@ -844,13 +844,15 @@
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp">
<Filter>System\Ps2\Debug</Filter>
</ClCompile>
<ClCompile Include="..\..\gui\Debugger\DebuggerLists.cpp">
<Filter>AppHost\Debugger</Filter>
</ClCompile>
<ClCompile Include="..\..\sif2.cpp">
<Filter>System\Ps2\EmotionEngine\DMAC\Sif</Filter>
</ClCompile>
<ClCompile Include="..\..\Gte.cpp">
<Filter>System\Ps2\Iop\PS1 Components</Filter>
</ClCompile>
</ItemGroup>
</ClCompile> </ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Patch.h">
<Filter>Misc</Filter>
@ -1255,10 +1257,12 @@
<ClInclude Include="..\..\DebugTools\SymbolMap.h">
<Filter>System\Ps2\Debug</Filter>
</ClInclude>
<ClInclude Include="..\..\gui\Debugger\DebuggerLists.h">
<Filter>AppHost\Debugger</Filter>
</ClInclude>
<ClInclude Include="..\..\Gte.h">
<Filter>System\Ps2\Iop\PS1 Components</Filter>
</ClInclude>
</ItemGroup>
</ClInclude> </ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\..\3rdparty\wxWidgets\include\wx\msw\wx.rc">
<Filter>AppHost\Resources</Filter>

View File

@ -424,6 +424,7 @@
<ClCompile Include="..\..\gui\Debugger\CtrlMemView.cpp" />
<ClCompile Include="..\..\gui\Debugger\CtrlRegisterList.cpp" />
<ClCompile Include="..\..\gui\Debugger\DebugEvents.cpp" />
<ClCompile Include="..\..\gui\Debugger\DebuggerLists.cpp" />
<ClCompile Include="..\..\gui\Debugger\DisassemblyDialog.cpp" />
<ClCompile Include="..\..\gui\Dialogs\GameDatabaseDialog.cpp" />
<ClCompile Include="..\..\gui\Dialogs\McdConfigDialog.cpp" />
@ -708,6 +709,7 @@
<ClInclude Include="..\..\gui\Debugger\CtrlMemView.h" />
<ClInclude Include="..\..\gui\Debugger\CtrlRegisterList.h" />
<ClInclude Include="..\..\gui\Debugger\DebugEvents.h" />
<ClInclude Include="..\..\gui\Debugger\DebuggerLists.h" />
<ClInclude Include="..\..\gui\Debugger\DisassemblyDialog.h" />
<ClInclude Include="..\..\gui\Panels\MemoryCardPanels.h" />
<ClInclude Include="..\..\IPU\IPUdma.h" />

View File

@ -850,7 +850,9 @@
<ClCompile Include="..\..\Gte.cpp">
<Filter>System\Ps2\Iop\PS1 Components</Filter>
</ClCompile>
</ItemGroup>
<ClCompile Include="..\..\gui\Debugger\DebuggerLists.cpp">
<Filter>AppHost\Debugger</Filter>
</ClCompile> </ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Patch.h">
<Filter>Misc</Filter>
@ -1258,7 +1260,9 @@
<ClInclude Include="..\..\Gte.h">
<Filter>System\Ps2\Iop\PS1 Components</Filter>
</ClInclude>
</ItemGroup>
<ClInclude Include="..\..\gui\Debugger\DebuggerLists.h">
<Filter>AppHost\Debugger</Filter>
</ClInclude> </ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\..\3rdparty\wxWidgets\include\wx\msw\wx.rc">
<Filter>AppHost\Resources</Filter>

View File

@ -1271,20 +1271,76 @@ int cop2flags(u32 code)
void dynarecCheckBreakpoint()
{
u32 pc = cpuRegs.pc;
if (CBreakPoints::CheckSkipFirst(pc))
{
CBreakPoints::SetSkipFirst(0xFFFFFFFF);
if (CBreakPoints::CheckSkipFirst(pc) != 0)
return;
}
auto cond = CBreakPoints::GetBreakPointCondition(pc);
if (cond && !cond->Evaluate())
return;
CBreakPoints::SetBreakpointTriggered(true);
GetCoreThread().PauseSelf();
recExitExecution();
}
void dynarecMemcheck()
{
u32 pc = cpuRegs.pc;
if (CBreakPoints::CheckSkipFirst(pc) != 0)
return;
iFlushCall(FLUSH_INTERPRETER);
CBreakPoints::SetBreakpointTriggered(true);
GetCoreThread().PauseSelf();
recExitExecution();
}
void recMemcheck(u32 bits, bool store)
{
iFlushCall(FLUSH_INTERPRETER);
// compute accessed address
_eeMoveGPRtoR(ECX, _Rs_);
if (_Imm_ != 0)
xADD(ecx, _Imm_);
if (bits == 128)
xAND(ecx, ~0x0F);
xMOV(edx,ecx);
xADD(edx,bits/8);
// ecx = access address
// edx = access address+size
auto checks = CBreakPoints::GetMemChecks();
for (size_t i = 0; i < checks.size(); i++)
{
if ((checks[i].cond & MEMCHECK_BREAK) == 0)
continue;
if ((checks[i].cond & MEMCHECK_WRITE) == 0 && store == true)
continue;
if ((checks[i].cond & MEMCHECK_READ) == 0 && store == false)
continue;
// logic: memAddress < bpEnd && bpStart < memAddress+memSize
xMOV(eax,checks[i].end);
xCMP(ecx,eax); // address < end
xForwardJGE8 next1; // if address >= end then goto next1
xMOV(eax,checks[i].start);
xCMP(eax,edx); // start < address+size
xForwardJGE8 next2; // if start >= address+size then goto next2
// hit the breakpoint
xCALL(&dynarecMemcheck);
next1.SetTarget();
next2.SetTarget();
}
}
void recompileNextInstruction(int delayslot)
{
static u8 s_bFlushReg = 1;
@ -1296,14 +1352,62 @@ void recompileNextInstruction(int delayslot)
iFlushCall(FLUSH_EVERYTHING);
xCALL(&dynarecCheckBreakpoint);
}
s_pCode = (int *)PSM( pc );
pxAssert(s_pCode);
if( IsDebugBuild )
MOV32ItoR(EAX, pc); // acts as a tag for delimiting recompiled instructions when viewing x86 disasm.
cpuRegs.code = *(int *)s_pCode;
if (CBreakPoints::GetMemChecks().size() != 0)
{
switch (cpuRegs.code >> 26)
{
case 0x20: // lb
case 0x24: // lbu
recMemcheck(8,false);
break;
case 0x28: // sb
recMemcheck(8,true);
break;
case 0x21: // lh
case 0x25: // lhu
recMemcheck(16,false);
break;
case 0x22: // lwl
case 0x23: // lw
case 0x26: // lwr
recMemcheck(32,false);
break;
case 0x29: // sh
recMemcheck(16,true);
break;
case 0x2A: // swl
case 0x2B: // sw
case 0x2E: // swr
recMemcheck(32,true);
break;
case 0x37: // ld
case 0x1B: // ldr
case 0x1A: // ldl
recMemcheck(64,false);
break;
case 0x3F: // sd
case 0x3D: // sdr
case 0x2C: // sdl
recMemcheck(64,true);
break;
case 0x1E: // lq
recMemcheck(128,false);
break;
case 0x1F: // sq
recMemcheck(128,true);
break;
}
}
if (!delayslot) {
pc += 4;
g_cpuFlushedPC = false;
@ -1554,6 +1658,44 @@ bool skipMPEG_By_Pattern(u32 sPC) {
return 0;
}
inline bool needsBreakpoint(u32 pc)
{
if (CBreakPoints::IsAddressBreakPoint(pc))
return true;
if (CBreakPoints::GetMemChecks().size() == 0)
return false;
u32 op = memRead32(pc);
switch (op >> 26)
{
case 0x20: // lb
case 0x21: // lh
case 0x22: // lwl
case 0x23: // lw
case 0x24: // lbu
case 0x25: // lhu
case 0x26: // lwr
case 0x28: // sb
case 0x29: // sh
case 0x2A: // swl
case 0x2B: // sw
case 0x2E: // swr
case 0x37: // ld
case 0x1B: // ldr
case 0x3F: // sd
case 0x3D: // sdr
case 0x1A: // ldl
case 0x2C: // sdl
case 0x1E: // lq
case 0x1F: // sq
return true;
default:
return false;
}
}
static void __fastcall recRecompile( const u32 startpc )
{
u32 i = 0;
@ -1634,7 +1776,7 @@ static void __fastcall recRecompile( const u32 startpc )
s_branchTo = -1;
// compile breakpoints as individual blocks
if (CBreakPoints::IsAddressBreakPoint(i))
if (needsBreakpoint(i))
{
s_nEndBlock = i + 4;
goto StartRecomp;
@ -1644,7 +1786,7 @@ static void __fastcall recRecompile( const u32 startpc )
BASEBLOCK* pblock = PC_GETBLOCK(i);
// stop before breakpoints
if (CBreakPoints::IsAddressBreakPoint(i))
if (needsBreakpoint(i))
{
s_nEndBlock = i;
break;