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