Changes to debugger classes:

- fixed step-out to work properly
- added ability to enable/disable debugging of individual CPUs
Changes to console-based debugger:
- tidied up syntax and split some commands out to make easier to use
- implemented missing commands (loademustate, saveemustate, resetemu, configinput, configallinputs, help)
- added new commands (nextframe, enablecpu, disablecpu, removeallcomments)
- added ability to redirect command output to a file
This commit is contained in:
Nik Henson 2011-06-28 23:20:19 +00:00
parent 0153a02b19
commit 8e2e8adeb3
9 changed files with 1311 additions and 1051 deletions

View file

@ -12,11 +12,11 @@ namespace Debugger
CCPUDebug::CCPUDebug(const char *cpuName, UINT8 cpuMinInstrLen, UINT8 cpuMaxInstrLen, bool cpuBigEndian, UINT8 cpuMemBusWidth, UINT8 cpuMaxMnemLen) :
name(cpuName), minInstrLen(cpuMinInstrLen), maxInstrLen(cpuMaxInstrLen),
bigEndian(cpuBigEndian), memBusWidth(cpuMemBusWidth), maxMnemLen(cpuMaxMnemLen),
addrFmt(HexDollar), portFmt(Decimal), dataFmt(HexDollar), debugger(NULL),
enabled(true), addrFmt(HexDollar), portFmt(Decimal), dataFmt(HexDollar), debugger(NULL),
numExCodes(0), numIntCodes(0), numPorts(0), memSize(0), instrCount(0), pc(0), opcode(0),
m_break(false), m_userBreak(false), m_step(false), m_steppingOver(false), m_steppingOut(false), m_count(0), m_until(false), m_untilAddr(0),
m_mappedIOTable(NULL), m_memWatchTable(NULL), m_bpTable(NULL), m_numRegMons(0), m_regMonArray(NULL), m_analyser(NULL),
m_stateUpdated(false), m_exTrapped(NULL), m_intTrapped(NULL), m_bpReached(NULL),
m_mappedIOTable(NULL), m_memWatchTable(NULL), m_bpTable(NULL), m_numRegMons(0), m_regMonArray(NULL),
m_analyser(NULL), m_stateUpdated(false), m_exRaised(NULL), m_exTrapped(NULL), m_intRaised(NULL), m_intTrapped(NULL), m_bpReached(NULL),
m_memWatchTriggered(NULL), m_ioWatchTriggered(NULL), m_regMonTriggered(NULL)
{
memset(m_exArray, NULL, sizeof(m_exArray));
@ -1171,6 +1171,7 @@ namespace Debugger
if (ex == NULL)
return;
m_exRaised = ex;
ex->count++;
if (!ex->trap)
return;
@ -1188,6 +1189,7 @@ namespace Debugger
if (in == NULL)
return;
m_intRaised = in;
in->count++;
if (!in->trap)
return;

View file

@ -104,16 +104,18 @@ namespace Debugger
void DetachFromDebugger(CDebugger *theDebugger);
protected:
CCodeAnalyser *m_analyser;
bool m_stateUpdated;
CException *m_exRaised;
CException *m_exTrapped;
CInterrupt *m_intRaised;
CInterrupt *m_intTrapped;
CBreakpoint *m_bpReached;
CWatch *m_memWatchTriggered;
CWatch *m_ioWatchTriggered;
CRegMonitor *m_regMonTriggered;
CCodeAnalyser *m_analyser;
CCPUDebug(const char *cpuName, UINT8 cpuMinInstrLen, UINT8 cpuMaxInstrLen, bool cpuBigEndian, UINT8 cpuMemBusWidth, UINT8 cpuMaxMnemLen);
//
@ -163,6 +165,7 @@ namespace Debugger
const UINT8 memBusWidth;
const UINT8 maxMnemLen;
bool enabled;
EFormat addrFmt;
EFormat portFmt;
EFormat dataFmt;
@ -173,6 +176,7 @@ namespace Debugger
UINT16 numIntCodes;
UINT16 numPorts;
UINT32 memSize;
UINT64 instrCount;
UINT32 pc;
UINT32 opcode;
@ -744,6 +748,16 @@ namespace Debugger
inline bool CCPUDebug::CheckExecution(UINT32 newPC, UINT32 newOpcode)
{
// Check if debugging is enabled for this CPU
if (!enabled)
{
// If not, update instruction count, pc and opcode but don't allow any execution control
instrCount++;
pc = newPC;
opcode = newOpcode;
return false;
}
// Check not just updating state
bool stepBreak, countBreak, untilBreak;
if (!m_stateUpdated)
@ -764,7 +778,7 @@ namespace Debugger
}
}
// Now increase instruction count and update pc and opcode
// Now update instruction count, pc and opcode
instrCount++;
pc = newPC;
opcode = newOpcode;
@ -799,16 +813,17 @@ namespace Debugger
stepBreak = !m_steppingOver || pc == m_stepOverAddr;
break;
case StepOut:
// Step-out steps over any jumps and breaks when it reaches a return
// TODO - following doesn't work if an exception occurs before the return
if (m_steppingOver)
// Step-out steps over any jumps and breaks when it reaches a return or an exception/interrupt is raised
if (m_exRaised != NULL || m_intRaised != NULL)
stepBreak = true;
else if (m_steppingOver)
{
if (pc == m_stepOverAddr)
m_steppingOver = false;
stepBreak = false;
}
else if (m_steppingOut)
stepBreak = pc == m_stepOutAddr;
else
stepBreak = m_steppingOut && pc == m_stepOutAddr;
break;
default:
stepBreak = false;
@ -825,7 +840,7 @@ namespace Debugger
}
else
{
// Update pc and opcode
// Just updating state so update pc and opcode, but do not update instruction count
pc = newPC;
opcode = newOpcode;
@ -867,7 +882,9 @@ namespace Debugger
m_steppingOut = false;
m_count = 0;
m_until = false;
m_exRaised = NULL;
m_exTrapped = NULL;
m_intRaised = NULL;
m_intTrapped = NULL;
m_bpReached = NULL;
m_memWatchTriggered = NULL;
@ -894,8 +911,6 @@ namespace Debugger
// If stepping out, then make sure step over any jumps and if encounter a return instruction then break at return address
if (m_step && m_stepMode == StepOut)
{
// TODO - following is flakey, for example if an exception occurs whilst stepping out, then it obviously doesn't work
// - really ought to keep proper call stack, but harder to implement and will slow down debugger even more
if (!m_steppingOver)
m_steppingOver = GetJumpRetAddr(pc, opcode, m_stepOverAddr);
if (!m_steppingOver)

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@
#include "Types.h"
#include <stdarg.h>
#include <stdio.h>
#define NUM_LISTAUTOLABELS (sizeof(s_listAutoLabels) / sizeof(ELabelFlags))
@ -16,6 +17,7 @@ namespace Debugger
class CException;
class CWatch;
class CBreakpoint;
class CRegister;
class CRegMonitor;
/*
@ -29,6 +31,9 @@ namespace Debugger
CCPUDebug *m_cpu;
bool m_nextFrame;
unsigned m_nextFrameCount;
UINT32 m_listDism;
UINT32 m_listMem;
@ -41,11 +46,27 @@ namespace Debugger
bool m_showOpCodes;
unsigned m_memBytesPerRow;
FILE *m_file;
protected:
void Read(char *str, size_t maxLen);
void Print(const char *fmtStr, ...);
void Error(const char *fmtStr, ...);
void PrintVL(const char *fmtStr, va_list vl);
void Flush();
bool CheckToken(const char *token, const char *simple, const char *full);
bool CheckToken(const char *token, const char *simple, const char *full, char *modifier, size_t modSize, const char *defaultMod);
void Truncate(char *dst, size_t maxLen, const char *src);
void UpperFirst(char *dst, const char *src);
void FormatOpCodes(char *str, int addr, int codesLen);
bool GetLabelText(char *str, int maxLen, UINT32 addr);
@ -62,6 +83,10 @@ namespace Debugger
bool ParseDataSize(const char *str, unsigned &dataSize);
bool ParseCPU(const char *str, CCPUDebug *&cpu);
bool ParseRegister(const char *str, CRegister *&reg);
void ListCPUs();
void ListRegisters();
@ -92,10 +117,6 @@ namespace Debugger
UINT32 ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow);
void Attach();
void Detach();
virtual void ApplyConfig();
virtual void Attached();
@ -122,12 +143,18 @@ namespace Debugger
virtual bool ProcessToken(const char *token, const char *cmd);
virtual void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl);
virtual void FlushOut(CCPUDebug *cpu);
public:
CConsoleDebugger();
void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl);
void FlushOut(CCPUDebug *cpu);
void Attach();
void Detach();
virtual void Poll();
};
}

View file

@ -198,7 +198,7 @@ namespace Debugger
frameCount++;
}
void CDebugger::WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...)
void CDebugger::Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...)
{
va_list vl;
va_start(vl, fmtStr);

View file

@ -77,6 +77,9 @@ namespace Debugger
class CDebugger
#endif // DEBUGGER_HASLOGGER
{
friend class CCPUDebug;
friend class CCodeAnalyser;
private:
bool m_exit;
bool m_pause;
@ -88,6 +91,32 @@ namespace Debugger
virtual bool SaveState(CBlockFile *state);
#endif
//
// Protected virtual methods for sub-classes to implement
//
virtual void AnalysisUpdated(CCodeAnalyser *analyser) = 0;
virtual void ExceptionTrapped(CException *ex) = 0;
virtual void InterruptTrapped(CInterrupt *in) = 0;
virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) = 0;
virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) = 0;
virtual void BreakpointReached(CBreakpoint *bp) = 0;
virtual void MonitorTriggered(CRegMonitor *regMon) = 0;
virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) = 0;
virtual void WaitCommand(CCPUDebug *cpu) = 0;
virtual void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0;
virtual void FlushOut(CCPUDebug *cpu) = 0;
public:
vector<CCPUDebug*> cpus;
@ -134,10 +163,10 @@ namespace Debugger
bool CheckPause();
//
// Console output
// Log
//
void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...);
void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...);
#ifdef DEBUGGER_HASLOGGER
//
@ -182,28 +211,6 @@ namespace Debugger
virtual void Reset();
virtual void Poll();
virtual void AnalysisUpdated(CCodeAnalyser *analyser) = 0;
virtual void ExceptionTrapped(CException *ex) = 0;
virtual void InterruptTrapped(CInterrupt *in) = 0;
virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) = 0;
virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) = 0;
virtual void BreakpointReached(CBreakpoint *bp) = 0;
virtual void MonitorTriggered(CRegMonitor *regMon) = 0;
virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) = 0;
virtual void WaitCommand(CCPUDebug *cpu) = 0;
virtual void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0;
virtual void FlushOut(CCPUDebug *cpu) = 0;
};
//

View file

@ -12,7 +12,8 @@
namespace Debugger
{
CSupermodelDebugger::CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger) :
CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger)
CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger),
m_loadEmuState(false), m_saveEmuState(false), m_resetEmu(false)
{
AddCPU(new CPPCDebug());
#ifdef SUPERMODEL_SOUND
@ -31,62 +32,64 @@ namespace Debugger
bool CSupermodelDebugger::ProcessToken(const char *token, const char *cmd)
{
// TODO - load/saving emu state not supported
//if (CheckToken(token, "les", "loademustate")) // loademustate FILENAME
//{
// // Parse arguments
// token = strtok(NULL, " ");
// if (token == NULL)
// {
// puts("Missing filename.");
// return false;
// }
// if (LoadModel3State(token))
// printf("Emulator state successfully loaded from <%s>\n", token);
// else
// printf("Unable to load emulator state from <%s>\n", token);
// return false;
//}
//else if (CheckToken(token, "ses", "savestate")) // saveemustate FILENAME
//{
// // Parse arguments
// token = strtok(NULL, " ");
// if (token == NULL)
// {
// puts("Missing filename.");
// return false;
// }
// if (SaveModel3State(token))
// printf("Emulator state successfully saved to <%s>\n", token);
// else
// printf("Unable to save emulator state to <%s>\n", token);
// return false;
//}
if (CheckToken(token, "lip", "listinputs")) // listinputs
{
ListInputs();
return false;
}
else if (CheckToken(token, "pip", "printinput")) // printinput NAME
if (CheckToken(token, "les", "loademustate")) // loademustate FILENAME
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
puts("Mising input name.");
Print("Missing filename.\n");
return false;
}
strncpy(m_stateFile, token, 254);
m_stateFile[254] = '\0';
m_loadEmuState = true;
return true;
}
else if (CheckToken(token, "ses", "saveemustate")) // saveemustate FILENAME
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Missing filename.\n");
return false;
}
strncpy(m_stateFile, token, 254);
m_stateFile[254] = '\0';
m_saveEmuState = true;
return true;
}
else if (CheckToken(token, "res", "resetemu")) // resetemu
{
m_resetEmu = true;
return true;
}
else if (CheckToken(token, "lip", "listinputs")) // listinputs
{
ListInputs();
return false;
}
else if (CheckToken(token, "pip", "printinput")) // printinput (<id>|<label>)
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input name.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
if (input == NULL)
{
printf("No input with id or label '%s'.\n", token);
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
printf("Input '%s' is not valid for current game.\n", token);
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
@ -94,58 +97,115 @@ namespace Debugger
{
char mapTrunc[41];
Truncate(mapTrunc, 40, input->GetMapping());
printf("Input %s (%s) [%s] = %04X (%d)\n", input->id, input->label, mapTrunc, input->value, input->value);
Print("Input %s (%s) [%s] = %04X (%d)\n", input->id, input->label, mapTrunc, input->value, input->value);
}
else
printf("Input %s (%s) = %04X (%d)\n", input->id, input->label, input->value, input->value);
Print("Input %s (%s) = %04X (%d)\n", input->id, input->label, input->value, input->value);
return false;
}
else if (CheckToken(token, "sip", "setinput")) // setinput NAME MAPPING
else if (CheckToken(token, "sip", "setinput")) // setinput (<id>|<label>) <mapping>
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
puts("Mising input name.");
Print("Mising input id or label.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
if (input == NULL)
{
printf("No input with id or label '%s'.\n", token);
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
printf("Input '%s' is not valid for current game.\n", token);
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
token = strtok(NULL, " ");
if (token == NULL)
{
puts("Missing mapping to set.");
Print("Missing mapping to set.\n");
return false;
}
input->SetMapping(token);
printf("Set input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
Print("Set input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
return false;
}
else if (CheckToken(token, "cip", "configinput")) // configinput NAME
else if (CheckToken(token, "rip", "resetinput")) // resetinput (<id>|<label>)
{
//// Parse arguments
// TODO
//token = strtok(NULL, " ");
//if (token == NULL)
//{
// puts("Missing mode (a)ll, (s)et single, (a)ppend single or (r)eset single");
// return false;
//}
//if (CheckToken("a", "all"))
//{
// //m_inputs->ConfigureInputs();
//}
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input id or label.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
input->ResetToDefaultMapping();
Print("Reset input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
return false;
}
else if (CheckToken(token, "cip", "configinput")) // configinput (<id>|<label>) [(s)et|(a)ppend]
{
// Parse arguments
token = strtok(NULL, " ");
if (token == NULL)
{
Print("Mising input id or label.\n");
return false;
}
CInput *input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
return false;
}
if (!InputIsValid(input))
{
Print("Input '%s' is not valid for current game.\n", token);
return false;
}
token = strtok(NULL, " ");
bool append;
if (token == NULL || CheckToken(token, "s", "set"))
append = false;
else if (CheckToken(token, "a", "append"))
append = true;
else
{
Print("Enter a valid mode (s)et or (a)ppend.\n");
return false;
}
Print("Configure input %s [%s]: %s...", input->label, input->GetMapping(), (append ? "Appending" : "Setting"));
fflush(stdout); // required on terminals that use buffering
// Configure the input
if (input->Configure(append, "KEY_ESCAPE"))
Print(" %s\n", input->GetMapping());
else
Print(" [Cancelled]\n");
return false;
}
else if (CheckToken(token, "caip", "configallinputs")) // configallinputs
{
m_inputs->ConfigureInputs(m_model3->GetGameInfo());
return false;
}
else
@ -159,9 +219,9 @@ namespace Debugger
void CSupermodelDebugger::ListInputs()
{
puts("Inputs:");
Print("Inputs:\n");
if (m_inputs->Count() == 0)
puts(" None");
Print(" None\n");
// Get maximum id, label and mapping widths
size_t idAndLabelWidth = 0;
@ -191,16 +251,16 @@ namespace Debugger
if (groupLabel == NULL || stricmp(groupLabel, input->GetInputGroup()) != 0)
{
groupLabel = input->GetInputGroup();
printf(" %s:\n", groupLabel);
Print(" %s:\n", groupLabel);
}
sprintf(idAndLabel, "%s (%s)", input->id, input->label);
printf(" %-*s", (int)idAndLabelWidth, idAndLabel);
Print(" %-*s", (int)idAndLabelWidth, idAndLabel);
if (!input->IsVirtual())
Truncate(mapping, 20, input->GetMapping());
else
mapping[0] = '\0';
printf(" %-*s %04X (%d)\n", (int)mappingWidth, mapping, input->value, input->value);
Print(" %-*s %04X (%d)\n", (int)mappingWidth, mapping, input->value, input->value);
}
}
@ -269,6 +329,15 @@ namespace Debugger
return true;
}
void CSupermodelDebugger::ResetModel3()
{
// Reset Model3
m_model3->Reset();
// Reset debugger
Reset();
}
void CSupermodelDebugger::DebugLog(const char *fmt, va_list vl)
{
// Use the supplied logger, if any
@ -295,6 +364,31 @@ namespace Debugger
else
CConsoleDebugger::ErrorLog(fmt, vl);
}
void CSupermodelDebugger::Poll()
{
CConsoleDebugger::Poll();
// Load/saving of emulator state and resetting emulator must be done here
if (m_loadEmuState)
{
LoadModel3State(m_stateFile);
m_loadEmuState = false;
ForceBreak(false);
}
else if (m_saveEmuState)
{
SaveModel3State(m_stateFile);
m_saveEmuState = false;
ForceBreak(false);
}
else if (m_resetEmu)
{
ResetModel3();
m_resetEmu = false;
ForceBreak(false);
}
}
}
#endif // SUPERMODEL_DEBUGGER

View file

@ -26,6 +26,11 @@ namespace Debugger
::CInputs *m_inputs;
::CLogger *m_logger;
bool m_loadEmuState;
bool m_saveEmuState;
bool m_resetEmu;
char m_stateFile[255];
bool InputIsValid(CInput *input);
void ListInputs();
@ -35,17 +40,21 @@ namespace Debugger
bool ProcessToken(const char *token, const char *cmd);
public:
CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger);
void Attached();
void Detaching();
public:
CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger);
void Poll();
bool LoadModel3State(const char *fileName);
bool SaveModel3State(const char *fileName);
void ResetModel3();
void DebugLog(const char *fmt, va_list vl);
void InfoLog(const char *fmt, va_list vl);

View file

@ -270,10 +270,10 @@ namespace Debugger
if (io != NULL)
{
io->GetLocation(locStr);
cpu->debugger->WriteOut(cpu, NULL, "%s data [%s] %s I/O %s\n", rwStr, dataStr, tfStr, locStr);
cpu->debugger->Log(cpu, NULL, "%s data [%s] %s I/O %s\n", rwStr, dataStr, tfStr, locStr);
}
else
cpu->debugger->WriteOut(cpu, NULL, "%s data [%s] %s memory address %s\n", rwStr, dataStr, tfStr, addrStr);
cpu->debugger->Log(cpu, NULL, "%s data [%s] %s memory address %s\n", rwStr, dataStr, tfStr, addrStr);
return false;
}
}