mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 22:05:38 +00:00
1650 lines
41 KiB
C++
1650 lines
41 KiB
C++
/**
|
|
** Supermodel
|
|
** A Sega Model 3 Arcade Emulator.
|
|
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
|
**
|
|
** This file is part of Supermodel.
|
|
**
|
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
|
** the terms of the GNU General Public License as published by the Free
|
|
** Software Foundation, either version 3 of the License, or (at your option)
|
|
** any later version.
|
|
**
|
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
** more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License along
|
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
/*
|
|
* CPUDebug.cpp
|
|
*/
|
|
|
|
#ifdef SUPERMODEL_DEBUGGER
|
|
|
|
#include "CPUDebug.h"
|
|
#include "CodeAnalyser.h"
|
|
#include "Label.h"
|
|
|
|
#include <cctype>
|
|
#include <string>
|
|
|
|
using namespace std;
|
|
|
|
namespace Debugger
|
|
{
|
|
CCPUDebug::CCPUDebug(const char *cpuType, const char *cpuName,
|
|
UINT8 cpuMinInstrLen, UINT8 cpuMaxInstrLen, bool cpuBigEndian, UINT8 cpuMemBusWidth, UINT8 cpuMaxMnemLen) :
|
|
type(cpuType), name(cpuName), minInstrLen(cpuMinInstrLen), maxInstrLen(cpuMaxInstrLen),
|
|
bigEndian(cpuBigEndian), memBusWidth(cpuMemBusWidth), maxMnemLen(cpuMaxMnemLen),
|
|
addrFmt(HexDollar), portFmt(Decimal), dataFmt(HexDollar), debugger(NULL),
|
|
numExCodes(0), numIntCodes(0), numPorts(0), memSize(0), active(false), instrCount(0), totalCycles(0), cyclesPerPoll(0), pc(0), opcode(0),
|
|
m_enabled(true), m_break(false), m_breakUser(false), m_halted(false), m_step(false), m_steppingOver(false), m_steppingOut(false),
|
|
m_count(0), m_until(false), m_untilAddr(0),
|
|
m_mappedIOTable(NULL), m_memWatchTable(NULL), m_bpTable(NULL), m_numRegMons(0), m_regMonArray(NULL),
|
|
m_analyser(NULL), m_stateUpdated(false), m_exRaised(NULL), m_exTrapped(NULL), m_intRaised(NULL), m_intTrapped(NULL), m_bpReached(NULL),
|
|
m_memWatchTriggered(NULL), m_ioWatchTriggered(NULL), m_regMonTriggered(NULL), m_prevTotalCycles(0)
|
|
{
|
|
memset(m_exArray, NULL, sizeof(m_exArray));
|
|
memset(m_intArray, NULL, sizeof(m_intArray));
|
|
memset(m_portArray, NULL, sizeof(m_portArray));
|
|
|
|
#ifdef DEBUGGER_HASTHREAD
|
|
m_breakWait = false;
|
|
m_mutex = CThread::CreateMutex();
|
|
m_condVar = CThread::CreateCondVar();
|
|
#endif // DEBUGGER_HASTHREAD
|
|
}
|
|
|
|
CCPUDebug::~CCPUDebug()
|
|
{
|
|
// Delete registers
|
|
for (vector<CRegister*>::iterator it = regs.begin(); it != regs.end(); it++)
|
|
delete *it;
|
|
regs.clear();
|
|
|
|
// Delete exceptions
|
|
for (vector<CException*>::iterator it = exceps.begin(); it != exceps.end(); it++)
|
|
delete *it;
|
|
exceps.clear();
|
|
memset(m_exArray, NULL, sizeof(m_exArray));
|
|
|
|
// Delete interrupts
|
|
for (vector<CInterrupt*>::iterator it = inters.begin(); it != inters.end(); it++)
|
|
delete *it;
|
|
inters.clear();
|
|
memset(m_intArray, NULL, sizeof(m_intArray));
|
|
|
|
// Delete IOs
|
|
for (vector<CIO*>::iterator it = ios.begin(); it != ios.end(); it++)
|
|
delete *it;
|
|
ios.clear();
|
|
memset(m_portArray, NULL, sizeof(m_portArray));
|
|
delete m_mappedIOTable;
|
|
|
|
// Delete regions
|
|
for (vector<CRegion*>::iterator it = regions.begin(); it != regions.end(); it++)
|
|
delete *it;
|
|
regions.clear();
|
|
|
|
// Delete labels, comments, memory watches, I/O watches, breakpoints and register monitors
|
|
RemoveAllLabels();
|
|
RemoveAllComments();
|
|
RemoveAllMemWatches();
|
|
RemoveAllIOWatches();
|
|
RemoveAllBreakpoints();
|
|
RemoveAllRegMonitors();
|
|
|
|
// Delete analyser, if any
|
|
if (m_analyser != NULL)
|
|
delete m_analyser;
|
|
}
|
|
|
|
void CCPUDebug::AttachToDebugger(CDebugger *theDebugger)
|
|
{
|
|
UpdateExecMasks();
|
|
UpdateMemMasks();
|
|
|
|
debugger = theDebugger;
|
|
}
|
|
|
|
void CCPUDebug::DetachFromDebugger(CDebugger *theDebugger)
|
|
{
|
|
if (debugger == theDebugger)
|
|
debugger = NULL;
|
|
}
|
|
|
|
CPCRegister *CCPUDebug::AddPCRegister(const char *name, const char *group)
|
|
{
|
|
CPCRegister *reg = new CPCRegister(this, name, group);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CAddrRegister *CCPUDebug::AddAddrRegister(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc)
|
|
{
|
|
CAddrRegister *reg = new CAddrRegister(this, name, group, id, getFunc, setFunc);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CIntRegister *CCPUDebug::AddInt8Register(const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc)
|
|
{
|
|
CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CIntRegister *CCPUDebug::AddInt16Register(const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc)
|
|
{
|
|
CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CIntRegister *CCPUDebug::AddInt32Register(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc)
|
|
{
|
|
CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CIntRegister *CCPUDebug::AddInt64Register(const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc)
|
|
{
|
|
CIntRegister *reg = new CIntRegister(this, name, group, id, getFunc, setFunc);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CStatusRegister *CCPUDebug::AddStatus8Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt8FPtr getFunc, SetInt8FPtr setFunc)
|
|
{
|
|
CStatusRegister *reg = new CStatusRegister(this, name, group, id, getFunc, setFunc);
|
|
reg->SetBits(bitStr);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CStatusRegister *CCPUDebug::AddStatus16Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt16FPtr getFunc, SetInt16FPtr setFunc)
|
|
{
|
|
CStatusRegister *reg = new CStatusRegister(this, name, group, id, getFunc, setFunc);
|
|
reg->SetBits(bitStr);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CStatusRegister *CCPUDebug::AddStatus32Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt32FPtr getFunc, SetInt32FPtr setFunc)
|
|
{
|
|
CStatusRegister *reg = new CStatusRegister(this, name, group, id, getFunc, setFunc);
|
|
reg->SetBits(bitStr);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CFPointRegister *CCPUDebug::AddFPointRegister(const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc)
|
|
{
|
|
CFPointRegister *reg = new CFPointRegister(this, name, group, id, getFunc, setFunc);
|
|
regs.push_back(reg);
|
|
return reg;
|
|
}
|
|
|
|
CException *CCPUDebug::AddException(const char *id, UINT16 code, const char *name)
|
|
{
|
|
if (code >= MAX_EXCEPTIONS)
|
|
return NULL;
|
|
CException *ex = new CException(this, id, code, name);
|
|
exceps.push_back(ex);
|
|
m_exArray[code] = ex;
|
|
numExCodes = max<UINT16>(numExCodes, code + 1);
|
|
return ex;
|
|
}
|
|
|
|
CInterrupt *CCPUDebug::AddInterrupt(const char *id, UINT16 code, const char *name)
|
|
{
|
|
if (code >= MAX_INTERRUPTS)
|
|
return NULL;
|
|
CInterrupt *in = new CInterrupt(this, id, code, name);
|
|
inters.push_back(in);
|
|
m_intArray[code] = in;
|
|
numIntCodes = max<UINT16>(numIntCodes, code + 1);
|
|
return in;
|
|
}
|
|
|
|
CRegion *CCPUDebug::AddRegion(UINT32 start, UINT32 end, bool isCode, bool isReadOnly, const char *name)
|
|
{
|
|
if (debugger != NULL)
|
|
return NULL;
|
|
CRegion *region = new CRegion(this, start, end, isCode, isReadOnly, name);
|
|
if (regions.size() > 0)
|
|
{
|
|
region->prevRegion = regions[regions.size() - 1];
|
|
region->prevRegion->nextRegion = region;
|
|
}
|
|
regions.push_back(region);
|
|
memSize = max<UINT32>(memSize, end);
|
|
return region;
|
|
}
|
|
|
|
CPortIO *CCPUDebug::AddPortIO(UINT16 portNum, unsigned dataSize, const char *name, const char *group)
|
|
{
|
|
if (debugger != NULL || portNum >= MAX_IOPORTS)
|
|
return NULL;
|
|
CPortIO *port = new CPortIO(this, name, group, dataSize, portNum);
|
|
ios.push_back(port);
|
|
m_portArray[portNum] = port;
|
|
numPorts = max<UINT16>(numPorts, portNum + 1);
|
|
return port;
|
|
}
|
|
|
|
CMappedIO *CCPUDebug::AddMappedIO(UINT32 addr, unsigned dataSize, const char *name, const char *group)
|
|
{
|
|
if (debugger != NULL)
|
|
return NULL;
|
|
if (m_mappedIOTable == NULL)
|
|
m_mappedIOTable = new CAddressTable();
|
|
CMappedIO *mapped = new CMappedIO(this, name, group, dataSize, addr);
|
|
ios.push_back(mapped);
|
|
m_mappedIOTable->Add(mapped);
|
|
return mapped;
|
|
}
|
|
|
|
bool CCPUDebug::ParseData(const char *str, unsigned dataSize, UINT64 *data, bool registers)
|
|
{
|
|
// Check registers, if required
|
|
if (registers)
|
|
{
|
|
CRegister *reg = GetRegister(str);
|
|
if (reg != NULL)
|
|
{
|
|
*data = reg->GetValueAsInt();
|
|
return *data == CDebugger::MaskData(dataSize, *data);
|
|
}
|
|
}
|
|
|
|
return debugger->ParseData(str, dataFmt, dataSize, data) && *data == CDebugger::MaskData(dataSize, *data);
|
|
}
|
|
|
|
void CCPUDebug::FormatData(char *str, unsigned dataSize, UINT64 data)
|
|
{
|
|
debugger->FormatData(str, dataFmt, dataSize, data);
|
|
}
|
|
|
|
bool CCPUDebug::ParseAddress(const char *str, UINT32 *addr, bool customLabels, bool autoLabels, bool registers)
|
|
{
|
|
// Check custom labels, if required
|
|
if (customLabels)
|
|
{
|
|
CLabel *label = GetLabel(str);
|
|
if (label != NULL)
|
|
{
|
|
*addr = label->addr;
|
|
return GetRegion(*addr) != NULL;
|
|
}
|
|
}
|
|
|
|
// Check auto-labels, if required
|
|
if (autoLabels && m_analyser != NULL)
|
|
{
|
|
CAutoLabel *autoLabel = m_analyser->analysis->GetAutoLabel(str);
|
|
if (autoLabel != NULL)
|
|
{
|
|
*addr = autoLabel->addr;
|
|
return GetRegion(*addr) != NULL;
|
|
}
|
|
}
|
|
|
|
// Check registers, if required
|
|
if (registers)
|
|
{
|
|
CRegister *reg = GetRegister(str);
|
|
if (reg != NULL)
|
|
{
|
|
*addr = reg->GetValueAsInt();
|
|
return GetRegion(*addr) != NULL;
|
|
}
|
|
}
|
|
|
|
// Parse address
|
|
unsigned dataSize = (unsigned)(memBusWidth / 8);
|
|
UINT64 vlAddr;
|
|
if (!debugger->ParseData(str, addrFmt, dataSize, &vlAddr) || vlAddr > 0xFFFFFFFFULL)
|
|
return false;
|
|
*addr = (UINT32)vlAddr;
|
|
return GetRegion(*addr) != NULL;
|
|
}
|
|
|
|
void CCPUDebug::FormatAddress(char *str, UINT32 addr, bool customLabels, ELabelFlags autoLabelFlags)
|
|
{
|
|
// Check custom labels, if required
|
|
if (customLabels)
|
|
{
|
|
CLabel *label = GetLabel(addr);
|
|
if (label != NULL)
|
|
{
|
|
strcpy(str, label->name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check auto-labels, if required
|
|
if (autoLabelFlags != LFNone && m_analyser != NULL)
|
|
{
|
|
CAutoLabel *autoLabel = m_analyser->analysis->GetAutoLabel(addr);
|
|
if (autoLabel != NULL && autoLabel->GetLabel(str, autoLabelFlags))
|
|
{
|
|
autoLabel->GetLabel(str, autoLabelFlags);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Format address
|
|
debugger->FormatData(str, addrFmt, (unsigned)(memBusWidth / 8), (UINT64)addr);
|
|
}
|
|
|
|
void CCPUDebug::FormatJumpAddress(char *str, UINT32 jumpAddr, EOpFlags opFlags)
|
|
{
|
|
if (opFlags & JumpSub) FormatAddress(str, jumpAddr, true, LFSubroutine);
|
|
else if (opFlags & JumpLoop) FormatAddress(str, jumpAddr, true, LFLoopPoint);
|
|
else if (opFlags & JumpSimple) FormatAddress(str, jumpAddr, true, LFJumpTarget);
|
|
else FormatAddress(str, jumpAddr, true, LFNone);
|
|
}
|
|
|
|
bool CCPUDebug::ParsePortNum(const char *str, UINT16 *portNum, bool customLabels, bool registers)
|
|
{
|
|
// Check custom labels, if required
|
|
if (customLabels)
|
|
{
|
|
for (UINT16 loopNum = 0; loopNum < numPorts; loopNum++)
|
|
{
|
|
CPortIO *port = m_portArray[loopNum];
|
|
if (port == NULL || port->label == NULL)
|
|
continue;
|
|
if (stricmp(port->label, str) == 0)
|
|
{
|
|
*portNum = loopNum;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check registers, if required
|
|
if (registers)
|
|
{
|
|
CRegister *reg = GetRegister(str);
|
|
if (reg != NULL)
|
|
{
|
|
*portNum = reg->GetValueAsInt();
|
|
return *portNum < numPorts;
|
|
}
|
|
}
|
|
|
|
// Parse port num
|
|
unsigned dataSize = CDebugger::GetDataSize(numPorts);
|
|
UINT64 vlPortNum;
|
|
if (!debugger->ParseData(str, portFmt, dataSize, &vlPortNum))
|
|
return false;
|
|
*portNum = (UINT16)vlPortNum;
|
|
return *portNum < numPorts && GetPortIO(*portNum) != NULL;
|
|
}
|
|
|
|
void CCPUDebug::FormatPortNum(char *str, UINT16 portNum, bool customLabels)
|
|
{
|
|
if (portNum >= numPorts)
|
|
{
|
|
str[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
// Check custom labels, if reqired
|
|
if (customLabels)
|
|
{
|
|
CPortIO *port = m_portArray[portNum];
|
|
if (port != NULL && port->label != NULL)
|
|
{
|
|
strcpy(str, port->label);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Format port num
|
|
unsigned dataSize = CDebugger::GetDataSize(numPorts);
|
|
debugger->FormatData(str, portFmt, dataSize, (UINT64)portNum);
|
|
}
|
|
|
|
bool CCPUDebug::IsEnabled()
|
|
{
|
|
return m_enabled;
|
|
}
|
|
|
|
void CCPUDebug::SetEnabled(bool enabled)
|
|
{
|
|
m_enabled = enabled;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::ForceBreak(bool user)
|
|
{
|
|
m_breakUser |= user;
|
|
m_break = true;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::ClearBreak()
|
|
{
|
|
#ifdef DEBUGGER_HASTHREAD
|
|
m_breakWait = false;
|
|
#endif // DEBUGGER_HASTHREAD
|
|
m_breakUser = false;
|
|
m_break = false;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::SetContinue()
|
|
{
|
|
m_step = false;
|
|
m_count = 0;
|
|
m_until = false;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::SetStepMode(EStepMode stepMode)
|
|
{
|
|
m_step = true;
|
|
m_stepMode = stepMode;
|
|
m_steppingOver = false;
|
|
m_steppingOut = false;
|
|
m_count = 0;
|
|
m_until = false;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::SetCount(int count)
|
|
{
|
|
m_step = false;
|
|
m_count = count;
|
|
m_until = false;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::SetUntil(UINT32 untilAddr)
|
|
{
|
|
m_step = false;
|
|
m_count = 0;
|
|
m_until = true;
|
|
m_untilAddr = untilAddr;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
bool CCPUDebug::SetPC(UINT32 newPC)
|
|
{
|
|
if (!UpdatePC(newPC))
|
|
return false;
|
|
pc = newPC;
|
|
m_stateUpdated = true;
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::GenerateException(CException *ex)
|
|
{
|
|
if (!ForceException(ex))
|
|
return false;
|
|
m_stateUpdated = true;
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::GenerateInterrupt(CInterrupt *in)
|
|
{
|
|
if (!ForceInterrupt(in))
|
|
return false;
|
|
m_stateUpdated = true;
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
CRegister *CCPUDebug::GetRegister(const char *name)
|
|
{
|
|
for (vector<CRegister*>::iterator it = regs.begin(); it != regs.end(); it++)
|
|
{
|
|
if (stricmp((*it)->name, name) == 0)
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CException *CCPUDebug::GetException(const char *id)
|
|
{
|
|
for (vector<CException*>::iterator it = exceps.begin(); it != exceps.end(); it++)
|
|
{
|
|
if (stricmp((*it)->id, id) == 0)
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CException *CCPUDebug::GetException(UINT16 code)
|
|
{
|
|
if (code >= numExCodes)
|
|
return NULL;
|
|
return m_exArray[code];
|
|
}
|
|
|
|
CInterrupt *CCPUDebug::GetInterrupt(const char *id)
|
|
{
|
|
for (vector<CInterrupt*>::iterator it = inters.begin(); it != inters.end(); it++)
|
|
{
|
|
if (stricmp((*it)->id, id) == 0)
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CInterrupt *CCPUDebug::GetInterrupt(UINT16 code)
|
|
{
|
|
if (code >= numIntCodes)
|
|
return NULL;
|
|
return m_intArray[code];
|
|
}
|
|
|
|
CPortIO *CCPUDebug::GetPortIO(UINT16 portNum)
|
|
{
|
|
if (portNum >= numPorts)
|
|
return NULL;
|
|
return m_portArray[portNum];
|
|
}
|
|
|
|
CMappedIO *CCPUDebug::GetMappedIO(UINT32 addr)
|
|
{
|
|
if (m_mappedIOTable == NULL)
|
|
return NULL;
|
|
return (CMappedIO*)m_mappedIOTable->Get(addr);
|
|
}
|
|
|
|
CRegion *CCPUDebug::GetRegion(UINT32 addr)
|
|
{
|
|
for (vector<CRegion*>::iterator it = regions.begin(); it != regions.end(); it++)
|
|
{
|
|
if ((*it)->CheckAddr(addr))
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CLabel *CCPUDebug::GetLabel(UINT32 addr)
|
|
{
|
|
for (vector<CLabel*>::iterator it = labels.begin(); it != labels.end(); it++)
|
|
{
|
|
if ((*it)->CheckAddr(addr))
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CLabel *CCPUDebug::GetLabel(const char *name)
|
|
{
|
|
// TODO - use binary search
|
|
for (vector<CLabel*>::iterator it = labels.begin(); it != labels.end(); it++)
|
|
{
|
|
if (stricmp((*it)->name, name) == 0)
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CLabel *CCPUDebug::AddLabel(UINT32 addr, const char *name)
|
|
{
|
|
RemoveLabel(addr);
|
|
RemoveLabel(name);
|
|
CLabel *label = new CLabel(this, addr, name);
|
|
// TODO - keep labels in order of increasing address so can use binary search
|
|
labels.push_back(label);
|
|
return label;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveLabel(const char *name)
|
|
{
|
|
CLabel *label = GetLabel(name);
|
|
if (label == NULL)
|
|
return false;
|
|
return RemoveLabel(label);
|
|
}
|
|
|
|
bool CCPUDebug::RemoveLabel(UINT32 addr)
|
|
{
|
|
CLabel *label = GetLabel(addr);
|
|
if (label == NULL)
|
|
return false;
|
|
RemoveLabel(label);
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveLabel(CLabel *label)
|
|
{
|
|
vector<CLabel*>::iterator it = find(labels.begin(), labels.end(), label);
|
|
if (it == labels.end())
|
|
return false;
|
|
labels.erase(it);
|
|
delete label;
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveAllLabels()
|
|
{
|
|
if (labels.size() == 0)
|
|
return false;
|
|
for (vector<CLabel*>::iterator it = labels.begin(); it != labels.end(); it++)
|
|
delete *it;
|
|
labels.clear();
|
|
return true;
|
|
}
|
|
|
|
CComment *CCPUDebug::GetComment(UINT32 addr)
|
|
{
|
|
// TODO - use binary search
|
|
for (vector<CComment*>::iterator it = comments.begin(); it != comments.end(); it++)
|
|
{
|
|
if ((*it)->CheckAddr(addr))
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CComment *CCPUDebug::AddComment(UINT32 addr, const char *commentText)
|
|
{
|
|
RemoveComment(addr);
|
|
CComment *comment = new CComment(this, addr, commentText);
|
|
// TODO - keep comments in order of increasing address so can use binary search
|
|
comments.push_back(comment);
|
|
return comment;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveComment(UINT32 addr)
|
|
{
|
|
CComment *comment = GetComment(addr);
|
|
if (comment == NULL)
|
|
return false;
|
|
RemoveComment(comment);
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveComment(CComment *comment)
|
|
{
|
|
vector<CComment*>::iterator it = find(comments.begin(), comments.end(), comment);
|
|
if (it == comments.end())
|
|
return false;
|
|
comments.erase(it);
|
|
delete comment;
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveAllComments()
|
|
{
|
|
if (comments.size() == 0)
|
|
return false;
|
|
for (vector<CComment*>::iterator it = comments.begin(); it != comments.end(); it++)
|
|
delete *it;
|
|
comments.clear();
|
|
return true;
|
|
}
|
|
|
|
CSimpleWatch *CCPUDebug::AddSimpleMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite)
|
|
{
|
|
CSimpleWatch *watch = NULL;
|
|
// First check for mapped I/O at given addr
|
|
if (m_mappedIOTable != NULL)
|
|
{
|
|
CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size);
|
|
// If found, create watch for I/O
|
|
if (mapped != NULL)
|
|
watch = new CSimpleWatch(this, mapped, trigRead, trigWrite);
|
|
}
|
|
// Otherwise, create watch for memory addr
|
|
if (watch == NULL)
|
|
watch = new CSimpleWatch(this, addr, size, trigRead, trigWrite);
|
|
AddWatch(watch);
|
|
return watch;
|
|
}
|
|
|
|
CCountWatch *CCPUDebug::AddCountMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned count)
|
|
{
|
|
CCountWatch *watch = NULL;
|
|
// First check for mapped I/O at given addr
|
|
if (m_mappedIOTable != NULL)
|
|
{
|
|
CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size);
|
|
// If found, create watch for I/O
|
|
if (mapped != NULL)
|
|
watch = new CCountWatch(this, mapped, trigRead, trigWrite, count);
|
|
}
|
|
// Otherwise, create watch for memory addr
|
|
if (watch == NULL)
|
|
watch = new CCountWatch(this, addr, size, trigRead, trigWrite, count);
|
|
AddWatch(watch);
|
|
return watch;
|
|
}
|
|
|
|
CMatchWatch *CCPUDebug::AddMatchMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, vector<UINT64> &dataSeq)
|
|
{
|
|
CMatchWatch *watch = NULL;
|
|
// First check for mapped I/O at given addr
|
|
if (m_mappedIOTable != NULL)
|
|
{
|
|
CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size);
|
|
// If found, create watch for I/O
|
|
if (mapped != NULL)
|
|
watch = new CMatchWatch(this, mapped, trigRead, trigWrite, dataSeq);
|
|
}
|
|
// Otherwise, create watch for memory addr
|
|
if (watch == NULL)
|
|
watch = new CMatchWatch(this, addr, size, trigRead, trigWrite, dataSeq);
|
|
AddWatch(watch);
|
|
return watch;
|
|
}
|
|
|
|
CPrintWatch *CCPUDebug::AddPrintMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite)
|
|
{
|
|
CPrintWatch *watch = NULL;
|
|
// First check for mapped I/O at given addr
|
|
if (m_mappedIOTable != NULL)
|
|
{
|
|
CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size);
|
|
// If found, create watch for I/O
|
|
if (mapped != NULL)
|
|
watch = new CPrintWatch(this, mapped, trigRead, trigWrite);
|
|
}
|
|
// Otherwise, create watch for memory addr
|
|
if (watch == NULL)
|
|
watch = new CPrintWatch(this, addr, size, trigRead, trigWrite);
|
|
AddWatch(watch);
|
|
return watch;
|
|
}
|
|
|
|
CCaptureWatch *CCPUDebug::AddCaptureMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned maxLen)
|
|
{
|
|
CCaptureWatch *watch = NULL;
|
|
// First check for mapped I/O at given addr
|
|
if (m_mappedIOTable != NULL)
|
|
{
|
|
CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size);
|
|
// If found, create watch for I/O
|
|
if (mapped != NULL)
|
|
watch = new CCaptureWatch(this, mapped, trigRead, trigWrite, maxLen);
|
|
}
|
|
// Otherwise, create watch for memory addr
|
|
if (watch == NULL)
|
|
watch = new CCaptureWatch(this, addr, size, trigRead, trigWrite, maxLen);
|
|
AddWatch(watch);
|
|
return watch;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveAllMemWatches()
|
|
{
|
|
if (memWatches.size() == 0)
|
|
return false;
|
|
for (vector<CWatch*>::iterator it = memWatches.begin(); it != memWatches.end(); it++)
|
|
delete *it;
|
|
memWatches.clear();
|
|
delete m_memWatchTable;
|
|
m_memWatchTable = NULL;
|
|
UpdateMemMasks();
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveAllIOWatches()
|
|
{
|
|
if (ioWatches.size() == 0)
|
|
return false;
|
|
for (vector<CWatch*>::iterator it = ioWatches.begin(); it != ioWatches.end(); it++)
|
|
{
|
|
(*it)->io->watch = NULL;
|
|
delete *it;
|
|
}
|
|
ioWatches.clear();
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveMemWatch(UINT32 addr, unsigned size)
|
|
{
|
|
CWatch *watch;
|
|
bool removed = false;
|
|
while ((watch = GetMemWatch(addr, size)) != NULL)
|
|
removed |= RemoveWatch(watch);
|
|
return removed;
|
|
}
|
|
|
|
CWatch *CCPUDebug::GetMemWatch(UINT32 addr, unsigned size)
|
|
{
|
|
// First check for mapped I/O at given addr and size
|
|
if (m_mappedIOTable != NULL)
|
|
{
|
|
CMappedIO *mapped = (CMappedIO*)m_mappedIOTable->Get(addr, size);
|
|
// If found, check watch on I/O
|
|
if (mapped != NULL)
|
|
return mapped->watch;
|
|
}
|
|
// If none, check memory table for watch
|
|
if (m_memWatchTable == NULL)
|
|
return NULL;
|
|
return (CWatch*)m_memWatchTable->Get(addr, size);
|
|
}
|
|
|
|
void CCPUDebug::AddWatch(CWatch *watch)
|
|
{
|
|
CIO *io = watch->io;
|
|
if (io != NULL)
|
|
{
|
|
if (io->watch != NULL)
|
|
RemoveWatch(io->watch);
|
|
ioWatches.push_back(watch);
|
|
UpdateIOWatchNums();
|
|
io->watch = watch;
|
|
}
|
|
else
|
|
{
|
|
RemoveMemWatch(watch->addr, watch->size);
|
|
memWatches.push_back(watch);
|
|
UpdateMemWatchNums();
|
|
if (m_memWatchTable == NULL)
|
|
m_memWatchTable = new CAddressTable();
|
|
m_memWatchTable->Add(watch);
|
|
UpdateMemMasks();
|
|
}
|
|
}
|
|
|
|
bool CCPUDebug::RemoveWatch(CWatch *watch)
|
|
{
|
|
CIO *io = watch->io;
|
|
if (io != NULL)
|
|
{
|
|
io->watch = NULL;
|
|
vector<CWatch*>::iterator it = find(ioWatches.begin(), ioWatches.end(), watch);
|
|
if (it == ioWatches.end())
|
|
return false;
|
|
ioWatches.erase(it);
|
|
UpdateIOWatchNums();
|
|
}
|
|
else
|
|
{
|
|
vector<CWatch*>::iterator it = find(memWatches.begin(), memWatches.end(), watch);
|
|
if (it == memWatches.end())
|
|
return false;
|
|
memWatches.erase(it);
|
|
UpdateMemWatchNums();
|
|
m_memWatchTable->Remove(watch);
|
|
if (m_memWatchTable->IsEmpty())
|
|
{
|
|
delete m_memWatchTable;
|
|
m_memWatchTable = NULL;
|
|
}
|
|
UpdateMemMasks();
|
|
}
|
|
delete watch;
|
|
return true;
|
|
}
|
|
|
|
void CCPUDebug::UpdateIOWatchNums()
|
|
{
|
|
unsigned num = 0;
|
|
for (vector<CWatch*>::iterator it = ioWatches.begin(); it != ioWatches.end(); it++)
|
|
(*it)->num = num++;
|
|
}
|
|
|
|
void CCPUDebug::UpdateMemWatchNums()
|
|
{
|
|
unsigned num = 0;
|
|
for (vector<CWatch*>::iterator it = memWatches.begin(); it != memWatches.end(); it++)
|
|
(*it)->num = num++;
|
|
}
|
|
|
|
void CCPUDebug::UpdateExecMasks()
|
|
{
|
|
if (!m_enabled)
|
|
{
|
|
m_execAndMask = 0xFFFFFFFF;
|
|
m_execOrMask = 0xFFFFFFFF;
|
|
}
|
|
else if (m_break || m_stateUpdated || m_numRegMons > 0 || m_step || m_count > 0)
|
|
{
|
|
m_execAndMask = 0;
|
|
m_execOrMask = 0;
|
|
}
|
|
else if (bps.size() > 0 || m_until)
|
|
{
|
|
UINT32 andMask = 0xFFFFFFFF;
|
|
UINT32 orMask = 0;
|
|
for (vector<CBreakpoint*>::iterator it = bps.begin(), end = bps.end(); it != end; it++)
|
|
{
|
|
UINT32 addr = (*it)->addr;
|
|
andMask &= addr;
|
|
orMask |= addr;
|
|
}
|
|
if (m_until)
|
|
{
|
|
andMask &= m_untilAddr;
|
|
orMask |= m_untilAddr;
|
|
}
|
|
m_execAndMask = andMask;
|
|
m_execOrMask = ~orMask;
|
|
}
|
|
else
|
|
{
|
|
m_execAndMask = 0xFFFFFFFF;
|
|
m_execOrMask = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
void CCPUDebug::UpdateMemMasks()
|
|
{
|
|
UINT32 and8Mask = 0xFFFFFFFF;
|
|
UINT32 and16Mask = 0xFFFFFFFF;
|
|
UINT32 and32Mask = 0xFFFFFFFF;
|
|
UINT32 and64Mask = 0xFFFFFFFF;
|
|
UINT32 or8Mask = 0;
|
|
UINT32 or16Mask = 0;
|
|
UINT32 or32Mask = 0;
|
|
UINT32 or64Mask = 0;
|
|
for (vector<CIO*>::iterator it = ios.begin(), end = ios.end(); it != end; it++)
|
|
{
|
|
CMappedIO *mapped = dynamic_cast<CMappedIO*>(*it);
|
|
if (!mapped)
|
|
continue;
|
|
UINT32 addr = mapped->addr;
|
|
CRegion *region = GetRegion(addr);
|
|
int intSize = (int)mapped->size;
|
|
for (int offset = -7; offset < intSize; offset++)
|
|
{
|
|
if (offset < 0 && addr < abs(offset) || offset > 0 && addr > 0xFFFFFFFF - offset)
|
|
continue;
|
|
UINT32 offAddr = addr + offset;
|
|
if (!region->CheckAddr(offAddr))
|
|
continue;
|
|
if (offset >= 0)
|
|
{
|
|
and8Mask &= offAddr;
|
|
or8Mask |= offAddr;
|
|
and16Mask &= offAddr;
|
|
or16Mask |= offAddr;
|
|
and32Mask &= offAddr;
|
|
or32Mask |= offAddr;
|
|
and64Mask &= offAddr;
|
|
or64Mask |= offAddr;
|
|
}
|
|
else
|
|
{
|
|
if (offset > -2)
|
|
{
|
|
and16Mask &= offAddr;
|
|
or16Mask |= offAddr;
|
|
}
|
|
if (offset > -4)
|
|
{
|
|
and32Mask &= offAddr;
|
|
or32Mask |= offAddr;
|
|
}
|
|
and64Mask &= offAddr;
|
|
or64Mask |= offAddr;
|
|
}
|
|
}
|
|
}
|
|
for (vector<CWatch*>::iterator it = memWatches.begin(), end = memWatches.end(); it != end; it++)
|
|
{
|
|
UINT32 addr = (*it)->addr;
|
|
CRegion *region = GetRegion(addr);
|
|
int intSize = (int)(*it)->size;
|
|
for (int offset = -7; offset < intSize; offset++)
|
|
{
|
|
if (offset < 0 && addr < abs(offset) || offset > 0 && addr > 0xFFFFFFFF - offset)
|
|
continue;
|
|
UINT32 offAddr = addr + offset;
|
|
if (!region->CheckAddr(offAddr))
|
|
continue;
|
|
if (offset >= 0)
|
|
{
|
|
and8Mask &= offAddr;
|
|
or8Mask |= offAddr;
|
|
and16Mask &= offAddr;
|
|
or16Mask |= offAddr;
|
|
and32Mask &= offAddr;
|
|
or32Mask |= offAddr;
|
|
and64Mask &= offAddr;
|
|
or64Mask |= offAddr;
|
|
}
|
|
else
|
|
{
|
|
if (offset > -2)
|
|
{
|
|
and16Mask &= offAddr;
|
|
or16Mask |= offAddr;
|
|
}
|
|
if (offset > -4)
|
|
{
|
|
and32Mask &= offAddr;
|
|
or32Mask |= offAddr;
|
|
}
|
|
and64Mask &= offAddr;
|
|
or64Mask |= offAddr;
|
|
}
|
|
}
|
|
}
|
|
m_mem8AndMask = and8Mask;
|
|
m_mem8OrMask = ~or8Mask;
|
|
m_mem16AndMask = and16Mask;
|
|
m_mem16OrMask = ~or16Mask;
|
|
m_mem32AndMask = and32Mask;
|
|
m_mem32OrMask = ~or32Mask;
|
|
m_mem64AndMask = and64Mask;
|
|
m_mem64OrMask = ~or64Mask;
|
|
}
|
|
|
|
CSimpleBreakpoint *CCPUDebug::AddSimpleBreakpoint(UINT32 addr)
|
|
{
|
|
CSimpleBreakpoint *bp = new CSimpleBreakpoint(this, addr);
|
|
AddBreakpoint(bp);
|
|
return bp;
|
|
}
|
|
|
|
CCountBreakpoint *CCPUDebug::AddCountBreakpoint(UINT32 addr, unsigned count)
|
|
{
|
|
CCountBreakpoint *bp = new CCountBreakpoint(this, addr, count);
|
|
AddBreakpoint(bp);
|
|
return bp;
|
|
}
|
|
|
|
CPrintBreakpoint *CCPUDebug::AddPrintBreakpoint(UINT32 addr)
|
|
{
|
|
CPrintBreakpoint *bp = new CPrintBreakpoint(this, addr);
|
|
AddBreakpoint(bp);
|
|
return bp;
|
|
}
|
|
|
|
CBreakpoint *CCPUDebug::GetBreakpoint(UINT32 addr)
|
|
{
|
|
if (m_bpTable == NULL)
|
|
return NULL;
|
|
return (CBreakpoint*)m_bpTable->Get(addr);
|
|
}
|
|
|
|
void CCPUDebug::AddBreakpoint(CBreakpoint *bp)
|
|
{
|
|
RemoveBreakpoint(bp->addr);
|
|
bps.push_back(bp);
|
|
UpdateBreakpointNums();
|
|
if (m_bpTable == NULL)
|
|
m_bpTable = new CAddressTable();
|
|
m_bpTable->Add(bp);
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
bool CCPUDebug::RemoveBreakpoint(UINT32 addr)
|
|
{
|
|
CBreakpoint *bp = GetBreakpoint(addr);
|
|
if (bp == NULL)
|
|
return false;
|
|
return RemoveBreakpoint(bp);
|
|
}
|
|
|
|
bool CCPUDebug::RemoveBreakpoint(CBreakpoint *bp)
|
|
{
|
|
vector<CBreakpoint*>::iterator it = find(bps.begin(), bps.end(), bp);
|
|
if (it == bps.end())
|
|
return false;
|
|
bps.erase(it);
|
|
UpdateBreakpointNums();
|
|
m_bpTable->Remove(bp);
|
|
if (m_bpTable->IsEmpty())
|
|
{
|
|
delete m_bpTable;
|
|
m_bpTable = NULL;
|
|
}
|
|
delete bp;
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveAllBreakpoints()
|
|
{
|
|
if (bps.size() == 0)
|
|
return false;
|
|
for (vector<CBreakpoint*>::iterator it = bps.begin(); it != bps.end(); it++)
|
|
delete *it;
|
|
bps.clear();
|
|
delete m_bpTable;
|
|
m_bpTable = NULL;
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
void CCPUDebug::UpdateBreakpointNums()
|
|
{
|
|
unsigned num = 0;
|
|
for (vector<CBreakpoint*>::iterator it = bps.begin(); it != bps.end(); it++)
|
|
(*it)->num = num++;
|
|
}
|
|
|
|
CRegMonitor *CCPUDebug::AddRegMonitor(const char *regName)
|
|
{
|
|
RemoveRegMonitor(regName);
|
|
|
|
CRegister *reg = GetRegister(regName);
|
|
if (reg == NULL)
|
|
return NULL;
|
|
CRegMonitor *regMon = reg->CreateMonitor();
|
|
AddRegMonitor(regMon);
|
|
return regMon;
|
|
}
|
|
|
|
CRegMonitor *CCPUDebug::GetRegMonitor(const char *regName)
|
|
{
|
|
for (vector<CRegMonitor*>::iterator it = regMons.begin(); it != regMons.end(); it++)
|
|
{
|
|
if (stricmp((*it)->reg->name, regName) == 0)
|
|
return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CCPUDebug::AddRegMonitor(CRegMonitor *regMon)
|
|
{
|
|
regMons.push_back(regMon);
|
|
UpdateRegMonArray();
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
bool CCPUDebug::RemoveRegMonitor(const char *regName)
|
|
{
|
|
CRegMonitor *regMon = GetRegMonitor(regName);
|
|
if (regMon == NULL)
|
|
return false;
|
|
return RemoveRegMonitor(regMon);
|
|
}
|
|
|
|
bool CCPUDebug::RemoveRegMonitor(CRegMonitor *regMon)
|
|
{
|
|
vector<CRegMonitor*>::iterator it = find(regMons.begin(), regMons.end(), regMon);
|
|
if (it == regMons.end())
|
|
return false;
|
|
regMons.erase(it);
|
|
delete regMon;
|
|
UpdateRegMonArray();
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::RemoveAllRegMonitors()
|
|
{
|
|
if (regMons.size() == 0)
|
|
return false;
|
|
for (vector<CRegMonitor*>::iterator it = regMons.begin(); it != regMons.end(); it++)
|
|
delete *it;
|
|
regMons.clear();
|
|
UpdateRegMonArray();
|
|
UpdateExecMasks();
|
|
return true;
|
|
}
|
|
|
|
void CCPUDebug::UpdateRegMonArray()
|
|
{
|
|
if (m_regMonArray != NULL)
|
|
delete m_regMonArray;
|
|
m_numRegMons = regMons.size();
|
|
if (m_numRegMons > 0)
|
|
{
|
|
m_regMonArray = new CRegMonitor*[m_numRegMons];
|
|
copy(regMons.begin(), regMons.end(), m_regMonArray);
|
|
}
|
|
else
|
|
m_regMonArray = NULL;
|
|
}
|
|
|
|
CCodeAnalyser *CCPUDebug::GetCodeAnalyser()
|
|
{
|
|
if (m_analyser == NULL)
|
|
m_analyser = new CCodeAnalyser(this);
|
|
return m_analyser;
|
|
}
|
|
|
|
bool CCPUDebug::FindInMem(UINT32 start, UINT32 end, const char *str, bool matchCase, UINT32 &findAddr)
|
|
{
|
|
size_t len = strlen(str);
|
|
const char *matchEnd = str + len - 1;
|
|
const char *matchPos;
|
|
if (start <= end)
|
|
{
|
|
matchPos = str;
|
|
for (UINT32 addr = start; addr <= end + len - 1; addr++)
|
|
{
|
|
char c = (char)ReadMem(addr, 1);
|
|
if (matchCase && c == *matchPos || !matchCase && toupper(c) == toupper(*matchPos))
|
|
{
|
|
if (matchPos == matchEnd)
|
|
{
|
|
findAddr = addr - len + 1;
|
|
return true;
|
|
}
|
|
else
|
|
matchPos++;
|
|
}
|
|
else
|
|
matchPos = str;
|
|
if (addr == 0xFFFFFFFFU)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
matchPos = matchEnd;
|
|
for (UINT32 addr = start + len - 1; addr >= end; addr--)
|
|
{
|
|
char c = (char)ReadMem(addr, 1);
|
|
if (matchCase && c == *matchPos || !matchCase && toupper(c) == toupper(*matchPos))
|
|
{
|
|
if (matchPos == str)
|
|
{
|
|
findAddr = addr;
|
|
return true;
|
|
}
|
|
else
|
|
matchPos--;
|
|
}
|
|
else
|
|
matchPos = matchEnd;
|
|
if (addr == 0)
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CCPUDebug::FindInMem(UINT32 start, UINT32 end, UINT8 *bytes, unsigned length, UINT32 &findAddr)
|
|
{
|
|
UINT32 matchEnd = length - 1;
|
|
UINT32 matchPos;
|
|
if (start <= end)
|
|
{
|
|
matchPos = 0;
|
|
for (UINT32 addr = start; addr <= end; addr++)
|
|
{
|
|
UINT8 b = (UINT8)ReadMem(addr, 1);
|
|
if (b == bytes[matchPos])
|
|
{
|
|
if (matchPos == matchEnd)
|
|
{
|
|
findAddr = addr - matchPos;
|
|
return true;
|
|
}
|
|
else
|
|
matchPos++;
|
|
}
|
|
else
|
|
matchPos = 0;
|
|
if (addr == 0xFFFFFFFFU)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
matchPos = matchEnd;
|
|
for (UINT32 addr = end; addr >= start; addr--)
|
|
{
|
|
UINT8 b = (UINT8)ReadMem(addr, 1);
|
|
if (b == bytes[matchPos])
|
|
{
|
|
if (matchPos == 0)
|
|
{
|
|
findAddr = addr;
|
|
return true;
|
|
}
|
|
else
|
|
matchPos++;
|
|
}
|
|
else
|
|
matchPos = matchEnd;
|
|
if (addr == 0)
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef DEBUGGER_HASBLOCKFILE
|
|
bool CCPUDebug::LoadState(CBlockFile *state)
|
|
{
|
|
char blockStr[255];
|
|
UINT32 addr;
|
|
UINT32 addrEnd;
|
|
UINT32 strLen;
|
|
char str[4096];
|
|
|
|
// Load labels
|
|
sprintf(blockStr, "%s.labels", name);
|
|
if (state->FindBlock(blockStr) == OKAY)
|
|
{
|
|
labels.clear();
|
|
UINT32 numLabels;
|
|
state->Read(&numLabels, sizeof(numLabels));
|
|
for (UINT32 i = 0; i < numLabels; i++)
|
|
{
|
|
state->Read(&addr, sizeof(addr));
|
|
state->Read(&addrEnd, sizeof(addrEnd));
|
|
state->Read(&strLen, sizeof(strLen));
|
|
state->Read(str, strLen * sizeof(char));
|
|
str[strLen] = '\0';
|
|
CLabel *label = new CLabel(this, addr, addrEnd, str);
|
|
labels.push_back(label);
|
|
}
|
|
}
|
|
|
|
// Load comments
|
|
sprintf(blockStr, "%s.comments", name);
|
|
if (state->FindBlock(blockStr) == OKAY)
|
|
{
|
|
comments.clear();
|
|
UINT32 numComments;
|
|
state->Read(&numComments, sizeof(numComments));
|
|
for (UINT32 i = 0; i < numComments; i++)
|
|
{
|
|
state->Read(&addr, sizeof(addr));
|
|
state->Read(&strLen, sizeof(strLen));
|
|
state->Read(str, strLen * sizeof(char));
|
|
str[strLen] = '\0';
|
|
CComment *comment = new CComment(this, addr, str);
|
|
comments.push_back(comment);
|
|
}
|
|
}
|
|
|
|
// Load analyser state
|
|
CCodeAnalyser *analyser = GetCodeAnalyser();
|
|
if (!analyser->LoadState(state))
|
|
return false;
|
|
|
|
// TODO - load breakpoints, watches, exception/interrupt traps and register monitors
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CCPUDebug::SaveState(CBlockFile *state)
|
|
{
|
|
char blockStr[255];
|
|
UINT32 addr;
|
|
UINT32 addrEnd;
|
|
UINT32 strLen;
|
|
const char *str;
|
|
|
|
// Save labels
|
|
sprintf(blockStr, "%s.labels", name);
|
|
state->NewBlock(blockStr, __FILE__);
|
|
UINT32 numLabels = (UINT32)labels.size();
|
|
state->Write(&numLabels, sizeof(numLabels));
|
|
for (vector<CLabel*>::iterator it = labels.begin(); it != labels.end(); it++)
|
|
{
|
|
addr = (*it)->addr;
|
|
addrEnd = (*it)->addrEnd;
|
|
str = (*it)->name;
|
|
strLen = min<UINT32>(4095, (UINT32)strlen(str));
|
|
state->Write(&addr, sizeof(addr));
|
|
state->Write(&addrEnd, sizeof(addrEnd));
|
|
state->Write(&strLen, sizeof(strLen));
|
|
state->Write(str, strLen * sizeof(char));
|
|
}
|
|
|
|
// Save comments
|
|
sprintf(blockStr, "%s.comments", name);
|
|
state->NewBlock(blockStr, __FILE__);
|
|
UINT32 numComments = (UINT32)comments.size();
|
|
state->Write(&numComments, sizeof(numComments));
|
|
for (vector<CComment*>::iterator it = comments.begin(); it != comments.end(); it++)
|
|
{
|
|
addr = (*it)->addr;
|
|
addrEnd = (*it)->addrEnd;
|
|
str = (*it)->text;
|
|
strLen = min<UINT32>(4095, (UINT32)strlen(str));
|
|
state->Write(&addr, sizeof(addr));
|
|
state->Write(&strLen, sizeof(strLen));
|
|
state->Write(str, strLen * sizeof(char));
|
|
}
|
|
|
|
// Save analyser state, if available
|
|
if (m_analyser != NULL && !m_analyser->SaveState(state))
|
|
return false;
|
|
|
|
// TODO - save breakpoints, watches, exception/interrupt traps and register monitors
|
|
|
|
return true;
|
|
}
|
|
#endif // DEBUGGER_HASBLOCKFILE
|
|
|
|
void CCPUDebug::CPUException(UINT16 exCode)
|
|
{
|
|
if (exCode >= numExCodes)
|
|
return;
|
|
CException *ex = m_exArray[exCode];
|
|
if (ex == NULL)
|
|
return;
|
|
|
|
m_exRaised = ex;
|
|
ex->count++;
|
|
if (!ex->trap)
|
|
return;
|
|
|
|
m_exTrapped = ex;
|
|
debugger->ExceptionTrapped(ex);
|
|
m_break = true;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::CPUInterrupt(UINT16 intCode)
|
|
{
|
|
if (intCode >= numIntCodes)
|
|
return;
|
|
CInterrupt *in = m_intArray[intCode];
|
|
if (in == NULL)
|
|
return;
|
|
|
|
m_intRaised = in;
|
|
in->count++;
|
|
if (!in->trap)
|
|
return;
|
|
|
|
m_intTrapped = in;
|
|
debugger->InterruptTrapped(in);
|
|
m_break = true;
|
|
UpdateExecMasks();
|
|
}
|
|
|
|
void CCPUDebug::CPUActive()
|
|
{
|
|
#ifdef DEBUGGER_HASTHREAD
|
|
m_mutex->Lock();
|
|
|
|
active = true;
|
|
m_condVar->Signal();
|
|
|
|
m_mutex->Unlock();
|
|
#else
|
|
active = true;
|
|
#endif // DEBUGGER_HASTHREAD
|
|
}
|
|
|
|
void CCPUDebug::CPUInactive()
|
|
{
|
|
#ifdef DEBUGGER_HASTHREAD
|
|
m_mutex->Lock();
|
|
|
|
active = false;
|
|
m_condVar->Signal();
|
|
|
|
m_mutex->Unlock();
|
|
#else
|
|
active = false;
|
|
#endif // DEBUGGER_HASTHREAD
|
|
}
|
|
|
|
void CCPUDebug::WaitCommand(EHaltReason reason)
|
|
{
|
|
#ifdef DEBUGGER_HASTHREAD
|
|
m_mutex->Lock();
|
|
|
|
m_halted = true;
|
|
m_condVar->Signal();
|
|
|
|
if (debugger->MakePrimary(this))
|
|
{
|
|
if (reason != HaltNone)
|
|
debugger->ExecutionHalted(this, reason);
|
|
debugger->WaitCommand(this);
|
|
|
|
if (!m_stateUpdated)
|
|
debugger->ReleasePrimary();
|
|
}
|
|
else
|
|
{
|
|
if (reason != HaltNone)
|
|
debugger->ExecutionHalted(this, reason);
|
|
while (m_breakWait)
|
|
m_condVar->Wait(m_mutex);
|
|
}
|
|
|
|
m_halted = false;
|
|
m_condVar->Signal();
|
|
|
|
m_mutex->Unlock();
|
|
#else
|
|
if (reason != HaltNone)
|
|
debugger->ExecutionHalted(this, reason);
|
|
debugger->WaitCommand(this);
|
|
|
|
debugger->ClearBreak();
|
|
#endif // DEBUGGER_HASTHREAD
|
|
}
|
|
|
|
#ifdef DEBUGGER_HASTHREAD
|
|
void CCPUDebug::ForceWait()
|
|
{
|
|
m_mutex->Lock();
|
|
|
|
m_breakWait = true;
|
|
m_break = true;
|
|
UpdateExecMasks();
|
|
m_condVar->Signal();
|
|
|
|
m_mutex->Unlock();
|
|
}
|
|
|
|
void CCPUDebug::WaitForHalt()
|
|
{
|
|
m_mutex->Lock();
|
|
|
|
// Wait for CPU to become inactive or halt
|
|
while (active && !m_halted)
|
|
m_condVar->Wait(m_mutex);
|
|
|
|
m_mutex->Unlock();
|
|
}
|
|
|
|
void CCPUDebug::ClearWait()
|
|
{
|
|
m_mutex->Lock();
|
|
|
|
ClearBreak();
|
|
m_condVar->Signal();
|
|
|
|
m_mutex->Unlock();
|
|
}
|
|
#endif // DEBUGGER_HASTHREAD
|
|
|
|
void CCPUDebug::DebuggerReset()
|
|
{
|
|
instrCount = 0;
|
|
|
|
// TODO - reset breakpoints, watches, I/O ports, exceptions, interrupts and reg monitors
|
|
|
|
// Reset code analyser
|
|
if (m_analyser != NULL)
|
|
m_analyser->Reset();
|
|
}
|
|
|
|
void CCPUDebug::DebuggerPolled()
|
|
{
|
|
cyclesPerPoll = totalCycles - m_prevTotalCycles;
|
|
m_prevTotalCycles = totalCycles;
|
|
}
|
|
|
|
UINT64 CCPUDebug::ReadMem(UINT32 addr, unsigned dataSize)
|
|
{
|
|
// TODO - currently assumes big-endian - should act according to this.bigEndian
|
|
UINT64 dataH, dataL;
|
|
switch (dataSize)
|
|
{
|
|
case 1:
|
|
// Sub-class should at the very least override and implement this case
|
|
return 0;
|
|
case 2:
|
|
dataH = ReadMem(addr, 1);
|
|
dataL = ReadMem(addr + 1, 1);
|
|
return (dataH<<8) | dataL;
|
|
case 4:
|
|
dataH = ReadMem(addr, 2);
|
|
dataL = ReadMem(addr + 2, 2);
|
|
return (dataH<<16) | dataL;
|
|
case 8:
|
|
dataH = ReadMem(addr, 4);
|
|
dataL = ReadMem(addr + 4, 4);
|
|
return (dataH<<32) | dataL;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
bool CCPUDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data)
|
|
{
|
|
// TODO - currently assumes big-endian - should act according to this.bigEndian
|
|
UINT64 dataH, dataL;
|
|
switch (dataSize)
|
|
{
|
|
case 1:
|
|
// Sub-class should at the very least override and implement this case
|
|
return false;
|
|
case 2:
|
|
dataH = (data>>8)&0xFF;
|
|
dataL = data&0xFF;
|
|
return WriteMem(addr, 1, dataH) && WriteMem(addr + 1, 1, dataL);
|
|
case 4:
|
|
dataH = (data>>16)&0xFFFF;
|
|
dataL = data&0xFFFF;
|
|
return WriteMem(addr, 2, dataH) && WriteMem(addr + 2, 2, dataL);
|
|
case 8:
|
|
dataH = data>>32;
|
|
dataL = data&0xFFFFFFFF;
|
|
return WriteMem(addr, 4, dataH) && WriteMem(addr + 4, 4, dataL);
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
UINT64 CCPUDebug::ReadPort(UINT16 portNum)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool CCPUDebug::WritePort(UINT16 portNum, UINT64 data)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int CCPUDebug::GetOpLength(UINT32 addr)
|
|
{
|
|
// Default is to use disassemble to work out instruction length
|
|
char mnemonic[255];
|
|
char operands[255];
|
|
return Disassemble(addr, mnemonic, operands);
|
|
}
|
|
|
|
UINT32 CCPUDebug::GetOpcode(UINT32 addr)
|
|
{
|
|
return (UINT32)ReadMem(addr, min<int>(4, minInstrLen));
|
|
}
|
|
}
|
|
|
|
#endif // SUPERMODEL_DEBUGGER
|