mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-03-06 14:27:44 +00:00
New debugger classes
This commit is contained in:
parent
187f0016fb
commit
eddc1df03a
122
Src/Debugger/AddressTable.cpp
Normal file
122
Src/Debugger/AddressTable.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "AddressTable.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CAddressRef::CAddressRef(CCPUDebug *refCPU, UINT32 refAddr, UINT32 refSize) :
|
||||
cpu(refCPU), addr(refAddr), size(refSize), addrEnd(refAddr + refSize - 1)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CAddressTable::CAddressTable() : m_count(0)
|
||||
{
|
||||
memset(m_tables, NULL, sizeof(m_tables));
|
||||
}
|
||||
|
||||
CAddressTable::~CAddressTable()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
CAddressRef **CAddressTable::GetTableCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable)
|
||||
{
|
||||
// Convert address to table index
|
||||
tableIndex = addr >> INDEX_SHIFT;
|
||||
// See if have a table at index
|
||||
if (m_tables[tableIndex] == NULL)
|
||||
{
|
||||
// If not, create one now and increase count
|
||||
m_tables[tableIndex] = new CAddressRef*[TABLE_SIZE];
|
||||
memset(m_tables[tableIndex], NULL, sizeof(CAddressRef*) * TABLE_SIZE);
|
||||
m_count++;
|
||||
}
|
||||
// Calculate index within table
|
||||
indexInTable = addr & TABLE_MASK;
|
||||
return m_tables[tableIndex];
|
||||
}
|
||||
|
||||
void CAddressTable::CheckAndReleaseTable(int tableIndex)
|
||||
{
|
||||
// See if have table at give index
|
||||
CAddressRef **table = m_tables[tableIndex];
|
||||
if (table == NULL)
|
||||
return;
|
||||
// If so, check if it is empty
|
||||
for (int i = 0; i < TABLE_SIZE; i++)
|
||||
{
|
||||
if (table[i] != NULL)
|
||||
return;
|
||||
}
|
||||
// If so, delete it and decrease count
|
||||
delete[] table;
|
||||
m_tables[tableIndex] = NULL;
|
||||
m_count--;
|
||||
}
|
||||
|
||||
bool CAddressTable::IsEmpty()
|
||||
{
|
||||
return m_count == 0;
|
||||
}
|
||||
|
||||
void CAddressTable::Clear()
|
||||
{
|
||||
if (m_count == 0)
|
||||
return;
|
||||
// Delete all tables and reset count
|
||||
for (int i = 0; i < NUM_TABLES; i++)
|
||||
{
|
||||
if (m_tables[i] != NULL)
|
||||
{
|
||||
delete[] m_tables[i];
|
||||
m_tables[i] = NULL;
|
||||
}
|
||||
}
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
void CAddressTable::Add(CAddressRef *value)
|
||||
{
|
||||
int tableIndex;
|
||||
unsigned indexInTable;
|
||||
CAddressRef **table = GetTableCreate(value->addr, tableIndex, indexInTable);
|
||||
for (UINT32 i = 0; i < value->size; i++)
|
||||
{
|
||||
table[indexInTable] = value;
|
||||
if (indexInTable >= TABLE_SIZE - 1)
|
||||
table = GetTableCreate(value->addr + i, tableIndex, indexInTable);
|
||||
else
|
||||
indexInTable++;
|
||||
}
|
||||
}
|
||||
|
||||
bool CAddressTable::Remove(CAddressRef *value)
|
||||
{
|
||||
int tableIndex;
|
||||
unsigned indexInTable;
|
||||
CAddressRef **table = GetTableNoCreate(value->addr, tableIndex, indexInTable);
|
||||
bool removed = false;
|
||||
for (UINT32 i = 0; i < value->size; i++)
|
||||
{
|
||||
if (table != NULL)
|
||||
{
|
||||
removed |= table[indexInTable] != NULL;
|
||||
table[indexInTable] = NULL;
|
||||
}
|
||||
if (indexInTable >= TABLE_SIZE - 1)
|
||||
{
|
||||
CheckAndReleaseTable(tableIndex);
|
||||
table = GetTableNoCreate(value->addr + i, tableIndex, indexInTable);
|
||||
}
|
||||
else
|
||||
indexInTable++;
|
||||
}
|
||||
CheckAndReleaseTable(tableIndex);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
131
Src/Debugger/AddressTable.h
Normal file
131
Src/Debugger/AddressTable.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_ADDRESSTABLE_H
|
||||
#define INCLUDED_ADDRESSTABLE_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#define TABLE_WIDTH 16
|
||||
#define TABLE_SIZE (1 << TABLE_WIDTH)
|
||||
#define TABLE_MASK (TABLE_SIZE - 1)
|
||||
#define INDEX_SHIFT TABLE_WIDTH
|
||||
#define NUM_TABLES (0x100000000ULL / TABLE_SIZE)
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
|
||||
/*
|
||||
* Base class for objects that reference an address, such as a label or a comment.
|
||||
*/
|
||||
class CAddressRef
|
||||
{
|
||||
protected:
|
||||
CAddressRef(CCPUDebug *refCPU, UINT32 refAddr, UINT32 refSize = 1);
|
||||
|
||||
public:
|
||||
CCPUDebug *cpu;
|
||||
const UINT32 addr;
|
||||
const UINT32 size;
|
||||
const UINT32 addrEnd;
|
||||
|
||||
bool CheckAddr(UINT32 loc);
|
||||
|
||||
// TODO - implement operators
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that holds a table of address references. It does so in an a memory-expensive manner but with the result that it
|
||||
* performs better (hopefully!) as lookups are constant-time.
|
||||
*/
|
||||
class CAddressTable
|
||||
{
|
||||
private:
|
||||
int m_count;
|
||||
CAddressRef **m_tables[NUM_TABLES];
|
||||
|
||||
CAddressRef **GetTableNoCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable);
|
||||
|
||||
CAddressRef **GetTableCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable);
|
||||
|
||||
void CheckAndReleaseTable(int tableIndex);
|
||||
|
||||
public:
|
||||
CAddressTable();
|
||||
|
||||
~CAddressTable();
|
||||
|
||||
bool IsEmpty();
|
||||
|
||||
void Clear();
|
||||
|
||||
CAddressRef *Get(UINT32 addr);
|
||||
|
||||
CAddressRef *Get(UINT32 addr, UINT32 size);
|
||||
|
||||
void Add(CAddressRef *value);
|
||||
|
||||
bool Remove(CAddressRef *value);
|
||||
};
|
||||
|
||||
//
|
||||
// Inlined methods
|
||||
//
|
||||
|
||||
inline bool CAddressRef::CheckAddr(UINT32 loc)
|
||||
{
|
||||
return addr <= loc && loc <= addrEnd;
|
||||
}
|
||||
|
||||
inline CAddressRef **CAddressTable::GetTableNoCreate(UINT32 addr, int &tableIndex, unsigned &indexInTable)
|
||||
{
|
||||
tableIndex = addr >> INDEX_SHIFT;
|
||||
if (m_tables[tableIndex] == NULL)
|
||||
return NULL;
|
||||
indexInTable = addr & TABLE_MASK;
|
||||
return m_tables[tableIndex];
|
||||
}
|
||||
|
||||
inline CAddressRef *CAddressTable::Get(UINT32 addr)
|
||||
{
|
||||
int tableIndex;
|
||||
unsigned indexInTable;
|
||||
CAddressRef **table = GetTableNoCreate(addr, tableIndex, indexInTable);
|
||||
if (table == NULL)
|
||||
return NULL;
|
||||
return table[indexInTable];
|
||||
}
|
||||
|
||||
inline CAddressRef *CAddressTable::Get(UINT32 addr, UINT32 size)
|
||||
{
|
||||
int tableIndex;
|
||||
unsigned indexInTable;
|
||||
CAddressRef **table = GetTableNoCreate(addr, tableIndex, indexInTable);
|
||||
for (UINT32 i = 0; i < size; i++)
|
||||
{
|
||||
if (table != NULL)
|
||||
{
|
||||
CAddressRef *ref = table[indexInTable];
|
||||
if (ref != NULL)
|
||||
return ref;
|
||||
addr++;
|
||||
if (indexInTable >= TABLE_SIZE - 1)
|
||||
table = GetTableNoCreate(addr, tableIndex, indexInTable);
|
||||
else
|
||||
indexInTable++;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr++;
|
||||
table = GetTableNoCreate(addr, tableIndex, indexInTable);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INCLUDED_ADDRESSTABLE_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
63
Src/Debugger/Breakpoint.cpp
Normal file
63
Src/Debugger/Breakpoint.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "Breakpoint.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CBreakpoint::CBreakpoint(CCPUDebug *bpCPU, int bpAddr, char bpSymbol, const char *bpType) :
|
||||
CAddressRef(bpCPU, bpAddr), symbol(bpSymbol), type(bpType), active(true)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CBreakpoint::Check(UINT32 pc, UINT32 opcode)
|
||||
{
|
||||
return CheckAddr(pc) && active && CheckBreak(pc, opcode);
|
||||
}
|
||||
|
||||
void CBreakpoint::Reset()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CSimpleBreakpoint::CSimpleBreakpoint(CCPUDebug *bpCPU, int bpAddr) : CBreakpoint(bpCPU, bpAddr, '*', "simple")
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CSimpleBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSimpleBreakpoint::GetInfo(char *str)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CCountBreakpoint::CCountBreakpoint(CCPUDebug *bpCPU, int bpAddr, int count) : CBreakpoint(bpCPU, bpAddr, '+', "count"),
|
||||
m_count(count), m_counter(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CCountBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode)
|
||||
{
|
||||
return ++m_counter == m_count;
|
||||
}
|
||||
|
||||
void CCountBreakpoint::Reset()
|
||||
{
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
bool CCountBreakpoint::GetInfo(char *str)
|
||||
{
|
||||
sprintf(str, "%d / %d", m_counter, m_count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
74
Src/Debugger/Breakpoint.h
Normal file
74
Src/Debugger/Breakpoint.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_BREAKPOINT_H
|
||||
#define INCLUDED_BREAKPOINT_H
|
||||
|
||||
#include "AddressTable.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
|
||||
/*
|
||||
* Base class for a breakpoint.
|
||||
*/
|
||||
class CBreakpoint : public CAddressRef
|
||||
{
|
||||
protected:
|
||||
CBreakpoint(CCPUDebug *bpCPU, int bpAddr, char bpSymbol, const char *bpType);
|
||||
|
||||
public:
|
||||
const char symbol;
|
||||
const char *type;
|
||||
|
||||
bool active;
|
||||
|
||||
bool Check(UINT32 pc, UINT32 opcode);
|
||||
|
||||
virtual bool CheckBreak(UINT32 pc, UINT32 opcode) = 0;
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual bool GetInfo(char *str) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple breakpoint that will always halt execution when hit.
|
||||
*/
|
||||
class CSimpleBreakpoint : public CBreakpoint
|
||||
{
|
||||
public:
|
||||
CSimpleBreakpoint(CCPUDebug *bpCPU, int bpAddr);
|
||||
|
||||
bool CheckBreak(UINT32 pc, UINT32 opcode);
|
||||
|
||||
bool GetInfo(char *str);
|
||||
};
|
||||
|
||||
/*
|
||||
* Count breakpoint that will halt execution after it has been hit a certain number of times.
|
||||
*/
|
||||
class CCountBreakpoint : public CBreakpoint
|
||||
{
|
||||
private:
|
||||
int m_count;
|
||||
int m_counter;
|
||||
|
||||
public:
|
||||
CCountBreakpoint(CCPUDebug *bpCPU, int bpAddr, int count);
|
||||
|
||||
bool CheckBreak(UINT32 pc, UINT32 opcode);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool GetInfo(char *str);
|
||||
};
|
||||
|
||||
//class CConditionBreakpoint : public CBrekapoint
|
||||
//{
|
||||
// // TODO
|
||||
//}
|
||||
}
|
||||
|
||||
#endif // INCLUDED_BREAKPOINT_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
1285
Src/Debugger/CPUDebug.cpp
Normal file
1285
Src/Debugger/CPUDebug.cpp
Normal file
File diff suppressed because it is too large
Load diff
910
Src/Debugger/CPUDebug.h
Normal file
910
Src/Debugger/CPUDebug.h
Normal file
|
@ -0,0 +1,910 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_CPUDEBUG_H
|
||||
#define INCLUDED_CPUDEBUG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include "CodeAnalyser.h"
|
||||
#include "AddressTable.h"
|
||||
#include "Breakpoint.h"
|
||||
#include "Debugger.h"
|
||||
#include "Exception.h"
|
||||
#include "Interrupt.h"
|
||||
#include "IO.h"
|
||||
#include "Register.h"
|
||||
#include "Watch.h"
|
||||
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
#include "BlockFile.h"
|
||||
#endif // DEBUGGER_HASBLOCKFILE
|
||||
|
||||
#define MAX_EXCEPTIONS 255
|
||||
#define MAX_INTERRUPTS 255
|
||||
#define MAX_IOPORTS 255
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CRegion;
|
||||
class CLabel;
|
||||
class CComment;
|
||||
|
||||
enum EStepMode
|
||||
{
|
||||
StepInto = 0,
|
||||
StepOver,
|
||||
StepOut
|
||||
};
|
||||
|
||||
enum EOpFlags
|
||||
{
|
||||
NormalOp = 0,
|
||||
JumpSimple = 1,
|
||||
JumpLoop = 2,
|
||||
JumpSub = 4,
|
||||
JumpEx = 8,
|
||||
ReturnSub = 16,
|
||||
ReturnEx = 32,
|
||||
HaltExec = 64,
|
||||
Relative = 128,
|
||||
Conditional = 256,
|
||||
NotFixed = 512
|
||||
};
|
||||
|
||||
/*
|
||||
* Base class that facilitates the debugging of a particular emulated CPU.
|
||||
* It holds the debugging hooks that the CPU should call to implement debugging.
|
||||
* Also contains CPU-specific information and provides access to CPU-specific objects such as CRegister, CException etc.
|
||||
*/
|
||||
class CCPUDebug
|
||||
{
|
||||
friend class CDebugger;
|
||||
|
||||
private:
|
||||
bool m_break;
|
||||
bool m_userBreak;
|
||||
bool m_step;
|
||||
EStepMode m_stepMode;
|
||||
bool m_stepBreak;
|
||||
bool m_steppingOver;
|
||||
UINT32 m_stepOverAddr;
|
||||
bool m_steppingOut;
|
||||
UINT32 m_stepOutAddr;
|
||||
int m_count;
|
||||
bool m_until;
|
||||
UINT32 m_untilAddr;
|
||||
|
||||
CException *m_exArray[MAX_EXCEPTIONS];
|
||||
CInterrupt *m_intArray[MAX_INTERRUPTS];
|
||||
CPortIO *m_portArray[MAX_IOPORTS];
|
||||
|
||||
CAddressTable *m_mappedIOTable;
|
||||
CAddressTable *m_memWatchTable;
|
||||
CAddressTable *m_bpTable;
|
||||
|
||||
int m_numRegMons;
|
||||
CRegMonitor **m_regMonArray;
|
||||
|
||||
bool ShiftAddress(UINT32 &addr, unsigned &dataSize, UINT64 &data, CAddressRef *ref);
|
||||
|
||||
void CheckRead(UINT32 addr, unsigned dataSize, UINT64 data);
|
||||
|
||||
void CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data);
|
||||
|
||||
void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead);
|
||||
|
||||
void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isRead);
|
||||
|
||||
void AttachToDebugger(CDebugger *theDebugger);
|
||||
|
||||
void DetachFromDebugger(CDebugger *theDebugger);
|
||||
|
||||
protected:
|
||||
bool m_stateUpdated;
|
||||
CException *m_exTrapped;
|
||||
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);
|
||||
|
||||
//
|
||||
// Methods to define CPU features, such as registers, exceptions and interrupts
|
||||
//
|
||||
|
||||
CPCRegister *AddPCRegister(const char *name, const char *group);
|
||||
|
||||
CAddrRegister *AddAddrRegister(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);
|
||||
|
||||
CIntRegister *AddInt8Register(const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc = NULL);
|
||||
|
||||
CIntRegister *AddInt16Register(const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc = NULL);
|
||||
|
||||
CIntRegister *AddInt32Register(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);
|
||||
|
||||
CIntRegister *AddInt64Register(const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc = NULL);
|
||||
|
||||
CStatusRegister *AddStatus8Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt8FPtr getFunc, SetInt8FPtr setFunc = NULL);
|
||||
|
||||
CStatusRegister *AddStatus16Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt16FPtr getFunc, SetInt16FPtr setFunc = NULL);
|
||||
|
||||
CStatusRegister *AddStatus32Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);
|
||||
|
||||
CFPointRegister *AddFPointRegister(const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc = NULL);
|
||||
|
||||
CException *AddException(const char *id, UINT16 code, const char *name);
|
||||
|
||||
CInterrupt *AddInterrupt(const char *id, UINT16 code, const char *name);
|
||||
|
||||
//
|
||||
// Protected virtual methods for sub-class to implement
|
||||
//
|
||||
|
||||
virtual bool UpdatePC(UINT32 pc) = 0;
|
||||
|
||||
virtual bool ForceException(CException *ex) = 0;
|
||||
|
||||
virtual bool ForceInterrupt(CInterrupt *in) = 0;
|
||||
|
||||
public:
|
||||
const char *name;
|
||||
|
||||
const UINT8 minInstrLen;
|
||||
const UINT8 maxInstrLen;
|
||||
const bool bigEndian;
|
||||
const UINT8 memBusWidth;
|
||||
const UINT8 maxMnemLen;
|
||||
|
||||
EFormat addrFmt;
|
||||
EFormat portFmt;
|
||||
EFormat dataFmt;
|
||||
|
||||
CDebugger *debugger;
|
||||
|
||||
UINT16 numExCodes;
|
||||
UINT16 numIntCodes;
|
||||
UINT16 numPorts;
|
||||
UINT32 memSize;
|
||||
UINT64 instrCount;
|
||||
UINT32 pc;
|
||||
UINT32 opcode;
|
||||
|
||||
vector<CRegister*> regs;
|
||||
vector<CException*> exceps;
|
||||
vector<CInterrupt*> inters;
|
||||
vector<CIO*> ios;
|
||||
// TODO - should use map<UINT32,T*> for T=CRegion,CLabel&CComment so that look-ups via address are faster
|
||||
vector<CRegion*> regions;
|
||||
vector<CLabel*> labels;
|
||||
vector<CComment*> comments;
|
||||
vector<CWatch*> memWatches;
|
||||
vector<CWatch*> ioWatches;
|
||||
vector<CBreakpoint*> bps;
|
||||
vector<CRegMonitor*> regMons;
|
||||
|
||||
virtual ~CCPUDebug();
|
||||
|
||||
//
|
||||
// Methods to define memory layout (must be called before attached to debugger)
|
||||
//
|
||||
|
||||
CRegion *AddRegion(UINT32 start, UINT32 end, bool isCode, bool isReadOnly, const char *name);
|
||||
|
||||
CPortIO *AddPortIO(UINT16 portNum, unsigned dataSize, const char *name, const char *group);
|
||||
|
||||
CMappedIO *AddMappedIO(UINT32 addr, unsigned dataSize, const char *name, const char *group);
|
||||
|
||||
//
|
||||
// Parsing and formatting methods
|
||||
//
|
||||
|
||||
bool ParseData(const char *str, unsigned dataSize, UINT64 *data, bool registers = true);
|
||||
|
||||
void FormatData(char *str, unsigned dataSize, UINT64 data);
|
||||
|
||||
bool ParseAddress(const char *str, UINT32 *addr, bool customLabels = true, bool autoLabels = true, bool registers = true);
|
||||
|
||||
void FormatAddress(char *str, UINT32 addr, bool customLabels = false, ELabelFlags autoLabelFlags = LFNone);
|
||||
|
||||
void FormatJumpAddress(char *str, UINT32 jumpAddr, EOpFlags opFlags);
|
||||
|
||||
bool ParsePortNum(const char *str, UINT16 *portNum, bool customLabels = true, bool registers = true);
|
||||
|
||||
void FormatPortNum(char *str, UINT16 portNum, bool customLabels = false);
|
||||
|
||||
//
|
||||
// Checking methods that hook into CPU emulation code
|
||||
//
|
||||
|
||||
/*
|
||||
* Should be called after every 8-bit read.
|
||||
*/
|
||||
void CheckRead8(UINT32 addr, UINT8 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 16-bit read.
|
||||
*/
|
||||
void CheckRead16(UINT32 addr, UINT16 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 32-bit read.
|
||||
*/
|
||||
void CheckRead32(UINT32 addr, UINT32 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 64-bit read.
|
||||
*/
|
||||
void CheckRead64(UINT32 addr, UINT64 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 8-bit write.
|
||||
*/
|
||||
void CheckWrite8(UINT32 addr, UINT8 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 16-bit write.
|
||||
*/
|
||||
void CheckWrite16(UINT32 addr, UINT16 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 32-bit write.
|
||||
*/
|
||||
void CheckWrite32(UINT32 addr, UINT32 data);
|
||||
|
||||
/*
|
||||
* Should be called after every 64-bit write.
|
||||
*/
|
||||
void CheckWrite64(UINT32 addr, UINT64 data);
|
||||
|
||||
/*
|
||||
* Should be called after every read from an I/O port.
|
||||
*/
|
||||
void CheckPortInput(UINT16 portNum, UINT64 data);
|
||||
|
||||
/*
|
||||
* Should be called after every write to an I/O port.
|
||||
*/
|
||||
void CheckPortOutput(UINT16 portNum, UINT64 data);
|
||||
|
||||
/*
|
||||
* Should be called before every instruction.
|
||||
* If returns true, then PC may have been changed by user and/or an exception/interrupt may have forced, so CPU core should
|
||||
* check for this and handle appropriately. Otherwise, it can continue to execute as normal.
|
||||
*/
|
||||
bool CheckExecution(UINT32 newPC, UINT32 newOpcode);
|
||||
|
||||
/*
|
||||
* Should be called whenever a CPU exception is raised (and before the exception handler is executed).
|
||||
*/
|
||||
virtual void CheckException(UINT16 exCode);
|
||||
|
||||
/*
|
||||
* Should be called whenever a CPU interrupt is raised (and before the interrupt handler is executed).
|
||||
*/
|
||||
virtual void CheckInterrupt(UINT16 intCode);
|
||||
|
||||
//
|
||||
// Execution control
|
||||
//
|
||||
|
||||
void ForceBreak(bool user);
|
||||
|
||||
void ClearBreak();
|
||||
|
||||
void SetContinue();
|
||||
|
||||
void SetStepMode(EStepMode stepMode);
|
||||
|
||||
void SetCount(int count);
|
||||
|
||||
void SetUntil(UINT32 untilAddr);
|
||||
|
||||
bool SetPC(UINT32 newPC);
|
||||
|
||||
bool GenerateException(CException *ex);
|
||||
|
||||
bool GenerateInterrupt(CInterrupt *in);
|
||||
|
||||
//
|
||||
// Register access
|
||||
//
|
||||
|
||||
CRegister *GetRegister(const char *name);
|
||||
|
||||
//
|
||||
// Exception access
|
||||
//
|
||||
|
||||
CException *GetException(const char *id);
|
||||
|
||||
CException *GetException(UINT16 code);
|
||||
|
||||
//
|
||||
// Interrupt access
|
||||
//
|
||||
|
||||
CInterrupt *GetInterrupt(const char *id);
|
||||
|
||||
CInterrupt *GetInterrupt(UINT16 code);
|
||||
|
||||
//
|
||||
// I/O access
|
||||
//
|
||||
|
||||
CPortIO *GetPortIO(UINT16 portNum);
|
||||
|
||||
CMappedIO *GetMappedIO(UINT32 addr);
|
||||
|
||||
//
|
||||
// Region access
|
||||
//
|
||||
|
||||
CRegion *GetRegion(UINT32 addr);
|
||||
|
||||
//
|
||||
// Label handling
|
||||
//
|
||||
|
||||
CLabel *AddLabel(UINT32 addr, const char *name);
|
||||
|
||||
CLabel *GetLabel(UINT32 addr);
|
||||
|
||||
CLabel *GetLabel(const char *name);
|
||||
|
||||
bool RemoveLabel(const char *name);
|
||||
|
||||
bool RemoveLabel(UINT32 addr);
|
||||
|
||||
bool RemoveLabel(CLabel *label);
|
||||
|
||||
bool RemoveAllLabels();
|
||||
|
||||
//
|
||||
// Comment handling
|
||||
//
|
||||
|
||||
CComment *AddComment(UINT32 addr, const char *commentText);
|
||||
|
||||
CComment *GetComment(UINT32 addr);
|
||||
|
||||
bool RemoveComment(UINT32 addr);
|
||||
|
||||
bool RemoveComment(CComment *comment);
|
||||
|
||||
bool RemoveAllComments();
|
||||
|
||||
//
|
||||
// Watch handling
|
||||
//
|
||||
|
||||
CSimpleWatch *AddSimpleMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite);
|
||||
|
||||
CCountWatch *AddCountMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned count);
|
||||
|
||||
CMatchWatch *AddMatchMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, vector<UINT64> &dataSeq);
|
||||
|
||||
CPrintWatch *AddPrintMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite);
|
||||
|
||||
CCaptureWatch *AddCaptureMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned maxLen);
|
||||
|
||||
bool RemoveAllMemWatches();
|
||||
|
||||
bool RemoveAllIOWatches();
|
||||
|
||||
bool RemoveMemWatch(UINT32 addr, unsigned size);
|
||||
|
||||
CWatch *GetMemWatch(UINT32 addr, unsigned size);
|
||||
|
||||
void AddWatch(CWatch *watch);
|
||||
|
||||
bool RemoveWatch(CWatch *watch);
|
||||
|
||||
//
|
||||
// Breakpoint handling
|
||||
//
|
||||
|
||||
CSimpleBreakpoint *AddSimpleBreakpoint(UINT32 addr);
|
||||
|
||||
CCountBreakpoint *AddCountBreakpoint(UINT32 addr, unsigned count);
|
||||
|
||||
void AddBreakpoint(CBreakpoint *bp);
|
||||
|
||||
CBreakpoint *GetBreakpoint(UINT32 addr);
|
||||
|
||||
bool RemoveBreakpoint(UINT32 addr);
|
||||
|
||||
bool RemoveBreakpoint(CBreakpoint *bp);
|
||||
|
||||
bool RemoveAllBreakpoints();
|
||||
|
||||
//
|
||||
// Register monitor handling
|
||||
//
|
||||
|
||||
CRegMonitor *AddRegMonitor(const char *regName);
|
||||
|
||||
CRegMonitor *GetRegMonitor(const char *regName);
|
||||
|
||||
void AddRegMonitor(CRegMonitor *regMon);
|
||||
|
||||
bool RemoveRegMonitor(const char *regName);
|
||||
|
||||
bool RemoveRegMonitor(CRegMonitor *regMon);
|
||||
|
||||
bool RemoveAllRegMonitors();
|
||||
|
||||
void UpdateRegMonArray();
|
||||
|
||||
//
|
||||
// Code analyser
|
||||
//
|
||||
|
||||
CCodeAnalyser *GetCodeAnalyser();
|
||||
|
||||
//
|
||||
// Memory searching
|
||||
//
|
||||
|
||||
bool FindInMem(UINT32 start, UINT32 end, const char *str, bool matchCase, UINT32 &findAddr);
|
||||
|
||||
bool FindInMem(UINT32 start, UINT32 end, UINT8 *bytes, unsigned length, UINT32 &findAddr);
|
||||
|
||||
//
|
||||
// Debugger state loading/saving
|
||||
//
|
||||
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
bool LoadState(CBlockFile *state);
|
||||
|
||||
bool SaveState(CBlockFile *state);
|
||||
#endif // DEBUGGER_HASBLOCKFILE
|
||||
|
||||
//
|
||||
// Public virtual methods for sub-class to implement
|
||||
//
|
||||
|
||||
virtual void AttachToCPU() = 0;
|
||||
|
||||
virtual void DetachFromCPU() = 0;
|
||||
|
||||
virtual void DebuggerReset();
|
||||
|
||||
virtual UINT32 GetResetAddr() = 0;
|
||||
|
||||
virtual UINT64 ReadMem(UINT32 addr, unsigned dataSize);
|
||||
|
||||
virtual bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data);
|
||||
|
||||
virtual UINT64 ReadPort(UINT16 portNum);
|
||||
|
||||
virtual bool WritePort(UINT16 portNum, UINT64 data);
|
||||
|
||||
virtual int Disassemble(UINT32 addr, char *mnemonic, char *operands) = 0;
|
||||
|
||||
virtual int GetOpLength(UINT32 addr);
|
||||
|
||||
// Returns head (no more than 32-bits or min instr length) of full opcode for use with following methods
|
||||
virtual UINT32 GetOpcode(UINT32 addr);
|
||||
|
||||
virtual EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode) = 0;
|
||||
|
||||
virtual bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) = 0;
|
||||
|
||||
virtual bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) = 0;
|
||||
|
||||
virtual bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) = 0;
|
||||
|
||||
virtual bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr) = 0;
|
||||
|
||||
virtual bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// Inlined methods
|
||||
//
|
||||
|
||||
inline bool CCPUDebug::ShiftAddress(UINT32 &addr, unsigned &dataSize, UINT64 &data, CAddressRef *ref)
|
||||
{
|
||||
UINT32 refAddr = ref->addr;
|
||||
unsigned refSize = ref->size;
|
||||
unsigned offset;
|
||||
if (refAddr >= addr)
|
||||
{
|
||||
offset = refAddr - addr;
|
||||
// dataSize will always be >= offset
|
||||
dataSize -= offset;
|
||||
if (dataSize <= refSize)
|
||||
return false;
|
||||
addr = refAddr + refSize;
|
||||
dataSize -= refSize;
|
||||
//data <<= (8 * (offset + refSize));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = refAddr + refSize - addr;
|
||||
if (dataSize <= offset)
|
||||
return false;
|
||||
addr += offset;
|
||||
dataSize -= offset;
|
||||
//data <<= (8 * offset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckRead(UINT32 addr, unsigned dataSize, UINT64 data)
|
||||
{
|
||||
// TODO - currently assumes big-endian - should act according to this.bigEndian
|
||||
|
||||
// For reads larger than 1 byte, care is taken with mapped I/O or watches that overlap within the read region
|
||||
while (dataSize > 0)
|
||||
{
|
||||
// Check if address is mapped I/O
|
||||
CMappedIO *mappedIO = NULL;
|
||||
if (m_mappedIOTable != NULL)
|
||||
{
|
||||
mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr, dataSize);
|
||||
if (mappedIO != NULL)
|
||||
{
|
||||
// Check with I/O if its watch is triggered
|
||||
if (mappedIO->CheckInput(addr, dataSize, data))
|
||||
IOWatchTriggered(mappedIO->watch, mappedIO, data, true);
|
||||
|
||||
// See if still have remaining data to check
|
||||
if (!ShiftAddress(addr, dataSize, data, mappedIO))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if address has a watch
|
||||
CWatch *watch = NULL;
|
||||
if (m_memWatchTable != NULL)
|
||||
{
|
||||
watch = (CWatch*)m_memWatchTable->Get(addr, dataSize);
|
||||
if (watch != NULL)
|
||||
{
|
||||
// Check if watch is triggered
|
||||
if (watch->CheckRead(addr, dataSize, data))
|
||||
MemWatchTriggered(watch, addr, dataSize, data, true);
|
||||
|
||||
// See if still have remaining data to check
|
||||
if (!ShiftAddress(addr, dataSize, data, watch))
|
||||
return;
|
||||
}
|
||||
else if (mappedIO == NULL)
|
||||
return;
|
||||
}
|
||||
else if (mappedIO == NULL)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data)
|
||||
{
|
||||
// TODO - currently assumes big-endian - should act according to this.bigEndian
|
||||
|
||||
// For writes larger than 1 byte, care is taken with mapped I/O or watches that overlap within the written region
|
||||
while (dataSize > 0)
|
||||
{
|
||||
// Check if writing to mapped I/O address
|
||||
CMappedIO *mappedIO = NULL;
|
||||
if (m_mappedIOTable != NULL)
|
||||
{
|
||||
mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr, dataSize);
|
||||
if (mappedIO != NULL)
|
||||
{
|
||||
// Check with I/O if its watch is triggered
|
||||
if (mappedIO->CheckOutput(addr, dataSize, data))
|
||||
IOWatchTriggered(mappedIO->watch, mappedIO, data, false);
|
||||
|
||||
// See if still have remaining data to check
|
||||
if (!ShiftAddress(addr, dataSize, data, mappedIO))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if address has a watch
|
||||
CWatch *watch = NULL;
|
||||
if (m_memWatchTable != NULL)
|
||||
{
|
||||
watch = (CWatch*)m_memWatchTable->Get(addr, dataSize);
|
||||
if (watch != NULL)
|
||||
{
|
||||
// Check if watch is triggered
|
||||
if (watch->CheckWrite(addr, dataSize, data))
|
||||
MemWatchTriggered(watch, addr, dataSize, data, false);
|
||||
|
||||
// See if still have remaining data to check
|
||||
if (!ShiftAddress(addr, dataSize, data, watch))
|
||||
return;
|
||||
}
|
||||
else if (mappedIO == NULL)
|
||||
return;
|
||||
}
|
||||
else if (mappedIO == NULL)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CCPUDebug::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool read)
|
||||
{
|
||||
m_memWatchTriggered = watch;
|
||||
debugger->MemWatchTriggered(watch, addr, dataSize, data, read);
|
||||
watch->Reset();
|
||||
m_break = true;
|
||||
}
|
||||
|
||||
inline void CCPUDebug::IOWatchTriggered(CWatch* watch, CIO *io, UINT64 data, bool read)
|
||||
{
|
||||
m_ioWatchTriggered = watch;
|
||||
debugger->IOWatchTriggered(watch, io, data, read);
|
||||
watch->Reset();
|
||||
m_break = true;
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckRead8(UINT32 addr, UINT8 data)
|
||||
{
|
||||
// Check if reading from mapped I/O address
|
||||
unsigned dataSize = 1;
|
||||
if (m_mappedIOTable != NULL)
|
||||
{
|
||||
CMappedIO *mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr);
|
||||
if (mappedIO != NULL)
|
||||
{
|
||||
if (mappedIO->CheckInput(addr, dataSize, data))
|
||||
IOWatchTriggered(mappedIO->watch, mappedIO, data, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check if address has a watch
|
||||
if (m_memWatchTable == NULL)
|
||||
return;
|
||||
CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
|
||||
if (watch != NULL && watch->CheckRead(addr, dataSize, data))
|
||||
MemWatchTriggered(watch, addr, dataSize, data, true);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckRead16(UINT32 addr, UINT16 data)
|
||||
{
|
||||
CheckRead(addr, 2, data);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckRead32(UINT32 addr, UINT32 data)
|
||||
{
|
||||
CheckRead(addr, 4, data);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckRead64(UINT32 addr, UINT64 data)
|
||||
{
|
||||
CheckRead(addr, 8, data);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckWrite8(UINT32 addr, UINT8 data)
|
||||
{
|
||||
// Check if writing to mapped I/O address
|
||||
unsigned dataSize = 1;
|
||||
if (m_mappedIOTable != NULL)
|
||||
{
|
||||
CMappedIO *mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr);
|
||||
if (mappedIO != NULL)
|
||||
{
|
||||
if (mappedIO->CheckOutput(addr, dataSize, data))
|
||||
IOWatchTriggered(mappedIO->watch, mappedIO, data, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check if address has a watch
|
||||
if (m_memWatchTable == NULL)
|
||||
return;
|
||||
CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
|
||||
if (watch != NULL && watch->CheckWrite(addr, dataSize, data))
|
||||
MemWatchTriggered(watch, addr, dataSize, data, false);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckWrite16(UINT32 addr, UINT16 data)
|
||||
{
|
||||
CheckWrite(addr, 2, data);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckWrite32(UINT32 addr, UINT32 data)
|
||||
{
|
||||
CheckWrite(addr, 4, data);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckWrite64(UINT32 addr, UINT64 data)
|
||||
{
|
||||
CheckWrite(addr, 8, data);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckPortInput(UINT16 portNum, UINT64 data)
|
||||
{
|
||||
if (portNum >= numPorts)
|
||||
return;
|
||||
CPortIO *port = m_portArray[portNum];
|
||||
if (port != NULL && port->CheckInput(data))
|
||||
IOWatchTriggered(port->watch, port, data, true);
|
||||
}
|
||||
|
||||
inline void CCPUDebug::CheckPortOutput(UINT16 portNum, UINT64 data)
|
||||
{
|
||||
if (portNum >= numPorts)
|
||||
return;
|
||||
CPortIO *port = m_portArray[portNum];
|
||||
if (port != NULL && port->CheckOutput(data))
|
||||
IOWatchTriggered(port->watch, port, data, false);
|
||||
}
|
||||
|
||||
inline bool CCPUDebug::CheckExecution(UINT32 newPC, UINT32 newOpcode)
|
||||
{
|
||||
// Check not just updating state
|
||||
bool stepBreak, countBreak, untilBreak;
|
||||
if (!m_stateUpdated)
|
||||
{
|
||||
// If so, then first check to see if any registers have changed from previous instruction before updating pc and opcode
|
||||
if (m_numRegMons > 0)
|
||||
{
|
||||
for (int i = 0; i < m_numRegMons; i++)
|
||||
{
|
||||
CRegMonitor *regMon = m_regMonArray[i];
|
||||
if (regMon->HasChanged())
|
||||
{
|
||||
m_regMonTriggered = regMon;
|
||||
debugger->MonitorTriggered(regMon);
|
||||
regMon->Reset();
|
||||
m_break = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now increase instruction count and update pc and opcode
|
||||
instrCount++;
|
||||
pc = newPC;
|
||||
opcode = newOpcode;
|
||||
|
||||
// Next check for breakpoints at new pc and opcode
|
||||
if (m_bpTable != NULL)
|
||||
{
|
||||
CBreakpoint *bp = (CBreakpoint*)m_bpTable->Get(pc);
|
||||
if (bp != NULL && bp->Check(pc, opcode))
|
||||
{
|
||||
m_bpReached = bp;
|
||||
debugger->BreakpointReached(bp);
|
||||
m_break = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if currently implementing a step mode
|
||||
if (m_step)
|
||||
{
|
||||
if (instrCount == 1)
|
||||
stepBreak = true;
|
||||
else
|
||||
{
|
||||
switch (m_stepMode)
|
||||
{
|
||||
case StepInto:
|
||||
// Step-into always breaks
|
||||
stepBreak = true;
|
||||
break;
|
||||
case StepOver:
|
||||
// Step-over breaks if was not stepping over a jump or was stepping over and has returned from jump
|
||||
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)
|
||||
{
|
||||
if (pc == m_stepOverAddr)
|
||||
m_steppingOver = false;
|
||||
stepBreak = false;
|
||||
}
|
||||
else if (m_steppingOut)
|
||||
stepBreak = pc == m_stepOutAddr;
|
||||
break;
|
||||
default:
|
||||
stepBreak = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
stepBreak = false;
|
||||
|
||||
// Lastly, check if counting instructions or if running until a given address
|
||||
countBreak = m_count > 0 && --m_count == 0;
|
||||
untilBreak = m_until && pc == m_untilAddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update pc and opcode
|
||||
pc = newPC;
|
||||
opcode = newOpcode;
|
||||
|
||||
stepBreak = false;
|
||||
countBreak = false;
|
||||
untilBreak = false;
|
||||
}
|
||||
|
||||
// If a break has been forced, state was updated, stepping through code or have run a set number of instructions or to a
|
||||
// given address then break now and wait for command from user
|
||||
if (m_break || m_stateUpdated || stepBreak || countBreak || untilBreak)
|
||||
{
|
||||
// See if execution halt was caused by user manually breaking execution in some manner
|
||||
if (m_stateUpdated || m_userBreak || stepBreak || countBreak || untilBreak)
|
||||
{
|
||||
EHaltReason reason = HaltNone;
|
||||
if (m_stateUpdated) reason = (EHaltReason)(reason | HaltState);
|
||||
if (m_userBreak) reason = (EHaltReason)(reason | HaltUser);
|
||||
if (stepBreak) reason = (EHaltReason)(reason | HaltStep);
|
||||
if (countBreak) reason = (EHaltReason)(reason | HaltCount);
|
||||
if (untilBreak) reason = (EHaltReason)(reason | HaltUntil);
|
||||
debugger->ExecutionHalted(this, reason);
|
||||
}
|
||||
|
||||
// Keep hold of breakpoint, if any, and its address so that can reset it later
|
||||
UINT32 bpAddr = 0;
|
||||
CBreakpoint *bpToReset = NULL;
|
||||
if (m_bpReached != NULL)
|
||||
{
|
||||
bpAddr = m_bpReached->addr;
|
||||
bpToReset = m_bpReached;
|
||||
}
|
||||
|
||||
// Reset all control flags
|
||||
debugger->ClearBreak();
|
||||
m_stateUpdated = false;
|
||||
m_step = false;
|
||||
m_steppingOver = false;
|
||||
m_steppingOut = false;
|
||||
m_count = 0;
|
||||
m_until = false;
|
||||
m_exTrapped = NULL;
|
||||
m_intTrapped = NULL;
|
||||
m_bpReached = NULL;
|
||||
m_memWatchTriggered = NULL;
|
||||
m_ioWatchTriggered = NULL;
|
||||
m_regMonTriggered = NULL;
|
||||
|
||||
// Wait for instruction from user
|
||||
debugger->WaitCommand(this);
|
||||
|
||||
// Reset breakpoint, if any
|
||||
if (bpToReset != NULL && m_bpTable != NULL)
|
||||
{
|
||||
// Check breakpoint to reset was not deleted by user
|
||||
CBreakpoint *bp = (CBreakpoint*)m_bpTable->Get(bpAddr);
|
||||
if (bp == bpToReset)
|
||||
bp->Reset();
|
||||
}
|
||||
|
||||
// If stepping over, get step over address if available
|
||||
if (m_step && m_stepMode == StepOver)
|
||||
m_steppingOver = GetJumpRetAddr(pc, opcode, m_stepOverAddr);
|
||||
}
|
||||
|
||||
// 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)
|
||||
m_steppingOut = GetReturnAddr(pc, opcode, m_stepOutAddr);
|
||||
}
|
||||
|
||||
return m_stateUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INCLUDED_CPUDEBUG_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
671
Src/Debugger/CodeAnalyser.cpp
Normal file
671
Src/Debugger/CodeAnalyser.cpp
Normal file
|
@ -0,0 +1,671 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "CodeAnalyser.h"
|
||||
#include "CPUDebug.h"
|
||||
#include "Label.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CEntryPoint::CEntryPoint(const CEntryPoint &other) : addr(other.addr), autoFlag(other.autoFlag)
|
||||
{
|
||||
if (other.autoLabel != NULL)
|
||||
{
|
||||
strncpy(autoLabelStr, other.autoLabel, 254);
|
||||
autoLabelStr[254] = '\0';
|
||||
autoLabel = autoLabelStr;
|
||||
}
|
||||
else
|
||||
autoLabel = NULL;
|
||||
}
|
||||
|
||||
CEntryPoint::CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel) :
|
||||
addr(eAddr), autoFlag(eAutoFlag)
|
||||
{
|
||||
if (eAutoLabel != NULL)
|
||||
{
|
||||
strncpy(autoLabelStr, eAutoLabel, 254);
|
||||
autoLabelStr[254] = '\0';
|
||||
autoLabel = autoLabelStr;
|
||||
}
|
||||
else
|
||||
autoLabel = NULL;
|
||||
}
|
||||
|
||||
CEntryPoint &CEntryPoint::operator=(const CEntryPoint &other)
|
||||
{
|
||||
addr = other.addr;
|
||||
autoFlag = other.autoFlag;
|
||||
if (other.autoLabel != NULL)
|
||||
{
|
||||
strncpy(autoLabelStr, other.autoLabel, 254);
|
||||
autoLabelStr[254] = '\0';
|
||||
autoLabel = autoLabelStr;
|
||||
}
|
||||
else
|
||||
autoLabel = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CEntryPoint::operator==(const CEntryPoint &other)
|
||||
{
|
||||
return addr == other.addr && autoFlag == other.autoFlag;
|
||||
}
|
||||
|
||||
bool CEntryPoint::operator!=(const CEntryPoint &other)
|
||||
{
|
||||
return addr != other.addr || autoFlag != other.autoFlag;
|
||||
}
|
||||
|
||||
const char *CAutoLabel::s_defaultLabelFmts[] = { "Entry%s", "Ex%s", "Int%s", "Jmp%s", "Loop%s", "Sub%s", NULL };
|
||||
|
||||
const unsigned CAutoLabel::numLabelFlags = sizeof(s_defaultLabelFmts) / sizeof(char*);
|
||||
|
||||
ELabelFlags CAutoLabel::GetLabelFlag(int index)
|
||||
{
|
||||
if (index < 0 || index >= numLabelFlags)
|
||||
return LFNone;
|
||||
return (ELabelFlags)(1 << index);
|
||||
}
|
||||
|
||||
int CAutoLabel::GetFlagIndex(ELabelFlags flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case LFEntryPoint: return 0;
|
||||
case LFExcepHandler: return 1;
|
||||
case LFInterHandler: return 2;
|
||||
case LFJumpTarget: return 3;
|
||||
case LFLoopPoint: return 4;
|
||||
case LFSubroutine: return 5;
|
||||
case LFUnseenCode: return 6;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *CAutoLabel::GetFlagString(ELabelFlags flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case LFEntryPoint: return "Entry Point";
|
||||
case LFExcepHandler: return "Exception Handler";
|
||||
case LFInterHandler: return "Interrupt Handler";
|
||||
case LFJumpTarget: return "Jump Target";
|
||||
case LFLoopPoint: return "Loop Point";
|
||||
case LFSubroutine: return "Subroutine";
|
||||
case LFUnseenCode: return "Unseen Code";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
CAutoLabel::CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr) : CAddressRef(lCPU, lAddr), flags(LFNone), m_acquired(0)
|
||||
{
|
||||
memset(m_subLabels, NULL, sizeof(m_subLabels));
|
||||
}
|
||||
|
||||
CAutoLabel::~CAutoLabel()
|
||||
{
|
||||
// Delete all sub-labels
|
||||
for (int index = 0; index < numLabelFlags; index++)
|
||||
{
|
||||
if (m_subLabels[index] != NULL)
|
||||
{
|
||||
delete[] m_subLabels[index];
|
||||
m_subLabels[index] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAutoLabel::Acquire()
|
||||
{
|
||||
m_acquired++;
|
||||
}
|
||||
|
||||
void CAutoLabel::Release()
|
||||
{
|
||||
if (--m_acquired == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CAutoLabel::AddFlag(ELabelFlags flag, const char *subLabel)
|
||||
{
|
||||
int index = GetFlagIndex(flag);
|
||||
if (index == -1)
|
||||
return;
|
||||
flags = (ELabelFlags)((unsigned)flags | (unsigned)flag);
|
||||
if (subLabel != NULL)
|
||||
{
|
||||
size_t len = strlen(subLabel);
|
||||
char *label = new char[len + 1];
|
||||
strncpy(label, subLabel, len);
|
||||
label[len] = '\0';
|
||||
m_subLabels[index] = label;
|
||||
}
|
||||
else
|
||||
m_subLabels[index] = CreateDefaultSubLabel(flag);
|
||||
}
|
||||
|
||||
bool CAutoLabel::GetLabel(char *labelStr, ELabelFlags subFlags)
|
||||
{
|
||||
char *p = labelStr;
|
||||
*p = '\0';
|
||||
for (int index = 0; index < numLabelFlags; index++)
|
||||
{
|
||||
ELabelFlags flag = GetLabelFlag(index);
|
||||
if (!(subFlags & flag))
|
||||
continue;
|
||||
const char *subLabel = m_subLabels[index];
|
||||
if (subLabel == NULL)
|
||||
continue;
|
||||
if (p > labelStr)
|
||||
{
|
||||
(*p++) = '/';
|
||||
*p = '\0';
|
||||
}
|
||||
strcat(p, subLabel);
|
||||
p += strlen(subLabel);
|
||||
*p = '\0';
|
||||
}
|
||||
return p > labelStr;
|
||||
}
|
||||
|
||||
bool CAutoLabel::ContainsSubLabel(const char *subLabel)
|
||||
{
|
||||
for (int i = 0; i < numLabelFlags; i++)
|
||||
{
|
||||
if (m_subLabels[i] != NULL && stricmp(subLabel, m_subLabels[i]) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *CAutoLabel::CreateDefaultSubLabel(ELabelFlags flag)
|
||||
{
|
||||
int index = GetFlagIndex(flag);
|
||||
if (index == -1)
|
||||
return NULL;
|
||||
const char *labelFmt = s_defaultLabelFmts[index];
|
||||
if (labelFmt == NULL)
|
||||
return NULL;
|
||||
char addrStr[50];
|
||||
cpu->debugger->FormatData(addrStr, Hex, (unsigned)(cpu->memBusWidth / 8), (UINT64)addr);
|
||||
char *label = new char[255];
|
||||
sprintf(label, labelFmt, addrStr);
|
||||
return label;
|
||||
}
|
||||
|
||||
CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser) : analyser(aAnalyser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CCodeAnalysis::CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs) :
|
||||
analyser(aAnalyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs),
|
||||
m_seenIndices(aTotalIndices), m_validIndices(aTotalIndices), m_acquired(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CCodeAnalysis::CCodeAnalysis(CCodeAnalysis *oldAnalysis, vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs) :
|
||||
analyser(oldAnalysis->analyser), m_entryPoints(entryPoints), m_unseenEntryAddrs(unseenEntryAddrs),
|
||||
m_seenIndices(oldAnalysis->m_seenIndices), m_validIndices(oldAnalysis->m_validIndices),
|
||||
m_autoLabelsMap(oldAnalysis->m_autoLabelsMap), m_acquired(0), validIndexSet(oldAnalysis->validIndexSet)
|
||||
{
|
||||
for (map<unsigned,CAutoLabel*>::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++)
|
||||
it->second->Acquire();
|
||||
}
|
||||
|
||||
CCodeAnalysis::~CCodeAnalysis()
|
||||
{
|
||||
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
|
||||
(*it)->Release();
|
||||
autoLabels.clear();
|
||||
m_autoLabelsMap.clear();
|
||||
}
|
||||
|
||||
void CCodeAnalysis::Acquire()
|
||||
{
|
||||
m_acquired++;
|
||||
}
|
||||
|
||||
void CCodeAnalysis::Release()
|
||||
{
|
||||
if (--m_acquired == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CCodeAnalysis::FinishAnalysis()
|
||||
{
|
||||
for (map<UINT32,CAutoLabel*>::iterator it = m_autoLabelsMap.begin(); it != m_autoLabelsMap.end(); it++)
|
||||
autoLabels.push_back(it->second);
|
||||
}
|
||||
|
||||
bool CCodeAnalysis::IsAddrValid(UINT32 addr)
|
||||
{
|
||||
unsigned index;
|
||||
if (!analyser->GetIndexOfAddr(addr, index))
|
||||
return false;
|
||||
return IsIndexValid(index);
|
||||
}
|
||||
|
||||
bool CCodeAnalysis::GetNextValidAddr(UINT32 &addr)
|
||||
{
|
||||
unsigned index;
|
||||
if (!analyser->GetIndexOfAddr(addr, index))
|
||||
return false;
|
||||
if (IsIndexValid(index))
|
||||
return true;
|
||||
set<unsigned>::iterator it = validIndexSet.lower_bound(index);
|
||||
if (it == validIndexSet.end())
|
||||
return false;
|
||||
return analyser->GetAddrOfIndex(*it, addr);
|
||||
}
|
||||
|
||||
bool CCodeAnalysis::IsIndexValid(unsigned index)
|
||||
{
|
||||
return index < m_validIndices.size() && m_validIndices[index];
|
||||
}
|
||||
|
||||
bool CCodeAnalysis::GetNextValidIndex(unsigned &index)
|
||||
{
|
||||
if (IsIndexValid(index))
|
||||
return true;
|
||||
set<unsigned>::iterator it = validIndexSet.lower_bound(index);
|
||||
if (it == validIndexSet.end())
|
||||
return false;
|
||||
index = *it;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCodeAnalysis::HasSeenAddr(UINT32 addr)
|
||||
{
|
||||
unsigned index;
|
||||
if (!analyser->GetIndexOfAddr(addr, index))
|
||||
return false;
|
||||
return HaveSeenIndex(index);
|
||||
}
|
||||
|
||||
bool CCodeAnalysis::HaveSeenIndex(unsigned index)
|
||||
{
|
||||
return index < m_seenIndices.size() && m_seenIndices[index];
|
||||
}
|
||||
|
||||
CAutoLabel *CCodeAnalysis::GetAutoLabel(UINT32 addr)
|
||||
{
|
||||
map<UINT32,CAutoLabel*>::iterator it = m_autoLabelsMap.find(addr);
|
||||
if (it == m_autoLabelsMap.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
CAutoLabel *CCodeAnalysis::GetAutoLabel(const char *subLabel)
|
||||
{
|
||||
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
|
||||
{
|
||||
if ((*it)->ContainsSubLabel(subLabel))
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vector<CAutoLabel*> CCodeAnalysis::GetAutoLabels(ELabelFlags flag)
|
||||
{
|
||||
vector<CAutoLabel*> matched;
|
||||
for (vector<CAutoLabel*>::iterator it = autoLabels.begin(); it != autoLabels.end(); it++)
|
||||
{
|
||||
if ((*it)->flags & flag)
|
||||
matched.push_back(*it);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
CCodeAnalyser::CCodeAnalyser(CCPUDebug *aCPU) : cpu(aCPU), emptyAnalysis(this), analysis(&emptyAnalysis)
|
||||
{
|
||||
instrAlign = cpu->minInstrLen;
|
||||
|
||||
totalIndices = 0;
|
||||
for (vector<CRegion*>::iterator it = cpu->regions.begin(); it != cpu->regions.end(); it++)
|
||||
{
|
||||
if (!(*it)->isCode)
|
||||
continue;
|
||||
m_codeRegions.push_back(*it);
|
||||
totalIndices += (*it)->size / instrAlign;
|
||||
m_indexBounds.push_back(totalIndices);
|
||||
}
|
||||
}
|
||||
|
||||
CCodeAnalyser::~CCodeAnalyser()
|
||||
{
|
||||
if (analysis != &emptyAnalysis)
|
||||
analysis->Release();
|
||||
}
|
||||
|
||||
void CCodeAnalyser::Reset()
|
||||
{
|
||||
CCodeAnalysis *oldAnalysis = analysis;
|
||||
analysis = &emptyAnalysis;
|
||||
if (oldAnalysis != &emptyAnalysis)
|
||||
oldAnalysis->Release();
|
||||
}
|
||||
|
||||
bool CCodeAnalyser::GetAddrOfIndex(unsigned index, UINT32 &addr)
|
||||
{
|
||||
unsigned regIndex = 0;
|
||||
unsigned prevBound = 0;
|
||||
for (vector<unsigned>::iterator it = m_indexBounds.begin(); it != m_indexBounds.end(); it++)
|
||||
{
|
||||
if (*it > index)
|
||||
{
|
||||
addr = m_codeRegions[regIndex]->addr + (UINT32)(index - prevBound) * instrAlign;
|
||||
return true;
|
||||
}
|
||||
prevBound = *it;
|
||||
regIndex++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCodeAnalyser::GetIndexOfAddr(UINT32 addr, unsigned &index)
|
||||
{
|
||||
unsigned regIndex = 0;
|
||||
for (vector<CRegion*>::iterator it = m_codeRegions.begin(); it != m_codeRegions.end(); it++)
|
||||
{
|
||||
if ((*it)->addr <= addr && addr <= (*it)->addrEnd)
|
||||
{
|
||||
unsigned offset = (unsigned)((addr - (*it)->addr) / instrAlign);
|
||||
index = (regIndex > 0 ? m_indexBounds[regIndex - 1] + offset : (unsigned)offset);
|
||||
return true;
|
||||
}
|
||||
regIndex++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCodeAnalyser::CheckEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, vector<CEntryPoint> &prevPoints,
|
||||
bool &needsAnalysis, bool &reanalyse)
|
||||
{
|
||||
needsAnalysis = false;
|
||||
|
||||
// Gather entry points
|
||||
GatherEntryPoints(entryPoints, unseenEntryAddrs, reanalyse);
|
||||
if (reanalyse)
|
||||
{
|
||||
needsAnalysis = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare new entry points with previous ones
|
||||
for (size_t i = 0; i < entryPoints.size(); i++)
|
||||
{
|
||||
// Check if have more than before
|
||||
if (i >= prevPoints.size())
|
||||
{
|
||||
needsAnalysis = true;
|
||||
return;
|
||||
}
|
||||
// Check if any have changed
|
||||
if (entryPoints[i] != prevPoints[i])
|
||||
{
|
||||
// If entry points, exception handlers or interrupt handlers have changed, then force reanalysis
|
||||
if (entryPoints[i].autoFlag == LFEntryPoint || prevPoints[i].autoFlag == LFEntryPoint ||
|
||||
entryPoints[i].autoFlag == LFExcepHandler || prevPoints[i].autoFlag == LFExcepHandler ||
|
||||
entryPoints[i].autoFlag == LFInterHandler || prevPoints[i].autoFlag == LFInterHandler)
|
||||
reanalyse = true;
|
||||
needsAnalysis = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check if have less than before
|
||||
if (entryPoints.size() < prevPoints.size())
|
||||
{
|
||||
// If so, force reanalysis
|
||||
reanalyse = true;
|
||||
needsAnalysis = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CCodeAnalyser::GatherEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, bool &reanalyse)
|
||||
{
|
||||
char labelStr[255];
|
||||
UINT32 addr;
|
||||
unsigned index;
|
||||
|
||||
entryPoints.clear();
|
||||
reanalyse = false;
|
||||
|
||||
// Add reset address as main entry point
|
||||
AddEntryPoint(entryPoints, cpu->GetResetAddr(), LFEntryPoint, "MainEntry");
|
||||
|
||||
// Add exception handlers as entry points
|
||||
for (vector<CException*>::iterator it = cpu->exceps.begin(); it != cpu->exceps.end(); it++)
|
||||
{
|
||||
if (!cpu->GetHandlerAddr(*it, addr))
|
||||
continue;
|
||||
sprintf(labelStr, "Ex%s", (*it)->id);
|
||||
AddEntryPoint(entryPoints, addr, LFExcepHandler, labelStr);
|
||||
}
|
||||
|
||||
// Add interrupt handlers as entry points
|
||||
for (vector<CInterrupt*>::iterator it = cpu->inters.begin(); it != cpu->inters.end(); it++)
|
||||
{
|
||||
if (!cpu->GetHandlerAddr(*it, addr))
|
||||
continue;
|
||||
sprintf(labelStr, "Int%s", (*it)->id);
|
||||
AddEntryPoint(entryPoints, addr, LFInterHandler, labelStr);
|
||||
}
|
||||
|
||||
// Add custom entry addresses
|
||||
unsigned i = 0;
|
||||
for (vector<UINT32>::iterator it = m_customEntryAddrs.begin(); it != m_customEntryAddrs.end(); it++)
|
||||
{
|
||||
sprintf(labelStr, "Custom%s", i++);
|
||||
AddEntryPoint(entryPoints, *it, LFEntryPoint, labelStr);
|
||||
}
|
||||
|
||||
// If current PC address is at an unseen location or at location that was previously invalid, then add address as unseen entry point
|
||||
if (cpu->instrCount > 0 && GetIndexOfAddr(cpu->pc, index) && (!analysis->HaveSeenIndex(index) || !analysis->IsIndexValid(index)))
|
||||
{
|
||||
// If at location that was previously seen and was invalid, then force reanalysis (ie because code may have been modified)
|
||||
if (analysis->HaveSeenIndex(index) && !analysis->IsIndexValid(index))
|
||||
reanalyse = true;
|
||||
|
||||
// Check that address not already included in previous entry points
|
||||
bool unseen = true;
|
||||
for (vector<CEntryPoint>::iterator it = entryPoints.begin(); it != entryPoints.end(); it++)
|
||||
{
|
||||
if ((*it).addr == cpu->pc)
|
||||
{
|
||||
unseen = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unseen && find(unseenEntryAddrs.begin(), unseenEntryAddrs.end(), cpu->pc) == unseenEntryAddrs.end())
|
||||
unseenEntryAddrs.push_back(cpu->pc);
|
||||
}
|
||||
|
||||
// Add unseen entry points
|
||||
for (vector<UINT32>::iterator it = unseenEntryAddrs.begin(); it != unseenEntryAddrs.end(); it++)
|
||||
AddEntryPoint(entryPoints, *it, LFUnseenCode, NULL);
|
||||
}
|
||||
|
||||
void CCodeAnalyser::AddEntryPoint(vector<CEntryPoint> &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel)
|
||||
{
|
||||
CEntryPoint entryPoint(addr, autoFlag, autoLabel);
|
||||
if (find(entryPoints.begin(), entryPoints.end(), entryPoint) == entryPoints.end())
|
||||
entryPoints.push_back(entryPoint);
|
||||
}
|
||||
|
||||
bool CCodeAnalyser::NeedsAnalysis()
|
||||
{
|
||||
vector<CEntryPoint> entryPoints;
|
||||
vector<UINT32> unseenEntryAddrs(analysis->m_unseenEntryAddrs);
|
||||
bool needsAnalysis;
|
||||
bool reanalyse;
|
||||
CheckEntryPoints(entryPoints, unseenEntryAddrs, analysis->m_entryPoints, needsAnalysis, reanalyse);
|
||||
return needsAnalysis;
|
||||
}
|
||||
|
||||
bool CCodeAnalyser::AnalyseCode()
|
||||
{
|
||||
m_abortAnalysis = false;
|
||||
|
||||
CCodeAnalysis *oldAnalysis = analysis;
|
||||
|
||||
vector<CEntryPoint> entryPoints;
|
||||
vector<UINT32> unseenEntryAddrs(oldAnalysis->m_unseenEntryAddrs);
|
||||
bool needsAnalysis;
|
||||
bool reanalyse;
|
||||
CheckEntryPoints(entryPoints, unseenEntryAddrs, oldAnalysis->m_entryPoints, needsAnalysis, reanalyse);
|
||||
if (!needsAnalysis)
|
||||
return false;
|
||||
|
||||
CCodeAnalysis *newAnalysis;
|
||||
if (reanalyse || oldAnalysis == &emptyAnalysis)
|
||||
newAnalysis = new CCodeAnalysis(this, totalIndices, entryPoints, unseenEntryAddrs);
|
||||
else
|
||||
newAnalysis = new CCodeAnalysis(oldAnalysis, entryPoints, unseenEntryAddrs);
|
||||
newAnalysis->Acquire();
|
||||
|
||||
for (vector<CEntryPoint>::iterator it = newAnalysis->m_entryPoints.begin(); it != newAnalysis->m_entryPoints.end(); it++)
|
||||
{
|
||||
AddFlagToAddr(newAnalysis->m_autoLabelsMap, it->addr, it->autoFlag, it->autoLabel);
|
||||
AnalyseCode(newAnalysis->m_seenIndices, newAnalysis->m_validIndices, newAnalysis->validIndexSet, newAnalysis->m_autoLabelsMap, it->addr);
|
||||
}
|
||||
newAnalysis->FinishAnalysis();
|
||||
|
||||
if (m_abortAnalysis)
|
||||
{
|
||||
newAnalysis->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
analysis = newAnalysis;
|
||||
if (oldAnalysis != &emptyAnalysis)
|
||||
oldAnalysis->Release();
|
||||
|
||||
cpu->debugger->AnalysisUpdated(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCodeAnalyser::AnalyseCode(vector<bool> &seenIndices, vector<bool> &validIndices, set<unsigned> &validIndexSet,
|
||||
map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr)
|
||||
{
|
||||
if (m_abortAnalysis)
|
||||
return;
|
||||
|
||||
unsigned index;
|
||||
if (!GetIndexOfAddr(addr, index) || seenIndices[index])
|
||||
return;
|
||||
|
||||
CRegion *region = cpu->GetRegion(addr);
|
||||
if (region == NULL || !region->isCode)
|
||||
return;
|
||||
|
||||
set<unsigned>::iterator setIt = validIndexSet.end();
|
||||
|
||||
unsigned startIndex = index;
|
||||
do
|
||||
{
|
||||
if (m_abortAnalysis)
|
||||
return;
|
||||
|
||||
// Flag that have seen this address index
|
||||
seenIndices[index] = true;
|
||||
|
||||
// If unit is not valid (ie doesn't disassemble) then code block must be invalid (TODO - invalidate whole code block?)
|
||||
int codesLen = cpu->GetOpLength(addr);
|
||||
if (codesLen <= 0)
|
||||
return;
|
||||
|
||||
validIndices[index] = true;
|
||||
if (setIt != validIndexSet.end())
|
||||
setIt = validIndexSet.insert(setIt, index);
|
||||
else
|
||||
setIt = validIndexSet.insert(index).first;
|
||||
|
||||
UINT32 opcode = cpu->GetOpcode(addr);
|
||||
EOpFlags opFlags = cpu->GetOpFlags(addr, opcode);
|
||||
|
||||
// See if instruction is jump
|
||||
if (opFlags & (JumpSimple|JumpLoop|JumpSub))
|
||||
{
|
||||
// If so, see if address is valid (ie known at disassemble time)
|
||||
UINT32 jumpAddr;
|
||||
if (cpu->GetJumpAddr(addr, opcode, jumpAddr))
|
||||
{
|
||||
// If so, add flags to jump address and analyse destination code block too
|
||||
if (opFlags & JumpSub) AddFlagToAddr(autoLabelsMap, jumpAddr, LFSubroutine, NULL);
|
||||
else if (opFlags & JumpLoop) AddFlagToAddr(autoLabelsMap, jumpAddr, LFLoopPoint, NULL);
|
||||
else AddFlagToAddr(autoLabelsMap, jumpAddr, LFJumpTarget, NULL);
|
||||
AnalyseCode(seenIndices, validIndices, validIndexSet, autoLabelsMap, jumpAddr);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish if instruction terminates code block (ie is not conditional and is either a non-returning jump, a return or some sort
|
||||
// of reset/halting instruction)
|
||||
if (!(opFlags & Conditional) && (opFlags & (JumpSimple|JumpLoop|ReturnEx|ReturnSub|HaltExec)))
|
||||
return;
|
||||
|
||||
// Move to next index
|
||||
index += (unsigned)codesLen / instrAlign;
|
||||
|
||||
// If reach end of address indices, code block must be invalid (TODO - invalidate whole code block?)
|
||||
if (index >= totalIndices)
|
||||
return;
|
||||
|
||||
// Move to next address
|
||||
addr += (UINT32)codesLen;
|
||||
|
||||
// If move between regions, check new region is valid
|
||||
if (addr > region->addrEnd)
|
||||
{
|
||||
region = cpu->GetRegion(addr);
|
||||
if (region == NULL || !region->isCode) // (TODO - invalidate whole code block?)
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (!seenIndices[index]);
|
||||
}
|
||||
|
||||
void CCodeAnalyser::AddFlagToAddr(map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr, ELabelFlags flag, const char *subLabel)
|
||||
{
|
||||
if (flag == LFNone)
|
||||
return;
|
||||
map<UINT32,CAutoLabel*>::iterator it = autoLabelsMap.find(addr);
|
||||
CAutoLabel *label;
|
||||
if (it == autoLabelsMap.end())
|
||||
{
|
||||
label = new CAutoLabel(cpu, addr);
|
||||
label->Acquire();
|
||||
autoLabelsMap[addr] = label;
|
||||
}
|
||||
else
|
||||
label = it->second;
|
||||
label->AddFlag(flag, subLabel);
|
||||
}
|
||||
|
||||
void CCodeAnalyser::AbortAnalysis()
|
||||
{
|
||||
m_abortAnalysis = true;
|
||||
}
|
||||
|
||||
void CCodeAnalyser::AddCustomEntryAddr(UINT32 entryAddr)
|
||||
{
|
||||
if (find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr) == m_customEntryAddrs.end())
|
||||
m_customEntryAddrs.push_back(entryAddr);
|
||||
}
|
||||
|
||||
bool CCodeAnalyser::RemoveCustomEntryAddr(UINT32 entryAddr)
|
||||
{
|
||||
vector<UINT32>::iterator it = find(m_customEntryAddrs.begin(), m_customEntryAddrs.end(), entryAddr);
|
||||
if (it == m_customEntryAddrs.end())
|
||||
return false;
|
||||
m_customEntryAddrs.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
218
Src/Debugger/CodeAnalyser.h
Normal file
218
Src/Debugger/CodeAnalyser.h
Normal file
|
@ -0,0 +1,218 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_CODEANALYSER_H
|
||||
#define INCLUDED_CODEANALYSER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include "AddressTable.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
class CRegion;
|
||||
class CLabel;
|
||||
|
||||
enum ELabelFlags
|
||||
{
|
||||
LFNone = 0,
|
||||
LFEntryPoint = 1,
|
||||
LFExcepHandler = 2,
|
||||
LFInterHandler = 4,
|
||||
LFJumpTarget = 8,
|
||||
LFLoopPoint = 16,
|
||||
LFSubroutine = 32,
|
||||
LFUnseenCode = 64,
|
||||
LFAll = (LFEntryPoint | LFExcepHandler | LFInterHandler | LFJumpTarget | LFSubroutine | LFLoopPoint | LFUnseenCode)
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents an entry point into a program's code in memory, eg a CPU's reset address or exception handler's address etc.
|
||||
*/
|
||||
class CEntryPoint
|
||||
{
|
||||
private:
|
||||
char autoLabelStr[255];
|
||||
|
||||
public:
|
||||
UINT32 addr;
|
||||
ELabelFlags autoFlag;
|
||||
const char *autoLabel;
|
||||
|
||||
CEntryPoint(const CEntryPoint &other);
|
||||
|
||||
CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel);
|
||||
|
||||
CEntryPoint &operator=(const CEntryPoint &other);
|
||||
|
||||
bool operator==(const CEntryPoint &other);
|
||||
|
||||
bool operator!=(const CEntryPoint &other);
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents an auto-generated label that resulted from code analysis, eg a known entry point or the destination of a subroutine call.
|
||||
*/
|
||||
class CAutoLabel : public CAddressRef
|
||||
{
|
||||
private:
|
||||
static const char *s_defaultLabelFmts[];
|
||||
|
||||
const char *m_subLabels[7];
|
||||
|
||||
unsigned m_acquired;
|
||||
|
||||
const char *CreateDefaultSubLabel(ELabelFlags flag);
|
||||
|
||||
public:
|
||||
static const unsigned numLabelFlags;
|
||||
|
||||
static ELabelFlags GetLabelFlag(int index);
|
||||
|
||||
static int GetFlagIndex(ELabelFlags flag);
|
||||
|
||||
static const char *GetFlagString(ELabelFlags flag);
|
||||
|
||||
ELabelFlags flags;
|
||||
|
||||
CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr);
|
||||
|
||||
~CAutoLabel();
|
||||
|
||||
void Acquire();
|
||||
|
||||
void Release();
|
||||
|
||||
void AddFlag(ELabelFlags flag, const char *subLabel);
|
||||
|
||||
bool GetLabel(char *label, ELabelFlags flag = LFAll);
|
||||
|
||||
bool ContainsSubLabel(const char *subLabel);
|
||||
};
|
||||
|
||||
class CCodeAnalyser;
|
||||
|
||||
/*
|
||||
* Class that holds the results of having analysed the program code.
|
||||
*/
|
||||
class CCodeAnalysis
|
||||
{
|
||||
friend class CCodeAnalyser;
|
||||
|
||||
private:
|
||||
vector<CEntryPoint> m_entryPoints;
|
||||
vector<UINT32> m_unseenEntryAddrs;
|
||||
vector<bool> m_seenIndices;
|
||||
vector<bool> m_validIndices;
|
||||
map<UINT32,CAutoLabel*> m_autoLabelsMap;
|
||||
|
||||
unsigned m_acquired;
|
||||
|
||||
CCodeAnalysis(CCodeAnalyser *aAnalyser);
|
||||
|
||||
CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, vector<CEntryPoint> &entryPoints, vector<UINT32> &m_unseenEntryAddrs);
|
||||
|
||||
CCodeAnalysis(CCodeAnalysis *oldAnalysis, vector<CEntryPoint> &entryPoints, vector<UINT32> &m_unseenEntryAddrs);
|
||||
|
||||
void FinishAnalysis();
|
||||
|
||||
public:
|
||||
CCodeAnalyser *analyser;
|
||||
set<unsigned> validIndexSet;
|
||||
vector<CAutoLabel*> autoLabels;
|
||||
|
||||
~CCodeAnalysis();
|
||||
|
||||
void Acquire();
|
||||
|
||||
void Release();
|
||||
|
||||
bool IsAddrValid(UINT32 addr);
|
||||
|
||||
bool GetNextValidAddr(UINT32 &addr);
|
||||
|
||||
bool IsIndexValid(unsigned index);
|
||||
|
||||
bool GetNextValidIndex(unsigned &index);
|
||||
|
||||
bool HasSeenAddr(UINT32 addr);
|
||||
|
||||
bool HaveSeenIndex(unsigned index);
|
||||
|
||||
CAutoLabel *GetAutoLabel(UINT32 addr);
|
||||
|
||||
CAutoLabel *GetAutoLabel(const char *subLabel);
|
||||
|
||||
vector<CAutoLabel*> GetAutoLabels(ELabelFlags flag);
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that analyses a program's code to work out all the reachable program locations from a given set of entry points into the code.
|
||||
* During the analysis, it keeps track of valid memory locations (to help with the disassembly of code for CPUs with variable-length
|
||||
* instruction sets) and generates a set of auto-labels that identify places of interest such as subroutines, jump destinations and
|
||||
* exception handlers etc.
|
||||
* This sort of analysis works well for static addressing modes but not so well for dynamic address referencing or self-modifying code.
|
||||
* To allow for the latter cases the analyser updates its analysis whenever it encounters an unseen memory location or it sees
|
||||
* that code has changed from a previous inspection.
|
||||
*/
|
||||
class CCodeAnalyser
|
||||
{
|
||||
private:
|
||||
vector<CRegion*> m_codeRegions;
|
||||
vector<unsigned> m_indexBounds;
|
||||
|
||||
vector<UINT32> m_customEntryAddrs;
|
||||
|
||||
bool m_abortAnalysis;
|
||||
|
||||
void CheckEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, vector<CEntryPoint> &prevPoints,
|
||||
bool &needsAnalysis, bool &reanalyse);
|
||||
|
||||
void GatherEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, bool &reanalyse);
|
||||
|
||||
void AddEntryPoint(vector<CEntryPoint> &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel);
|
||||
|
||||
void AnalyseCode(vector<bool> &seenIndices, vector<bool> &validIndices, set<unsigned> &validIndexSet, map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr);
|
||||
|
||||
void AddFlagToAddr(map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel);
|
||||
|
||||
public:
|
||||
CCPUDebug *cpu;
|
||||
|
||||
CCodeAnalysis emptyAnalysis;
|
||||
|
||||
CCodeAnalysis *analysis;
|
||||
|
||||
unsigned instrAlign;
|
||||
unsigned totalIndices;
|
||||
|
||||
CCodeAnalyser(CCPUDebug *aCPU);
|
||||
|
||||
~CCodeAnalyser();
|
||||
|
||||
void Reset();
|
||||
|
||||
bool GetAddrOfIndex(unsigned index, UINT32 &addr);
|
||||
|
||||
bool GetIndexOfAddr(UINT32 addr, unsigned &index);
|
||||
|
||||
bool NeedsAnalysis();
|
||||
|
||||
bool AnalyseCode();
|
||||
|
||||
void AbortAnalysis();
|
||||
|
||||
void AddCustomEntryAddr(UINT32 entryAddr);
|
||||
|
||||
bool RemoveCustomEntryAddr(UINT32 entryAddr);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_CODEANALYSER_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
2443
Src/Debugger/ConsoleDebugger.cpp
Normal file
2443
Src/Debugger/ConsoleDebugger.cpp
Normal file
File diff suppressed because it is too large
Load diff
135
Src/Debugger/ConsoleDebugger.h
Normal file
135
Src/Debugger/ConsoleDebugger.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_CONSOLEDEBUGGER_H
|
||||
#define INCLUDED_CONSOLEDEBUGGER_H
|
||||
|
||||
#include "Debugger.h"
|
||||
#include "CodeAnalyser.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define NUM_LISTAUTOLABELS (sizeof(s_listAutoLabels) / sizeof(ELabelFlags))
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
class CException;
|
||||
class CWatch;
|
||||
class CBreakpoint;
|
||||
class CRegMonitor;
|
||||
|
||||
/*
|
||||
* Base class for a console-based debugger.
|
||||
* TODO - a lot of commands are not yet implemented/missing and the main loop is a big ugly mess :-) !
|
||||
*/
|
||||
class CConsoleDebugger : public CDebugger
|
||||
{
|
||||
private:
|
||||
static ELabelFlags s_listAutoLabels[];
|
||||
|
||||
CCPUDebug *m_cpu;
|
||||
|
||||
UINT32 m_listDism;
|
||||
UINT32 m_listMem;
|
||||
|
||||
bool m_analyseCode;
|
||||
EFormat m_addrFmt;
|
||||
EFormat m_portFmt;
|
||||
EFormat m_dataFmt;
|
||||
bool m_showLabels;
|
||||
bool m_labelsOverAddr;
|
||||
bool m_showOpCodes;
|
||||
unsigned m_memBytesPerRow;
|
||||
|
||||
protected:
|
||||
bool CheckToken(const char *token, const char *simple, const char *full);
|
||||
|
||||
void Truncate(char *dst, size_t maxLen, const char *src);
|
||||
|
||||
void FormatOpCodes(char *str, int addr, int codesLen);
|
||||
|
||||
bool GetLabelText(char *str, int maxLen, UINT32 addr);
|
||||
|
||||
bool SetBoolConfig(const char *str, bool &cfg);
|
||||
|
||||
bool SetNumConfig(const char *str, unsigned &cfg);
|
||||
|
||||
const char *GetFmtConfig(EFormat fmt);
|
||||
|
||||
bool SetFmtConfig(const char *str, EFormat &cfg);
|
||||
|
||||
bool ParseAddress(CCPUDebug *cpu, const char *str, UINT32 *addr);
|
||||
|
||||
bool ParseDataSize(const char *str, unsigned &dataSize);
|
||||
|
||||
void ListCPUs();
|
||||
|
||||
void ListRegisters();
|
||||
|
||||
void ListExceptions();
|
||||
|
||||
void ListInterrupts();
|
||||
|
||||
void ListIOs();
|
||||
|
||||
void ListRegions();
|
||||
|
||||
void ListLabels(bool customLabels, ELabelFlags autoLabelFlags);
|
||||
|
||||
void GetAllMemWatches(vector<CWatch*> &watches);
|
||||
|
||||
void ListMemWatches();
|
||||
|
||||
void GetAllPortWatches(vector<CWatch*> &watches);
|
||||
|
||||
void ListPortWatches();
|
||||
|
||||
void ListBreakpoints();
|
||||
|
||||
void ListMonitors();
|
||||
|
||||
UINT32 ListDisassembly(UINT32 start, UINT32 end, unsigned numInstrs);
|
||||
|
||||
UINT32 ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow);
|
||||
|
||||
void Attach();
|
||||
|
||||
void Detach();
|
||||
|
||||
virtual void ApplyConfig();
|
||||
|
||||
virtual void Attached();
|
||||
|
||||
virtual void Detaching();
|
||||
|
||||
virtual void AnalysisUpdated(CCodeAnalyser *analyser);
|
||||
|
||||
virtual void ExceptionTrapped(CException *ex);
|
||||
|
||||
virtual void InterruptTrapped(CInterrupt *ex);
|
||||
|
||||
virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead);
|
||||
|
||||
virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput);
|
||||
|
||||
virtual void BreakpointReached(CBreakpoint *bp);
|
||||
|
||||
virtual void MonitorTriggered(CRegMonitor *regMon);
|
||||
|
||||
virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason);
|
||||
|
||||
virtual void WaitCommand(CCPUDebug *cpu);
|
||||
|
||||
virtual bool ProcessToken(const char *token, const char *cmd);
|
||||
|
||||
public:
|
||||
CConsoleDebugger();
|
||||
|
||||
void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl);
|
||||
|
||||
void FlushOut(CCPUDebug *cpu);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_CONSOLEDEBUGGER_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
437
Src/Debugger/Debugger.cpp
Normal file
437
Src/Debugger/Debugger.cpp
Normal file
|
@ -0,0 +1,437 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "Debugger.h"
|
||||
#include "CPUDebug.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
unsigned CDebugger::GetDataSize(UINT64 data)
|
||||
{
|
||||
if (data <= 0xFFULL) return 1;
|
||||
else if (data <= 0xFFFFULL) return 2;
|
||||
else if (data <= 0xFFFFFFULL) return 3;
|
||||
else if (data <= 0xFFFFFFFFULL) return 4;
|
||||
else if (data <= 0xFFFFFFFFFFULL) return 5;
|
||||
else if (data <= 0xFFFFFFFFFFFFULL) return 6;
|
||||
else if (data <= 0xFFFFFFFFFFFFFFULL) return 7;
|
||||
else return 8;
|
||||
}
|
||||
|
||||
const char *CDebugger::GetSizeString(unsigned dataSize)
|
||||
{
|
||||
switch (dataSize)
|
||||
{
|
||||
case 1: return "8-bit";
|
||||
case 2: return "16-bit";
|
||||
case 3: return "24-bit";
|
||||
case 4: return "32-bit";
|
||||
case 5: return "40-bit";
|
||||
case 6: return "48-bit";
|
||||
case 7: return "56-bit";
|
||||
case 8: return "64-bit";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
UINT64 CDebugger::MaskData(unsigned dataSize, UINT64 data)
|
||||
{
|
||||
switch (dataSize)
|
||||
{
|
||||
case 0: return 0;
|
||||
case 1: return data&0xFFULL;
|
||||
case 2: return data&0xFFFFULL;
|
||||
case 3: return data&0xFFFFFFULL;
|
||||
case 4: return data&0xFFFFFFFFULL;
|
||||
case 5: return data&0xFFFFFFFFFFULL;
|
||||
case 6: return data&0xFFFFFFFFFFFFULL;
|
||||
case 7: return data&0xFFFFFFFFFFFFFFULL;
|
||||
default: return data;
|
||||
}
|
||||
}
|
||||
|
||||
UINT64 CDebugger::GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize)
|
||||
{
|
||||
if (addr == slotAddr && dataSize == slotDataSize)
|
||||
{
|
||||
// Data perfectly aligned with slot
|
||||
return MaskData(slotDataSize, data);
|
||||
}
|
||||
unsigned overlap, shift;
|
||||
if (addr >= slotAddr)
|
||||
{
|
||||
// Data starts after slot beginning
|
||||
overlap = slotAddr + slotDataSize - addr;
|
||||
if (overlap < dataSize)
|
||||
{
|
||||
// Data ends after slot end
|
||||
shift = dataSize - overlap;
|
||||
return MaskData(overlap, data>>(8 * shift));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data falls completely inside slot
|
||||
shift = overlap - dataSize;
|
||||
return MaskData(dataSize, data)<<(8 * shift);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data starts before slot beginning
|
||||
overlap = addr + dataSize - slotAddr;
|
||||
if (overlap < slotDataSize)
|
||||
{
|
||||
// Data ends before slot end
|
||||
shift = slotDataSize - overlap;
|
||||
return MaskData(overlap, data)<<(8 * shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Slot lies completely inside data
|
||||
shift = overlap - slotDataSize;
|
||||
return MaskData(slotDataSize, data>>(8 * shift));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CDebugger::ParseInt(const char *str, int *val)
|
||||
{
|
||||
return sscanf(str, "%d", val) == 1;
|
||||
}
|
||||
|
||||
CDebugger::CDebugger() : m_exit(false), m_pause(false), frameCount(0), logDebug(true), logInfo(true), logError(true)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CDebugger::~CDebugger()
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void CDebugger::AddCPU(CCPUDebug *cpu)
|
||||
{
|
||||
cpu->AttachToDebugger(this);
|
||||
cpus.push_back(cpu);
|
||||
}
|
||||
|
||||
void CDebugger::RemoveCPU(CCPUDebug *cpu)
|
||||
{
|
||||
cpu->DetachFromDebugger(this);
|
||||
vector<CCPUDebug*>::iterator it = find(cpus.begin(), cpus.end(), cpu);
|
||||
if (it != cpus.end())
|
||||
cpus.erase(it);
|
||||
}
|
||||
|
||||
CCPUDebug *CDebugger::GetCPU(const char *name)
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
{
|
||||
if (stricmp(name, (*it)->name) == 0)
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CDebugger::SetExit()
|
||||
{
|
||||
m_exit = true;
|
||||
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
{
|
||||
//(*it)->RemoveAllExceptionTraps();
|
||||
(*it)->RemoveAllMemWatches();
|
||||
(*it)->RemoveAllIOWatches();
|
||||
(*it)->RemoveAllBreakpoints();
|
||||
(*it)->RemoveAllRegMonitors();
|
||||
(*it)->SetContinue();
|
||||
}
|
||||
}
|
||||
|
||||
void CDebugger::SetPause(bool pause)
|
||||
{
|
||||
m_pause = pause;
|
||||
}
|
||||
|
||||
void CDebugger::ForceBreak(bool user)
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
(*it)->ForceBreak(user);
|
||||
}
|
||||
|
||||
void CDebugger::ClearBreak()
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
(*it)->ClearBreak();
|
||||
}
|
||||
|
||||
void CDebugger::SetContinue()
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
(*it)->SetContinue();
|
||||
}
|
||||
|
||||
void CDebugger::Attach()
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
(*it)->AttachToCPU();
|
||||
}
|
||||
|
||||
void CDebugger::Detach()
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
(*it)->DetachFromCPU();
|
||||
}
|
||||
|
||||
void CDebugger::Reset()
|
||||
{
|
||||
frameCount = 0;
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
(*it)->DebuggerReset();
|
||||
}
|
||||
|
||||
void CDebugger::Poll()
|
||||
{
|
||||
frameCount++;
|
||||
}
|
||||
|
||||
void CDebugger::WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, fmtStr);
|
||||
WriteOut(cpu, typeStr, fmtStr, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
#ifdef DEBUGGER_HASLOGGER
|
||||
void CDebugger::DebugLog(const char *fmt, va_list vl)
|
||||
{
|
||||
if (logDebug)
|
||||
WriteOut(NULL, "Debug", fmt, vl);
|
||||
}
|
||||
|
||||
void CDebugger::InfoLog(const char *fmt, va_list vl)
|
||||
{
|
||||
if (logInfo)
|
||||
WriteOut(NULL, "Info", fmt, vl);
|
||||
}
|
||||
|
||||
void CDebugger::ErrorLog(const char *fmt, va_list vl)
|
||||
{
|
||||
if (logError)
|
||||
WriteOut(NULL, "Error", fmt, vl);
|
||||
}
|
||||
#endif // DEBUGGER_HASLOGGER
|
||||
|
||||
bool CDebugger::ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data)
|
||||
{
|
||||
unsigned i;
|
||||
char upperStr[255];
|
||||
const char *p;
|
||||
switch (format)
|
||||
{
|
||||
case Hex:
|
||||
case Hex0x:
|
||||
case HexDollar:
|
||||
case HexPostH:
|
||||
for (i = 0; str[i] && i < 254; i++)
|
||||
upperStr[i] = toupper(str[i]);
|
||||
upperStr[i] = '\0';
|
||||
if (sscanf(upperStr, "%llX", data) != 1 &&
|
||||
sscanf(upperStr, "0x%llX", data) != 1 &&
|
||||
sscanf(upperStr, "$%llX", data) != 1 &&
|
||||
sscanf(upperStr, "%llXH", data) != 1)
|
||||
return false;
|
||||
break;
|
||||
case Decimal:
|
||||
if (sscanf(str, "%llu", data) != 1)
|
||||
return false;
|
||||
break;
|
||||
case Binary:
|
||||
p = str;
|
||||
while (*p != '\0' && *p != '0' && *p != '1')
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
return false;
|
||||
*data = 0;
|
||||
for (i = 0; i < dataSize * 8; i++)
|
||||
{
|
||||
if (*p == '1')
|
||||
*data |= 1;
|
||||
else if (*p != '0')
|
||||
break;
|
||||
*data <<= 1;
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
case ASCII:
|
||||
// TODO
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (dataSize)
|
||||
{
|
||||
case 0: return 0;
|
||||
case 1: return *data <= (UINT64)0xFF;
|
||||
case 2: return *data <= (UINT64)0xFFFF;
|
||||
case 3: return *data <= (UINT64)0xFFFFFF;
|
||||
case 4: return *data <= (UINT64)0xFFFFFFFF;
|
||||
case 5: return *data <= (UINT64)0xFFFFFFFFFF;
|
||||
case 6: return *data <= (UINT64)0xFFFFFFFFFFFF;
|
||||
case 7: return *data <= (UINT64)0xFFFFFFFFFFFFFF;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CDebugger::FormatData(char *str, EFormat format, unsigned dataSize, INT64 data)
|
||||
{
|
||||
if (data >= 0)
|
||||
FormatData(str, format, dataSize, (UINT64)data);
|
||||
else
|
||||
{
|
||||
*str++ = '-';
|
||||
FormatData(str, format, dataSize, (UINT64)(data >= 0 ? data : -data)); // No portable abs64 available
|
||||
}
|
||||
}
|
||||
|
||||
void CDebugger::FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data)
|
||||
{
|
||||
data = MaskData(dataSize, data);
|
||||
unsigned i;
|
||||
switch (format)
|
||||
{
|
||||
case Hex: sprintf(str, "%0*llX", (int)dataSize * 2, data); break;
|
||||
case Hex0x: sprintf(str, "0x%0*llX", (int)dataSize * 2, data); break;
|
||||
case HexDollar: sprintf(str, "$%0*llX", (int)dataSize * 2, data); break;
|
||||
case HexPostH: sprintf(str, "%0*llXh", (int)dataSize * 2, data); break;
|
||||
case Decimal: sprintf(str, "%llu", data); break;
|
||||
case Binary:
|
||||
str += dataSize * 8;
|
||||
*str-- = '\0';
|
||||
for (i = 0; i < dataSize * 8; i++)
|
||||
{
|
||||
*str-- = ((data&1) ? '1' : '0');
|
||||
data >>= 1;
|
||||
}
|
||||
break;
|
||||
case ASCII:
|
||||
for (i = 0; i < dataSize; i++)
|
||||
{
|
||||
*str++ = (char)(data & 0xFF);
|
||||
data >>= 8;
|
||||
}
|
||||
*str++ = '\0';
|
||||
break;
|
||||
default:
|
||||
str[0] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDebugger::HasState()
|
||||
{
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
{
|
||||
if ((*it)->labels.size() + (*it)->comments.size() > 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDebugger::LoadState(const char *fileName)
|
||||
{
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
// Open file and find header
|
||||
CBlockFile state;
|
||||
if (state.Load(fileName) != OKAY)
|
||||
return false;
|
||||
if (state.FindBlock("Debugger State") != OKAY)
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check version in header matches
|
||||
unsigned version;
|
||||
state.Read(&version, sizeof(version));
|
||||
if (version != DEBUGGER_STATEFILE_VERSION)
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load debugger state
|
||||
if (!LoadState(&state))
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load state for each CPU
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
{
|
||||
if (!(*it)->LoadState(&state))
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
state.Close();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // DEBUGGER_HASBLOCKFILE
|
||||
}
|
||||
|
||||
bool CDebugger::SaveState(const char *fileName)
|
||||
{
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
// Create file with header
|
||||
CBlockFile state;
|
||||
if (state.Create(fileName, "Debugger State", __FILE__) != OKAY)
|
||||
return false;
|
||||
|
||||
// Write out version in header
|
||||
unsigned version = DEBUGGER_STATEFILE_VERSION;
|
||||
state.Write(&version, sizeof(version));
|
||||
|
||||
// Save debugger state
|
||||
if (!SaveState(&state))
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save state for each CPU
|
||||
for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
|
||||
{
|
||||
if (!(*it)->SaveState(&state))
|
||||
return false;
|
||||
}
|
||||
|
||||
state.Close();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // DEBUGGER_HASBLOCKFILE
|
||||
}
|
||||
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
bool CDebugger::LoadState(CBlockFile *state)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDebugger::SaveState(CBlockFile *state)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
225
Src/Debugger/Debugger.h
Normal file
225
Src/Debugger/Debugger.h
Normal file
|
@ -0,0 +1,225 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_DEBUGGER_H
|
||||
#define INCLUDED_DEBUGGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#if defined(SUPERMODEL_WIN32) || defined(SUPERMODEL_UNIX) || defined(SUPERMODEL_OSX)
|
||||
#define DEBUGGER_HASBLOCKFILE
|
||||
#define DEBUGGER_HASLOGGER
|
||||
#endif
|
||||
|
||||
#define DEBUGGER_STATEFILE_VERSION 0
|
||||
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
#include "BlockFile.h"
|
||||
#endif // DEBUGGER_HASBLOCKFILE
|
||||
|
||||
#ifdef DEBUGGER_HASLOGGER
|
||||
#include "Logger.h"
|
||||
#endif // DEBUGGER_HASLOGGER
|
||||
|
||||
#ifndef stricmp
|
||||
#ifdef _MSC_VER // MS VisualC++
|
||||
#define stricmp _stricmp
|
||||
#else // Assume GC
|
||||
#define stricmp strcasecmp
|
||||
#endif // _MSC_VER
|
||||
#endif // stricmp
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
class CCodeAnalyser;
|
||||
class CException;
|
||||
class CInterrupt;
|
||||
class CIO;
|
||||
class CWatch;
|
||||
class CBreakpoint;
|
||||
class CRegMonitor;
|
||||
|
||||
enum EHaltReason
|
||||
{
|
||||
HaltNone = 0,
|
||||
HaltState = 1,
|
||||
HaltUser = 2,
|
||||
HaltStep = 4,
|
||||
HaltCount = 8,
|
||||
HaltUntil = 16
|
||||
};
|
||||
|
||||
enum EFormat
|
||||
{
|
||||
Hex = 0,
|
||||
Hex0x,
|
||||
HexDollar,
|
||||
HexPostH,
|
||||
Decimal,
|
||||
Binary,
|
||||
ASCII
|
||||
};
|
||||
|
||||
/*
|
||||
* Base class for a debugger.
|
||||
* Provides common methods for loading/saving debugger state, formatting/parsing data and general control of the debugger.
|
||||
* Also holds references to the CCPUDebug objects, one for each debuggable CPU, and contains pure virtual methods for sub-classes to
|
||||
* implement which act as callbacks for the various debugging events that can occur.
|
||||
*/
|
||||
#ifdef DEBUGGER_HASLOGGER
|
||||
class CDebugger : public CLogger
|
||||
#else
|
||||
class CDebugger
|
||||
#endif // DEBUGGER_HASLOGGER
|
||||
{
|
||||
private:
|
||||
bool m_exit;
|
||||
bool m_pause;
|
||||
|
||||
protected:
|
||||
#ifdef DEBUGGER_HASBLOCKFILE
|
||||
virtual bool LoadState(CBlockFile *state);
|
||||
|
||||
virtual bool SaveState(CBlockFile *state);
|
||||
#endif
|
||||
|
||||
public:
|
||||
vector<CCPUDebug*> cpus;
|
||||
|
||||
UINT64 frameCount;
|
||||
|
||||
#ifdef DEBUGGER_HASLOGGER
|
||||
bool logDebug;
|
||||
bool logInfo;
|
||||
bool logError;
|
||||
#endif // DEBUGGER_HASLOGGER
|
||||
|
||||
static unsigned GetDataSize(UINT64 data);
|
||||
|
||||
static const char *GetSizeString(unsigned dataSize);
|
||||
|
||||
static UINT64 MaskData(unsigned dataSize, UINT64 data);
|
||||
|
||||
static UINT64 GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize);
|
||||
|
||||
static bool ParseInt(const char *str, int *val);
|
||||
|
||||
CDebugger();
|
||||
|
||||
virtual ~CDebugger();
|
||||
|
||||
void AddCPU(CCPUDebug *cpu);
|
||||
|
||||
void RemoveCPU(CCPUDebug *cpu);
|
||||
|
||||
CCPUDebug *GetCPU(const char *name);
|
||||
|
||||
void SetExit();
|
||||
|
||||
void SetPause(bool pause);
|
||||
|
||||
void ForceBreak(bool user);
|
||||
|
||||
void ClearBreak();
|
||||
|
||||
void SetContinue();
|
||||
|
||||
bool CheckExit();
|
||||
|
||||
bool CheckPause();
|
||||
|
||||
//
|
||||
// Console output
|
||||
//
|
||||
|
||||
void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...);
|
||||
|
||||
#ifdef DEBUGGER_HASLOGGER
|
||||
//
|
||||
// CLogger logging methods
|
||||
//
|
||||
virtual void DebugLog(const char *fmt, va_list vl);
|
||||
|
||||
virtual void InfoLog(const char *fmt, va_list vl);
|
||||
|
||||
virtual void ErrorLog(const char *fmt, va_list vl);
|
||||
#endif // DEBUGGER_HASLOGGER
|
||||
|
||||
//
|
||||
// Formatting/parsing
|
||||
//
|
||||
|
||||
// TODO - add in bufSize
|
||||
virtual bool ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data);
|
||||
|
||||
virtual void FormatData(char *str, EFormat format, unsigned dataSize, INT64 data);
|
||||
|
||||
virtual void FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data);
|
||||
|
||||
//
|
||||
// State loading/saving
|
||||
//
|
||||
|
||||
virtual bool HasState();
|
||||
|
||||
virtual bool LoadState(const char *fileName);
|
||||
|
||||
virtual bool SaveState(const char *fileName);
|
||||
|
||||
//
|
||||
// Public virtual methods for sub-classes to implement
|
||||
//
|
||||
|
||||
virtual void Attach();
|
||||
|
||||
virtual void Detach();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
//
|
||||
// Inlined methods
|
||||
//
|
||||
|
||||
inline bool CDebugger::CheckExit()
|
||||
{
|
||||
return m_exit;
|
||||
}
|
||||
|
||||
inline bool CDebugger::CheckPause()
|
||||
{
|
||||
return m_pause;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INCLUDED_DEBUGGER_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
14
Src/Debugger/Exception.cpp
Normal file
14
Src/Debugger/Exception.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "Exception.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CException::CException(CCPUDebug *exCPU, const char *exId, UINT16 exCode, const char *exName) :
|
||||
cpu(exCPU), id(exId), code(exCode), name(exName), trap(false), count(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
30
Src/Debugger/Exception.h
Normal file
30
Src/Debugger/Exception.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_EXCEPTION_H
|
||||
#define INCLUDED_EXCEPTION_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
|
||||
/*
|
||||
* Class that represents a CPU exception.
|
||||
*/
|
||||
class CException
|
||||
{
|
||||
public:
|
||||
CCPUDebug *cpu;
|
||||
const char *id;
|
||||
const UINT16 code;
|
||||
const char *name;
|
||||
|
||||
bool trap;
|
||||
unsigned long count;
|
||||
|
||||
CException(CCPUDebug *exCPU, const char *exId, UINT16 exCode, const char *exName);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_EXCEPTION_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
166
Src/Debugger/IO.cpp
Normal file
166
Src/Debugger/IO.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "CPUDebug.h"
|
||||
#include "IO.h"
|
||||
#include "Watch.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CIO::CIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize) :
|
||||
cpu(ioCPU), name(ioName), group(ioGroup), dataSize(ioDataSize),
|
||||
watch(NULL), inCount(0), outCount(0), lastIn(0), lastOut(0), last(&lastIn)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CIO::CheckInput(UINT64 data)
|
||||
{
|
||||
inCount++;
|
||||
RecordInput(data);
|
||||
|
||||
// Check watch, if any
|
||||
return watch != NULL && watch->CheckInput(this, data);
|
||||
}
|
||||
|
||||
bool CIO::CheckOutput(UINT64 data)
|
||||
{
|
||||
outCount++;
|
||||
RecordOutput(data);
|
||||
|
||||
// Check watch, if any
|
||||
return watch != NULL && watch->CheckOutput(this, data);
|
||||
}
|
||||
|
||||
CSimpleWatch *CIO::AddSimpleWatch(bool trigInput, bool trigOutput)
|
||||
{
|
||||
CSimpleWatch *sWatch = new CSimpleWatch(cpu, this, trigInput, trigOutput);
|
||||
cpu->AddWatch(sWatch);
|
||||
return sWatch;
|
||||
}
|
||||
|
||||
CCountWatch *CIO::AddCountWatch(bool trigInput, bool trigOutput, unsigned count)
|
||||
{
|
||||
CCountWatch *cWatch = new CCountWatch(cpu, this, trigInput, trigOutput, count);
|
||||
cpu->AddWatch(cWatch);
|
||||
return cWatch;
|
||||
}
|
||||
|
||||
CMatchWatch *CIO::AddMatchWatch(bool trigInput, bool trigOutput, vector<UINT64> &dataSeq)
|
||||
{
|
||||
CMatchWatch *mWatch = new CMatchWatch(cpu, this, trigInput, trigOutput, dataSeq);
|
||||
cpu->AddWatch(mWatch);
|
||||
return mWatch;
|
||||
}
|
||||
|
||||
CPrintWatch *CIO::AddPrintWatch(bool trigInput, bool trigOutput)
|
||||
{
|
||||
CPrintWatch *pWatch = new CPrintWatch(cpu, this, trigInput, trigOutput);
|
||||
cpu->AddWatch(pWatch);
|
||||
return pWatch;
|
||||
}
|
||||
|
||||
CCaptureWatch *CIO::AddCaptureWatch(bool trigInput, bool trigOutput, unsigned maxLen)
|
||||
{
|
||||
CCaptureWatch *cWatch = new CCaptureWatch(cpu, this, trigInput, trigOutput, maxLen);
|
||||
cpu->AddWatch(cWatch);
|
||||
return cWatch;
|
||||
}
|
||||
|
||||
bool CIO::RemoveWatch()
|
||||
{
|
||||
return watch != NULL && cpu->RemoveWatch(watch);
|
||||
}
|
||||
|
||||
CPortIO::CPortIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT16 ioPortNum) :
|
||||
CIO(ioCPU, ioName, ioGroup, ioDataSize), portNum(ioPortNum), label(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void CPortIO::AddLabel(const char *pLabel)
|
||||
{
|
||||
strncpy(labelStr, pLabel, MAX_LABEL_LENGTH);
|
||||
labelStr[MAX_LABEL_LENGTH] = '\0';
|
||||
label = labelStr;
|
||||
}
|
||||
|
||||
void CPortIO::RemoveLabel()
|
||||
{
|
||||
label = NULL;
|
||||
}
|
||||
|
||||
void CPortIO::GetLocation(char *str)
|
||||
{
|
||||
if (name != NULL)
|
||||
sprintf(str, "port %s", name);
|
||||
else
|
||||
sprintf(str, "port %u", portNum);
|
||||
}
|
||||
|
||||
UINT64 CPortIO::Read()
|
||||
{
|
||||
UINT64 data = cpu->ReadPort(portNum);
|
||||
RecordInput(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool CPortIO::Write(UINT64 data)
|
||||
{
|
||||
if (!cpu->WritePort(portNum, data))
|
||||
return false;
|
||||
RecordOutput(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
CMappedIO::CMappedIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT32 ioAddr) :
|
||||
CIO(ioCPU, ioName, ioGroup, ioDataSize), CAddressRef(ioCPU, ioAddr, ioDataSize)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CMappedIO::CheckInput(UINT32 cAddr, unsigned cDataSize, UINT64 data)
|
||||
{
|
||||
// Take care with input that was not aligned with mapped I/O address and size
|
||||
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, dataSize);
|
||||
return CIO::CheckInput(sData);
|
||||
}
|
||||
|
||||
bool CMappedIO::CheckOutput(UINT32 cAddr, unsigned cDataSize, UINT64 data)
|
||||
{
|
||||
// Take care with output that was not aligned with mapped I/O address and size
|
||||
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, dataSize);
|
||||
return CIO::CheckOutput(sData);
|
||||
}
|
||||
|
||||
void CMappedIO::AddLabel(const char *label)
|
||||
{
|
||||
CIO::cpu->AddLabel(addr, label);
|
||||
}
|
||||
|
||||
void CMappedIO::RemoveLabel()
|
||||
{
|
||||
CIO::cpu->RemoveLabel(addr);
|
||||
}
|
||||
|
||||
void CMappedIO::GetLocation(char *str)
|
||||
{
|
||||
CIO::cpu->FormatAddress(str, addr);
|
||||
}
|
||||
|
||||
UINT64 CMappedIO::Read()
|
||||
{
|
||||
UINT64 data = CIO::cpu->ReadMem(addr, dataSize);
|
||||
RecordInput(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool CMappedIO::Write(UINT64 data)
|
||||
{
|
||||
if (!CIO::cpu->WriteMem(addr, dataSize, data))
|
||||
return false;
|
||||
RecordOutput(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
140
Src/Debugger/IO.h
Normal file
140
Src/Debugger/IO.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_IOPORT_H
|
||||
#define INCLUDED_IOPORT_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#define MAX_LABEL_LENGTH 255
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
class CWatch;
|
||||
class CSimpleWatch;
|
||||
class CCountWatch;
|
||||
class CMatchWatch;
|
||||
class CPrintWatch;
|
||||
class CCaptureWatch;
|
||||
|
||||
/*
|
||||
* Base class that represents CPU input/output.
|
||||
*/
|
||||
class CIO
|
||||
{
|
||||
public:
|
||||
CCPUDebug *cpu;
|
||||
const char *name;
|
||||
const char *group;
|
||||
unsigned dataSize;
|
||||
|
||||
CWatch *watch;
|
||||
|
||||
unsigned inCount;
|
||||
unsigned outCount;
|
||||
UINT64 lastIn;
|
||||
UINT64 lastOut;
|
||||
UINT64 *last;
|
||||
|
||||
CIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize);
|
||||
|
||||
void RecordInput(UINT64 data);
|
||||
|
||||
void RecordOutput(UINT64 data);
|
||||
|
||||
bool CheckInput(UINT64 data);
|
||||
|
||||
bool CheckOutput(UINT64 data);
|
||||
|
||||
virtual void AddLabel(const char *name) = 0;
|
||||
|
||||
virtual void RemoveLabel() = 0;
|
||||
|
||||
CSimpleWatch *AddSimpleWatch(bool trigInput, bool trigOutput);
|
||||
|
||||
CCountWatch *AddCountWatch(bool trigInput, bool trigOutput, unsigned count);
|
||||
|
||||
CMatchWatch *AddMatchWatch(bool trigInput, bool trigOutput, vector<UINT64> &dataSeq);
|
||||
|
||||
CPrintWatch *AddPrintWatch(bool trigInput, bool trigOutput);
|
||||
|
||||
CCaptureWatch *AddCaptureWatch(bool trigInput, bool trigOutput, unsigned maxLen);
|
||||
|
||||
bool RemoveWatch();
|
||||
|
||||
virtual void GetLocation(char *str) = 0;
|
||||
|
||||
virtual UINT64 Read() = 0;
|
||||
|
||||
virtual bool Write(UINT64 data) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents a CPU input/ouput port.
|
||||
*/
|
||||
class CPortIO : public CIO
|
||||
{
|
||||
private:
|
||||
char labelStr[MAX_LABEL_LENGTH + 1];
|
||||
|
||||
public:
|
||||
const UINT16 portNum;
|
||||
|
||||
const char *label;
|
||||
|
||||
CPortIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT16 ioPortNum);
|
||||
|
||||
void AddLabel(const char *pLabel);
|
||||
|
||||
void RemoveLabel();
|
||||
|
||||
void GetLocation(char *str);
|
||||
|
||||
UINT64 Read();
|
||||
|
||||
bool Write(UINT64 data);
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents CPU input/output that is mapped to a memory location.
|
||||
*/
|
||||
class CMappedIO : public CIO, public CAddressRef
|
||||
{
|
||||
public:
|
||||
CMappedIO(CCPUDebug *ioCPU, const char *ioName, const char *ioGroup, unsigned ioDataSize, UINT32 ioAddr);
|
||||
|
||||
bool CheckInput(UINT32 cAddr, unsigned cDataSize, UINT64 data);
|
||||
|
||||
bool CheckOutput(UINT32 cAddr, unsigned cDataSize, UINT64 data);
|
||||
|
||||
void AddLabel(const char *name);
|
||||
|
||||
void RemoveLabel();
|
||||
|
||||
void GetLocation(char *str);
|
||||
|
||||
UINT64 Read();
|
||||
|
||||
bool Write(UINT64 data);
|
||||
};
|
||||
|
||||
//
|
||||
// Inlined methods
|
||||
//
|
||||
|
||||
inline void CIO::RecordInput(UINT64 data)
|
||||
{
|
||||
// Keep track of data coming in
|
||||
lastIn = data;
|
||||
last = &lastIn;
|
||||
}
|
||||
|
||||
inline void CIO::RecordOutput(UINT64 data)
|
||||
{
|
||||
// Keep track of data going out
|
||||
lastOut = data;
|
||||
last = &lastOut;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INCLUDED_IOPORT_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
14
Src/Debugger/Interrupt.cpp
Normal file
14
Src/Debugger/Interrupt.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "Interrupt.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CInterrupt::CInterrupt(CCPUDebug *intCPU, const char *intId, UINT16 intCode, const char *intName) :
|
||||
cpu(intCPU), id(intId), code(intCode), name(intName), trap(false), count(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
30
Src/Debugger/Interrupt.h
Normal file
30
Src/Debugger/Interrupt.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_INTERRUPT_H
|
||||
#define INCLUDED_INTERRUPT_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
|
||||
/*
|
||||
* Class that represents a CPU interrupt (just a specialised CPU exception).
|
||||
*/
|
||||
class CInterrupt
|
||||
{
|
||||
public:
|
||||
CCPUDebug *cpu;
|
||||
const char *id;
|
||||
const UINT16 code;
|
||||
const char *name;
|
||||
|
||||
bool trap;
|
||||
unsigned long count;
|
||||
|
||||
CInterrupt(CCPUDebug *intCPU, const char *intId, UINT16 intCode, const char *intName);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_INTERRUPT_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
45
Src/Debugger/Label.cpp
Normal file
45
Src/Debugger/Label.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "Label.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CLabel::CLabel(CCPUDebug *lCPU, UINT32 lAddr, const char *lName) : CAddressRef(lCPU, lAddr)
|
||||
{
|
||||
strncpy(nameStr, lName, MAX_LABEL_LENGTH);
|
||||
nameStr[MAX_LABEL_LENGTH] = '\0';
|
||||
name = nameStr;
|
||||
}
|
||||
|
||||
CLabel::CLabel(CCPUDebug *lCPU, UINT32 lAddrStart, UINT32 lAddrEnd, const char *lName) :
|
||||
CAddressRef(lCPU, lAddrStart, lAddrEnd - lAddrStart + 1)
|
||||
{
|
||||
strncpy(nameStr, lName, MAX_LABEL_LENGTH);
|
||||
nameStr[MAX_LABEL_LENGTH] = '\0';
|
||||
name = nameStr;
|
||||
}
|
||||
|
||||
CComment::CComment(CCPUDebug *cCPU, UINT32 cAddr, const char *cText) : CAddressRef(cCPU, cAddr)
|
||||
{
|
||||
size_t size = strlen(cText);
|
||||
char *textStr = new char[size + 1];
|
||||
strncpy(textStr, cText, size);
|
||||
textStr[size] = '\0';
|
||||
text = textStr;
|
||||
}
|
||||
|
||||
CComment::~CComment()
|
||||
{
|
||||
delete text;
|
||||
}
|
||||
|
||||
CRegion::CRegion(CCPUDebug *rCPU, UINT32 rAddrStart, UINT32 rAddrEnd, bool rIsCode, bool rIsReadOnly, const char *rName) :
|
||||
CLabel(rCPU, rAddrStart, rAddrEnd, rName), isCode(rIsCode), isReadOnly(rIsReadOnly), prevRegion(NULL), nextRegion(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
60
Src/Debugger/Label.h
Normal file
60
Src/Debugger/Label.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_LABEL_H
|
||||
#define INCLUDED_LABEL_H
|
||||
|
||||
#include "AddressTable.h"
|
||||
#include "Types.h"
|
||||
|
||||
#define MAX_LABEL_LENGTH 255
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
|
||||
/*
|
||||
* Class that represents a custom label added by user to a particular memory location.
|
||||
*/
|
||||
class CLabel : public CAddressRef
|
||||
{
|
||||
private:
|
||||
char nameStr[MAX_LABEL_LENGTH + 1];
|
||||
|
||||
public:
|
||||
const char *name;
|
||||
|
||||
CLabel(CCPUDebug *lCPU, UINT32 lAddr, const char *lName);
|
||||
|
||||
CLabel(CCPUDebug *lCPU, UINT32 lAddrStart, UINT32 lAddrEnd, const char *lName);
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that stores a comment added by a user to a particular memory location.
|
||||
*/
|
||||
class CComment : public CAddressRef
|
||||
{
|
||||
public:
|
||||
const char *text;
|
||||
|
||||
CComment(CCPUDebug *cCPU, UINT32 cAddr, const char *cText);
|
||||
|
||||
~CComment();
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents various memory regions of a CPU, with information about the region.
|
||||
*/
|
||||
class CRegion : public CLabel
|
||||
{
|
||||
public:
|
||||
const bool isCode;
|
||||
const bool isReadOnly;
|
||||
|
||||
CRegion *prevRegion;
|
||||
CRegion *nextRegion;
|
||||
|
||||
CRegion(CCPUDebug *rCPU, UINT32 rAddrStart, UINT32 rAddrEnd, bool rIsCode, bool rIsReadOnly, const char *rName);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_LABEL_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
396
Src/Debugger/Register.cpp
Normal file
396
Src/Debugger/Register.cpp
Normal file
|
@ -0,0 +1,396 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "CPUDebug.h"
|
||||
#include "Debugger.h"
|
||||
#include "Register.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CRegister::CRegister(CCPUDebug *regCPU, const char *regName, const char *regGroup, UINT8 regDataWidth) :
|
||||
cpu(regCPU), name(regName), group(regGroup), dataWidth(regDataWidth)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
double CRegister::GetValueAsFPoint()
|
||||
{
|
||||
return (double)GetValueAsInt();
|
||||
}
|
||||
|
||||
CRegMonitor::CRegMonitor(CRegister *theReg) : reg(theReg)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CPCRegister::CPCRegMonitor::CPCRegMonitor(CPCRegister *theReg) : CRegMonitor(theReg)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CPCRegister::CPCRegMonitor::HasChanged()
|
||||
{
|
||||
return m_prevPC != reg->cpu->pc;
|
||||
}
|
||||
|
||||
void CPCRegister::CPCRegMonitor::GetBeforeValue(char *str)
|
||||
{
|
||||
reg->cpu->FormatAddress(str, m_prevPC);
|
||||
}
|
||||
|
||||
void CPCRegister::CPCRegMonitor::Reset()
|
||||
{
|
||||
m_prevPC = reg->cpu->pc;
|
||||
}
|
||||
|
||||
CPCRegister::CPCRegister(CCPUDebug *cpu, const char *name, const char *group) : CRegister(cpu, name, group, cpu->memBusWidth)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
UINT32 CPCRegister::Get()
|
||||
{
|
||||
return cpu->pc;
|
||||
}
|
||||
|
||||
bool CPCRegister::Set(UINT32 val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPCRegister::GetValue(char *str)
|
||||
{
|
||||
if (cpu->instrCount > 0)
|
||||
cpu->FormatAddress(str, cpu->pc);
|
||||
else
|
||||
{
|
||||
str[0] = '-';
|
||||
str[1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
UINT64 CPCRegister::GetValueAsInt()
|
||||
{
|
||||
return cpu->pc;
|
||||
}
|
||||
|
||||
bool CPCRegister::SetValue(const char *str)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CRegMonitor *CPCRegister::CreateMonitor()
|
||||
{
|
||||
return new CPCRegMonitor(this);
|
||||
}
|
||||
|
||||
CAddrRegister::CAddrRegMonitor::CAddrRegMonitor(CAddrRegister *theReg) : CRegMonitor(theReg)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CAddrRegister::CAddrRegMonitor::HasChanged()
|
||||
{
|
||||
return m_prevAddr != ((CAddrRegister*)reg)->Get();
|
||||
}
|
||||
|
||||
void CAddrRegister::CAddrRegMonitor::GetBeforeValue(char *str)
|
||||
{
|
||||
reg->cpu->FormatAddress(str, m_prevAddr);
|
||||
}
|
||||
|
||||
void CAddrRegister::CAddrRegMonitor::Reset()
|
||||
{
|
||||
m_prevAddr = ((CAddrRegister*)reg)->Get();
|
||||
}
|
||||
|
||||
CAddrRegister::CAddrRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) :
|
||||
CRegister(cpu, name, group, cpu->memBusWidth), m_id(id), m_getFunc(getFunc), m_setFunc(setFunc)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
UINT32 CAddrRegister::Get()
|
||||
{
|
||||
return m_getFunc(cpu, m_id);
|
||||
}
|
||||
|
||||
bool CAddrRegister::Set(UINT32 val)
|
||||
{
|
||||
return m_setFunc(cpu, m_id, val);
|
||||
}
|
||||
|
||||
void CAddrRegister::GetValue(char *str)
|
||||
{
|
||||
cpu->FormatAddress(str, Get());
|
||||
}
|
||||
|
||||
UINT64 CAddrRegister::GetValueAsInt()
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
bool CAddrRegister::SetValue(const char *str)
|
||||
{
|
||||
UINT32 addr;
|
||||
if (!cpu->ParseAddress(str, &addr))
|
||||
return false;
|
||||
return Set(addr);
|
||||
}
|
||||
|
||||
CRegMonitor *CAddrRegister::CreateMonitor()
|
||||
{
|
||||
return new CAddrRegMonitor(this);
|
||||
}
|
||||
|
||||
CIntRegister::CIntRegMonitor::CIntRegMonitor(CIntRegister *theReg) : CRegMonitor(theReg)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CIntRegister::CIntRegMonitor::HasChanged()
|
||||
{
|
||||
return m_prevVal != ((CIntRegister*)reg)->Get();
|
||||
}
|
||||
|
||||
void CIntRegister::CIntRegMonitor::GetBeforeValue(char *str)
|
||||
{
|
||||
reg->cpu->FormatData(str, reg->dataWidth / 8, m_prevVal);
|
||||
}
|
||||
|
||||
void CIntRegister::CIntRegMonitor::Reset()
|
||||
{
|
||||
m_prevVal = ((CIntRegister*)reg)->Get();
|
||||
}
|
||||
|
||||
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc) :
|
||||
CRegister(cpu, name, group, 8), m_id(id),
|
||||
m_get8Func(getFunc), m_set8Func(setFunc), m_get16Func(NULL), m_set16Func(NULL),
|
||||
m_get32Func(NULL), m_set32Func(NULL), m_get64Func(NULL), m_set64Func(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc) :
|
||||
CRegister(cpu, name, group, 16), m_id(id),
|
||||
m_get8Func(NULL), m_set8Func(NULL), m_get16Func(getFunc), m_set16Func(setFunc),
|
||||
m_get32Func(NULL), m_set32Func(NULL), m_get64Func(NULL), m_set64Func(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) :
|
||||
CRegister(cpu, name, group, 32), m_id(id),
|
||||
m_get8Func(NULL), m_set8Func(NULL), m_get16Func(NULL), m_set16Func(NULL),
|
||||
m_get32Func(getFunc), m_set32Func(setFunc), m_get64Func(NULL), m_set64Func(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CIntRegister::CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc) :
|
||||
CRegister(cpu, name, group, 64), m_id(id),
|
||||
m_get8Func(NULL), m_set8Func(NULL), m_get16Func(NULL), m_set16Func(NULL),
|
||||
m_get32Func(NULL), m_set32Func(NULL), m_get64Func(getFunc), m_set64Func(setFunc)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
UINT64 CIntRegister::Get()
|
||||
{
|
||||
switch (dataWidth / 8)
|
||||
{
|
||||
case 1: return (UINT64)m_get8Func(cpu, m_id);
|
||||
case 2: return (UINT64)m_get16Func(cpu, m_id);
|
||||
case 4: return (UINT64)m_get32Func(cpu, m_id);
|
||||
case 8: return m_get64Func(cpu, m_id);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CIntRegister::Set(UINT64 val)
|
||||
{
|
||||
switch (dataWidth / 8)
|
||||
{
|
||||
case 1: return m_set8Func != NULL && m_set8Func(cpu, m_id, (UINT8)val);
|
||||
case 2: return m_set16Func != NULL && m_set16Func(cpu, m_id, (UINT16)val);
|
||||
case 4: return m_set32Func != NULL && m_set32Func(cpu, m_id, (UINT32)val);
|
||||
case 8: return m_set64Func != NULL && m_set64Func(cpu, m_id, val);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CIntRegister::GetValue(char *str)
|
||||
{
|
||||
cpu->FormatData(str, dataWidth / 8, Get());
|
||||
}
|
||||
|
||||
UINT64 CIntRegister::GetValueAsInt()
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
bool CIntRegister::SetValue(const char *str)
|
||||
{
|
||||
UINT64 data;
|
||||
if (!cpu->ParseData(str, dataWidth / 8, &data))
|
||||
return false;
|
||||
return Set(data);
|
||||
}
|
||||
|
||||
CRegMonitor *CIntRegister::CreateMonitor()
|
||||
{
|
||||
return new CIntRegMonitor(this);
|
||||
}
|
||||
|
||||
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc) :
|
||||
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
|
||||
{
|
||||
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
|
||||
}
|
||||
|
||||
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc) :
|
||||
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
|
||||
{
|
||||
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
|
||||
}
|
||||
|
||||
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc) :
|
||||
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
|
||||
{
|
||||
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
|
||||
}
|
||||
|
||||
CStatusRegister::CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc) :
|
||||
CIntRegister(cpu, name, group, id, getFunc, setFunc), numBits(0), offChr('.')
|
||||
{
|
||||
memset(&m_bitChrs, '\0', sizeof(m_bitChrs));
|
||||
}
|
||||
|
||||
char &CStatusRegister::operator[](const unsigned index)
|
||||
{
|
||||
if (index >= dataWidth)
|
||||
exit(1);
|
||||
numBits = max<unsigned>(numBits, index);
|
||||
return m_bitChrs[index];
|
||||
}
|
||||
|
||||
void CStatusRegister::SetBits(const char *bitStr)
|
||||
{
|
||||
// Find end
|
||||
const char *p = bitStr;
|
||||
while (*p != '\0')
|
||||
p++;
|
||||
unsigned i = 0;
|
||||
while (p-- > bitStr && i < dataWidth)
|
||||
{
|
||||
if (*p != offChr)
|
||||
m_bitChrs[i] = *p;
|
||||
i++;
|
||||
}
|
||||
numBits = i;
|
||||
}
|
||||
|
||||
void CStatusRegister::GetValue(char *str)
|
||||
{
|
||||
UINT64 val = Get();
|
||||
char *p = str + numBits;
|
||||
*p-- = '\0';
|
||||
for (unsigned i = 0; i < numBits; i++)
|
||||
{
|
||||
*p-- = (val&0x01 ? m_bitChrs[i] : offChr);
|
||||
val >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool CStatusRegister::SetValue(const char *str)
|
||||
{
|
||||
UINT64 val = 0;
|
||||
while (*str != '\0')
|
||||
{
|
||||
if (*str != offChr)
|
||||
{
|
||||
for (unsigned i = 0; i < numBits; i++)
|
||||
{
|
||||
if (m_bitChrs[i] == *str)
|
||||
{
|
||||
val |= 1LL<<i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
str++;
|
||||
}
|
||||
return Set(val);
|
||||
}
|
||||
|
||||
CFPointRegister::CFPointRegMonitor::CFPointRegMonitor(CFPointRegister *theReg) : CRegMonitor(theReg)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CFPointRegister::CFPointRegMonitor::HasChanged()
|
||||
{
|
||||
return m_prevVal != ((CFPointRegister*)reg)->Get();
|
||||
}
|
||||
|
||||
void CFPointRegister::CFPointRegMonitor::GetBeforeValue(char *str)
|
||||
{
|
||||
sprintf(str, "%f", m_prevVal);
|
||||
}
|
||||
|
||||
void CFPointRegister::CFPointRegMonitor::Reset()
|
||||
{
|
||||
m_prevVal = ((CFPointRegister*)reg)->Get();
|
||||
}
|
||||
|
||||
CFPointRegister::CFPointRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc) :
|
||||
CRegister(cpu, name, group, 8), m_id(id), m_getFunc(getFunc), m_setFunc(setFunc)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
double CFPointRegister::Get()
|
||||
{
|
||||
return m_getFunc(cpu, m_id);
|
||||
}
|
||||
|
||||
bool CFPointRegister::Set(double val)
|
||||
{
|
||||
if (m_setFunc == NULL)
|
||||
return false;
|
||||
m_setFunc(cpu, m_id, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFPointRegister::GetValue(char *str)
|
||||
{
|
||||
sprintf(str, "%lf", Get());
|
||||
}
|
||||
|
||||
UINT64 CFPointRegister::GetValueAsInt()
|
||||
{
|
||||
return (UINT64)Get();
|
||||
}
|
||||
|
||||
double CFPointRegister::GetValueAsFPoint()
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
bool CFPointRegister::SetValue(const char *str)
|
||||
{
|
||||
double val;
|
||||
if (sscanf(str, "%lf", &val) != 1)
|
||||
return false;
|
||||
return Set(val);
|
||||
}
|
||||
|
||||
CRegMonitor *CFPointRegister::CreateMonitor()
|
||||
{
|
||||
return new CFPointRegMonitor(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
272
Src/Debugger/Register.h
Normal file
272
Src/Debugger/Register.h
Normal file
|
@ -0,0 +1,272 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_REGISTER_H
|
||||
#define INCLUDED_REGISTER_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
class CRegMonitor;
|
||||
|
||||
typedef UINT8 (*GetInt8FPtr)(CCPUDebug*, unsigned);
|
||||
typedef bool (*SetInt8FPtr)(CCPUDebug*, unsigned, UINT8);
|
||||
typedef UINT16 (*GetInt16FPtr)(CCPUDebug*, unsigned);
|
||||
typedef bool (*SetInt16FPtr)(CCPUDebug*, unsigned, UINT16);
|
||||
typedef UINT32 (*GetInt32FPtr)(CCPUDebug*, unsigned);
|
||||
typedef bool (*SetInt32FPtr)(CCPUDebug*, unsigned, UINT32);
|
||||
typedef UINT64 (*GetInt64FPtr)(CCPUDebug*, unsigned);
|
||||
typedef bool (*SetInt64FPtr)(CCPUDebug*, unsigned, UINT64);
|
||||
typedef double (*GetFPointFPtr)(CCPUDebug*, unsigned);
|
||||
typedef bool (*SetFPointFPtr)(CCPUDebug*, unsigned, double);
|
||||
|
||||
/*
|
||||
* Base class for a CPU register.
|
||||
*/
|
||||
class CRegister
|
||||
{
|
||||
protected:
|
||||
CRegister(CCPUDebug *regCPU, const char *regName, const char *regGroup, UINT8 regDataWidth);
|
||||
|
||||
public:
|
||||
CCPUDebug *cpu;
|
||||
const char *name;
|
||||
const char *group;
|
||||
const UINT8 dataWidth;
|
||||
|
||||
virtual void GetValue(char *str) = 0;
|
||||
|
||||
virtual UINT64 GetValueAsInt() = 0;
|
||||
|
||||
virtual double GetValueAsFPoint();
|
||||
|
||||
virtual bool SetValue(const char *str) = 0;
|
||||
|
||||
virtual CRegMonitor *CreateMonitor() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that monitors a register to see if its value has changed.
|
||||
*/
|
||||
class CRegMonitor
|
||||
{
|
||||
protected:
|
||||
CRegMonitor(CRegister *theReg);
|
||||
|
||||
public:
|
||||
CRegister *reg;
|
||||
|
||||
virtual bool HasChanged() = 0;
|
||||
|
||||
virtual void GetBeforeValue(char *str) = 0;
|
||||
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents the program counter of a CPU.
|
||||
*/
|
||||
class CPCRegister : public CRegister
|
||||
{
|
||||
private:
|
||||
class CPCRegMonitor : public CRegMonitor
|
||||
{
|
||||
private:
|
||||
UINT32 m_prevPC;
|
||||
|
||||
public:
|
||||
CPCRegMonitor(CPCRegister *theReg);
|
||||
|
||||
bool HasChanged();
|
||||
|
||||
void GetBeforeValue(char *str);
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
public:
|
||||
CPCRegister(CCPUDebug *cpu, const char *name, const char *group);
|
||||
|
||||
UINT32 Get();
|
||||
|
||||
bool Set(UINT32 val);
|
||||
|
||||
virtual void GetValue(char *str);
|
||||
|
||||
UINT64 GetValueAsInt();
|
||||
|
||||
virtual bool SetValue(const char *str);
|
||||
|
||||
CRegMonitor *CreateMonitor();
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents a CPU address register.
|
||||
* TODO - formatting as per CPCRegister?
|
||||
*/
|
||||
class CAddrRegister : public CRegister
|
||||
{
|
||||
private:
|
||||
unsigned m_id;
|
||||
GetInt32FPtr m_getFunc;
|
||||
SetInt32FPtr m_setFunc;
|
||||
|
||||
class CAddrRegMonitor : public CRegMonitor
|
||||
{
|
||||
private:
|
||||
UINT32 m_prevAddr;
|
||||
|
||||
public:
|
||||
CAddrRegMonitor(CAddrRegister *theReg);
|
||||
|
||||
bool HasChanged();
|
||||
|
||||
void GetBeforeValue(char *str);
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
public:
|
||||
CAddrRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);
|
||||
|
||||
UINT32 Get();
|
||||
|
||||
bool Set(UINT32 val);
|
||||
|
||||
virtual void GetValue(char *str);
|
||||
|
||||
UINT64 GetValueAsInt();
|
||||
|
||||
virtual bool SetValue(const char *str);
|
||||
|
||||
CRegMonitor *CreateMonitor();
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents a general purpose integer CPU register.
|
||||
*/
|
||||
class CIntRegister : public CRegister
|
||||
{
|
||||
private:
|
||||
unsigned m_id;
|
||||
GetInt8FPtr m_get8Func;
|
||||
SetInt8FPtr m_set8Func;
|
||||
GetInt16FPtr m_get16Func;
|
||||
SetInt16FPtr m_set16Func;
|
||||
GetInt32FPtr m_get32Func;
|
||||
SetInt32FPtr m_set32Func;
|
||||
GetInt64FPtr m_get64Func;
|
||||
SetInt64FPtr m_set64Func;
|
||||
|
||||
class CIntRegMonitor : public CRegMonitor
|
||||
{
|
||||
private:
|
||||
UINT64 m_prevVal;
|
||||
|
||||
public:
|
||||
CIntRegMonitor(CIntRegister *theReg);
|
||||
|
||||
bool HasChanged();
|
||||
|
||||
void GetBeforeValue(char *str);
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
public:
|
||||
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc);
|
||||
|
||||
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc);
|
||||
|
||||
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc);
|
||||
|
||||
CIntRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc);
|
||||
|
||||
UINT64 Get();
|
||||
|
||||
bool Set(UINT64 val);
|
||||
|
||||
virtual void GetValue(char *str);
|
||||
|
||||
UINT64 GetValueAsInt();
|
||||
|
||||
virtual bool SetValue(const char *str);
|
||||
|
||||
CRegMonitor *CreateMonitor();
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents a CPU status register that contains bit flags.
|
||||
*/
|
||||
class CStatusRegister : public CIntRegister
|
||||
{
|
||||
private:
|
||||
char m_bitChrs[64];
|
||||
|
||||
public:
|
||||
unsigned numBits;
|
||||
char offChr;
|
||||
|
||||
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc);
|
||||
|
||||
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc);
|
||||
|
||||
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc);
|
||||
|
||||
CStatusRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc);
|
||||
|
||||
char &operator[](const unsigned index);
|
||||
|
||||
void SetBits(const char *bitStr);
|
||||
|
||||
void GetValue(char *str);
|
||||
|
||||
bool SetValue(const char *str);
|
||||
};
|
||||
|
||||
/*
|
||||
* Class that represents a floating-point CPU register.
|
||||
*/
|
||||
class CFPointRegister : public CRegister
|
||||
{
|
||||
private:
|
||||
unsigned m_id;
|
||||
GetFPointFPtr m_getFunc;
|
||||
SetFPointFPtr m_setFunc;
|
||||
|
||||
class CFPointRegMonitor : public CRegMonitor
|
||||
{
|
||||
private:
|
||||
double m_prevVal;
|
||||
|
||||
public:
|
||||
CFPointRegMonitor(CFPointRegister *theReg);
|
||||
|
||||
bool HasChanged();
|
||||
|
||||
void GetBeforeValue(char *str);
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
public:
|
||||
CFPointRegister(CCPUDebug *cpu, const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc);
|
||||
|
||||
double Get();
|
||||
|
||||
bool Set(double val);
|
||||
|
||||
void GetValue(char *str);
|
||||
|
||||
UINT64 GetValueAsInt();
|
||||
|
||||
double GetValueAsFPoint();
|
||||
|
||||
bool SetValue(const char *str);
|
||||
|
||||
CRegMonitor *CreateMonitor();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_REGISTER_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
298
Src/Debugger/SupermodelDebugger.cpp
Normal file
298
Src/Debugger/SupermodelDebugger.cpp
Normal file
|
@ -0,0 +1,298 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "Supermodel.h"
|
||||
|
||||
#include "ConsoleDebugger.h"
|
||||
#include "CPUDebug.h"
|
||||
#include "Label.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
CSupermodelDebugger::CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger) :
|
||||
CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger)
|
||||
{
|
||||
AddCPU(new CPPCDebug());
|
||||
#ifdef SUPERMODEL_SOUND
|
||||
AddCPU(new C68KDebug());
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::WaitCommand(CCPUDebug *cpu)
|
||||
{
|
||||
m_inputs->GetInputSystem()->UngrabMouse();
|
||||
|
||||
CConsoleDebugger::WaitCommand(cpu);
|
||||
|
||||
m_inputs->GetInputSystem()->GrabMouse();
|
||||
}
|
||||
|
||||
bool CSupermodelDebugger::ProcessToken(const char *token, const char *cmd)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else if (CheckToken(token, "lip", "listinputs")) // listinputs
|
||||
{
|
||||
ListInputs();
|
||||
return false;
|
||||
}
|
||||
else if (CheckToken(token, "pip", "printinput")) // printinput NAME
|
||||
{
|
||||
// Parse arguments
|
||||
token = strtok(NULL, " ");
|
||||
if (token == NULL)
|
||||
{
|
||||
puts("Mising input name.");
|
||||
return false;
|
||||
}
|
||||
::CInput *input = (*m_inputs)[token];
|
||||
if (input == NULL)
|
||||
{
|
||||
printf("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);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!input->IsVirtual())
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
printf("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
|
||||
{
|
||||
// Parse arguments
|
||||
token = strtok(NULL, " ");
|
||||
if (token == NULL)
|
||||
{
|
||||
puts("Mising input name.");
|
||||
return false;
|
||||
}
|
||||
::CInput *input = (*m_inputs)[token];
|
||||
if (input == NULL)
|
||||
{
|
||||
printf("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);
|
||||
return false;
|
||||
}
|
||||
token = strtok(NULL, " ");
|
||||
if (token == NULL)
|
||||
{
|
||||
puts("Missing mapping to set.");
|
||||
return false;
|
||||
}
|
||||
|
||||
input->SetMapping(token);
|
||||
|
||||
printf("Set input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
|
||||
return false;
|
||||
}
|
||||
else if (CheckToken(token, "cip", "configinput")) // configinput NAME
|
||||
{
|
||||
//// Parse arguments
|
||||
//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();
|
||||
//}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return CConsoleDebugger::ProcessToken(token, cmd);
|
||||
}
|
||||
|
||||
bool CSupermodelDebugger::InputIsValid(::CInput *input)
|
||||
{
|
||||
return input->IsUIInput() || (input->gameFlags & m_model3->GetGameInfo()->inputFlags);
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::ListInputs()
|
||||
{
|
||||
puts("Inputs:");
|
||||
if (m_inputs->Count() == 0)
|
||||
puts(" None");
|
||||
|
||||
// Get maximum id, label and mapping widths
|
||||
size_t idAndLabelWidth = 0;
|
||||
size_t mappingWidth = 0;
|
||||
for (unsigned i = 0; i < m_inputs->Count(); i++)
|
||||
{
|
||||
::CInput *input = (*m_inputs)[i];
|
||||
if (!InputIsValid(input))
|
||||
continue;
|
||||
|
||||
idAndLabelWidth = max<size_t>(idAndLabelWidth, strlen(input->id) + strlen(input->label) + 3);
|
||||
if (!input->IsVirtual())
|
||||
mappingWidth = max<size_t>(mappingWidth, strlen(input->GetMapping()));
|
||||
}
|
||||
mappingWidth = min<size_t>(mappingWidth, 20);
|
||||
|
||||
// Print labels, mappings and values for each input
|
||||
const char *groupLabel = NULL;
|
||||
char idAndLabel[255];
|
||||
char mapping[21];
|
||||
for (unsigned i = 0; i < m_inputs->Count(); i++)
|
||||
{
|
||||
::CInput *input = (*m_inputs)[i];
|
||||
if (!InputIsValid(input))
|
||||
continue;
|
||||
|
||||
if (groupLabel == NULL || stricmp(groupLabel, input->GetInputGroup()) != 0)
|
||||
{
|
||||
groupLabel = input->GetInputGroup();
|
||||
printf(" %s:\n", groupLabel);
|
||||
}
|
||||
|
||||
sprintf(idAndLabel, "%s (%s)", input->id, input->label);
|
||||
printf(" %-*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);
|
||||
}
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::Attached()
|
||||
{
|
||||
CConsoleDebugger::Attached();
|
||||
|
||||
char fileName[25];
|
||||
sprintf(fileName, "Debug/%s.ds", m_model3->GetGameInfo()->id);
|
||||
LoadState(fileName);
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::Detaching()
|
||||
{
|
||||
char fileName[25];
|
||||
sprintf(fileName, "Debug/%s.ds", m_model3->GetGameInfo()->id);
|
||||
SaveState(fileName);
|
||||
|
||||
CConsoleDebugger::Detaching();
|
||||
}
|
||||
|
||||
bool CSupermodelDebugger::LoadModel3State(const char *fileName)
|
||||
{
|
||||
// Open file and find header
|
||||
CBlockFile state;
|
||||
if (state.Load(fileName) != OKAY)
|
||||
return false;
|
||||
if (state.FindBlock("Debugger Model3 State") != OKAY)
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check version in header matches
|
||||
unsigned version;
|
||||
state.Read(&version, sizeof(version));
|
||||
if (version != MODEL3_STATEFILE_VERSION)
|
||||
{
|
||||
state.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load Model3 state
|
||||
m_model3->LoadState(&state);
|
||||
state.Close();
|
||||
|
||||
// Reset debugger
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSupermodelDebugger::SaveModel3State(const char *fileName)
|
||||
{
|
||||
// Create file with header
|
||||
CBlockFile state;
|
||||
if (state.Create(fileName, "Debugger Model3 State", __FILE__) != OKAY)
|
||||
return false;
|
||||
|
||||
// Write out version in header
|
||||
unsigned version = MODEL3_STATEFILE_VERSION;
|
||||
state.Write(&version, sizeof(version));
|
||||
|
||||
// Save Model3 state
|
||||
m_model3->SaveState(&state);
|
||||
state.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::DebugLog(const char *fmt, va_list vl)
|
||||
{
|
||||
// Use the supplied logger, if any
|
||||
if (m_logger != NULL)
|
||||
m_logger->DebugLog(fmt, vl);
|
||||
else
|
||||
CConsoleDebugger::DebugLog(fmt, vl);
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::InfoLog(const char *fmt, va_list vl)
|
||||
{
|
||||
// Use the supplied logger, if any
|
||||
if (m_logger != NULL)
|
||||
m_logger->InfoLog(fmt, vl);
|
||||
else
|
||||
CConsoleDebugger::InfoLog(fmt, vl);
|
||||
}
|
||||
|
||||
void CSupermodelDebugger::ErrorLog(const char *fmt, va_list vl)
|
||||
{
|
||||
// Use the supplied logger, if any
|
||||
if (m_logger != NULL)
|
||||
m_logger->ErrorLog(fmt, vl);
|
||||
else
|
||||
CConsoleDebugger::ErrorLog(fmt, vl);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
58
Src/Debugger/SupermodelDebugger.h
Normal file
58
Src/Debugger/SupermodelDebugger.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_SUPERMODELDEBUGGER_H
|
||||
#define INCLUDED_SUPERMODELDEBUGGER_H
|
||||
|
||||
#include "ConsoleDebugger.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define MODEL3_STATEFILE_VERSION 0
|
||||
|
||||
class CModel3;
|
||||
class CInputs;
|
||||
class CInput;
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
|
||||
/*
|
||||
* Console-based debugger used by Supermodel.
|
||||
*/
|
||||
class CSupermodelDebugger : public CConsoleDebugger
|
||||
{
|
||||
private:
|
||||
::CModel3 *m_model3;
|
||||
::CInputs *m_inputs;
|
||||
::CLogger *m_logger;
|
||||
|
||||
bool InputIsValid(CInput *input);
|
||||
|
||||
void ListInputs();
|
||||
|
||||
protected:
|
||||
void WaitCommand(CCPUDebug *cpu);
|
||||
|
||||
bool ProcessToken(const char *token, const char *cmd);
|
||||
|
||||
public:
|
||||
CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger);
|
||||
|
||||
void Attached();
|
||||
|
||||
void Detaching();
|
||||
|
||||
bool LoadModel3State(const char *fileName);
|
||||
|
||||
bool SaveModel3State(const char *fileName);
|
||||
|
||||
void DebugLog(const char *fmt, va_list vl);
|
||||
|
||||
void InfoLog(const char *fmt, va_list vl);
|
||||
|
||||
void ErrorLog(const char *fmt, va_list vl);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_SUPERMODELDEBUGGER_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
281
Src/Debugger/Watch.cpp
Normal file
281
Src/Debugger/Watch.cpp
Normal file
|
@ -0,0 +1,281 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
#include "CPUDebug.h"
|
||||
#include "Debugger.h"
|
||||
#include "IO.h"
|
||||
#include "Watch.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
UINT32 CWatch::GetIOAddress(CIO *io)
|
||||
{
|
||||
CMappedIO *mappedIO = dynamic_cast<CMappedIO*>(io);
|
||||
return (mappedIO != NULL ? mappedIO->addr : 0);
|
||||
}
|
||||
|
||||
CWatch::CWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, char wSymbol, const char *wType, bool wTrigRead, bool wTrigWrite) :
|
||||
CAddressRef(wCPU, wAddr, wSize), io(NULL), symbol(wSymbol), type(wType), trigRead(wTrigRead), trigWrite(wTrigWrite), active(true),
|
||||
readCount(0), writeCount(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CWatch::CWatch(CCPUDebug *wCPU, CIO *wIO, char wSymbol, const char *wType, bool wTrigInput, bool mTrigOutput) :
|
||||
CAddressRef(wCPU, GetIOAddress(wIO), wIO->dataSize), io(wIO), symbol(wSymbol), type(wType), trigRead(wTrigInput), trigWrite(mTrigOutput), active(true),
|
||||
readCount(0), writeCount(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CWatch::HasValue()
|
||||
{
|
||||
if (io != NULL)
|
||||
return io->inCount + io->outCount > 0;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
UINT64 CWatch::GetValue()
|
||||
{
|
||||
if (io != NULL)
|
||||
return *io->last;
|
||||
else
|
||||
return cpu->ReadMem(addr, size);
|
||||
}
|
||||
|
||||
bool CWatch::CheckRead(UINT32 cAddr, unsigned cDataSize, UINT64 data)
|
||||
{
|
||||
if (!active || !trigRead)
|
||||
return false;
|
||||
// Take care with read that was not aligned with watch address and size
|
||||
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, size);
|
||||
return CheckBreak(true, sData);
|
||||
}
|
||||
|
||||
bool CWatch::CheckWrite(UINT32 cAddr, unsigned cDataSize, UINT64 data)
|
||||
{
|
||||
if (!active || !trigWrite)
|
||||
return false;
|
||||
// Take care with write that was not aligned with watch address and size
|
||||
UINT64 sData = CDebugger::GetSlottedData(cAddr, cDataSize, data, addr, size);
|
||||
return CheckBreak(false, sData);
|
||||
}
|
||||
|
||||
bool CWatch::CheckInput(CIO *cIO, UINT64 data)
|
||||
{
|
||||
// No need to align data as already aligned by CIO
|
||||
return active && trigRead && CheckBreak(true, data);
|
||||
}
|
||||
|
||||
bool CWatch::CheckOutput(CIO *cIO, UINT64 data)
|
||||
{
|
||||
// No need to align data as already aligned by CIO
|
||||
return active && trigWrite && CheckBreak(false, data);
|
||||
}
|
||||
|
||||
void CWatch::Reset()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CWatch::GetInfo(char *str)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CSimpleWatch::CSimpleWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite) :
|
||||
CWatch(wCPU, wAddr, wSize, '*', "simple", wTrigRead, wTrigWrite)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CSimpleWatch::CSimpleWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput) :
|
||||
CWatch(wCPU, wIO, '*', "simple", wTrigInput, mTrigOutput)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
bool CSimpleWatch::CheckBreak(bool isRead, UINT64 data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CCountWatch::CCountWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned count) :
|
||||
CWatch(wCPU, wAddr, wSize, '+', "count", wTrigRead, wTrigWrite), m_count(count), m_counter(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CCountWatch::CCountWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput, unsigned count) :
|
||||
CWatch(wCPU, wIO, '+', "count", wTrigInput, mTrigOutput), m_count(count), m_counter(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CCountWatch::CheckBreak(bool isRead, UINT64 data)
|
||||
{
|
||||
return ++m_counter == m_count;
|
||||
}
|
||||
|
||||
void CCountWatch::Reset()
|
||||
{
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
bool CCountWatch::GetInfo(char *str)
|
||||
{
|
||||
sprintf(str, "%d / %d", m_counter, m_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
CMatchWatch::CMatchWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, vector<UINT64> &dataSeq) :
|
||||
CWatch(wCPU, wAddr, wSize, 'M', "match", wTrigRead, wTrigWrite), m_dataLen(dataSeq.size()), m_counter(0)
|
||||
{
|
||||
m_dataArray = new UINT64[m_dataLen];
|
||||
copy(dataSeq.begin(), dataSeq.end(), m_dataArray);
|
||||
}
|
||||
|
||||
CMatchWatch::CMatchWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput, vector<UINT64> &dataSeq) :
|
||||
CWatch(wCPU, wIO, 'M', "match", wTrigInput, mTrigOutput), m_dataLen(dataSeq.size()), m_counter(0)
|
||||
{
|
||||
m_dataArray = new UINT64[m_dataLen];
|
||||
copy(dataSeq.begin(), dataSeq.end(), m_dataArray);
|
||||
}
|
||||
|
||||
CMatchWatch::~CMatchWatch()
|
||||
{
|
||||
delete m_dataArray;
|
||||
}
|
||||
|
||||
bool CMatchWatch::CheckBreak(bool isRead, UINT64 data)
|
||||
{
|
||||
if (m_dataArray[m_counter] == data)
|
||||
m_counter++;
|
||||
else
|
||||
m_counter = 0;
|
||||
return m_counter == m_dataLen;
|
||||
}
|
||||
|
||||
void CMatchWatch::Reset()
|
||||
{
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
bool CMatchWatch::GetInfo(char *str)
|
||||
{
|
||||
sprintf(str, "%d / %d matched", m_counter, m_dataLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
CCaptureWatch::CCaptureWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned wMaxLen) :
|
||||
CWatch(wCPU, wAddr, wSize, 'C', "capture", wTrigRead, wTrigWrite), m_maxLen(min<unsigned>(255, wMaxLen)), m_len(0), m_index(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CCaptureWatch::CCaptureWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool mTrigOutput, unsigned wMaxLen) :
|
||||
CWatch(wCPU, wIO, 'C', "capture", wTrigInput, mTrigOutput), m_maxLen(min<unsigned>(255, wMaxLen)), m_len(0), m_index(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CCaptureWatch::CheckBreak(bool isRead, UINT64 data)
|
||||
{
|
||||
if (m_len < m_maxLen)
|
||||
{
|
||||
m_dataVals[m_len] = data;
|
||||
m_dataRW[m_len] = isRead;
|
||||
m_len++;
|
||||
m_index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dataVals[m_index] = data;
|
||||
m_dataRW[m_index] = isRead;
|
||||
m_index++;
|
||||
if (m_index >= m_maxLen)
|
||||
m_index = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCaptureWatch::Reset()
|
||||
{
|
||||
m_len = 0;
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
bool CCaptureWatch::GetInfo(char *str)
|
||||
{
|
||||
char dataStr[25];
|
||||
unsigned ind, len;
|
||||
str[0] = '\0';
|
||||
if (m_len == 0)
|
||||
return true;
|
||||
else if (m_len < m_maxLen)
|
||||
{
|
||||
ind = m_len - 1;
|
||||
len = m_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_index == 0)
|
||||
ind = m_maxLen - 1;
|
||||
else
|
||||
ind = m_index - 1;
|
||||
len = m_maxLen;
|
||||
}
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
{
|
||||
UINT64 data = m_dataVals[ind];
|
||||
bool isRead = m_dataRW[ind];
|
||||
cpu->FormatData(dataStr, size, data);
|
||||
if (i > 0)
|
||||
strcat(str, " ");
|
||||
strcat(str, (isRead ? "<" : ">"));
|
||||
strcat(str, dataStr);
|
||||
if (ind == 0)
|
||||
ind = m_maxLen - 1;
|
||||
else
|
||||
ind--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CPrintWatch::CPrintWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite) :
|
||||
CWatch(wCPU, wAddr, wSize, 'P', "print", wTrigRead, wTrigWrite)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CPrintWatch::CPrintWatch(CCPUDebug *wCPU, CIO *io, bool wTrigInput, bool mTrigOutput) :
|
||||
CWatch(wCPU, io, 'P', "print", wTrigInput, mTrigOutput)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
bool CPrintWatch::CheckBreak(bool isRead, UINT64 data)
|
||||
{
|
||||
const char *sizeStr = CDebugger::GetSizeString(size);
|
||||
char dataStr[50];
|
||||
char addrStr[50];
|
||||
char locStr[50];
|
||||
cpu->FormatData(dataStr, size, data);
|
||||
cpu->FormatAddress(addrStr, addr, true);
|
||||
const char *rwStr = (isRead ? "Read" : "Wrote");
|
||||
const char *tfStr = (isRead ? "from" : "to");
|
||||
if (io != NULL)
|
||||
{
|
||||
io->GetLocation(locStr);
|
||||
cpu->debugger->WriteOut(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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SUPERMODEL_DEBUGGER
|
157
Src/Debugger/Watch.h
Normal file
157
Src/Debugger/Watch.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
#ifdef SUPERMODEL_DEBUGGER
|
||||
#ifndef INCLUDED_WATCH_H
|
||||
#define INCLUDED_WATCH_H
|
||||
|
||||
#include "AddressTable.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
namespace Debugger
|
||||
{
|
||||
class CCPUDebug;
|
||||
class CIOPort;
|
||||
|
||||
/*
|
||||
* Base class for a memory/IO watch. All watches keep track of the last read/write to the memory location or I/O port.
|
||||
*/
|
||||
class CWatch : public CAddressRef
|
||||
{
|
||||
private:
|
||||
static UINT32 GetIOAddress(CIO *io);
|
||||
|
||||
protected:
|
||||
CWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, char wSymbol, const char *wType, bool wTrigRead, bool wTrigWrite);
|
||||
|
||||
CWatch(CCPUDebug *wCPU, CIO *wIO, char wSymbol, const char *wType, bool wTrigInput, bool wTrigOutput);
|
||||
|
||||
public:
|
||||
CIO *io;
|
||||
const char symbol;
|
||||
const char *type;
|
||||
bool trigRead;
|
||||
bool trigWrite;
|
||||
|
||||
bool active;
|
||||
|
||||
unsigned readCount;
|
||||
unsigned writeCount;
|
||||
|
||||
bool HasValue();
|
||||
|
||||
UINT64 GetValue();
|
||||
|
||||
bool CheckRead(UINT32 cAddr, unsigned cDataSize, UINT64 data);
|
||||
|
||||
bool CheckWrite(UINT32 cAddr, unsigned cDataSize, UINT64 data);
|
||||
|
||||
bool CheckInput(CIO *cIO, UINT64 data);
|
||||
|
||||
bool CheckOutput(CIO *cIO, UINT64 data);
|
||||
|
||||
virtual bool CheckBreak(bool isRead, UINT64 data) = 0;
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual bool GetInfo(char *str);
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple watch that will always halt execution when read from and/or written to.
|
||||
*/
|
||||
class CSimpleWatch : public CWatch
|
||||
{
|
||||
public:
|
||||
CSimpleWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite);
|
||||
|
||||
CSimpleWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput);
|
||||
|
||||
bool CheckBreak(bool isRead, UINT64 data);
|
||||
};
|
||||
|
||||
/*
|
||||
* Count watch that will halt execution after it has been read from and/or written to a certain number of times.
|
||||
*/
|
||||
class CCountWatch : public CWatch
|
||||
{
|
||||
private:
|
||||
unsigned m_count;
|
||||
unsigned m_counter;
|
||||
|
||||
public:
|
||||
CCountWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned count);
|
||||
|
||||
CCountWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput, unsigned count);
|
||||
|
||||
bool CheckBreak(bool isRead, UINT64 data);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool GetInfo(char *str);
|
||||
};
|
||||
|
||||
/*
|
||||
* Match watch that will halt execution after a particular data sequence has been read from and/or written to it.
|
||||
*/
|
||||
class CMatchWatch : public CWatch
|
||||
{
|
||||
private:
|
||||
unsigned m_dataLen;
|
||||
UINT64 *m_dataArray;
|
||||
unsigned m_counter;
|
||||
|
||||
public:
|
||||
CMatchWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, vector<UINT64> &dataSeq);
|
||||
|
||||
CMatchWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput, vector<UINT64> &dataSeq);
|
||||
|
||||
~CMatchWatch();
|
||||
|
||||
bool CheckBreak(bool isRead, UINT64 data);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool GetInfo(char *str);
|
||||
};
|
||||
|
||||
/*
|
||||
* Capture watch that will halt not execution but will keep track of all data read from and/or written to it.
|
||||
*/
|
||||
class CCaptureWatch : public CWatch
|
||||
{
|
||||
private:
|
||||
unsigned m_maxLen;
|
||||
unsigned m_len;
|
||||
unsigned m_index;
|
||||
UINT64 m_dataVals[255];
|
||||
bool m_dataRW[255];
|
||||
|
||||
public:
|
||||
CCaptureWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite, unsigned wMaxLen);
|
||||
|
||||
CCaptureWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput, unsigned wMaxLen);
|
||||
|
||||
bool CheckBreak(bool isRead, UINT64 data);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool GetInfo(char *str);
|
||||
};
|
||||
|
||||
/*
|
||||
* Print watch that will halt not execution but will output all data read from and/or written to it.
|
||||
*/
|
||||
class CPrintWatch : public CWatch
|
||||
{
|
||||
public:
|
||||
CPrintWatch(CCPUDebug *wCPU, UINT32 wAddr, unsigned wSize, bool wTrigRead, bool wTrigWrite);
|
||||
|
||||
CPrintWatch(CCPUDebug *wCPU, CIO *wIO, bool wTrigInput, bool wTrigOutput);
|
||||
|
||||
bool CheckBreak(bool isRead, UINT64 data);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_WATCH_H
|
||||
#endif // SUPERMODEL_DEBUGGER
|
Loading…
Reference in a new issue