/** ** 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 . **/ /* * CPUDebug.cpp */ #ifdef SUPERMODEL_DEBUGGER #include "Supermodel.h" #include "CPUDebug.h" #include "CodeAnalyser.h" #include "Label.h" #include #include 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::iterator it = regs.begin(); it != regs.end(); it++) delete *it; regs.clear(); // Delete exceptions for (vector::iterator it = exceps.begin(); it != exceps.end(); it++) delete *it; exceps.clear(); memset(m_exArray, NULL, sizeof(m_exArray)); // Delete interrupts for (vector::iterator it = inters.begin(); it != inters.end(); it++) delete *it; inters.clear(); memset(m_intArray, NULL, sizeof(m_intArray)); // Delete IOs for (vector::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::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(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(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(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(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::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::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::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::iterator it = regions.begin(); it != regions.end(); it++) { if ((*it)->CheckAddr(addr)) return *it; } return NULL; } CLabel *CCPUDebug::GetLabel(UINT32 addr) { for (vector::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::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::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::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::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::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::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 &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::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::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::iterator it = find(ioWatches.begin(), ioWatches.end(), watch); if (it == ioWatches.end()) return false; ioWatches.erase(it); UpdateIOWatchNums(); } else { vector::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::iterator it = ioWatches.begin(); it != ioWatches.end(); it++) (*it)->num = num++; } void CCPUDebug::UpdateMemWatchNums() { unsigned num = 0; for (vector::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::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::iterator it = ios.begin(), end = ios.end(); it != end; it++) { CMappedIO *mapped = dynamic_cast(*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::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::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::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::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::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::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::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::iterator it = labels.begin(); it != labels.end(); it++) { addr = (*it)->addr; addrEnd = (*it)->addrEnd; str = (*it)->name; strLen = min(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::iterator it = comments.begin(); it != comments.end(); it++) { addr = (*it)->addr; addrEnd = (*it)->addrEnd; str = (*it)->text; strLen = min(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(4, minInstrLen)); } } #endif // SUPERMODEL_DEBUGGER