/**
** 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 .
**/
/*
* Debugger.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
#include "Supermodel.h"
#include "Debugger.h"
#include "CPUDebug.h"
#include
#include
using namespace std;
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), m_break(false), m_breakUser(false),
frameCount(0), logDebug(true), logInfo(true), logError(true)
{
#ifdef DEBUGGER_HASTHREAD
m_mutex = CThread::CreateMutex();
m_primaryCPU = NULL;
#endif // DEBUGGER_HASTHREAD
}
CDebugger::~CDebugger()
{
DeleteCPUs();
}
void CDebugger::DeleteCPUs()
{
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
delete *it;
cpus.clear();
}
void CDebugger::AddCPU(CCPUDebug *cpu)
{
cpu->AttachToDebugger(this);
cpus.push_back(cpu);
if (m_break)
cpu->ForceBreak(m_breakUser);
}
void CDebugger::RemoveCPU(CCPUDebug *cpu)
{
cpu->DetachFromDebugger(this);
vector::iterator it = find(cpus.begin(), cpus.end(), cpu);
if (it != cpus.end())
cpus.erase(it);
}
CCPUDebug *CDebugger::GetCPU(const char *name)
{
for (vector::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::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)
{
m_break = true;
m_breakUser = user;
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->ForceBreak(user);
}
void CDebugger::ClearBreak()
{
m_break = false;
m_breakUser = false;
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->ClearBreak();
}
#ifdef DEBUGGER_HASTHREAD
bool CDebugger::MakePrimary(CCPUDebug *cpu)
{
m_mutex->Lock();
bool isPrimary = m_primaryCPU == NULL || m_primaryCPU == cpu;
if (isPrimary)
m_primaryCPU = cpu;
m_mutex->Unlock();
if (isPrimary)
{
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it) != m_primaryCPU)
(*it)->ForceWait();
}
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it) != m_primaryCPU)
(*it)->WaitForHalt();
}
}
return isPrimary;
}
void CDebugger::ReleasePrimary()
{
m_mutex->Lock();
m_primaryCPU = NULL;
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
{
if ((*it) == m_primaryCPU)
(*it)->ClearBreak();
else
(*it)->ClearWait();
}
m_mutex->Unlock();
}
#endif // DEBUGGER_HASTHREAD
void CDebugger::Attach()
{
AddCPUs();
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->AttachToCPU();
}
void CDebugger::Detach()
{
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->DetachFromCPU();
DeleteCPUs();
}
void CDebugger::Reset()
{
frameCount = 0;
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->DebuggerReset();
}
void CDebugger::Poll()
{
frameCount++;
for (vector::iterator it = cpus.begin(); it != cpus.end(); it++)
(*it)->DebuggerPolled();
}
void CDebugger::PrintEvent(CCPUDebug *cpu, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
Log(cpu, NULL, fmt, vl);
va_end(vl);
}
#ifdef DEBUGGER_HASLOGGER
void CDebugger::DebugLog(const char *fmt, va_list vl)
{
if (logDebug)
Log(NULL, "Debug", fmt, vl);
}
void CDebugger::InfoLog(const char *fmt, va_list vl)
{
if (logInfo)
Log(NULL, "Info", fmt, vl);
}
void CDebugger::ErrorLog(const char *fmt, va_list vl)
{
if (logError)
Log(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::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::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::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