New debugger classes

This commit is contained in:
Nik Henson 2011-06-27 22:55:03 +00:00
parent 187f0016fb
commit eddc1df03a
26 changed files with 8675 additions and 0 deletions

View 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
View 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

View 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
View 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

File diff suppressed because it is too large Load diff

910
Src/Debugger/CPUDebug.h Normal file
View 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

View 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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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

View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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
View 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