Trace logger (missing file logging)

This commit is contained in:
Sour 2021-06-16 23:14:21 -04:00
parent a0a06e15a4
commit 078546329f
66 changed files with 2430 additions and 1307 deletions

View file

@ -43,14 +43,19 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Debugger\BaseTraceLogger.h" />
<ClInclude Include="Debugger\ITraceLogger.h" />
<ClInclude Include="Debugger\stdafx.h" />
<ClInclude Include="Debugger\TraceLogFileSaver.h" />
<ClInclude Include="Gameboy\Carts\GbsCart.h" />
<ClInclude Include="Gameboy\Debugger\GbTraceLogger.h" />
<ClInclude Include="Gameboy\GbConstants.h" />
<ClInclude Include="Gameboy\GbDefaultVideoFilter.h" />
<ClInclude Include="Gameboy\GbsHeader.h" />
<ClInclude Include="NES\BaseNesPpu.h" />
<ClInclude Include="NES\Debugger\NesAssembler.h" />
<ClInclude Include="NES\Debugger\NesEventManager.h" />
<ClInclude Include="NES\Debugger\NesTraceLogger.h" />
<ClInclude Include="NES\DefaultNesPpu.h" />
<ClInclude Include="NES\HdPacks\HdAudioDevice.h" />
<ClInclude Include="NES\HdPacks\HdData.h" />
@ -188,6 +193,11 @@
<ClInclude Include="SNES\Debugger\Cx4DisUtils.h" />
<ClInclude Include="SNES\Coprocessors\CX4\Cx4Types.h" />
<ClInclude Include="Debugger\DebugUtilities.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\Cx4TraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\NecDspTraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\GsuTraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\SpcTraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\SnesCpuTraceLogger.h" />
<ClInclude Include="SNES\DmaControllerTypes.h" />
<ClInclude Include="Shared\Emulator.h" />
<ClInclude Include="Gameboy\Gameboy.h" />
@ -367,17 +377,18 @@
<ClInclude Include="SNES\Coprocessors\SGB\SuperGameboy.h" />
<ClInclude Include="SNES\Input\SuperScope.h" />
<ClInclude Include="Shared\SystemActionManager.h" />
<ClInclude Include="Debugger\TraceLogger.h" />
<ClInclude Include="Shared\Video\VideoDecoder.h" />
<ClInclude Include="Shared\Video\VideoRenderer.h" />
<ClInclude Include="Shared\Audio\WaveRecorder.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gameboy\Debugger\GbTraceLogger.cpp" />
<ClCompile Include="Gameboy\GbControlManager.cpp" />
<ClCompile Include="Gameboy\GbDefaultVideoFilter.cpp" />
<ClCompile Include="NES\BaseNesPpu.cpp" />
<ClCompile Include="NES\Debugger\NesAssembler.cpp" />
<ClCompile Include="NES\Debugger\NesEventManager.cpp" />
<ClCompile Include="NES\Debugger\NesTraceLogger.cpp" />
<ClCompile Include="NES\HdPacks\HdAudioDevice.cpp" />
<ClCompile Include="NES\HdPacks\HdNesPack.cpp" />
<ClCompile Include="NES\HdPacks\HdNesPpu.cpp" />
@ -444,6 +455,11 @@
<ClCompile Include="Debugger\Debugger.cpp" />
<ClCompile Include="Shared\Video\DebugHud.cpp" />
<ClCompile Include="Shared\Video\DebugStats.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\Cx4TraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\NecDspTraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\GsuTraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\SpcTraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\SnesCpuTraceLogger.cpp" />
<ClCompile Include="SNES\SnesDefaultVideoFilter.cpp" />
<ClCompile Include="Debugger\Disassembler.cpp" />
<ClCompile Include="Debugger\DisassemblyInfo.cpp" />
@ -543,7 +559,6 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='PGO Optimize|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SNES\Coprocessors\SGB\SuperGameboy.cpp" />
<ClCompile Include="Debugger\TraceLogger.cpp" />
<ClCompile Include="Shared\Video\VideoDecoder.cpp" />
<ClCompile Include="Shared\Video\VideoRenderer.cpp" />
<ClCompile Include="Shared\Audio\WaveRecorder.cpp" />

View file

@ -10,9 +10,6 @@
<ClInclude Include="SNES\MemoryManager.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="Debugger\TraceLogger.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="Debugger\Debugger.h">
<Filter>Debugger</Filter>
</ClInclude>
@ -801,14 +798,21 @@
<ClInclude Include="Shared\KeyDefinitions.h" />
<ClInclude Include="NES\Debugger\NesAssembler.h" />
<ClInclude Include="NES\Debugger\NesEventManager.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\SnesCpuTraceLogger.h" />
<ClInclude Include="Debugger\BaseTraceLogger.h" />
<ClInclude Include="Debugger\TraceLogFileSaver.h" />
<ClInclude Include="Debugger\ITraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\SpcTraceLogger.h" />
<ClInclude Include="NES\Debugger\NesTraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\GsuTraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\Cx4TraceLogger.h" />
<ClInclude Include="SNES\Debugger\TraceLogger\NecDspTraceLogger.h" />
<ClInclude Include="Gameboy\Debugger\GbTraceLogger.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="SNES\Cpu.cpp">
<Filter>SNES</Filter>
</ClCompile>
<ClCompile Include="Debugger\TraceLogger.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\Debugger.cpp">
<Filter>Debugger</Filter>
</ClCompile>
@ -1238,6 +1242,13 @@
<ClCompile Include="NES\Debugger\NesAssembler.cpp" />
<ClCompile Include="NES\Debugger\NesEventManager.cpp" />
<ClCompile Include="NES\BaseNesPpu.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\SnesCpuTraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\SpcTraceLogger.cpp" />
<ClCompile Include="NES\Debugger\NesTraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\GsuTraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\Cx4TraceLogger.cpp" />
<ClCompile Include="SNES\Debugger\TraceLogger\NecDspTraceLogger.cpp" />
<ClCompile Include="Gameboy\Debugger\GbTraceLogger.cpp" />
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -0,0 +1,517 @@
#pragma once
#include "stdafx.h"
#include <regex>
#include "Utilities/SimpleLock.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/LabelManager.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DebugUtilities.h"
#include "Debugger/DebugBreakHelper.h"
#include "Debugger/ITraceLogger.h"
#include "Debugger/ExpressionEvaluator.h"
#include "Utilities/HexUtilities.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
class IConsole;
class Debugger;
class LabelManager;
class MemoryDumper;
class EmuSettings;
enum class RowDataType
{
Text = 0,
ByteCode,
Disassembly,
EffectiveAddress,
MemoryValue,
Align,
PC,
A,
B,
C,
D,
E,
F,
H,
K,
L,
M,
N,
X,
Y,
DB,
SP,
PS,
Cycle,
Scanline,
HClock,
FrameCount,
CycleCount,
R0,
R1,
R2,
R3,
R4,
R5,
R6,
R7,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
Src,
Dst,
MAR,
MDR,
DPR,
ML,
MH,
PB,
P,
RP,
DP,
DR,
SR,
TR,
TRB,
FlagsA,
FlagsB
};
struct TraceLogPpuState
{
uint32_t Cycle;
uint32_t HClock;
int32_t Scanline;
uint32_t FrameCount;
};
struct RowPart
{
RowDataType DataType;
string Text;
bool DisplayInHex;
int MinWidth;
};
template<typename TraceLoggerType, typename CpuStateType>
class BaseTraceLogger : public ITraceLogger
{
protected:
static constexpr int ExecutionLogSize = 30000;
TraceLoggerOptions _options;
IConsole* _console;
EmuSettings* _settings;
LabelManager* _labelManager;
MemoryDumper* _memoryDumper;
Debugger* _debugger;
CpuType _cpuType = CpuType::Cpu;
SnesMemoryType _cpuMemoryType = SnesMemoryType::CpuMemory;
vector<RowPart> _rowParts;
uint32_t _currentPos = 0;
bool _pendingLog = false;
CpuStateType _lastState = {};
DisassemblyInfo _lastDisassemblyInfo = {};
CpuStateType* _cpuState = nullptr;
DisassemblyInfo *_disassemblyCache = nullptr;
uint64_t* _rowIds = nullptr;
TraceLogPpuState* _ppuState = nullptr;
unique_ptr<ExpressionEvaluator> _expEvaluator;
ExpressionData _conditionData;
void WriteByteCode(DisassemblyInfo& info, RowPart& rowPart, string& output)
{
string byteCode;
info.GetByteCode(byteCode);
if(!rowPart.DisplayInHex) {
//Remove $ marks if not in "hex" mode (but still display the bytes as hex)
byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end());
}
WriteStringValue(output, byteCode, rowPart);
}
void WriteDisassembly(DisassemblyInfo& info, RowPart& rowPart, uint8_t sp, uint32_t pc, string& output)
{
int indentLevel = 0;
string code;
if(_options.IndentCode) {
indentLevel = 0xFF - (sp & 0xFF);
code = std::string(indentLevel, ' ');
}
LabelManager* labelManager = _options.UseLabels ? _labelManager : nullptr;
info.GetDisassembly(code, pc, labelManager, _settings);
WriteStringValue(output, code, rowPart);
}
void WriteEffectiveAddress(DisassemblyInfo& info, RowPart& rowPart, void* cpuState, string& output, SnesMemoryType cpuMemoryType, CpuType cpuType)
{
int32_t effectiveAddress = info.GetEffectiveAddress(_debugger, cpuState, cpuType);
if(effectiveAddress >= 0) {
if(_options.UseLabels) {
AddressInfo addr { effectiveAddress, cpuMemoryType };
string label = _labelManager->GetLabel(addr);
if(!label.empty()) {
WriteStringValue(output, " [" + label + "]", rowPart);
return;
}
}
WriteStringValue(output, " [" + HexUtilities::ToHex24(effectiveAddress) + "]", rowPart);
}
}
void WriteMemoryValue(DisassemblyInfo& info, RowPart& rowPart, void* cpuState, string& output, SnesMemoryType memType, CpuType cpuType)
{
int32_t address = info.GetEffectiveAddress(_debugger, cpuState, cpuType);
if(address >= 0) {
uint8_t valueSize;
uint16_t value = info.GetMemoryValue(address, _memoryDumper, memType, valueSize);
if(rowPart.DisplayInHex) {
output += "= $";
if(valueSize == 2) {
WriteIntValue(output, (uint16_t)value, rowPart);
} else {
WriteIntValue(output, (uint8_t)value, rowPart);
}
} else {
output += "= ";
}
}
}
void GetStatusFlag(const char* activeStatusLetters, const char* inactiveStatusLetters, string& output, uint8_t ps, RowPart& part, int length = 8)
{
if(part.DisplayInHex) {
WriteIntValue(output, ps, part);
} else {
string flags;
for(int i = 0; i < length; i++) {
if(ps & 0x80) {
flags += activeStatusLetters[i];
} else if(part.MinWidth >= length) {
flags += inactiveStatusLetters[i];
}
ps <<= 1;
}
WriteStringValue(output, flags, part);
}
}
void WriteAlign(int originalSize, RowPart& rowPart, string& output)
{
if((int)output.size() - originalSize < rowPart.MinWidth) {
output += std::string(rowPart.MinWidth - (output.size() - originalSize), ' ');
}
}
void WriteIntValue(string& output, uint32_t value, RowPart& rowPart)
{
string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value);
if(rowPart.MinWidth > (int)str.size()) {
if(rowPart.DisplayInHex) {
str = std::string(rowPart.MinWidth - str.size(), '0') + str;
} else {
str += std::string(rowPart.MinWidth - str.size(), ' ');
}
}
output += str;
}
void WriteIntValue(string& output, int32_t value, RowPart& rowPart)
{
string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value);
output += str;
if(rowPart.MinWidth > (int)str.size()) {
output += std::string(rowPart.MinWidth - str.size(), ' ');
}
}
void WriteIntValue(string& output, uint16_t value, RowPart& rowPart)
{
string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value);
output += str;
if(rowPart.MinWidth > (int)str.size()) {
output += std::string(rowPart.MinWidth - str.size(), ' ');
}
}
void WriteIntValue(string& output, uint8_t value, RowPart& rowPart)
{
string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value);
output += str;
if(rowPart.MinWidth > (int)str.size()) {
output += std::string(rowPart.MinWidth - str.size(), ' ');
}
}
void WriteStringValue(string& output, string value, RowPart& rowPart)
{
output += value;
if(rowPart.MinWidth > (int)value.size()) {
output += std::string(rowPart.MinWidth - value.size(), ' ');
}
}
void AddRow(CpuStateType& cpuState, DisassemblyInfo& disassemblyInfo)
{
_disassemblyCache[_currentPos] = disassemblyInfo;
_cpuState[_currentPos] = cpuState;
((TraceLoggerType*)this)->LogPpuState();
_rowIds[_currentPos] = ITraceLogger::NextRowId;
ITraceLogger::NextRowId++;
_pendingLog = false;
/*if(_logToFile) {
((TraceLoggerType*)this)->GetTraceRow(_outputBuffer, _disassemblyCache[_currentPos], cpuState);
if(_outputBuffer.size() > 32768) {
_outputFile << _outputBuffer;
_outputBuffer.clear();
}
}*/
_currentPos = (_currentPos + 1) % ExecutionLogSize;
}
void ParseFormatString(string format)
{
_rowParts.clear();
std::regex formatRegex = std::regex("(\\[\\s*([^[]*?)\\s*(,\\s*([\\d]*)\\s*(h){0,1}){0,1}\\s*\\])|([^[]*)", std::regex_constants::icase);
std::sregex_iterator start = std::sregex_iterator(format.cbegin(), format.cend(), formatRegex);
std::sregex_iterator end = std::sregex_iterator();
for(std::sregex_iterator it = start; it != end; it++) {
const std::smatch& match = *it;
if(match.str(1) == "") {
RowPart part = {};
part.DataType = RowDataType::Text;
part.Text = match.str(6);
_rowParts.push_back(part);
} else {
RowPart part = {};
string tag = match.str(2);
part.DataType = InternalGetFormatTagType(tag);
if(part.DataType == RowDataType::Text) {
part.Text = "[Invalid tag]";
}
if(!match.str(4).empty()) {
try {
part.MinWidth = std::stoi(match.str(4));
} catch(std::exception&) {
}
}
part.DisplayInHex = match.str(5) == "h";
_rowParts.push_back(part);
}
}
}
RowDataType InternalGetFormatTagType(string& tag)
{
if(tag == "ByteCode") {
return RowDataType::ByteCode;
} else if(tag == "Disassembly") {
return RowDataType::Disassembly;
} else if(tag == "EffectiveAddress") {
return RowDataType::EffectiveAddress;
} else if(tag == "MemoryValue") {
return RowDataType::MemoryValue;
} else if(tag == "Align") {
return RowDataType::Align;
} else if(tag == "PC") {
return RowDataType::PC;
} else if(tag == "Cycle") {
return RowDataType::Cycle;
} else if(tag == "HClock") {
return RowDataType::HClock;
} else if(tag == "Scanline") {
return RowDataType::Scanline;
} else if(tag == "FrameCount") {
return RowDataType::FrameCount;
} else if(tag == "CycleCount") {
return RowDataType::CycleCount;
}
return GetFormatTagType(tag);
}
virtual RowDataType GetFormatTagType(string& tag) = 0;
void ProcessSharedTag(RowPart& rowPart, string& output, CpuStateType& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo)
{
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, ((TraceLoggerType*)this)->GetStackPointer(cpuState), ((TraceLoggerType*)this)->GetProgramCounter(cpuState), output); break;
case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, _cpuMemoryType, _cpuType); break;
case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, _cpuMemoryType, _cpuType); break;
case RowDataType::Align: WriteAlign(0, rowPart, output); break;
case RowDataType::Cycle: WriteIntValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteIntValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteIntValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteIntValue(output, ppuState.FrameCount, rowPart); break;
case RowDataType::CycleCount:
WriteIntValue(output, (uint32_t)(((TraceLoggerType*)this)->GetCycleCount(cpuState) >> 32), rowPart);
WriteIntValue(output, (uint32_t)((TraceLoggerType*)this)->GetCycleCount(cpuState), rowPart);
break;
case RowDataType::PC: WriteStringValue(output, HexUtilities::ToHex(((TraceLoggerType*)this)->GetProgramCounter(cpuState)), rowPart); break;
}
}
public:
BaseTraceLogger(Debugger* debugger, CpuType cpuType)
{
_debugger = debugger;
_console = debugger->GetConsole();
_settings = debugger->GetEmulator()->GetSettings();
_labelManager = debugger->GetLabelManager().get();
_memoryDumper = debugger->GetMemoryDumper();
_options = {};
_currentPos = 0;
_pendingLog = false;
_disassemblyCache = new DisassemblyInfo[BaseTraceLogger::ExecutionLogSize];
_rowIds = new uint64_t[BaseTraceLogger::ExecutionLogSize];
memset(_disassemblyCache, 0, sizeof(DisassemblyInfo) * BaseTraceLogger::ExecutionLogSize);
memset(_rowIds, 0, sizeof(uint64_t) * BaseTraceLogger::ExecutionLogSize);
_ppuState = new TraceLogPpuState[BaseTraceLogger::ExecutionLogSize];
memset(_ppuState, 0, sizeof(TraceLogPpuState) * BaseTraceLogger::ExecutionLogSize);
_cpuState = new CpuStateType[BaseTraceLogger::ExecutionLogSize];
memset(_cpuState, 0, sizeof(CpuStateType) * BaseTraceLogger::ExecutionLogSize);
_cpuType = cpuType;
_cpuMemoryType = DebugUtilities::GetCpuMemoryType(cpuType);
_expEvaluator.reset(new ExpressionEvaluator(debugger, cpuType));
}
virtual ~BaseTraceLogger()
{
delete[] _disassemblyCache;
delete[] _rowIds;
delete[] _ppuState;
delete[] _cpuState;
}
void Clear()
{
}
void LogNonExec(MemoryOperationInfo& operation)
{
if(_pendingLog) {
int pos = _currentPos - 1;
if(pos < 0) {
pos = BaseTraceLogger::ExecutionLogSize - 1;
}
if(ConditionMatches(_lastState, _lastDisassemblyInfo, operation)) {
AddRow(_lastState, _lastDisassemblyInfo);
_pendingLog = false;
}
}
}
void Log(CpuStateType& cpuState, DisassemblyInfo& disassemblyInfo, MemoryOperationInfo& operation)
{
if(_enabled) {
//For the sake of performance, only log data for the CPUs we're actively displaying/logging
if(ConditionMatches(cpuState, disassemblyInfo, operation)) {
AddRow(cpuState, disassemblyInfo);
} else {
_pendingLog = true;
_lastState = cpuState;
_lastDisassemblyInfo = disassemblyInfo;
}
}
}
void SetOptions(TraceLoggerOptions options) override
{
DebugBreakHelper helper(_debugger);
_options = options;
_enabled = options.Enabled;
string condition = _options.Condition;
string format = _options.Format;
_conditionData = ExpressionData();
if(!condition.empty()) {
bool success = false;
ExpressionData rpnList = _expEvaluator->GetRpnList(condition, success);
if(success) {
_conditionData = rpnList;
}
}
ParseFormatString(format);
}
int64_t GetRowId(uint32_t offset) override
{
int32_t pos = ((int32_t)_currentPos - (int32_t)offset);
int32_t i = (pos > 0 ? pos : BaseTraceLogger::ExecutionLogSize + pos) - 1;
if(!_disassemblyCache[i].IsInitialized()) {
return -1;
}
return _rowIds[i];
}
bool ConditionMatches(BaseState &state, DisassemblyInfo &disassemblyInfo, MemoryOperationInfo &operationInfo)
{
if(!_conditionData.RpnQueue.empty()) {
EvalResultType type;
if(!_expEvaluator->Evaluate(_conditionData, state, type, operationInfo)) {
return false;
}
}
return true;
}
void GetExecutionTrace(TraceRow& row, uint32_t offset) override
{
int pos = ((int)_currentPos - offset);
int index = (pos > 0 ? pos : BaseTraceLogger::ExecutionLogSize + pos) - 1;
CpuStateType& state = _cpuState[index];
string logOutput;
((TraceLoggerType*)this)->GetTraceRow(logOutput, state, _ppuState[index], _disassemblyCache[index]);
row.Type = _cpuType;
_disassemblyCache[index].GetByteCode(row.ByteCode);
row.ByteCodeSize = _disassemblyCache[index].GetOpSize();
row.ProgramCounter = ((TraceLoggerType*)this)->GetProgramCounter(state);
memcpy(row.LogOutput, logOutput.c_str(), logOutput.size());
}
};

View file

@ -100,17 +100,15 @@ void CodeDataLogger::CalculateStats()
void CodeDataLogger::SetFlags(int32_t absoluteAddr, uint8_t flags)
{
if(absoluteAddr >= 0 && absoluteAddr < (int32_t)_prgSize) {
if((_cdlData[absoluteAddr] & flags) != flags) {
if(flags & CdlFlags::Code) {
_cdlData[absoluteAddr] = flags | (_cdlData[absoluteAddr] & ~(CdlFlags::Data | CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
} else if(flags & CdlFlags::Data) {
if(!IsCode(absoluteAddr)) {
_cdlData[absoluteAddr] |= flags;
}
} else {
if((_cdlData[absoluteAddr] & flags) != flags) {
if(flags & CdlFlags::Code) {
_cdlData[absoluteAddr] = flags | (_cdlData[absoluteAddr] & ~(CdlFlags::Data | CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
} else if(flags & CdlFlags::Data) {
if(!IsCode(absoluteAddr)) {
_cdlData[absoluteAddr] |= flags;
}
} else {
_cdlData[absoluteAddr] |= flags;
}
}
}

View file

@ -263,7 +263,7 @@ struct StepRequest
int32_t StepCount = -1;
int32_t PpuStepCount = -1;
int32_t BreakAddress = -1;
int32_t BreakScanline = -1;
int32_t BreakScanline = INT32_MIN;
};
enum class CpuType : uint8_t

View file

@ -6,7 +6,7 @@
class DebugUtilities
{
public:
static SnesMemoryType GetCpuMemoryType(CpuType type)
static constexpr SnesMemoryType GetCpuMemoryType(CpuType type)
{
switch(type) {
case CpuType::Cpu: return SnesMemoryType::CpuMemory;
@ -22,7 +22,7 @@ public:
throw std::runtime_error("Invalid CPU type");
}
static CpuType ToCpuType(SnesMemoryType type)
static constexpr CpuType ToCpuType(SnesMemoryType type)
{
switch(type) {
case SnesMemoryType::SpcMemory:
@ -81,7 +81,7 @@ public:
return SnesMemoryType::NesMemory;
}
static bool IsPpuMemory(SnesMemoryType memType)
static constexpr bool IsPpuMemory(SnesMemoryType memType)
{
switch(memType) {
case SnesMemoryType::VideoRam:
@ -102,7 +102,7 @@ public:
}
}
static bool IsRomMemory(SnesMemoryType memType)
static constexpr bool IsRomMemory(SnesMemoryType memType)
{
switch(memType) {
case SnesMemoryType::PrgRom:

View file

@ -2,7 +2,6 @@
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/MemoryDumper.h"
#include "Debugger/MemoryAccessCounter.h"
#include "Debugger/CodeDataLogger.h"
@ -46,6 +45,7 @@
#include "Shared/NotificationManager.h"
#include "Shared/BaseState.h"
#include "Shared/Emulator.h"
#include "Debugger/ITraceLogger.h"
#include "Shared/Interfaces/IConsole.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/FolderUtilities.h"
@ -53,6 +53,8 @@
#include "MemoryOperationType.h"
#include "EventType.h"
uint64_t ITraceLogger::NextRowId = 0;
Debugger::Debugger(Emulator* emu, IConsole* console)
{
_executionStopped = true;
@ -67,14 +69,13 @@ Debugger::Debugger(Emulator* emu, IConsole* console)
_memoryDumper.reset(new MemoryDumper(this));
_disassembler.reset(new Disassembler(console, this));
_traceLogger.reset(new TraceLogger(this));
_memoryAccessCounter.reset(new MemoryAccessCounter(this));
_ppuTools.reset(new PpuTools(_emu));
_ppuTools.reset(new PpuTools(this, _emu));
_scriptManager.reset(new ScriptManager(this));
vector<CpuType> cpuTypes = _emu->GetCpuTypes();
_mainCpuType = cpuTypes[0];
for(CpuType type : cpuTypes) {
_cpuTypes = _emu->GetCpuTypes();
_mainCpuType = _cpuTypes[0];
for(CpuType type : _cpuTypes) {
unique_ptr<IDebugger> &debugger = _debuggers[(int)type].Debugger;
switch(type) {
case CpuType::Cpu: debugger.reset(new CpuDebugger(this, CpuType::Cpu)); break;
@ -91,6 +92,10 @@ Debugger::Debugger(Emulator* emu, IConsole* console)
_debuggers[(int)type].Evaluator.reset(new ExpressionEvaluator(this, type));
}
for(CpuType type : _cpuTypes) {
_debuggers[(int)type].Debugger->Init();
}
_breakRequestCount = 0;
_suspendRequestCount = 0;
@ -134,10 +139,25 @@ void Debugger::Reset()
}
}
template<CpuType type, typename DebuggerType>
DebuggerType* Debugger::GetDebugger()
{
return (DebuggerType*)_debuggers[(int)type].Debugger.get();
}
template<CpuType type>
void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
_debuggers[(int)type].Debugger->ProcessRead(addr, value, opType);
switch(type) {
case CpuType::Cpu: GetDebugger<type, CpuDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Spc: GetDebugger<type, SpcDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::NecDsp: GetDebugger<type, NecDspDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Sa1: GetDebugger<type, CpuDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Gsu: GetDebugger<type, GsuDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Cx4: GetDebugger<type, Cx4Debugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Nes: GetDebugger<type, NesDebugger>()->ProcessRead(addr, value, opType); break;
}
if(_scriptManager->HasScript()) {
_scriptManager->ProcessMemoryOperation(addr, value, opType, type);
@ -147,7 +167,16 @@ void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationTy
template<CpuType type>
void Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
_debuggers[(int)type].Debugger->ProcessWrite(addr, value, opType);
switch(type) {
case CpuType::Cpu: GetDebugger<type, CpuDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Spc: GetDebugger<type, SpcDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::NecDsp: GetDebugger<type, NecDspDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Sa1: GetDebugger<type, CpuDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Gsu: GetDebugger<type, GsuDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Cx4: GetDebugger<type, Cx4Debugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Nes: GetDebugger<type, NesDebugger>()->ProcessWrite(addr, value, opType); break;
}
if(_scriptManager->HasScript()) {
_scriptManager->ProcessMemoryOperation(addr, value, opType, type);
@ -181,22 +210,17 @@ void Debugger::ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memo
template<CpuType type>
void Debugger::ProcessPpuCycle()
{
uint16_t scanline = 0;
uint16_t cycle = 0;
int16_t scanline;
uint16_t cycle;
switch(type) {
case CpuType::Cpu:
case CpuType::Gameboy:
case CpuType::Nes:
_debuggers[(int)type].Debugger->ProcessPpuCycle(scanline, cycle);
break;
case CpuType::Cpu: GetDebugger<type, CpuDebugger>()->ProcessPpuCycle(scanline, cycle); break;
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessPpuCycle(scanline, cycle); break;
case CpuType::Nes: GetDebugger<type, NesDebugger>()->ProcessPpuCycle(scanline, cycle); break;
default: throw std::runtime_error("Invalid cpu type");
}
_ppuTools->UpdateViewers(scanline, cycle, type);
if(_breakRequestCount > 0) {
SleepUntilResume(BreakSource::Unspecified);
}
}
void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operation, int breakpointId)
@ -205,11 +229,11 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati
return;
}
_emu->GetSoundMixer()->StopAudio();
_executionStopped = true;
if(source != BreakSource::Unspecified || _breakRequestCount == 0) {
_emu->GetSoundMixer()->StopAudio();
//Only trigger code break event if the pause was caused by user action
BreakEvent evt = {};
evt.BreakpointId = breakpointId;
@ -222,7 +246,7 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati
}
while((_waitForBreakResume && !_suspendRequestCount) || _breakRequestCount) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(_breakRequestCount ? 1 : 10));
}
_executionStopped = false;
@ -363,6 +387,7 @@ void Debugger::SuspendDebugger(bool release)
void Debugger::BreakImmediately(BreakSource source)
{
//TODO
bool gbDebugger = _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled);
if(source == BreakSource::GbDisableLcdOutsideVblank && (!gbDebugger || !_settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnDisableLcdOutsideVblank))) {
return;
@ -437,8 +462,8 @@ void Debugger::RebuildPrgCache(CpuType cpuType)
uint32_t prgRomSize = cdl->GetPrgSize();
AddressInfo addrInfo;
//TODO
addrInfo.Type = cpuType == CpuType::Gameboy ? SnesMemoryType::GbPrgRom : SnesMemoryType::PrgRom;
for(uint32_t i = 0; i < prgRomSize; i++) {
if(cdl->IsCode(i)) {
addrInfo.Address = (int32_t)i;
@ -535,9 +560,55 @@ void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption str
}*/
}
shared_ptr<TraceLogger> Debugger::GetTraceLogger()
ITraceLogger* Debugger::GetTraceLogger(CpuType cpuType)
{
return _traceLogger;
if(_debuggers[(int)cpuType].Debugger) {
return _debuggers[(int)cpuType].Debugger->GetTraceLogger();
}
return nullptr;
}
uint32_t Debugger::GetExecutionTrace(TraceRow output[], uint32_t startOffset, uint32_t maxLineCount)
{
DebugBreakHelper helper(this);
uint32_t offsetsByCpu[(int)DebugUtilities::GetLastCpuType() + 1] = {};
vector<CpuType> cpuTypes = _emu->GetCpuTypes();
uint32_t count = 0;
int64_t lastRowId = ITraceLogger::NextRowId;
while(count < maxLineCount) {
bool added = false;
for(CpuType cpuType : cpuTypes) {
ITraceLogger* logger = GetTraceLogger(cpuType);
if(logger) {
uint32_t& offset = offsetsByCpu[(int)cpuType];
int64_t rowId = logger->GetRowId(offset);
if(rowId == -1 || rowId != lastRowId - 1) {
continue;
}
lastRowId = rowId;
if(startOffset > 0) {
//Skip rows until the part the UI wants to display is reached
startOffset--;
} else {
logger->GetExecutionTrace(output[count], offset);
count++;
}
offset++;
added = true;
break;
}
}
if(!added) {
break;
}
}
return count;
}
MemoryDumper* Debugger::GetMemoryDumper()

View file

@ -16,7 +16,6 @@ class InternalRegisters;
class DmaController;
class EmuSettings;
class TraceLogger;
class ExpressionEvaluator;
class MemoryDumper;
class MemoryAccessCounter;
@ -31,7 +30,9 @@ class Breakpoint;
class IEventManager;
class IAssembler;
class IDebugger;
class ITraceLogger;
struct TraceRow;
struct BaseState;
enum class EventType;
@ -55,10 +56,10 @@ private:
CpuInfo _debuggers[(int)DebugUtilities::GetLastCpuType() + 1];
CpuType _mainCpuType = CpuType::Cpu;
vector<CpuType> _cpuTypes;
ConsoleType _consoleType = ConsoleType::Snes;
shared_ptr<ScriptManager> _scriptManager;
shared_ptr<TraceLogger> _traceLogger;
shared_ptr<MemoryDumper> _memoryDumper;
shared_ptr<MemoryAccessCounter> _memoryAccessCounter;
shared_ptr<CodeDataLogger> _codeDataLogger;
@ -77,6 +78,8 @@ private:
void Reset();
template<CpuType type, typename DebuggerType> DebuggerType* GetDebugger();
public:
Debugger(Emulator* emu, IConsole* console);
~Debugger();
@ -126,7 +129,9 @@ public:
void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption);
shared_ptr<TraceLogger> GetTraceLogger();
uint32_t GetExecutionTrace(TraceRow output[], uint32_t startOffset, uint32_t maxLineCount);
ITraceLogger* GetTraceLogger(CpuType cpuType);
MemoryDumper* GetMemoryDumper();
shared_ptr<MemoryAccessCounter> GetMemoryAccessCounter();
shared_ptr<CodeDataLogger> GetCodeDataLogger(CpuType cpuType);

View file

@ -55,7 +55,7 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy
int returnSize = 0;
int32_t address = addrInfo.Address;
while(address >= 0 && address < (int32_t)src.Cache.size()) {
do {
DisassemblyInfo &disInfo = src.Cache[address];
if(!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags)) {
disInfo.Initialize(src.Data+address, cpuFlags, type);
@ -77,7 +77,7 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy
disInfo.UpdateCpuFlags(cpuFlags);
address += disInfo.GetOpSize();
}
} while(address >= 0 && address < (int32_t)src.Cache.size());
return returnSize;
}

View file

@ -127,8 +127,8 @@ public:
struct ExpressionData
{
std::vector<int64_t> RpnQueue;
std::vector<string> Labels;
vector<int64_t> RpnQueue;
vector<string> Labels;
};
class ExpressionEvaluator
@ -138,9 +138,9 @@ private:
static const vector<int> _binaryPrecedence;
static const vector<string> _unaryOperators;
static const vector<int> _unaryPrecedence;
static const std::unordered_set<string> _operators;
static const unordered_set<string> _operators;
std::unordered_map<string, ExpressionData, StringHasher> _cache;
unordered_map<string, ExpressionData, StringHasher> _cache;
SimpleLock _cacheLock;
Debugger* _debugger;

View file

@ -7,7 +7,9 @@ class CallstackManager;
class IAssembler;
class IEventManager;
class CodeDataLogger;
class ITraceLogger;
struct BaseState;
enum class EventType;
enum class MemoryOperationType;
class IDebugger
@ -18,17 +20,17 @@ public:
virtual void Step(int32_t stepCount, StepType type) = 0;
virtual void Reset() = 0;
virtual void Run() = 0;
virtual void Init() {}
virtual void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType opType) {}
virtual void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType opType) {}
virtual void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) {}
virtual void ProcessPpuCycle(uint16_t& cycle, uint16_t& scanline) {}
virtual BreakpointManager* GetBreakpointManager() = 0;
virtual shared_ptr<CallstackManager> GetCallstackManager() = 0;
virtual shared_ptr<IAssembler> GetAssembler() = 0;
virtual shared_ptr<IEventManager> GetEventManager() = 0;
virtual shared_ptr<CodeDataLogger> GetCodeDataLogger() = 0;
virtual ITraceLogger* GetTraceLogger() = 0;
virtual BaseState& GetState() = 0;
};

View file

@ -0,0 +1,36 @@
#pragma once
#include "stdafx.h"
#include "DebugTypes.h"
struct TraceRow
{
uint32_t ProgramCounter;
CpuType Type;
uint8_t ByteCode[4];
uint8_t ByteCodeSize;
char LogOutput[500];
};
struct TraceLoggerOptions
{
bool Enabled;
bool IndentCode;
bool UseLabels;
char Condition[1000];
char Format[1000];
};
class ITraceLogger
{
protected:
bool _enabled = false;
public:
static uint64_t NextRowId;
virtual int64_t GetRowId(uint32_t offset) = 0;
virtual void GetExecutionTrace(TraceRow& row, uint32_t offset) = 0;
virtual void SetOptions(TraceLoggerOptions options) = 0;
__forceinline bool IsEnabled() { return _enabled; }
};

View file

@ -35,8 +35,11 @@ MemoryDumper::MemoryDumper(Debugger* debugger)
_spc = c->GetSpc().get();
_memoryManager = c->GetMemoryManager().get();
_cartridge = c->GetCartridge().get();
_gameboy = c->GetCartridge()->GetGameboy();
} else if(NesConsole* c = dynamic_cast<NesConsole*>(console)) {
_nesMemoryManager = c->GetMemoryManager();
} else if(Gameboy* c = dynamic_cast<Gameboy*>(console)) {
_gameboy = c;
}
}
@ -122,8 +125,8 @@ void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer)
break;
case SnesMemoryType::GameboyMemory: {
if(_cartridge->GetGameboy()) {
GbMemoryManager* memManager = _cartridge->GetGameboy()->GetMemoryManager();
if(_gameboy) {
GbMemoryManager* memManager = _gameboy->GetMemoryManager();
for(int i = 0; i <= 0xFFFF; i++) {
buffer[i] = memManager->DebugRead(i);
}
@ -183,7 +186,7 @@ void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, u
case SnesMemoryType::Sa1Memory: _cartridge->GetSa1()->GetMemoryMappings()->DebugWrite(address, value); break;
case SnesMemoryType::GsuMemory: _cartridge->GetGsu()->GetMemoryMappings()->DebugWrite(address, value); break;
case SnesMemoryType::Cx4Memory: _cartridge->GetCx4()->GetMemoryMappings()->DebugWrite(address, value); break;
case SnesMemoryType::GameboyMemory: _cartridge->GetGameboy()->GetMemoryManager()->DebugWrite(address, value); break;
case SnesMemoryType::GameboyMemory: _gameboy->GetMemoryManager()->DebugWrite(address, value); break;
case SnesMemoryType::NesMemory: _nesMemoryManager->DebugWrite(address, value); break;
default:
@ -217,7 +220,7 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address
case SnesMemoryType::Sa1Memory: return _cartridge->GetSa1()->GetMemoryMappings()->Peek(address);
case SnesMemoryType::GsuMemory: return _cartridge->GetGsu()->GetMemoryMappings()->Peek(address);
case SnesMemoryType::Cx4Memory: return _cartridge->GetCx4()->GetMemoryMappings()->Peek(address);
case SnesMemoryType::GameboyMemory: return _cartridge->GetGameboy()->GetMemoryManager()->DebugRead(address);
case SnesMemoryType::GameboyMemory: return _gameboy->GetMemoryManager()->DebugRead(address);
case SnesMemoryType::NesMemory: return _nesMemoryManager->DebugRead(address);
default:

View file

@ -8,6 +8,7 @@ class NesMemoryManager;
class BaseCartridge;
class Ppu;
class Spc;
class Gameboy;
class Emulator;
class Debugger;
class Disassembler;
@ -16,14 +17,15 @@ enum class SnesMemoryType;
class MemoryDumper
{
private:
Emulator* _emu;
Ppu* _ppu;
Spc* _spc;
MemoryManager* _memoryManager;
NesMemoryManager* _nesMemoryManager;
BaseCartridge* _cartridge;
Debugger* _debugger;
Disassembler* _disassembler;
Emulator* _emu = nullptr;
Ppu* _ppu = nullptr;
Spc* _spc = nullptr;
Gameboy* _gameboy = nullptr;
MemoryManager* _memoryManager = nullptr;
NesMemoryManager* _nesMemoryManager = nullptr;
BaseCartridge* _cartridge = nullptr;
Debugger* _debugger = nullptr;
Disassembler* _disassembler = nullptr;
public:
MemoryDumper(Debugger* debugger);

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "Debugger/PpuTools.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DebugBreakHelper.h"
#include "Shared/NotificationManager.h"
#include "Shared/SettingTypes.h"
#include "SNES/SnesDefaultVideoFilter.h"
@ -10,9 +11,10 @@
#include "SNES/MemoryManager.h"
#include "Gameboy/GbTypes.h"
PpuTools::PpuTools(Emulator *emu)
PpuTools::PpuTools(Debugger* debugger, Emulator *emu)
{
_emu = emu;
_debugger = debugger;
}
uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift)
@ -387,7 +389,8 @@ void PpuTools::GetSpritePreview(GetSpritePreviewOptions options, PpuState state,
void PpuTools::SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle, CpuType cpuType)
{
//TODO Thread safety
DebugBreakHelper helper(_debugger);
ViewerRefreshConfig cfg;
cfg.Scanline = scanline;
cfg.Cycle = cycle;
@ -397,7 +400,7 @@ void PpuTools::SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint1
void PpuTools::RemoveViewer(uint32_t viewerId)
{
//TODO Thread safety
DebugBreakHelper helper(_debugger);
_updateTimings.erase(viewerId);
}

View file

@ -5,6 +5,8 @@
#include "Shared/NotificationManager.h"
#include "SNES/PpuTypes.h"
class Debugger;
class Emulator;
class Ppu;
struct GbPpuState;
@ -18,7 +20,8 @@ struct ViewerRefreshConfig
class PpuTools
{
private:
Emulator *_emu;
Emulator* _emu;
Debugger* _debugger;
unordered_map<uint32_t, ViewerRefreshConfig> _updateTimings;
uint8_t GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift);
@ -29,7 +32,7 @@ private:
uint32_t GetRgbPixelColor(uint32_t* colors, uint8_t colorIndex, uint8_t palette, uint8_t bpp, bool directColorMode, uint16_t basePaletteOffset);
public:
PpuTools(Emulator *emu);
PpuTools(Debugger* debugger, Emulator *emu);
void GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, uint32_t* palette, uint32_t *outBuffer);
void GetTilemap(GetTilemapOptions options, PpuState state, uint8_t* vram, uint8_t* cgram, uint32_t *outBuffer);

View file

@ -0,0 +1,42 @@
#pragma once
#include "stdafx.h"
class TraceLogFileSaver
{
private:
bool _logToFile;
string _outputFilepath;
string _outputBuffer;
ofstream _outputFile;
public:
void StartLogging(string filename)
{
_outputBuffer.clear();
_outputFile.open(filename, ios::out | ios::binary);
_logToFile = true;
}
void StopLogging()
{
if(_logToFile) {
_logToFile = false;
if(_outputFile) {
if(!_outputBuffer.empty()) {
_outputFile << _outputBuffer;
}
_outputFile.close();
}
}
}
/*void LogExtraInfo(const char* log, uint32_t cycleCount)
{
if(_logToFile) {
//Flush current buffer
_outputFile << _outputBuffer;
_outputBuffer.clear();
_outputFile << "[" << log << " - Cycle: " << std::to_string(cycleCount) << "]" << (_options.UseWindowsEol ? "\r\n" : "\n");
}
}*/
};

View file

@ -1,789 +0,0 @@
#include "stdafx.h"
#include <regex>
#include <algorithm>
#include "Debugger/TraceLogger.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/LabelManager.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DebugUtilities.h"
#include "Debugger/DebugBreakHelper.h"
#include "SNES/MemoryManager.h"
#include "SNES/Console.h"
#include "SNES/CpuTypes.h"
#include "SNES/SpcTypes.h"
#include "SNES/Coprocessors/DSP/NecDspTypes.h"
#include "SNES/PpuTypes.h"
#include "SNES/Coprocessors/CX4/Cx4Types.h"
#include "SNES/Coprocessors/GSU/GsuTypes.h"
#include "Gameboy/GbTypes.h"
#include "NES/NesTypes.h"
#include "Utilities/HexUtilities.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
string TraceLogger::_executionTrace = "";
TraceLogger::TraceLogger(Debugger* debugger)
{
_debugger = debugger;
_console = debugger->GetConsole();
_settings = debugger->GetEmulator()->GetSettings();
_labelManager = debugger->GetLabelManager().get();
_memoryDumper = debugger->GetMemoryDumper();
_options = {};
_currentPos = 0;
_logCount = 0;
_logToFile = false;
_pendingLog = false;
_snesCpuState = new CpuState[TraceLogger::ExecutionLogSize];
_spcState = new SpcState[TraceLogger::ExecutionLogSize];
_necDspState = new NecDspState[TraceLogger::ExecutionLogSize];
_gsuState = new GsuState[TraceLogger::ExecutionLogSize];
_cx4State = new Cx4State[TraceLogger::ExecutionLogSize];
_gbCpuState = new GbCpuState[TraceLogger::ExecutionLogSize];
_nesCpuState = new NesCpuState[TraceLogger::ExecutionLogSize];
_disassemblyCache = new DisassemblyInfo[TraceLogger::ExecutionLogSize];
_logCpuType = new CpuType[TraceLogger::ExecutionLogSize];
SetOptions({});
_logCpu[(int)CpuType::Nes] = true;
}
TraceLogger::~TraceLogger()
{
StopLogging();
delete[] _disassemblyCache;
delete[] _logCpuType;
delete[] _snesCpuState;
delete[] _spcState;
delete[] _necDspState;
delete[] _gsuState;
delete[] _cx4State;
delete[] _gbCpuState;
delete[] _nesCpuState;
}
template<typename T>
void TraceLogger::WriteValue(string &output, T value, RowPart& rowPart)
{
string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value);
output += str;
if(rowPart.MinWidth > (int)str.size()) {
output += std::string(rowPart.MinWidth - str.size(), ' ');
}
}
template<>
void TraceLogger::WriteValue(string &output, string value, RowPart& rowPart)
{
output += value;
if(rowPart.MinWidth > (int)value.size()) {
output += std::string(rowPart.MinWidth - value.size(), ' ');
}
}
void TraceLogger::SetOptions(TraceLoggerOptions options)
{
DebugBreakHelper helper(_debugger);
_options = options;
_logCpu[(int)CpuType::Cpu] = options.LogCpu;
_logCpu[(int)CpuType::Spc] = options.LogSpc;
_logCpu[(int)CpuType::NecDsp] = options.LogNecDsp;
_logCpu[(int)CpuType::Sa1] = options.LogSa1;
_logCpu[(int)CpuType::Gsu] = options.LogGsu;
_logCpu[(int)CpuType::Cx4] = options.LogCx4;
_logCpu[(int)CpuType::Gameboy] = options.LogGameboy;
_logCpu[(int)CpuType::Nes] = options.LogNes;
string condition = _options.Condition;
string format = _options.Format;
/*_conditionData = ExpressionData();
if(!condition.empty()) {
bool success = false;
ExpressionData rpnList = _expEvaluator->GetRpnList(condition, success);
if(success) {
_conditionData = rpnList;
}
}*/
ParseFormatString(_rowParts, format);
ParseFormatString(_spcRowParts, "[PC,4h] [ByteCode,11h] [Disassembly][EffectiveAddress] [MemoryValue,h][Align,48] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,8] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_dspRowParts, "[PC,4h] [ByteCode,11h] [Disassembly] [Align,65] [A,2h] S:[SP,2h] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_gsuRowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,50] SRC:[X,2] DST:[Y,2] R0:[A,2h] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_cx4RowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,45] [A,2h] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_gbRowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,45] A:[A,2h] B:[B,2h] C:[C,2h] D:[D,2h] E:[E,2h] HL:[H,2h][L,2h] F:[F,2h] SP:[SP,4h] CYC:[Cycle,3] LY:[Scanline,3]");
ParseFormatString(_nesRowParts, "[PC,4h] [ByteCode,11h] [Disassembly][EffectiveAddress] [MemoryValue,2h][Align,48] A:[A,2h] X:[X,2h] Y:[Y,2h] P:[P,8] SP:[SP,2h] CYC:[Cycle,3] SL:[Scanline,3] FC:[FrameCount] CPU Cycle:[CycleCount]");
}
void TraceLogger::ParseFormatString(vector<RowPart> &rowParts, string format)
{
rowParts.clear();
std::regex formatRegex = std::regex("(\\[\\s*([^[]*?)\\s*(,\\s*([\\d]*)\\s*(h){0,1}){0,1}\\s*\\])|([^[]*)", std::regex_constants::icase);
std::sregex_iterator start = std::sregex_iterator(format.cbegin(), format.cend(), formatRegex);
std::sregex_iterator end = std::sregex_iterator();
for(std::sregex_iterator it = start; it != end; it++) {
const std::smatch& match = *it;
if(match.str(1) == "") {
RowPart part = {};
part.DataType = RowDataType::Text;
part.Text = match.str(6);
rowParts.push_back(part);
} else {
RowPart part = {};
string dataType = match.str(2);
if(dataType == "ByteCode") {
part.DataType = RowDataType::ByteCode;
} else if(dataType == "Disassembly") {
part.DataType = RowDataType::Disassembly;
} else if(dataType == "EffectiveAddress") {
part.DataType = RowDataType::EffectiveAddress;
} else if(dataType == "MemoryValue") {
part.DataType = RowDataType::MemoryValue;
} else if(dataType == "Align") {
part.DataType = RowDataType::Align;
} else if(dataType == "PC") {
part.DataType = RowDataType::PC;
} else if(dataType == "A") {
part.DataType = RowDataType::A;
} else if(dataType == "B") {
part.DataType = RowDataType::B;
} else if(dataType == "C") {
part.DataType = RowDataType::C;
} else if(dataType == "D") {
part.DataType = RowDataType::D;
} else if(dataType == "E") {
part.DataType = RowDataType::E;
} else if(dataType == "F") {
part.DataType = RowDataType::F;
} else if(dataType == "H") {
part.DataType = RowDataType::H;
} else if(dataType == "L") {
part.DataType = RowDataType::L;
} else if(dataType == "X") {
part.DataType = RowDataType::X;
} else if(dataType == "Y") {
part.DataType = RowDataType::Y;
} else if(dataType == "D") {
part.DataType = RowDataType::D;
} else if(dataType == "DB") {
part.DataType = RowDataType::DB;
} else if(dataType == "P") {
part.DataType = RowDataType::PS;
} else if(dataType == "SP") {
part.DataType = RowDataType::SP;
} else if(dataType == "Cycle") {
part.DataType = RowDataType::Cycle;
} else if(dataType == "HClock") {
part.DataType = RowDataType::HClock;
} else if(dataType == "Scanline") {
part.DataType = RowDataType::Scanline;
} else if(dataType == "FrameCount") {
part.DataType = RowDataType::FrameCount;
} else if(dataType == "CycleCount") {
part.DataType = RowDataType::CycleCount;
} else {
part.DataType = RowDataType::Text;
part.Text = "[Invalid tag]";
}
if(!match.str(4).empty()) {
try {
part.MinWidth = std::stoi(match.str(4));
} catch(std::exception&) {
}
}
part.DisplayInHex = match.str(5) == "h";
rowParts.push_back(part);
}
}
}
void TraceLogger::StartLogging(string filename)
{
_outputBuffer.clear();
_outputFile.open(filename, ios::out | ios::binary);
_logToFile = true;
}
void TraceLogger::StopLogging()
{
if(_logToFile) {
_logToFile = false;
if(_outputFile) {
if(!_outputBuffer.empty()) {
_outputFile << _outputBuffer;
}
_outputFile.close();
}
}
}
void TraceLogger::LogExtraInfo(const char *log, uint32_t cycleCount)
{
if(_logToFile && _options.ShowExtraInfo) {
//Flush current buffer
_outputFile << _outputBuffer;
_outputBuffer.clear();
_outputFile << "[" << log << " - Cycle: " << std::to_string(cycleCount) << "]" << (_options.UseWindowsEol ? "\r\n" : "\n");
}
}
template<CpuType cpuType>
void TraceLogger::GetStatusFlag(string &output, uint8_t ps, RowPart& part)
{
constexpr char cpuActiveStatusLetters[8] = { 'N', 'V', 'M', 'X', 'D', 'I', 'Z', 'C' };
constexpr char cpuInactiveStatusLetters[8] = { 'n', 'v', 'm', 'x', 'd', 'i', 'z', 'c' };
constexpr char spcActiveStatusLetters[8] = { 'N', 'V', 'P', 'B', 'H', 'I', 'Z', 'C' };
constexpr char spcInactiveStatusLetters[8] = { 'n', 'v', 'p', 'b', 'h', 'i', 'z', 'c' };
constexpr char nesActiveStatusLetters[8] = { 'N', 'V', '-', '-', 'D', 'I', 'Z', 'C' };
constexpr char nesInactiveStatusLetters[8] = { 'n', 'v', '-', '-', 'd', 'i', 'z', 'c' };
const char *activeStatusLetters = cpuType == CpuType::Cpu ? cpuActiveStatusLetters : (cpuType == CpuType::Spc ? spcActiveStatusLetters : nesActiveStatusLetters);
const char *inactiveStatusLetters = cpuType == CpuType::Cpu ? cpuInactiveStatusLetters : (cpuType == CpuType::Spc ? spcInactiveStatusLetters : nesInactiveStatusLetters);
if(part.DisplayInHex) {
WriteValue(output, ps, part);
} else {
string flags;
for(int i = 0; i < 8; i++) {
if(ps & 0x80) {
flags += activeStatusLetters[i];
} else if(part.MinWidth >= 8) {
flags += inactiveStatusLetters[i];
}
ps <<= 1;
}
WriteValue(output, flags, part);
}
}
void TraceLogger::WriteByteCode(DisassemblyInfo &info, RowPart &rowPart, string &output)
{
string byteCode;
info.GetByteCode(byteCode);
if(!rowPart.DisplayInHex) {
//Remove $ marks if not in "hex" mode (but still display the bytes as hex)
byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end());
}
WriteValue(output, byteCode, rowPart);
}
void TraceLogger::WriteDisassembly(DisassemblyInfo &info, RowPart &rowPart, uint8_t sp, uint32_t pc, string &output)
{
int indentLevel = 0;
string code;
if(_options.IndentCode) {
indentLevel = 0xFF - (sp & 0xFF);
code = std::string(indentLevel, ' ');
}
LabelManager* labelManager = _options.UseLabels ? _labelManager : nullptr;
info.GetDisassembly(code, pc, labelManager, _settings);
WriteValue(output, code, rowPart);
}
void TraceLogger::WriteEffectiveAddress(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType cpuMemoryType, CpuType cpuType)
{
int32_t effectiveAddress = info.GetEffectiveAddress(_debugger, cpuState, cpuType);
if(effectiveAddress >= 0) {
if(_options.UseLabels) {
AddressInfo addr { effectiveAddress, cpuMemoryType };
string label = _labelManager->GetLabel(addr);
if(!label.empty()) {
WriteValue(output, " [" + label + "]", rowPart);
return;
}
}
WriteValue(output, " [" + HexUtilities::ToHex24(effectiveAddress) + "]", rowPart);
}
}
void TraceLogger::WriteMemoryValue(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType memType, CpuType cpuType)
{
int32_t address = info.GetEffectiveAddress(_debugger, cpuState, cpuType);
if(address >= 0) {
uint8_t valueSize;
uint16_t value = info.GetMemoryValue(address, _memoryDumper, memType, valueSize);
if(rowPart.DisplayInHex) {
output += "= $";
if(valueSize == 2) {
WriteValue(output, (uint16_t)value, rowPart);
} else {
WriteValue(output, (uint8_t)value, rowPart);
}
} else {
output += "= ";
}
}
}
void TraceLogger::WriteAlign(int originalSize, RowPart &rowPart, string &output)
{
if((int)output.size() - originalSize < rowPart.MinWidth) {
output += std::string(rowPart.MinWidth - (output.size() - originalSize), ' ');
}
}
void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo, SnesMemoryType memType, CpuType cpuType)
{
int originalSize = (int)output.size();
uint32_t pcAddress = (cpuState.K << 16) | cpuState.PC;
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, (uint8_t)cpuState.SP, pcAddress, output); break;
case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, memType, cpuType); break;
case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, memType, cpuType); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break;
case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break;
case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break;
case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break;
case RowDataType::D: WriteValue(output, cpuState.D, rowPart); break;
case RowDataType::DB: WriteValue(output, cpuState.DBR, rowPart); break;
case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag<CpuType::Cpu>(output, cpuState.PS, rowPart); break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
case RowDataType::CycleCount:
WriteValue(output, (uint32_t)(cpuState.CycleCount >> 32), rowPart);
WriteValue(output, (uint32_t)cpuState.CycleCount, rowPart);
break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string &output, SpcState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
int originalSize = (int)output.size();
uint32_t pcAddress = cpuState.PC;
for(RowPart& rowPart : _spcRowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, cpuState.SP, pcAddress, output); break;
case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::SpcMemory, CpuType::Spc); break;
case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::SpcMemory, CpuType::Spc); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)pcAddress), rowPart); break;
case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break;
case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break;
case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break;
case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag<CpuType::Spc>(output, cpuState.PS, rowPart); break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string &output, NecDspState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
int originalSize = (int)output.size();
uint32_t pcAddress = cpuState.PC;
for(RowPart& rowPart : _dspRowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, cpuState.SP, pcAddress, output); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)pcAddress), rowPart); break;
case RowDataType::A:
output += "A:" + HexUtilities::ToHex(cpuState.A);
output += " B:" + HexUtilities::ToHex(cpuState.B);
output += " DR:" + HexUtilities::ToHex(cpuState.DR);
output += " DP:" + HexUtilities::ToHex(cpuState.DP);
output += " SR:" + HexUtilities::ToHex(cpuState.SR);
output += " K:" + HexUtilities::ToHex(cpuState.K);
output += " L:" + HexUtilities::ToHex(cpuState.L);
output += " M:" + HexUtilities::ToHex(cpuState.M);
output += " N:" + HexUtilities::ToHex(cpuState.N);
output += " RP:" + HexUtilities::ToHex(cpuState.RP);
output += " TR:" + HexUtilities::ToHex(cpuState.TR);
output += " TRB:" + HexUtilities::ToHex(cpuState.TRB) + " ";
//output += "FA=" + HexUtilities::ToHex(cpuState.FlagsA);
//output += "FB=" + HexUtilities::ToHex(cpuState.FlagsB);
WriteValue(output, cpuState.A, rowPart);
break;
case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string &output, GsuState &gsuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
int originalSize = (int)output.size();
uint32_t pcAddress = (gsuState.ProgramBank << 16) | gsuState.R[15];
for(RowPart& rowPart : _gsuRowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, 0, pcAddress, output); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break;
case RowDataType::A:
WriteValue(output, gsuState.R[0], rowPart);
for(int i = 1; i < 16; i++) {
output += " R" + std::to_string(i) + ":" + HexUtilities::ToHex(gsuState.R[i]);
}
break;
case RowDataType::X: WriteValue(output, gsuState.SrcReg, rowPart); break;
case RowDataType::Y: WriteValue(output, gsuState.DestReg, rowPart); break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string &output, Cx4State &cx4State, PpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
int originalSize = (int)output.size();
uint32_t pcAddress = (cx4State.Cache.Address[cx4State.Cache.Page] + (cx4State.PC * 2)) & 0xFFFFFF;
for(RowPart& rowPart : _cx4RowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, 0, pcAddress, output); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break;
case RowDataType::A:
output += " A:" + HexUtilities::ToHex24(cx4State.A);
output += string(" ") + (cx4State.Carry ? "C" : "c") + (cx4State.Zero ? "Z" : "z") + (cx4State.Overflow ? "V" : "v") + (cx4State.Negative ? "N" : "n");
output += " PC:" + HexUtilities::ToHex(cx4State.PC);
output += " MAR:" + HexUtilities::ToHex24(cx4State.MemoryAddressReg);
output += " MDR:" + HexUtilities::ToHex24(cx4State.MemoryDataReg);
output += " DPR:" + HexUtilities::ToHex24(cx4State.DataPointerReg);
output += " ML:" + HexUtilities::ToHex24((uint32_t)cx4State.Mult & 0xFFFFFF);
output += " MH:" + HexUtilities::ToHex24((uint32_t)(cx4State.Mult >> 24) & 0xFFFFFF);
for(int i = 0; i < 16; i++) {
output += " R" + std::to_string(i) + ":" + HexUtilities::ToHex24(cx4State.Regs[i]);
}
break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string& output, GbCpuState& cpuState, GbPpuState& ppuState, DisassemblyInfo& disassemblyInfo)
{
int originalSize = (int)output.size();
uint32_t pcAddress = cpuState.PC;
for(RowPart& rowPart : _gbRowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, (uint8_t)cpuState.SP, pcAddress, output); break;
case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::GameboyMemory, CpuType::Gameboy); break;
case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::GameboyMemory, CpuType::Gameboy); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)pcAddress), rowPart); break;
case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break;
case RowDataType::B: WriteValue(output, cpuState.B, rowPart); break;
case RowDataType::C: WriteValue(output, cpuState.C, rowPart); break;
case RowDataType::D: WriteValue(output, cpuState.D, rowPart); break;
case RowDataType::E: WriteValue(output, cpuState.E, rowPart); break;
case RowDataType::F: WriteValue(output, cpuState.Flags, rowPart); break;
case RowDataType::H: WriteValue(output, cpuState.H, rowPart); break;
case RowDataType::L: WriteValue(output, cpuState.L, rowPart); break;
case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string &output, NesCpuState &cpuState, NesPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
int originalSize = (int)output.size();
for(RowPart& rowPart : _nesRowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, cpuState.SP, cpuState.PC, output); break;
case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::NesMemory, CpuType::Nes); break;
case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::NesMemory, CpuType::Nes); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)cpuState.PC), rowPart); break;
case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break;
case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break;
case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break;
case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag<CpuType::Nes>(output, cpuState.PS, rowPart); break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
case RowDataType::CycleCount:
WriteValue(output, (uint32_t)(cpuState.CycleCount >> 32), rowPart);
WriteValue(output, (uint32_t)cpuState.CycleCount, rowPart);
break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
/*
bool TraceLogger::ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo)
{
if(!_conditionData.RpnQueue.empty()) {
EvalResultType type;
if(!_expEvaluator->Evaluate(_conditionData, state, type, operationInfo)) {
if(operationInfo.OperationType == MemoryOperationType::ExecOpCode) {
//Condition did not match, keep state/disassembly info for instruction's subsequent cycles
_lastState = state;
_lastDisassemblyInfo = disassemblyInfo;
_pendingLog = true;
}
return false;
}
}
return true;
}
*/
void TraceLogger::GetTraceRow(string &output, CpuType cpuType, DisassemblyInfo &disassemblyInfo, BaseState &state)
{
PpuState ppu = {};
GbPpuState gbPpu = {};
NesPpuState nesPpu = {};
switch(cpuType) {
case CpuType::Cpu: GetTraceRow(output, (CpuState&)state, ppu, disassemblyInfo, SnesMemoryType::CpuMemory, cpuType); break;
case CpuType::Spc: GetTraceRow(output, (SpcState&)state, ppu, disassemblyInfo); break;
case CpuType::NecDsp: GetTraceRow(output, (NecDspState&)state, ppu, disassemblyInfo); break;
case CpuType::Sa1: GetTraceRow(output, (CpuState&)state, ppu, disassemblyInfo, SnesMemoryType::Sa1Memory, cpuType); break;
case CpuType::Gsu: GetTraceRow(output, (GsuState&)state, ppu, disassemblyInfo); break;
case CpuType::Cx4: GetTraceRow(output, (Cx4State&)state, ppu, disassemblyInfo); break;
case CpuType::Gameboy: GetTraceRow(output, (GbCpuState&)state, gbPpu, disassemblyInfo); break;
case CpuType::Nes: GetTraceRow(output, (NesCpuState&)state, nesPpu, disassemblyInfo); break;
default: throw std::runtime_error("Trace logger - Unsupported CPU type");
}
}
template<class T>
void TraceLogger::AddRow(CpuType cpuType, T& cpuState, DisassemblyInfo &disassemblyInfo)
{
_logCpuType[_currentPos] = cpuType;
_disassemblyCache[_currentPos] = disassemblyInfo;
switch(cpuType) {
case CpuType::Cpu: _snesCpuState[_currentPos] = (CpuState&)cpuState; break;
case CpuType::Spc: _spcState[_currentPos] = (SpcState&)cpuState; break;
case CpuType::NecDsp: _necDspState[_currentPos] = (NecDspState&)cpuState; break;
case CpuType::Sa1: _snesCpuState[_currentPos] = (CpuState&)cpuState; break;
case CpuType::Gsu: _gsuState[_currentPos] = (GsuState&)cpuState; break;
case CpuType::Cx4: _cx4State[_currentPos] = (Cx4State&)cpuState; break;
case CpuType::Gameboy: _gbCpuState[_currentPos] = (GbCpuState&)cpuState; break;
case CpuType::Nes: _nesCpuState[_currentPos] = (NesCpuState&)cpuState; break;
default: throw std::runtime_error("Trace logger - Unsupported CPU type");
}
_pendingLog = false;
if(_logCount < ExecutionLogSize) {
_logCount++;
}
if(_logToFile) {
GetTraceRow(_outputBuffer, cpuType, _disassemblyCache[_currentPos], cpuState);
if(_outputBuffer.size() > 32768) {
_outputFile << _outputBuffer;
_outputBuffer.clear();
}
}
_currentPos = (_currentPos + 1) % ExecutionLogSize;
}
/*
void TraceLogger::LogNonExec(OperationInfo& operationInfo)
{
if(_pendingLog) {
if(ConditionMatches(_lastState, _lastDisassemblyInfo, operationInfo)) {
AddRow(_lastDisassemblyInfo, _lastState);
}
}
}*/
template<class T>
void TraceLogger::Log(CpuType cpuType, T& cpuState, DisassemblyInfo &disassemblyInfo)
{
if(_logCpu[(int)cpuType]) {
//For the sake of performance, only log data for the CPUs we're actively displaying/logging
//if(ConditionMatches(state, disassemblyInfo, operationInfo)) {
AddRow(cpuType, cpuState, disassemblyInfo);
//}
}
}
void TraceLogger::Clear()
{
_logCount = 0;
}
const char* TraceLogger::GetExecutionTrace(uint32_t lineCount)
{
DebugBreakHelper helper(_debugger);
_executionTrace.clear();
int startPos = (_currentPos > 0 ? _currentPos : TraceLogger::ExecutionLogSize) - 1;
bool enabled = false;
for(int i = 0; i <= (int)DebugUtilities::GetLastCpuType(); i++) {
enabled |= _logCpu[i];
}
if(enabled && lineCount > 0) {
for(int i = 0; i < TraceLogger::ExecutionLogSize; i++) {
int index = (startPos - i);
if(index < 0) {
index = TraceLogger::ExecutionLogSize + index;
}
if((i > 0 && startPos == index) || !_disassemblyCache[index].IsInitialized()) {
//If the entire array was checked, or this element is not initialized, stop
break;
}
CpuType cpuType = _logCpuType[index];
if(!_logCpu[(int)cpuType]) {
//This line isn't for a CPU currently being logged
continue;
}
BaseState* state;
switch(cpuType) {
case CpuType::Cpu: {
state = &_snesCpuState[index];
CpuState* snesCpu = (CpuState*)state;
_executionTrace += "\x2\x1" + HexUtilities::ToHex24((snesCpu->K << 16) | snesCpu->PC) + "\x1";
break;
}
case CpuType::Spc: {
state = &_spcState[index];
SpcState* spc = (SpcState*)state;
_executionTrace += "\x3\x1" + HexUtilities::ToHex(spc->PC) + "\x1";
break;
}
case CpuType::NecDsp: {
state = &_spcState[index];
NecDspState* necDsp = (NecDspState*)state;
_executionTrace += "\x4\x1" + HexUtilities::ToHex(necDsp->PC) + "\x1";
break;
}
case CpuType::Sa1: {
state = &_snesCpuState[index];
CpuState* sa1 = (CpuState*)state;
_executionTrace += "\x4\x1" + HexUtilities::ToHex24((sa1->K << 16) | sa1->PC) + "\x1";
break;
}
case CpuType::Gsu: {
state = &_gsuState[index];
GsuState* gsu = (GsuState*)state;
_executionTrace += "\x4\x1" + HexUtilities::ToHex24((gsu->ProgramBank << 16) | gsu->R[15]) + "\x1";
break;
}
case CpuType::Cx4: {
state = &_cx4State[index];
Cx4State* cx4 = (Cx4State*)state;
_executionTrace += "\x4\x1" + HexUtilities::ToHex24((cx4->Cache.Address[cx4->Cache.Page] + (cx4->PC * 2)) & 0xFFFFFF) + "\x1";
break;
}
case CpuType::Gameboy: {
state = &_gbCpuState[index];
GbCpuState* gb = (GbCpuState*)state;
_executionTrace += "\x4\x1" + HexUtilities::ToHex(gb->PC) + "\x1";
break;
}
case CpuType::Nes: {
state = &_nesCpuState[index];
NesCpuState* nes = (NesCpuState*)state;
_executionTrace += "\x4\x1" + HexUtilities::ToHex(nes->PC) + "\x1";
break;
}
default:
throw std::runtime_error("Trace logger - Unsupported CPU type");
}
string byteCode;
_disassemblyCache[index].GetByteCode(byteCode);
_executionTrace += byteCode + "\x1";
GetTraceRow(_executionTrace, cpuType, _disassemblyCache[index], *state);
lineCount--;
if(lineCount == 0) {
break;
}
}
}
return _executionTrace.c_str();
}
template void TraceLogger::Log(CpuType cpuType, CpuState& cpuState, DisassemblyInfo& disassemblyInfo);
template void TraceLogger::Log(CpuType cpuType, SpcState& cpuState, DisassemblyInfo& disassemblyInfo);
template void TraceLogger::Log(CpuType cpuType, GsuState& cpuState, DisassemblyInfo& disassemblyInfo);
template void TraceLogger::Log(CpuType cpuType, Cx4State& cpuState, DisassemblyInfo& disassemblyInfo);
template void TraceLogger::Log(CpuType cpuType, NecDspState& cpuState, DisassemblyInfo& disassemblyInfo);
template void TraceLogger::Log(CpuType cpuType, GbCpuState& cpuState, DisassemblyInfo& disassemblyInfo);
template void TraceLogger::Log(CpuType cpuType, NesCpuState& cpuState, DisassemblyInfo& disassemblyInfo);

View file

@ -1,172 +0,0 @@
#pragma once
#include "stdafx.h"
#include "DisassemblyInfo.h"
#include "DebugUtilities.h"
#include "Utilities/SimpleLock.h"
class IConsole;
class Debugger;
class LabelManager;
class MemoryDumper;
class EmuSettings;
struct CpuState;
struct NecDspState;
struct GsuState;
struct Cx4State;
struct PpuState;
struct SpcState;
struct GbPpuState;
struct GbCpuState;
struct NesCpuState;
struct NesPpuState;
struct BaseState;
struct TraceLoggerOptions
{
bool LogCpu;
bool LogSpc;
bool LogNecDsp;
bool LogSa1;
bool LogGsu;
bool LogCx4;
bool LogGameboy;
bool LogNes;
bool ShowExtraInfo;
bool IndentCode;
bool UseLabels;
bool UseWindowsEol;
bool ExtendZeroPage;
char Condition[1000];
char Format[1000];
};
enum class RowDataType
{
Text = 0,
ByteCode,
Disassembly,
EffectiveAddress,
MemoryValue,
Align,
PC,
A,
B,
C,
D,
E,
F,
H,
L,
X,
Y,
DB,
SP,
PS,
Cycle,
Scanline,
HClock,
FrameCount,
CycleCount
};
struct RowPart
{
RowDataType DataType;
string Text;
bool DisplayInHex;
int MinWidth;
};
class TraceLogger
{
private:
static constexpr int ExecutionLogSize = 30000;
//Must be static to be thread-safe when switching game
static string _executionTrace;
TraceLoggerOptions _options;
string _outputFilepath;
string _outputBuffer;
ofstream _outputFile;
IConsole* _console;
EmuSettings* _settings;
LabelManager* _labelManager;
MemoryDumper* _memoryDumper;
Debugger* _debugger;
vector<RowPart> _rowParts;
vector<RowPart> _spcRowParts;
vector<RowPart> _dspRowParts;
vector<RowPart> _gsuRowParts;
vector<RowPart> _cx4RowParts;
vector<RowPart> _gbRowParts;
vector<RowPart> _nesRowParts;
bool _logCpu[(int)DebugUtilities::GetLastCpuType() + 1] = {};
bool _pendingLog;
//CpuState _lastState;
//DisassemblyInfo _lastDisassemblyInfo;
bool _logToFile;
uint32_t _currentPos;
uint32_t _logCount;
CpuState* _snesCpuState;
SpcState* _spcState;
NecDspState* _necDspState;
GsuState* _gsuState;
Cx4State* _cx4State;
GbCpuState* _gbCpuState;
NesCpuState* _nesCpuState;
DisassemblyInfo *_disassemblyCache = nullptr;
CpuType* _logCpuType = nullptr;
template<CpuType cpuType> void GetStatusFlag(string &output, uint8_t ps, RowPart& part);
void WriteByteCode(DisassemblyInfo &info, RowPart &rowPart, string &output);
void WriteDisassembly(DisassemblyInfo &info, RowPart &rowPart, uint8_t sp, uint32_t pc, string &output);
void WriteEffectiveAddress(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType cpuMemoryType, CpuType cpuType);
void WriteMemoryValue(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType memType, CpuType cpuType);
void WriteAlign(int originalSize, RowPart &rowPart, string &output);
template<class T> void AddRow(CpuType cpuType, T& cpuState, DisassemblyInfo& disassemblyInfo);
//bool ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo);
void ParseFormatString(vector<RowPart> &rowParts, string format);
void GetTraceRow(string &output, CpuType cpuType, DisassemblyInfo &disassemblyInfo, BaseState &state);
void GetTraceRow(string &output, CpuState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo, SnesMemoryType memType, CpuType cpuType);
void GetTraceRow(string &output, SpcState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string &output, NecDspState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string &output, GsuState &gsuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string& output, Cx4State& cx4State, PpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void GetTraceRow(string &output, GbCpuState &gbState, GbPpuState &gbPpuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string& output, NesCpuState& cpuState, NesPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
template<typename T> void WriteValue(string &output, T value, RowPart& rowPart);
public:
TraceLogger(Debugger* debugger);
~TraceLogger();
__forceinline bool IsCpuLogged(CpuType type) { return _logCpu[(int)type]; }
template<class T> void Log(CpuType cpuType, T& cpuState, DisassemblyInfo& disassemblyInfo);
void Clear();
//void LogNonExec(OperationInfo& operationInfo);
void SetOptions(TraceLoggerOptions options);
void StartLogging(string filename);
void StopLogging();
void LogExtraInfo(const char *log, uint32_t cycleCount);
const char* GetExecutionTrace(uint32_t lineCount);
};

View file

@ -3,9 +3,9 @@
#include "Gameboy/Debugger/GbDebugger.h"
#include "Gameboy/Debugger/GameboyDisUtils.h"
#include "Gameboy/Debugger/GbEventManager.h"
#include "Gameboy/Debugger/GbTraceLogger.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/Debugger.h"
@ -28,7 +28,6 @@ GbDebugger::GbDebugger(Debugger* debugger)
_debugger = debugger;
_emu = debugger->GetEmulator();
_traceLogger = debugger->GetTraceLogger().get();
_disassembler = debugger->GetDisassembler().get();
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
@ -39,11 +38,13 @@ GbDebugger::GbDebugger(Debugger* debugger)
}
_cpu = _gameboy->GetCpu();
_ppu = _gameboy->GetPpu();
_settings = debugger->GetEmulator()->GetSettings();
_codeDataLogger.reset(new CodeDataLogger(SnesMemoryType::GbPrgRom, _gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom), CpuType::Gameboy));
_traceLogger.reset(new GbTraceLogger(debugger, _ppu));
_eventManager.reset(new GbEventManager(debugger, _gameboy->GetCpu(), _gameboy->GetPpu()));
_eventManager.reset(new GbEventManager(debugger, _gameboy->GetCpu(), _ppu));
_callstackManager.reset(new CallstackManager(debugger));
_breakpointManager.reset(new BreakpointManager(debugger, CpuType::Gameboy, _eventManager.get()));
_step.reset(new StepRequest());
@ -71,11 +72,11 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t
MemoryOperationInfo operation { addr, value, type };
BreakSource breakSource = BreakSource::Unspecified;
GbCpuState state = _cpu->GetState();
GbCpuState& state = _cpu->GetState();
uint16_t pc = state.PC;
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsCpuLogged(CpuType::Gameboy) || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) {
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) {
if(addressInfo.Address >= 0) {
if(addressInfo.Type == SnesMemoryType::GbPrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
@ -83,9 +84,9 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t
_disassembler->BuildCache(addressInfo, 0, CpuType::Gameboy);
}
if(_traceLogger->IsCpuLogged(CpuType::Gameboy)) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gameboy);
_traceLogger->Log(CpuType::Gameboy, state, disInfo);
_traceLogger->Log(state, disInfo, operation);
}
}
@ -137,12 +138,21 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t
if(addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _emu->GetMasterClock());
} else {
if(addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
if(addr < 0xFE00 || addr >= 0xFF80) {
if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _emu->GetMasterClock())) {
//Memory access was a read on an uninitialized memory address
@ -177,6 +187,10 @@ void GbDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
_disassembler->InvalidateCache(addressInfo, CpuType::Gameboy);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
if(addr == 0xFFFF || (addr >= 0xFE00 && addr < 0xFF80) || (addr >= 0x8000 && addr <= 0x9FFF)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
@ -225,10 +239,10 @@ void GbDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool
_eventManager->AddEvent(DebugEventType::Irq);
}
void GbDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle)
void GbDebugger::ProcessPpuCycle(int16_t &scanline, uint16_t &cycle)
{
scanline = _gameboy->GetPpu()->GetScanline();
cycle = _gameboy->GetPpu()->GetCycle();
scanline = _ppu->GetScanline();
cycle = _ppu->GetCycle();
if(_step->PpuStepCount > 0) {
_step->PpuStepCount--;
@ -238,7 +252,6 @@ void GbDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle)
}
if(cycle == 0 && scanline == _step->BreakScanline) {
_step->BreakScanline = -1;
_debugger->SleepUntilResume(BreakSource::PpuStep);
}
}
@ -272,3 +285,8 @@ BaseState& GbDebugger::GetState()
{
return _cpu->GetState();
}
ITraceLogger* GbDebugger::GetTraceLogger()
{
return _traceLogger.get();
}

View file

@ -5,7 +5,7 @@
class Disassembler;
class Debugger;
class TraceLogger;
class GbTraceLogger;
class Gameboy;
class CallstackManager;
class MemoryAccessCounter;
@ -17,6 +17,7 @@ class GbAssembler;
class Emulator;
class CodeDataLogger;
class GbCpu;
class GbPpu;
enum class MemoryOperationType;
@ -25,8 +26,8 @@ class GbDebugger final : public IDebugger
Debugger* _debugger;
Emulator* _emu;
GbCpu* _cpu;
GbPpu* _ppu;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
MemoryAccessCounter* _memoryAccessCounter;
Gameboy* _gameboy;
EmuSettings* _settings;
@ -37,6 +38,7 @@ class GbDebugger final : public IDebugger
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<StepRequest> _step;
shared_ptr<GbAssembler> _assembler;
unique_ptr<GbTraceLogger> _traceLogger;
uint8_t _prevOpCode = 0xFF;
uint32_t _prevProgramCounter = 0;
@ -48,10 +50,10 @@ public:
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override;
void ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) override;
void ProcessPpuCycle(int16_t &scanline, uint16_t &cycle);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
@ -61,6 +63,7 @@ public:
shared_ptr<CallstackManager> GetCallstackManager() override;
shared_ptr<CodeDataLogger> GetCodeDataLogger() override;
BreakpointManager* GetBreakpointManager() override;
ITraceLogger* GetTraceLogger() override;
BaseState& GetState() override;
};

View file

@ -0,0 +1,75 @@
#include "stdafx.h"
#include "Gameboy/Debugger/GbTraceLogger.h"
#include "Gameboy/GbPpu.h"
#include "Gameboy/GbTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Utilities/HexUtilities.h"
GbTraceLogger::GbTraceLogger(Debugger* debugger, GbPpu* ppu) : BaseTraceLogger(debugger, CpuType::Gameboy)
{
_ppu = ppu;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType GbTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "A") {
return RowDataType::A;
} else if(tag == "B") {
return RowDataType::B;
} else if(tag == "C") {
return RowDataType::C;
} else if(tag == "D") {
return RowDataType::D;
} else if(tag == "E") {
return RowDataType::E;
} else if(tag == "F") {
return RowDataType::F;
} else if(tag == "H") {
return RowDataType::H;
} else if(tag == "L") {
return RowDataType::L;
} else if(tag == "PS") {
return RowDataType::PS;
} else if(tag == "SP") {
return RowDataType::SP;
} else {
return RowDataType::Text;
}
}
void GbTraceLogger::GetTraceRow(string &output, GbCpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
constexpr char activeStatusLetters[8] = { 'Z', 'N', 'H', 'C' };
constexpr char inactiveStatusLetters[8] = { 'z', 'n', 'h', 'c' };
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break;
case RowDataType::B: WriteIntValue(output, cpuState.B, rowPart); break;
case RowDataType::C: WriteIntValue(output, cpuState.C, rowPart); break;
case RowDataType::D: WriteIntValue(output, cpuState.D, rowPart); break;
case RowDataType::E: WriteIntValue(output, cpuState.E, rowPart); break;
case RowDataType::F: WriteIntValue(output, cpuState.Flags, rowPart); break;
case RowDataType::H: WriteIntValue(output, cpuState.H, rowPart); break;
case RowDataType::L: WriteIntValue(output, cpuState.L, rowPart); break;
case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.Flags, rowPart, 4); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void GbTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_ppu->GetCycle(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "Gameboy/GbTypes.h"
class DisassemblyInfo;
class Debugger;
class GbPpu;
class GbTraceLogger : public BaseTraceLogger<GbTraceLogger, GbCpuState>
{
private:
GbPpu* _ppu = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
GbTraceLogger(Debugger* debugger, GbPpu* ppu);
void GetTraceRow(string& output, GbCpuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(GbCpuState& state) { return state.PC; }
__forceinline uint64_t GetCycleCount(GbCpuState& state) { return 0; } //TODO
__forceinline uint8_t GetStackPointer(GbCpuState& state) { return (uint8_t)state.SP; }
};

View file

@ -1,7 +1,6 @@
#include "stdafx.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/CodeDataLogger.h"
@ -17,6 +16,7 @@
#include "NES/Debugger/NesDebugger.h"
#include "NES/Debugger/NesAssembler.h"
#include "NES/Debugger/NesEventManager.h"
#include "NES/Debugger/NesTraceLogger.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/FolderUtilities.h"
#include "Shared/EmuSettings.h"
@ -34,7 +34,7 @@ NesDebugger::NesDebugger(Debugger* debugger)
_ppu = console->GetPpu();
_mapper = console->GetMapper();
_traceLogger = debugger->GetTraceLogger().get();
_traceLogger.reset(new NesTraceLogger(debugger, _ppu));
_disassembler = debugger->GetDisassembler().get();
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
_settings = debugger->GetEmulator()->GetSettings();
@ -64,13 +64,15 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
{
AddressInfo addressInfo = _mapper->GetAbsoluteAddress(addr);
MemoryOperationInfo operation = { addr, value, type };
NesCpuState state = _cpu->GetState();
NesCpuState& state = _cpu->GetState();
BreakSource breakSource = BreakSource::Unspecified;
if(IsRegister(operation)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
if(type == MemoryOperationType::ExecOpCode) {
bool needDisassemble = _traceLogger->IsCpuLogged(CpuType::Nes) || _settings->CheckDebuggerFlag(DebuggerFlags::NesDebuggerEnabled);
bool needDisassemble = _traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::NesDebuggerEnabled);
if(addressInfo.Address >= 0) {
if(addressInfo.Type == SnesMemoryType::NesPrgRom) {
uint8_t flags = CdlFlags::Code;
@ -85,9 +87,9 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
}
if(_traceLogger->IsCpuLogged(CpuType::Nes)) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, CpuType::Nes);
_traceLogger->Log(CpuType::Nes, state, disInfo);
_traceLogger->Log(state, disInfo, operation);
}
uint32_t pc = state.PC;
@ -127,11 +129,20 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
if(addressInfo.Type == SnesMemoryType::NesPrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _cpu->GetCycleCount());
} else {
if(addressInfo.Type == SnesMemoryType::NesPrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _cpu->GetCycleCount())) {
//Memory access was a read on an uninitialized memory address
@ -148,10 +159,6 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
}
if(IsRegister(operation)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
}
@ -167,6 +174,10 @@ void NesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
_eventManager->AddEvent(DebugEventType::Register, operation);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _cpu->GetCycleCount());
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
@ -208,7 +219,7 @@ void NesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool
//_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq);
}
void NesDebugger::ProcessPpuCycle(uint16_t& scanline, uint16_t& cycle)
void NesDebugger::ProcessPpuCycle(int16_t& scanline, uint16_t& cycle)
{
scanline = _ppu->GetCurrentScanline();
cycle = _ppu->GetCurrentCycle();
@ -221,7 +232,6 @@ void NesDebugger::ProcessPpuCycle(uint16_t& scanline, uint16_t& cycle)
}
if(cycle == 0 && scanline == _step->BreakScanline) {
_step->BreakScanline = -1;
_debugger->SleepUntilResume(BreakSource::PpuStep);
}
}
@ -269,3 +279,8 @@ BaseState& NesDebugger::GetState()
{
return _cpu->GetState();
}
ITraceLogger* NesDebugger::GetTraceLogger()
{
return _traceLogger.get();
}

View file

@ -5,7 +5,6 @@
class Disassembler;
class Debugger;
class TraceLogger;
class CallstackManager;
class MemoryAccessCounter;
class MemoryManager;
@ -15,6 +14,7 @@ class ScriptManager;
class BreakpointManager;
class IAssembler;
class IEventManager;
class NesTraceLogger;
class Emulator;
class NesCpu;
@ -28,7 +28,6 @@ class NesDebugger final : public IDebugger
Debugger* _debugger;
Emulator* _emu;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
MemoryAccessCounter* _memoryAccessCounter;
EmuSettings* _settings;
@ -42,6 +41,7 @@ class NesDebugger final : public IDebugger
shared_ptr<IAssembler> _assembler;
shared_ptr<CallstackManager> _callstackManager;
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<NesTraceLogger> _traceLogger;
unique_ptr<StepRequest> _step;
bool _enableBreakOnUninitRead = false;
@ -55,15 +55,16 @@ public:
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override;
void ProcessPpuCycle(uint16_t& scanline, uint16_t& cycle) override;
void ProcessPpuCycle(int16_t& scanline, uint16_t& cycle);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
BreakpointManager* GetBreakpointManager() override;
ITraceLogger* GetTraceLogger() override;
shared_ptr<CallstackManager> GetCallstackManager() override;
shared_ptr<IAssembler> GetAssembler() override;
shared_ptr<IEventManager> GetEventManager() override;

View file

@ -0,0 +1,60 @@
#include "stdafx.h"
#include "NES/Debugger/NesTraceLogger.h"
#include "NES/NesPpu.h"
#include "NES/NesTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Utilities/HexUtilities.h"
NesTraceLogger::NesTraceLogger(Debugger* debugger, BaseNesPpu* ppu) : BaseTraceLogger(debugger, CpuType::Nes)
{
_ppu = ppu;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType NesTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "A") {
return RowDataType::A;
} else if(tag == "X") {
return RowDataType::X;
} else if(tag == "Y") {
return RowDataType::Y;
} else if(tag == "P") {
return RowDataType::PS;
} else if(tag == "SP") {
return RowDataType::SP;
} else {
return RowDataType::Text;
}
}
void NesTraceLogger::GetTraceRow(string &output, NesCpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
constexpr char activeStatusLetters[8] = { 'N', 'V', '-', '-', 'D', 'I', 'Z', 'C' };
constexpr char inactiveStatusLetters[8] = { 'n', 'v', '-', '-', 'd', 'i', 'z', 'c' };
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break;
case RowDataType::X: WriteIntValue(output, cpuState.X, rowPart); break;
case RowDataType::Y: WriteIntValue(output, cpuState.Y, rowPart); break;
case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.PS, rowPart); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void NesTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCurrentCycle(),
_ppu->GetCurrentCycle(),
_ppu->GetCurrentScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "NES/NesTypes.h"
class DisassemblyInfo;
class Debugger;
class BaseNesPpu;
class NesTraceLogger : public BaseTraceLogger<NesTraceLogger, NesCpuState>
{
private:
BaseNesPpu* _ppu = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
NesTraceLogger(Debugger* debugger, BaseNesPpu* ppu);
void GetTraceRow(string& output, NesCpuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(NesCpuState& state) { return state.PC; }
__forceinline uint64_t GetCycleCount(NesCpuState& state) { return state.CycleCount; }
__forceinline uint8_t GetStackPointer(NesCpuState& state) { return (uint8_t)state.SP; }
};

View file

@ -630,6 +630,7 @@ bool BaseCartridge::LoadGameboy(VirtualFile& romFile)
if(_coprocessorType == CoprocessorType::SGB) {
_gameboy.reset(new Gameboy(_emu, true));
if(_gameboy->LoadRom(romFile) == LoadRomResult::Success) {
_emu->RegisterMemory(SnesMemoryType::PrgRom, _prgRom, _prgRomSize);
return _gameboy->IsSgb();
}
}

View file

@ -222,7 +222,19 @@ PpuFrameInfo Console::GetPpuFrame()
vector<CpuType> Console::GetCpuTypes()
{
return { CpuType::Cpu, CpuType::Spc };
vector<CpuType> cpuTypes = { CpuType::Cpu, CpuType::Spc };
if(_cart->GetGsu()) {
cpuTypes.push_back(CpuType::Gsu);
} else if(_cart->GetDsp()) {
cpuTypes.push_back(CpuType::NecDsp);
} else if(_cart->GetCx4()) {
cpuTypes.push_back(CpuType::Cx4);
} else if(_cart->GetGameboy()) {
cpuTypes.push_back(CpuType::Gameboy);
} else if(_cart->GetSa1()) {
cpuTypes.push_back(CpuType::Sa1);
}
return cpuTypes;
}
void Console::SaveBattery()

View file

@ -10,10 +10,10 @@
#include "SNES/Debugger/SnesAssembler.h"
#include "SNES/Debugger/CpuDebugger.h"
#include "SNES/Debugger/SnesEventManager.h"
#include "SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/CodeDataLogger.h"
@ -35,7 +35,6 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
_debugger = debugger;
Console* console = (Console*)debugger->GetConsole();
_traceLogger = debugger->GetTraceLogger().get();
_disassembler = debugger->GetDisassembler().get();
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
_cpu = console->GetCpu().get();
@ -45,7 +44,14 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
_cart = console->GetCartridge().get();
_spc = console->GetSpc().get();
_ppu = console->GetPpu().get();
_traceLogger.reset(new SnesCpuTraceLogger(debugger, cpuType, _ppu, _memoryManager));
if(_cpuType == CpuType::Cpu) {
_memoryMappings = _memoryManager->GetMemoryMappings();
} else {
_memoryMappings = _sa1->GetMemoryMappings();
}
if(cpuType == CpuType::Sa1) {
_codeDataLogger = _debugger->GetCodeDataLogger(CpuType::Cpu);
} else {
@ -62,6 +68,14 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
//Enable breaking on uninit reads when debugger is opened at power on
_enableBreakOnUninitRead = true;
}
_debuggerEnabledFlag = _cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled;
}
void CpuDebugger::Init()
{
_spcTraceLogger = _debugger->GetTraceLogger(CpuType::Spc);
_dspTraceLogger = _debugger->GetTraceLogger(CpuType::NecDsp);
}
void CpuDebugger::Reset()
@ -73,29 +87,33 @@ void CpuDebugger::Reset()
void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr);
AddressInfo addressInfo = _memoryMappings->GetAbsoluteAddress(addr);
MemoryOperationInfo operation = { addr, value, type };
CpuState state = GetCpuState();
CpuState& state = GetCpuState();
BreakSource breakSource = BreakSource::Unspecified;
if(IsRegister(addr)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
if(type == MemoryOperationType::ExecOpCode) {
bool needDisassemble = _traceLogger->IsCpuLogged(_cpuType) || _settings->CheckDebuggerFlag(_cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled);
if(addressInfo.Address >= 0) {
uint8_t cpuFlags = state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
if(addressInfo.Type == SnesMemoryType::PrgRom) {
uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
uint8_t flags = CdlFlags::Code | cpuFlags;
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
flags |= CdlFlags::SubEntryPoint;
}
_codeDataLogger->SetFlags(addressInfo.Address, flags);
}
if(needDisassemble) {
_disassembler->BuildCache(addressInfo, state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8), _cpuType);
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(_debuggerEnabledFlag)) {
_disassembler->BuildCache(addressInfo, cpuFlags, _cpuType);
}
}
if(_traceLogger->IsCpuLogged(_cpuType)) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType);
_traceLogger->Log(_cpuType, state, disInfo);
_traceLogger->Log(state, disInfo, operation);
}
uint32_t pc = (state.K << 16) | state.PC;
@ -103,8 +121,8 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
//JSR, JSL
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType);
uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF);
AddressInfo srcAddress = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
AddressInfo retAddress = GetMemoryMappings().GetAbsoluteAddress(returnPc);
AddressInfo srcAddress = _memoryMappings->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo retAddress = _memoryMappings->GetAbsoluteAddress(returnPc);
_callstackManager->Push(srcAddress, _prevProgramCounter, addressInfo, pc, retAddress, returnPc, StackFrameFlags::None);
} else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) {
//RTS, RTL, RTI
@ -146,11 +164,17 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else {
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock())) {
//Memory access was a read on an uninitialized memory address
@ -167,16 +191,12 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
}
if(IsRegister(addr)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
}
void CpuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr);
AddressInfo addressInfo = _memoryMappings->GetAbsoluteAddress(addr);
MemoryOperationInfo operation = { addr, value, type };
if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) {
_disassembler->InvalidateCache(addressInfo, _cpuType);
@ -186,6 +206,10 @@ void CpuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
_eventManager->AddEvent(DebugEventType::Register, operation);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
@ -225,25 +249,20 @@ void CpuDebugger::Step(int32_t stepCount, StepType type)
void CpuDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
AddressInfo src = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = GetMemoryMappings().GetAbsoluteAddress(originalPc);
AddressInfo dest = GetMemoryMappings().GetAbsoluteAddress(currentPc);
AddressInfo src = _memoryMappings->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = _memoryMappings->GetAbsoluteAddress(originalPc);
AddressInfo dest = _memoryMappings->GetAbsoluteAddress(currentPc);
_callstackManager->Push(src, _prevProgramCounter, dest, currentPc, ret, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq);
}
void CpuDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle)
void CpuDebugger::ProcessPpuCycle(int16_t &scanline, uint16_t &cycle)
{
if(_cpuType == CpuType::Cpu) {
scanline = _ppu->GetScanline();
cycle = _memoryManager->GetHClock();
//Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs)
if(_traceLogger->IsCpuLogged(CpuType::Spc) || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) {
_spc->Run();
} else if(_traceLogger->IsCpuLogged(CpuType::NecDsp)) {
_cart->RunCoprocessors();
}
//Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs)
if(_spcTraceLogger->IsEnabled()) {
_spc->Run();
} else if(_dspTraceLogger && _dspTraceLogger->IsEnabled()) {
_cart->RunCoprocessors();
}
if(_step->PpuStepCount > 0) {
@ -253,18 +272,11 @@ void CpuDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle)
}
}
if(cycle == 0 && scanline == _step->BreakScanline) {
_step->BreakScanline = -1;
_debugger->SleepUntilResume(BreakSource::PpuStep);
}
}
scanline = _ppu->GetScanline();
cycle = _memoryManager->GetHClock();
MemoryMappings& CpuDebugger::GetMemoryMappings()
{
if(_cpuType == CpuType::Cpu) {
return *_memoryManager->GetMemoryMappings();
} else {
return *_sa1->GetMemoryMappings();
if(cycle == 0 && scanline == _step->BreakScanline) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
}
}
@ -287,6 +299,11 @@ shared_ptr<CallstackManager> CpuDebugger::GetCallstackManager()
return _callstackManager;
}
ITraceLogger* CpuDebugger::GetTraceLogger()
{
return _traceLogger.get();
}
BreakpointManager* CpuDebugger::GetBreakpointManager()
{
return _breakpointManager.get();

View file

@ -6,7 +6,7 @@
class Disassembler;
class Debugger;
class TraceLogger;
class SnesCpuTraceLogger;
class Cpu;
class CallstackManager;
class MemoryAccessCounter;
@ -28,7 +28,6 @@ class CpuDebugger final : public IDebugger
{
Debugger* _debugger;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
MemoryAccessCounter* _memoryAccessCounter;
MemoryManager* _memoryManager;
EmuSettings* _settings;
@ -37,37 +36,43 @@ class CpuDebugger final : public IDebugger
BaseCartridge* _cart;
Spc* _spc;
Ppu* _ppu;
MemoryMappings* _memoryMappings;
shared_ptr<CodeDataLogger> _codeDataLogger;
shared_ptr<IEventManager> _eventManager;
shared_ptr<SnesAssembler> _assembler;
shared_ptr<CallstackManager> _callstackManager;
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<StepRequest> _step;
unique_ptr<SnesCpuTraceLogger> _traceLogger;
ITraceLogger* _spcTraceLogger = nullptr;
ITraceLogger* _dspTraceLogger = nullptr;
DebuggerFlags _debuggerEnabledFlag = DebuggerFlags::CpuDebuggerEnabled;
CpuType _cpuType;
bool _enableBreakOnUninitRead = false;
uint8_t _prevOpCode = 0xFF;
uint32_t _prevProgramCounter = 0;
MemoryMappings& GetMemoryMappings();
CpuState& GetCpuState();
bool IsRegister(uint32_t addr);
public:
CpuDebugger(Debugger* debugger, CpuType cpuType);
void Init() override;
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override;
void ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) override;
void ProcessPpuCycle(int16_t &scanline, uint16_t &cycle);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
ITraceLogger* GetTraceLogger() override;
BreakpointManager* GetBreakpointManager() override;
shared_ptr<CallstackManager> GetCallstackManager() override;
shared_ptr<IAssembler> GetAssembler() override;

View file

@ -2,7 +2,6 @@
#include "Cx4Debugger.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/Debugger.h"
@ -12,6 +11,7 @@
#include "SNES/BaseCartridge.h"
#include "SNES/MemoryManager.h"
#include "SNES/Console.h"
#include "SNES/Debugger/TraceLogger/Cx4TraceLogger.h"
#include "SNES/Coprocessors/CX4/Cx4.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
@ -20,14 +20,17 @@
Cx4Debugger::Cx4Debugger(Debugger* debugger)
{
Console* console = (Console*)debugger->GetConsole();
_debugger = debugger;
_codeDataLogger = debugger->GetCodeDataLogger(CpuType::Cpu).get();
_traceLogger = debugger->GetTraceLogger().get();
_disassembler = debugger->GetDisassembler().get();
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
_cx4 = ((Console*)debugger->GetConsole())->GetCartridge()->GetCx4();
_memoryManager = ((Console*)debugger->GetConsole())->GetMemoryManager().get();
_cx4 = console->GetCartridge()->GetCx4();
_memoryManager = console->GetMemoryManager().get();
_settings = debugger->GetEmulator()->GetSettings();
_traceLogger.reset(new Cx4TraceLogger(debugger, console->GetPpu().get(), _memoryManager));
_breakpointManager.reset(new BreakpointManager(debugger, CpuType::Cx4));
_step.reset(new StepRequest());
@ -39,7 +42,7 @@ void Cx4Debugger::Reset()
void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
{
Cx4State state = _cx4->GetState();
Cx4State& state = _cx4->GetState();
addr = (state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF;
AddressInfo addressInfo = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr);
@ -52,12 +55,12 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_codeDataLogger->SetFlags(addressInfo.Address + 1, CdlFlags::Code | CdlFlags::Cx4);
}
if(_traceLogger->IsCpuLogged(CpuType::Cx4) || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) {
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) {
_disassembler->BuildCache(addressInfo, 0, CpuType::Cx4);
if(_traceLogger->IsCpuLogged(CpuType::Cx4)) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Cx4);
_traceLogger->Log(CpuType::Cx4, state, disInfo);
_traceLogger->Log(state, disInfo, operation);
}
}
@ -73,6 +76,9 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
if(addressInfo.Type == SnesMemoryType::PrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | CdlFlags::Cx4);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
}
@ -85,6 +91,9 @@ void Cx4Debugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
MemoryOperationInfo operation { (uint32_t)addr, value, type };
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
}
void Cx4Debugger::Run()
@ -141,3 +150,8 @@ BaseState& Cx4Debugger::GetState()
{
return _cx4->GetState();
}
ITraceLogger* Cx4Debugger::GetTraceLogger()
{
return _traceLogger.get();
}

View file

@ -5,7 +5,7 @@
class Disassembler;
class Debugger;
class TraceLogger;
class Cx4TraceLogger;
class CodeDataLogger;
class Cx4;
class CallstackManager;
@ -20,7 +20,6 @@ class Cx4Debugger final : public IDebugger
{
Debugger* _debugger;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
CodeDataLogger* _codeDataLogger;
MemoryAccessCounter* _memoryAccessCounter;
MemoryManager* _memoryManager;
@ -29,6 +28,7 @@ class Cx4Debugger final : public IDebugger
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<StepRequest> _step;
unique_ptr<Cx4TraceLogger> _traceLogger;
uint32_t _prevProgramCounter = 0;
@ -37,8 +37,8 @@ public:
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
@ -48,6 +48,7 @@ public:
shared_ptr<IAssembler> GetAssembler() override;
shared_ptr<IEventManager> GetEventManager() override;
shared_ptr<CodeDataLogger> GetCodeDataLogger() override;
ITraceLogger* GetTraceLogger() override;
BaseState& GetState() override;
};

View file

@ -4,9 +4,9 @@
#include "SNES/Console.h"
#include "SNES/BaseCartridge.h"
#include "SNES/Coprocessors/GSU/Gsu.h"
#include "SNES/Debugger/TraceLogger/GsuTraceLogger.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/ExpressionEvaluator.h"
@ -19,14 +19,17 @@
GsuDebugger::GsuDebugger(Debugger* debugger)
{
Console* console = (Console*)debugger->GetConsole();
_debugger = debugger;
_codeDataLogger = debugger->GetCodeDataLogger(CpuType::Cpu).get();
_traceLogger = debugger->GetTraceLogger().get();
_disassembler = debugger->GetDisassembler().get();
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
_gsu = ((Console*)debugger->GetConsole())->GetCartridge()->GetGsu();
_memoryManager = ((Console*)debugger->GetConsole())->GetMemoryManager().get();
_gsu = console->GetCartridge()->GetGsu();
_memoryManager = console->GetMemoryManager().get();
_settings = debugger->GetEmulator()->GetSettings();
_traceLogger.reset(new GsuTraceLogger(debugger, console->GetPpu().get(), _memoryManager));
_breakpointManager.reset(new BreakpointManager(debugger, CpuType::Gsu));
_step.reset(new StepRequest());
@ -52,15 +55,15 @@ void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | CdlFlags::Gsu);
}
if(_traceLogger->IsCpuLogged(CpuType::Gsu) || _settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled)) {
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled)) {
GsuState gsuState = _gsu->GetState();
_disassembler->BuildCache(addressInfo, gsuState.SFR.GetFlagsHigh() & 0x13, CpuType::Gsu);
if(_traceLogger->IsCpuLogged(CpuType::Gsu)) {
if(_traceLogger->IsEnabled()) {
gsuState.R[15] = addr;
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gsu);
_traceLogger->Log(CpuType::Gsu, gsuState, disInfo);
_traceLogger->Log(gsuState, disInfo, operation);
}
}
@ -75,6 +78,9 @@ void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
if(addressInfo.Type == SnesMemoryType::PrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | CdlFlags::Gsu);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
}
@ -87,6 +93,10 @@ void GsuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
MemoryOperationInfo operation { addr, value, type };
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
_disassembler->InvalidateCache(addressInfo, CpuType::Gsu);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
}
@ -142,3 +152,8 @@ BaseState& GsuDebugger::GetState()
{
return _gsu->GetState();
}
ITraceLogger* GsuDebugger::GetTraceLogger()
{
return _traceLogger.get();
}

View file

@ -5,13 +5,13 @@
class Disassembler;
class Debugger;
class TraceLogger;
class CodeDataLogger;
class Gsu;
class MemoryAccessCounter;
class MemoryManager;
class BreakpointManager;
class EmuSettings;
class GsuTraceLogger;
enum class MemoryOperationType;
@ -19,7 +19,6 @@ class GsuDebugger final : public IDebugger
{
Debugger* _debugger;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
CodeDataLogger* _codeDataLogger;
MemoryAccessCounter* _memoryAccessCounter;
MemoryManager* _memoryManager;
@ -28,6 +27,7 @@ class GsuDebugger final : public IDebugger
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<StepRequest> _step;
unique_ptr<GsuTraceLogger> _traceLogger;
uint8_t _prevOpCode = 0xFF;
uint32_t _prevProgramCounter = 0;
@ -37,8 +37,8 @@ public:
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
@ -48,6 +48,7 @@ public:
shared_ptr<IAssembler> GetAssembler() override;
shared_ptr<IEventManager> GetEventManager() override;
shared_ptr<CodeDataLogger> GetCodeDataLogger() override;
ITraceLogger* GetTraceLogger() override;
BaseState& GetState() override;
};

View file

@ -4,9 +4,9 @@
#include "SNES/Console.h"
#include "SNES/Coprocessors/DSP/NecDsp.h"
#include "SNES/Debugger/NecDspDebugger.h"
#include "SNES/Debugger/TraceLogger/NecDspTraceLogger.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/Debugger.h"
@ -18,11 +18,14 @@
NecDspDebugger::NecDspDebugger(Debugger* debugger)
{
Console* console = (Console*)debugger->GetConsole();
_debugger = debugger;
_traceLogger = debugger->GetTraceLogger().get();
_disassembler = debugger->GetDisassembler().get();
_dsp = ((Console*)debugger->GetConsole())->GetCartridge()->GetDsp();
_dsp = console->GetCartridge()->GetDsp();
_settings = debugger->GetEmulator()->GetSettings();
_traceLogger.reset(new NecDspTraceLogger(debugger, console->GetPpu().get(), console->GetMemoryManager().get()));
_breakpointManager.reset(new BreakpointManager(debugger, CpuType::NecDsp));
_step.reset(new StepRequest());
@ -38,13 +41,12 @@ void NecDspDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationTy
MemoryOperationInfo operation { (uint32_t)addr, value, type };
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsCpuLogged(CpuType::NecDsp) || _settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled)) {
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled)) {
_disassembler->BuildCache(addressInfo, 0, CpuType::NecDsp);
if(_traceLogger->IsCpuLogged(CpuType::NecDsp)) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::NecDsp);
NecDspState state = _dsp->GetState();
_traceLogger->Log(CpuType::NecDsp, state, disInfo);
_traceLogger->Log(_dsp->GetState(), disInfo, operation);
}
}
@ -63,6 +65,10 @@ void NecDspDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationT
AddressInfo addressInfo { (int32_t)addr, SnesMemoryType::DspDataRam }; //Writes never affect the DSP ROM
MemoryOperationInfo operation { addr, value, type };
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
}
void NecDspDebugger::Run()
@ -119,3 +125,8 @@ BaseState& NecDspDebugger::GetState()
{
return _dsp->GetState();
}
ITraceLogger* NecDspDebugger::GetTraceLogger()
{
return _traceLogger.get();
}

View file

@ -5,7 +5,7 @@
class Disassembler;
class Debugger;
class TraceLogger;
class NecDspTraceLogger;
class NecDsp;
class CallstackManager;
class MemoryAccessCounter;
@ -19,12 +19,12 @@ class NecDspDebugger final : public IDebugger
{
Debugger* _debugger;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
NecDsp* _dsp;
EmuSettings* _settings;
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<StepRequest> _step;
unique_ptr<NecDspTraceLogger> _traceLogger;
uint32_t _prevProgramCounter = 0;
@ -33,8 +33,8 @@ public:
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
@ -44,7 +44,7 @@ public:
shared_ptr<IAssembler> GetAssembler() override;
shared_ptr<IEventManager> GetEventManager() override;
shared_ptr<CodeDataLogger> GetCodeDataLogger() override;
ITraceLogger* GetTraceLogger() override;
// Inherited via IDebugger
virtual BaseState& GetState() override;
BaseState& GetState() override;
};

View file

@ -3,9 +3,9 @@
#include "SNES/MemoryManager.h"
#include "SNES/Console.h"
#include "SNES/Debugger/SpcDebugger.h"
#include "SNES/Debugger/TraceLogger/SpcTraceLogger.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/TraceLogger.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/Debugger.h"
@ -18,13 +18,16 @@
SpcDebugger::SpcDebugger(Debugger* debugger)
{
_debugger = debugger;
_traceLogger = debugger->GetTraceLogger().get();
_disassembler = debugger->GetDisassembler().get();
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
_spc = ((Console*)debugger->GetConsole())->GetSpc().get();
_memoryManager = ((Console*)debugger->GetConsole())->GetMemoryManager().get();
Console* console = (Console*)debugger->GetConsole();
_spc = console->GetSpc().get();
_memoryManager = console->GetMemoryManager().get();
_settings = debugger->GetEmulator()->GetSettings();
_traceLogger.reset(new SpcTraceLogger(debugger, console->GetPpu().get(), console->GetMemoryManager().get()));
_callstackManager.reset(new CallstackManager(debugger));
_breakpointManager.reset(new BreakpointManager(debugger, CpuType::Spc));
_step.reset(new StepRequest());
@ -48,14 +51,14 @@ void SpcDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
BreakSource breakSource = BreakSource::Unspecified;
if(type == MemoryOperationType::ExecOpCode) {
SpcState spcState = _spc->GetState();
SpcState& spcState = _spc->GetState();
if(_traceLogger->IsCpuLogged(CpuType::Spc) || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) {
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) {
_disassembler->BuildCache(addressInfo, 0, CpuType::Spc);
if(_traceLogger->IsCpuLogged(CpuType::Spc)) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Spc);
_traceLogger->Log(CpuType::Spc, spcState, disInfo);
_traceLogger->Log(spcState, disInfo, operation);
}
}
@ -96,8 +99,14 @@ void SpcDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
} else {
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource);
@ -112,6 +121,10 @@ void SpcDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
_disassembler->InvalidateCache(addressInfo, CpuType::Spc);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
}
}
void SpcDebugger::Run()
@ -173,3 +186,8 @@ BaseState& SpcDebugger::GetState()
{
return _spc->GetState();
}
ITraceLogger* SpcDebugger::GetTraceLogger()
{
return _traceLogger.get();
}

View file

@ -5,7 +5,7 @@
class Disassembler;
class Debugger;
class TraceLogger;
class SpcTraceLogger;
class Spc;
class CallstackManager;
class MemoryAccessCounter;
@ -19,7 +19,6 @@ class SpcDebugger final : public IDebugger
{
Debugger* _debugger;
Disassembler* _disassembler;
TraceLogger* _traceLogger;
MemoryAccessCounter* _memoryAccessCounter;
MemoryManager* _memoryManager;
Spc* _spc;
@ -27,6 +26,7 @@ class SpcDebugger final : public IDebugger
shared_ptr<CallstackManager> _callstackManager;
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<SpcTraceLogger> _traceLogger;
unique_ptr<StepRequest> _step;
uint8_t _prevOpCode = 0xFF;
@ -37,8 +37,8 @@ public:
void Reset() override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override;
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type);
void Run() override;
void Step(int32_t stepCount, StepType type) override;
@ -48,6 +48,7 @@ public:
shared_ptr<IAssembler> GetAssembler() override;
shared_ptr<IEventManager> GetEventManager() override;
shared_ptr<CodeDataLogger> GetCodeDataLogger() override;
ITraceLogger* GetTraceLogger() override;
BaseState& GetState() override;
};

View file

@ -0,0 +1,128 @@
#include "stdafx.h"
#include "SNES/Debugger/TraceLogger/Cx4TraceLogger.h"
#include "SNES/Ppu.h"
#include "SNES/MemoryManager.h"
#include "SNES/Coprocessors/GSU/GsuTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Utilities/HexUtilities.h"
Cx4TraceLogger::Cx4TraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Gsu)
{
_ppu = ppu;
_memoryManager = memoryManager;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType Cx4TraceLogger::GetFormatTagType(string& tag)
{
if(tag == "R0") {
return RowDataType::R0;
} else if(tag == "R1") {
return RowDataType::R1;
} else if(tag == "R2") {
return RowDataType::R2;
} else if(tag == "R3") {
return RowDataType::R3;
} else if(tag == "R4") {
return RowDataType::R4;
} else if(tag == "R5") {
return RowDataType::R5;
} else if(tag == "R6") {
return RowDataType::R6;
} else if(tag == "R7") {
return RowDataType::R7;
} else if(tag == "R8") {
return RowDataType::R8;
} else if(tag == "R9") {
return RowDataType::R9;
} else if(tag == "R10") {
return RowDataType::R10;
} else if(tag == "R11") {
return RowDataType::R11;
} else if(tag == "R12") {
return RowDataType::R12;
} else if(tag == "R13") {
return RowDataType::R13;
} else if(tag == "R14") {
return RowDataType::R14;
} else if(tag == "R15") {
return RowDataType::R15;
} else if(tag == "MAR") {
return RowDataType::MAR;
} else if(tag == "MDR") {
return RowDataType::MDR;
} else if(tag == "DPR") {
return RowDataType::DPR;
} else if(tag == "ML") {
return RowDataType::ML;
} else if(tag == "MH") {
return RowDataType::MH;
} else if(tag == "PB") {
return RowDataType::PB;
} else if(tag == "P") {
return RowDataType::P;
} else if(tag == "PS") {
return RowDataType::PS;
} else if(tag == "A") {
return RowDataType::A;
} else {
return RowDataType::Text;
}
}
void Cx4TraceLogger::GetTraceRow(string& output, Cx4State& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo)
{
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::PS: {
string status = string(cpuState.Carry ? "C" : "c") + (cpuState.Zero ? "Z" : "z") + (cpuState.Overflow ? "V" : "v") + (cpuState.Negative ? "N" : "n");
WriteStringValue(output, status, rowPart);
break;
}
case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break;
case RowDataType::R0: WriteIntValue(output, cpuState.Regs[0], rowPart); break;
case RowDataType::R1: WriteIntValue(output, cpuState.Regs[1], rowPart); break;
case RowDataType::R2: WriteIntValue(output, cpuState.Regs[2], rowPart); break;
case RowDataType::R3: WriteIntValue(output, cpuState.Regs[3], rowPart); break;
case RowDataType::R4: WriteIntValue(output, cpuState.Regs[4], rowPart); break;
case RowDataType::R5: WriteIntValue(output, cpuState.Regs[5], rowPart); break;
case RowDataType::R6: WriteIntValue(output, cpuState.Regs[6], rowPart); break;
case RowDataType::R7: WriteIntValue(output, cpuState.Regs[7], rowPart); break;
case RowDataType::R8: WriteIntValue(output, cpuState.Regs[8], rowPart); break;
case RowDataType::R9: WriteIntValue(output, cpuState.Regs[9], rowPart); break;
case RowDataType::R10: WriteIntValue(output, cpuState.Regs[10], rowPart); break;
case RowDataType::R11: WriteIntValue(output, cpuState.Regs[11], rowPart); break;
case RowDataType::R12: WriteIntValue(output, cpuState.Regs[12], rowPart); break;
case RowDataType::R13: WriteIntValue(output, cpuState.Regs[13], rowPart); break;
case RowDataType::R14: WriteIntValue(output, cpuState.Regs[14], rowPart); break;
case RowDataType::R15: WriteIntValue(output, cpuState.Regs[15], rowPart); break;
case RowDataType::MAR: WriteIntValue(output, cpuState.MemoryAddressReg, rowPart); break;
case RowDataType::MDR: WriteIntValue(output, cpuState.MemoryDataReg, rowPart); break;
case RowDataType::DPR: WriteIntValue(output, cpuState.DataPointerReg, rowPart); break;
case RowDataType::ML: WriteIntValue(output, (uint32_t)(cpuState.Mult & 0xFFFFFF), rowPart); break;
case RowDataType::MH: WriteIntValue(output, (uint32_t)(cpuState.Mult >> 24), rowPart); break;
case RowDataType::PB: WriteIntValue(output, cpuState.PB, rowPart); break;
case RowDataType::P: WriteIntValue(output, cpuState.P, rowPart); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void Cx4TraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_memoryManager->GetHClock(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "SNES/Coprocessors/CX4/Cx4Types.h"
class DisassemblyInfo;
class Debugger;
class Ppu;
class MemoryManager;
class Cx4TraceLogger : public BaseTraceLogger<Cx4TraceLogger, Cx4State>
{
private:
Ppu* _ppu = nullptr;
MemoryManager* _memoryManager = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
Cx4TraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager);
void GetTraceRow(string& output, Cx4State& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(Cx4State& state) { return (state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF; }
__forceinline uint64_t GetCycleCount(Cx4State& state) { return state.CycleCount; }
__forceinline uint8_t GetStackPointer(Cx4State& state) { return state.SP; }
};

View file

@ -0,0 +1,100 @@
#include "stdafx.h"
#include "SNES/Debugger/TraceLogger/GsuTraceLogger.h"
#include "SNES/Ppu.h"
#include "SNES/MemoryManager.h"
#include "SNES/Coprocessors/GSU/GsuTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Utilities/HexUtilities.h"
GsuTraceLogger::GsuTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Gsu)
{
_ppu = ppu;
_memoryManager = memoryManager;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType GsuTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "R0") {
return RowDataType::R0;
} else if(tag == "R1") {
return RowDataType::R1;
} else if(tag == "R2") {
return RowDataType::R2;
} else if(tag == "R3") {
return RowDataType::R3;
} else if(tag == "R4") {
return RowDataType::R4;
} else if(tag == "R5") {
return RowDataType::R5;
} else if(tag == "R6") {
return RowDataType::R6;
} else if(tag == "R7") {
return RowDataType::R7;
} else if(tag == "R8") {
return RowDataType::R8;
} else if(tag == "R9") {
return RowDataType::R9;
} else if(tag == "R10") {
return RowDataType::R10;
} else if(tag == "R11") {
return RowDataType::R11;
} else if(tag == "R12") {
return RowDataType::R12;
} else if(tag == "R13") {
return RowDataType::R13;
} else if(tag == "R14") {
return RowDataType::R14;
} else if(tag == "R15") {
return RowDataType::R15;
} else if(tag == "SRC") {
return RowDataType::Src;
} else if(tag == "DST") {
return RowDataType::Dst;
} else {
return RowDataType::Text;
}
}
void GsuTraceLogger::GetTraceRow(string &output, GsuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::R0: WriteIntValue(output, cpuState.R[0], rowPart); break;
case RowDataType::R1: WriteIntValue(output, cpuState.R[1], rowPart); break;
case RowDataType::R2: WriteIntValue(output, cpuState.R[2], rowPart); break;
case RowDataType::R3: WriteIntValue(output, cpuState.R[3], rowPart); break;
case RowDataType::R4: WriteIntValue(output, cpuState.R[4], rowPart); break;
case RowDataType::R5: WriteIntValue(output, cpuState.R[5], rowPart); break;
case RowDataType::R6: WriteIntValue(output, cpuState.R[6], rowPart); break;
case RowDataType::R7: WriteIntValue(output, cpuState.R[7], rowPart); break;
case RowDataType::R8: WriteIntValue(output, cpuState.R[8], rowPart); break;
case RowDataType::R9: WriteIntValue(output, cpuState.R[9], rowPart); break;
case RowDataType::R10: WriteIntValue(output, cpuState.R[10], rowPart); break;
case RowDataType::R11: WriteIntValue(output, cpuState.R[11], rowPart); break;
case RowDataType::R12: WriteIntValue(output, cpuState.R[12], rowPart); break;
case RowDataType::R13: WriteIntValue(output, cpuState.R[13], rowPart); break;
case RowDataType::R14: WriteIntValue(output, cpuState.R[14], rowPart); break;
case RowDataType::R15: WriteIntValue(output, cpuState.R[15], rowPart); break;
case RowDataType::Src: WriteIntValue(output, cpuState.SrcReg, rowPart); break;
case RowDataType::Dst: WriteIntValue(output, cpuState.DestReg, rowPart); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void GsuTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_memoryManager->GetHClock(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "SNES/Coprocessors/GSU/GsuTypes.h"
class DisassemblyInfo;
class Debugger;
class Ppu;
class MemoryManager;
class GsuTraceLogger : public BaseTraceLogger<GsuTraceLogger, GsuState>
{
private:
Ppu* _ppu = nullptr;
MemoryManager* _memoryManager = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
GsuTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager);
void GetTraceRow(string& output, GsuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(GsuState& state) { return (state.ProgramBank << 16) | state.R[15]; }
__forceinline uint64_t GetCycleCount(GsuState& state) { return state.CycleCount; }
__forceinline uint8_t GetStackPointer(GsuState& state) { return 0; }
};

View file

@ -0,0 +1,105 @@
#include "stdafx.h"
#include "SNES/Debugger/TraceLogger/NecDspTraceLogger.h"
#include "SNES/Ppu.h"
#include "SNES/MemoryManager.h"
#include "SNES/Coprocessors/GSU/GsuTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Utilities/HexUtilities.h"
NecDspTraceLogger::NecDspTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Gsu)
{
_ppu = ppu;
_memoryManager = memoryManager;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType NecDspTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "A") {
return RowDataType::A;
} else if(tag == "B") {
return RowDataType::B;
}if(tag == "FlagsA") {
return RowDataType::FlagsA;
} else if(tag == "FlagsB") {
return RowDataType::FlagsB;
} else if(tag == "K") {
return RowDataType::K;
} else if(tag == "L") {
return RowDataType::L;
} else if(tag == "M") {
return RowDataType::M;
} else if(tag == "N") {
return RowDataType::N;
} else if(tag == "RP") {
return RowDataType::RP;
} else if(tag == "DP") {
return RowDataType::DP;
} else if(tag == "DR") {
return RowDataType::DR;
} else if(tag == "SR") {
return RowDataType::SR;
} else if(tag == "TR") {
return RowDataType::TR;
} else if(tag == "TRB") {
return RowDataType::TRB;
} else if(tag == "R12") {
return RowDataType::R12;
} else if(tag == "R13") {
return RowDataType::R13;
} else if(tag == "R14") {
return RowDataType::R14;
} else if(tag == "R15") {
return RowDataType::R15;
} else if(tag == "SRC") {
return RowDataType::Src;
} else if(tag == "DST") {
return RowDataType::Dst;
} else {
return RowDataType::Text;
}
}
void NecDspTraceLogger::WriteAccFlagsValue(string& output, NecDspAccFlags flags, RowPart& rowPart)
{
string status = string(flags.Carry ? "C" : "c") + (flags.Zero ? "Z" : "z") + (flags.Overflow0 ? "V" : "v") + (flags.Overflow1 ? "V" : "v") + (flags.Sign0 ? "N" : "n") + (flags.Sign1 ? "N" : "n");
WriteStringValue(output, status, rowPart);
}
void NecDspTraceLogger::GetTraceRow(string& output, NecDspState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo)
{
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break;
case RowDataType::FlagsA: WriteAccFlagsValue(output, cpuState.FlagsA, rowPart); break;
case RowDataType::B: WriteIntValue(output, cpuState.B, rowPart); break;
case RowDataType::FlagsB: WriteAccFlagsValue(output, cpuState.FlagsB, rowPart); break;
case RowDataType::K: WriteIntValue(output, cpuState.K, rowPart); break;
case RowDataType::L: WriteIntValue(output, cpuState.L, rowPart); break;
case RowDataType::M: WriteIntValue(output, cpuState.M, rowPart); break;
case RowDataType::N: WriteIntValue(output, cpuState.N, rowPart); break;
case RowDataType::RP: WriteIntValue(output, cpuState.RP, rowPart); break;
case RowDataType::DP: WriteIntValue(output, cpuState.DP, rowPart); break;
case RowDataType::DR: WriteIntValue(output, cpuState.DR, rowPart); break;
case RowDataType::SR: WriteIntValue(output, cpuState.SR, rowPart); break;
case RowDataType::TR: WriteIntValue(output, cpuState.TR, rowPart); break;
case RowDataType::TRB: WriteIntValue(output, cpuState.TRB, rowPart); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void NecDspTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_memoryManager->GetHClock(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,31 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "SNES/Coprocessors/DSP/NecDspTypes.h"
class DisassemblyInfo;
class Debugger;
class Ppu;
class MemoryManager;
class NecDspTraceLogger : public BaseTraceLogger<NecDspTraceLogger, NecDspState>
{
private:
Ppu* _ppu = nullptr;
MemoryManager* _memoryManager = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
void WriteAccFlagsValue(string& output, NecDspAccFlags flags, RowPart& rowPart);
public:
NecDspTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager);
void GetTraceRow(string& output, NecDspState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(NecDspState& state) { return state.PC; }
__forceinline uint64_t GetCycleCount(NecDspState& state) { return 0; } //TODO
__forceinline uint8_t GetStackPointer(NecDspState& state) { return state.SP; }
};

View file

@ -0,0 +1,67 @@
#include "stdafx.h"
#include "SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h"
#include "SNES/CpuTypes.h"
#include "SNES/Ppu.h"
#include "SNES/MemoryManager.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
SnesCpuTraceLogger::SnesCpuTraceLogger(Debugger* debugger, CpuType cpuType, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, cpuType)
{
_ppu = ppu;
_memoryManager = memoryManager;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType SnesCpuTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "A") {
return RowDataType::A;
} else if(tag == "X") {
return RowDataType::X;
} else if(tag == "Y") {
return RowDataType::Y;
} else if(tag == "D") {
return RowDataType::D;
} else if(tag == "DB") {
return RowDataType::DB;
} else if(tag == "P") {
return RowDataType::PS;
} else if(tag == "SP") {
return RowDataType::SP;
} else {
return RowDataType::Text;
}
}
void SnesCpuTraceLogger::GetTraceRow(string &output, CpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
constexpr char activeStatusLetters[8] = { 'N', 'V', 'M', 'X', 'D', 'I', 'Z', 'C' };
constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'm', 'x', 'd', 'i', 'z', 'c' };
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break;
case RowDataType::X: WriteIntValue(output, cpuState.X, rowPart); break;
case RowDataType::Y: WriteIntValue(output, cpuState.Y, rowPart); break;
case RowDataType::D: WriteIntValue(output, cpuState.D, rowPart); break;
case RowDataType::DB: WriteIntValue(output, cpuState.DBR, rowPart); break;
case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.PS, rowPart); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void SnesCpuTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_memoryManager->GetHClock(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "SNES/CpuTypes.h"
class DisassemblyInfo;
class Debugger;
class Ppu;
class MemoryManager;
class SnesCpuTraceLogger : public BaseTraceLogger<SnesCpuTraceLogger, CpuState>
{
private:
Ppu* _ppu = nullptr;
MemoryManager* _memoryManager = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
SnesCpuTraceLogger(Debugger* debugger, CpuType cpuType, Ppu* ppu, MemoryManager* memoryManager);
void GetTraceRow(string &output, CpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(CpuState& state) { return (state.K << 16) | state.PC; }
__forceinline uint64_t GetCycleCount(CpuState& state) { return state.CycleCount; }
__forceinline uint8_t GetStackPointer(CpuState& state) { return (uint8_t)state.SP; }
};

View file

@ -0,0 +1,61 @@
#include "stdafx.h"
#include "SNES/Debugger/TraceLogger/SpcTraceLogger.h"
#include "SNES/CpuTypes.h"
#include "SNES/Ppu.h"
#include "SNES/MemoryManager.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
SpcTraceLogger::SpcTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Spc)
{
_ppu = ppu;
_memoryManager = memoryManager;
TraceLoggerOptions options = {};
SetOptions(options);
}
RowDataType SpcTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "A") {
return RowDataType::A;
} else if(tag == "X") {
return RowDataType::X;
} else if(tag == "Y") {
return RowDataType::Y;
} else if(tag == "P") {
return RowDataType::PS;
} else if(tag == "SP") {
return RowDataType::SP;
} else {
return RowDataType::Text;
}
}
void SpcTraceLogger::GetTraceRow(string &output, SpcState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
constexpr char activeStatusLetters[8] = { 'N', 'V', 'P', 'B', 'H', 'I', 'Z', 'C' };
constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'p', 'b', 'h', 'i', 'z', 'c' };
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break;
case RowDataType::X: WriteIntValue(output, cpuState.X, rowPart); break;
case RowDataType::Y: WriteIntValue(output, cpuState.Y, rowPart); break;
case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break;
case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.PS, rowPart); break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void SpcTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_memoryManager->GetHClock(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include "Debugger/BaseTraceLogger.h"
#include "SNES/SpcTypes.h"
class DisassemblyInfo;
class Debugger;
class Ppu;
class MemoryManager;
class SpcTraceLogger : public BaseTraceLogger<SpcTraceLogger, SpcState>
{
private:
Ppu* _ppu = nullptr;
MemoryManager* _memoryManager = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
SpcTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager);
void GetTraceRow(string &output, SpcState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(SpcState& state) { return state.PC; }
__forceinline uint64_t GetCycleCount(SpcState& state) { return state.Cycle; }
__forceinline uint8_t GetStackPointer(SpcState& state) { return state.SP; }
};

View file

@ -337,7 +337,8 @@ uint8_t Spc::DspReadRam(uint16_t addr)
{
uint8_t value = _ram[addr];
#ifndef DUMMYSPC
_emu->ProcessMemoryRead<CpuType::Spc>(addr, value, MemoryOperationType::Read);
//TODO
//_emu->ProcessMemoryRead<CpuType::Spc>(addr, value, MemoryOperationType::Read);
#endif
return value;
}
@ -345,7 +346,8 @@ uint8_t Spc::DspReadRam(uint16_t addr)
void Spc::DspWriteRam(uint16_t addr, uint8_t value)
{
#ifndef DUMMYSPC
_emu->ProcessMemoryWrite<CpuType::Spc>(addr, value, MemoryOperationType::Write);
//TODO
//_emu->ProcessMemoryWrite<CpuType::Spc>(addr, value, MemoryOperationType::Write);
#endif
_ram[addr] = value;
}

View file

@ -1,7 +1,6 @@
#include "stdafx.h"
#include "Core/Shared/Emulator.h"
#include "Core/Debugger/Debugger.h"
#include "Core/Debugger/TraceLogger.h"
#include "Core/Debugger/MemoryDumper.h"
#include "Core/Debugger/MemoryAccessCounter.h"
#include "Core/Debugger/Disassembler.h"
@ -16,6 +15,7 @@
#include "Core/Debugger/Profiler.h"
#include "Core/Debugger/IAssembler.h"
#include "Core/Debugger/IEventManager.h"
#include "Core/Debugger/ITraceLogger.h"
#include "Core/Gameboy/GbTypes.h"
extern shared_ptr<Emulator> _emu;
@ -51,11 +51,13 @@ extern "C"
DllExport uint32_t __stdcall GetDisassemblyOutput(CpuType type, uint32_t lineIndex, CodeLineData output[], uint32_t rowCount) { return GetDebugger()->GetDisassembler()->GetDisassemblyOutput(type, lineIndex, output, rowCount); }
DllExport int32_t __stdcall SearchDisassembly(CpuType type, const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards) { return GetDebugger()->GetDisassembler()->SearchDisassembly(type, searchString, startPosition, endPosition, searchBackwards); }
DllExport void __stdcall SetTraceOptions(TraceLoggerOptions options) { GetDebugger()->GetTraceLogger()->SetOptions(options); }
DllExport void __stdcall StartTraceLogger(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); }
DllExport void __stdcall StopTraceLogger() { GetDebugger()->GetTraceLogger()->StopLogging(); }
DllExport void __stdcall ClearTraceLog() { GetDebugger()->GetTraceLogger()->Clear(); }
DllExport const char* GetExecutionTrace(uint32_t lineCount) { return GetDebugger()->GetTraceLogger()->GetExecutionTrace(lineCount); }
//DllExport void __stdcall ClearTraceLog() { GetDebugger()->GetTraceLogger()->Clear(); }
DllExport void __stdcall SetTraceOptions(CpuType type, TraceLoggerOptions options) { GetDebugger()->GetTraceLogger(type)->SetOptions(options); }
DllExport uint32_t __stdcall GetExecutionTrace(TraceRow output[], uint32_t startOffset, uint32_t lineCount) { return GetDebugger()->GetExecutionTrace(output, startOffset, lineCount); }
//DllExport void __stdcall StartLogTraceToFile(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); }
//DllExport void __stdcall StopLogTraceToFile() { GetDebugger()->GetTraceLogger()->StopLogging(); }
DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->SetBreakpoints(breakpoints, length); }
DllExport int32_t __stdcall EvaluateExpression(char* expression, CpuType cpuType, EvalResultType *resultType, bool useCache) { return GetDebugger()->EvaluateExpression(expression, cpuType, *resultType, useCache); }

View file

@ -13,7 +13,7 @@ namespace Mesen.Config
public class DebugConfig
{
//public DebuggerShortcutsConfig Shortcuts = new DebuggerShortcutsConfig();
public TraceLoggerInfo TraceLogger = new TraceLoggerInfo();
public TraceLoggerConfig TraceLogger = new TraceLoggerConfig();
public HexEditorConfig HexEditor = new HexEditorConfig();
public EventViewerConfig EventViewer = new EventViewerConfig();
public DebuggerConfig Debugger = new DebuggerConfig();

View file

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using Avalonia;
using Avalonia.Media;
using Mesen.Debugger;
using Mesen.ViewModels;
namespace Mesen.Config
{
public class TraceLoggerConfig : ViewModelBase
{
public bool AutoRefresh { get; set; } = true;
public Size WindowSize { get; set; } = new Size(0, 0);
public Point WindowLocation { get; set; } = new Point(0, 0);
public string FontFamily { get; set; } = DebuggerConfig.MonospaceFontFamily;
public FontStyle FontStyle { get; set; } = FontStyle.Normal;
public float FontSize { get; set; } = DebuggerConfig.DefaultFontSize;
public int TextZoom { get; set; } = 100;
public TraceLoggerCpuConfig SnesCpu { get; set; }
public TraceLoggerCpuConfig SpcCpu { get; set; }
public TraceLoggerCpuConfig Sa1Cpu { get; set; }
public TraceLoggerCpuConfig GsuCpu { get; set; }
public TraceLoggerCpuConfig Cx4Cpu { get; set; }
public TraceLoggerCpuConfig NecDspCpu { get; set; }
public TraceLoggerCpuConfig NesCpu { get; set; }
public TraceLoggerCpuConfig GameboyCpu { get; set; }
public TraceLoggerConfig()
{
SnesCpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,4h] X:[X,4h] Y:[Y,4h] S:[SP,4h] D:[D,4h] DB:[DB,2h] P:[P,h] H:[HClock,3] V:[Scanline,3]"
};
SpcCpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,8] H:[HClock,3] V:[Scanline,3]"
};
Sa1Cpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,4h] X:[X,4h] Y:[Y,4h] S:[SP,4h] D:[D,4h] DB:[DB,2h] P:[P,h] H:[HClock,3] V:[Scanline,3]"
};
GsuCpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][Align,28] SRC:[SRC,2h] DST:[DST,2h] R0:[R0,4h] R1:[R1,4h] R2:[R2,4h] R3:[R3,4h] R4:[R4,4h] R5:[R5,4h] R6:[R6,4h] R7:[R7,4h] R8:[R8,4h] R9:[R9,4h] R10:[R10,4h] R11:[R11,4h] R12:[R12,4h] R13:[R13,4h] R14:[R14,4h] R15:[R15,4h] H:[Cycle,3] V:[Scanline,3]"
};
Cx4Cpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][Align,28] A:[A,6h] MAR:[MAR,6h] MDR:[MDR,6h] DPR:[DPR,6h] ML:[ML,6h] MH:[MH,6h] P:[P,4h] PB:[PB,4h] PS:[PS] R0:[R0,6h] R1:[R1,6h] R2:[R2,6h] R3:[R3,6h] R4:[R4,6h] R5:[R5,6h] R6:[R6,6h] R7:[R7,6h] R8:[R8,6h] R9:[R9,6h] R10:[R10,6h] R11:[R11,6h] R12:[R12,6h] R13:[R13,6h] R14:[R14,6h] R15:[R15,6h] H:[Cycle,3] V:[Scanline,3]"
};
NecDspCpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][Align,28] A:[A,4h] [FlagsA] B:[A,4h] [FlagsB] K:[K,4h] L:[L,4h] M:[M,4h] N:[N,4h] RP:[RP,4h] DP:[DP,4h] DR:[DR,4h] SR:[SR,4h] TR:[TR,4h] TRB:[TRB,4h] H:[Cycle,3] V:[Scanline,3]"
};
NesCpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,h] V:[Scanline,3] H:[Cycle,3]"
};
GameboyCpu = new TraceLoggerCpuConfig() {
Enabled = true,
Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,2h] B:[B,2h] C:[C,2h] D:[D,2h] E:[E,2h] F:[PS,4] HL:[H,2h][L,2h] V:[Scanline,3] H:[Cycle,3]"
};
}
}
}

View file

@ -0,0 +1,35 @@
using ReactiveUI.Fody.Helpers;
namespace Mesen.Config
{
public class TraceLoggerCpuConfig : BaseConfig<TraceLoggerCpuConfig>
{
[Reactive] public bool Enabled { get; set; }
[Reactive] public bool ShowByteCode { get; set; }
[Reactive] public bool ShowRegisters { get; set; }
[Reactive] public bool ShowCpuCycles { get; set; }
[Reactive] public bool ShowPpuCycles { get; set; }
[Reactive] public bool ShowPpuScanline { get; set; }
[Reactive] public bool ShowPpuFrames { get; set; }
[Reactive] public bool IndentCode { get; set; }
[Reactive] public bool ShowEffectiveAddresses { get; set; }
[Reactive] public bool ShowMemoryValues { get; set; }
[Reactive] public bool UseLabels { get; set; }
[Reactive] public StatusFlagFormat StatusFormat { get; set; }
[Reactive] public bool OverrideFormat { get; set; }
[Reactive] public string Format { get; set; } = "";
[Reactive] public string Condition { get; set; } = "";
}
public enum StatusFlagFormat
{
Hexadecimal = 0,
Text = 1,
CompactText = 2
}
}

View file

@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using Avalonia;
using Avalonia.Media;
using Mesen.Debugger;
namespace Mesen.Config
{
public class TraceLoggerInfo
{
public TraceLoggerOptions LogOptions;
public bool AutoRefresh = true;
public int LineCount = 1000;
public Size WindowSize = new Size(0, 0);
public Point WindowLocation;
public string FontFamily = DebuggerConfig.MonospaceFontFamily;
public FontStyle FontStyle = FontStyle.Normal;
public float FontSize = DebuggerConfig.DefaultFontSize;
public int TextZoom = 100;
public TraceLoggerInfo()
{
LogOptions = new TraceLoggerOptions() {
LogCpu = true,
ShowByteCode = true,
ShowEffectiveAddresses = true,
ShowPpuFrames = false,
ShowPpuCycles = true,
ShowPpuScanline = true,
ShowRegisters = true,
UseLabels = false,
StatusFormat = StatusFlagFormat.Text
};
}
}
public class TraceLoggerOptions
{
public bool LogCpu;
public bool LogSpc;
public bool LogNecDsp;
public bool LogSa1;
public bool LogGsu;
public bool LogCx4;
public bool LogGameboy;
public bool ShowByteCode;
public bool ShowRegisters;
public bool ShowCpuCycles;
public bool ShowPpuCycles;
public bool ShowPpuScanline;
public bool ShowPpuFrames;
public bool ShowExtraInfo;
public bool IndentCode;
public bool ShowEffectiveAddresses;
public bool ShowMemoryValues;
public bool UseLabels;
public bool ExtendZeroPage;
public bool UseWindowsEol = true; //TODO
public StatusFlagFormat StatusFormat;
public bool OverrideFormat;
public string Format = "";
}
public enum StatusFlagFormat
{
Hexadecimal = 0,
Text = 1,
CompactText = 2
}
}

View file

@ -42,11 +42,17 @@ namespace Mesen.Debugger.Controls
private double RowHeight => this.LetterSize.Height;
private int VisibleRows => (int)(Bounds.Height / RowHeight) - 1;
private bool _scrollByAddress = false;
private bool _updatingScroll = false;
static DisassemblyViewer()
{
AffectsRender<DisassemblyViewer>(DataProviderProperty, ScrollPositionProperty, StyleProviderProperty);
//AffectsRender<DisassemblyViewer>(DataProviderProperty, ScrollPositionProperty, StyleProviderProperty);
DataProviderProperty.Changed.AddClassHandler<DisassemblyViewer>((x, e) => {
x.Refresh();
x.InvalidateVisual();
});
ScrollPositionProperty.Changed.AddClassHandler<DisassemblyViewer>((x, e) => {
if(x._updatingScroll) {
return;
@ -54,20 +60,28 @@ namespace Mesen.Debugger.Controls
x.Refresh();
x._updatingScroll = true;
CodeLineData[] lines = x._lines;
if(e.OldValue is int oldValue && e.NewValue is int newValue && oldValue < newValue) {
foreach(CodeLineData line in lines) {
if(line.Address >= 0 && newValue < line.Address) {
x.ScrollPosition = line.Address;
break;
if(x._scrollByAddress) {
x._updatingScroll = true;
CodeLineData[] lines = x._lines;
if(e.OldValue is int oldValue && e.NewValue is int newValue && oldValue < newValue) {
foreach(CodeLineData line in lines) {
if(line.Address >= 0 && newValue < line.Address) {
x.ScrollPosition = line.Address;
break;
}
}
}
x._updatingScroll = false;
}
x._updatingScroll = false;
x.InvalidateVisual();
});
}
public DisassemblyViewer()
{
ClipToBounds = true;
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
@ -76,13 +90,14 @@ namespace Mesen.Debugger.Controls
int rowNumber = (int)(p.Position.Y / LetterSize.Height);
CodeLineData[] lines = _lines;
if(rowNumber < lines.Length) {
CpuType cpuType = lines[rowNumber].CpuType;
AddressInfo relAddress = new AddressInfo() {
Address = lines[rowNumber].Address,
Type = DataProvider.CpuType.ToMemoryType()
Type = cpuType.ToMemoryType()
};
AddressInfo absAddress = DebugApi.GetAbsoluteAddress(relAddress);
BreakpointManager.ToggleBreakpoint(absAddress.Address < 0 ? relAddress : absAddress, DataProvider.CpuType);
BreakpointManager.ToggleBreakpoint(absAddress.Address < 0 ? relAddress : absAddress, cpuType);
}
}
@ -111,14 +126,16 @@ namespace Mesen.Debugger.Controls
InitFontAndLetterSize();
_lines = dp?.GetCodeLines(scrollPosition, VisibleRows + 3) ?? new CodeLineData[0];
foreach(CodeLineData line in _lines) {
if(line.Address >= 0) {
if(ScrollPosition != line.Address) {
_updatingScroll = true;
ScrollPosition = line.Address;
_updatingScroll = false;
if(_scrollByAddress) {
foreach(CodeLineData line in _lines) {
if(line.Address >= 0) {
if(ScrollPosition != line.Address) {
_updatingScroll = true;
ScrollPosition = line.Address;
_updatingScroll = false;
}
break;
}
break;
}
}
}
@ -141,7 +158,7 @@ namespace Mesen.Debugger.Controls
string addrFormat = "X" + DataProvider.CpuType.GetAddressSize();
double symbolMargin = 20;
double addressMargin = Math.Floor(LetterSize.Width * DataProvider.CpuType.GetAddressSize() + symbolMargin) + 0.5;
double byteCodeMargin = Math.Floor(LetterSize.Width * (4 * DataProvider.CpuType.GetByteCodeSize()));
double byteCodeMargin = Math.Floor(LetterSize.Width * (3 * DataProvider.CpuType.GetByteCodeSize()));
double codeIndent = Math.Floor(LetterSize.Width * 2) + 0.5;
//Draw margin (address)
@ -261,7 +278,6 @@ namespace Mesen.Debugger.Controls
public interface ILineStyleProvider
{
LineProperties GetLineStyle(CodeLineData lineData, int lineIndex);
string? GetLineComment(int lineIndex);
List<CodeColor> GetCodeColors(CodeLineData lineData, bool highlightCode, string addressFormat, Color? textColor, bool showMemoryValues);
}

View file

@ -15,8 +15,6 @@ namespace Mesen.Debugger.Disassembly
{
public int? ActiveAddress { get; set; }
public string? GetLineComment(int lineIndex) => null;
public static void ConfigureActiveStatement(LineProperties props)
{
props.FgColor = Colors.Black;

View file

@ -15,20 +15,20 @@ namespace Mesen.Debugger
public string Text = "";
public Int32 Address;
public Int32 AbsoluteAddress;
public Int32 Address = -1;
public Int32 AbsoluteAddress = -1;
public LineFlags Flags;
public int? CustomIndent = null;
public byte OpSize;
public byte OpSize = 0;
public string ByteCode = "";
public string Comment = "";
public Int32 EffectiveAddress;
public UInt16 Value;
public byte ValueSize;
public Int32 EffectiveAddress = -1;
public UInt16 Value = 0;
public byte ValueSize = 0;
public string GetEffectiveAddressString(string format)
{
@ -90,7 +90,7 @@ namespace Mesen.Debugger
this.OpSize = data.OpSize;
this.ByteCode = "";
for(int i = 0; i < this.OpSize; i++) {
this.ByteCode += "$" + data.ByteCode[i].ToString("X2") + " ";
this.ByteCode += data.ByteCode[i].ToString("X2") + " ";
}
this.Address = data.Address;

View file

@ -0,0 +1,178 @@
using Avalonia.Controls;
using Avalonia.Media;
using Mesen.Config;
using Mesen.Debugger.Controls;
using Mesen.Debugger.Disassembly;
using Mesen.Interop;
using Mesen.Localization;
using Mesen.ViewModels;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.Text;
namespace Mesen.Debugger.ViewModels
{
public class TraceLoggerViewModel : ViewModelBase
{
[Reactive] public TraceLoggerCodeDataProvider DataProvider { get; set; } = new TraceLoggerCodeDataProvider();
[Reactive] public TraceLoggerStyleProvider StyleProvider { get; set; } = new TraceLoggerStyleProvider();
[Reactive] public int ScrollPosition { get; set; } = 0;
[Reactive] public int MaxScrollPosition { get; set; } = 0;
[Reactive] public List<TraceLoggerOptionTab> Tabs { get; set; } = new List<TraceLoggerOptionTab>();
public TraceLoggerViewModel()
{
if(Design.IsDesignMode) {
return;
}
UpdateAvailableTabs();
}
public void UpdateAvailableTabs()
{
List<TraceLoggerOptionTab> tabs = new();
RomInfo romInfo = EmuApi.GetRomInfo();
StyleProvider.SetConsoleType(romInfo.ConsoleType);
foreach(CpuType type in romInfo.CpuTypes) {
tabs.Add(new TraceLoggerOptionTab() {
TabName = ResourceHelper.GetEnumText(type),
CpuType = type,
Options = type switch {
CpuType.Cpu => ConfigManager.Config.Debug.TraceLogger.SnesCpu.Clone(),
CpuType.Spc => ConfigManager.Config.Debug.TraceLogger.SpcCpu.Clone(),
CpuType.Gameboy => ConfigManager.Config.Debug.TraceLogger.GameboyCpu.Clone(),
CpuType.Nes => ConfigManager.Config.Debug.TraceLogger.NesCpu.Clone(),
CpuType.Sa1 => ConfigManager.Config.Debug.TraceLogger.Sa1Cpu.Clone(),
CpuType.Gsu => ConfigManager.Config.Debug.TraceLogger.GsuCpu.Clone(),
CpuType.Cx4 => ConfigManager.Config.Debug.TraceLogger.Cx4Cpu.Clone(),
CpuType.NecDsp => ConfigManager.Config.Debug.TraceLogger.NecDspCpu.Clone(),
_ => throw new Exception("Unsupported cpu type")
}
});
}
Tabs = tabs;
}
public void UpdateLog()
{
foreach(TraceLoggerOptionTab tab in Tabs) {
InteropTraceLoggerOptions options = new InteropTraceLoggerOptions() {
Enabled = tab.Options.Enabled,
UseLabels = true,
Format = Encoding.UTF8.GetBytes(tab.Options.Format),
Condition = Encoding.UTF8.GetBytes(tab.Options.Condition)
};
Array.Resize(ref options.Condition, 1000);
Array.Resize(ref options.Format, 1000);
DebugApi.SetTraceOptions(tab.CpuType, options);
}
DataProvider = new TraceLoggerCodeDataProvider();
MaxScrollPosition = DataProvider.GetLineCount();
ScrollPosition = MaxScrollPosition - 0x20;
}
}
public class TraceLoggerOptionTab
{
[Reactive] public string TabName { get; set; } = "";
[Reactive] public CpuType CpuType { get; set; } = CpuType.Cpu;
public TraceLoggerCpuConfig Options { get; set; } = new TraceLoggerCpuConfig();
}
public class TraceLoggerCodeDataProvider : ICodeDataProvider
{
public TraceLoggerCodeDataProvider()
{
}
public bool UseOptimizedSearch { get { return false; } }
public CpuType CpuType => CpuType.Cpu;
public int GetLineCount()
{
return 30000;
}
public int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards)
{
throw new NotImplementedException();
}
public CodeLineData[] GetCodeLines(int startIndex, int rowCount)
{
TraceRow[] rows = DebugApi.GetExecutionTrace((uint)(30000 - startIndex), (uint)rowCount);
List<CodeLineData> lines = new(rowCount);
for(int i = 0; i < rows.Length; i++) {
lines.Insert(0, new CodeLineData(rows[i].Type) {
Address = (int)rows[i].ProgramCounter,
Text = rows[i].GetOutput(),
ByteCode = rows[i].GetByteCode(),
EffectiveAddress = -1
});
}
while(lines.Count < rowCount) {
lines.Insert(0, new CodeLineData(CpuType.Cpu));
}
return lines.ToArray();
}
}
public class TraceLoggerStyleProvider : ILineStyleProvider
{
private LineProperties _defaultLine = new LineProperties() { AddressColor = null, LineBgColor = null };
private LineProperties _spcLine = new LineProperties() { AddressColor = Color.FromRgb(30, 145, 30), LineBgColor = Color.FromRgb(230, 245, 230) };
private LineProperties _coprocLine = new LineProperties() { AddressColor = Color.FromRgb(30, 30, 145), LineBgColor = Color.FromRgb(230, 230, 245) };
private ConsoleType _consoleType = ConsoleType.Snes;
public TraceLoggerStyleProvider()
{
}
public List<CodeColor> GetCodeColors(CodeLineData lineData, bool highlightCode, string addressFormat, Color? textColor, bool showMemoryValues)
{
return CodeHighlighting.GetCpuHighlights(lineData, highlightCode, addressFormat, textColor, showMemoryValues);
}
public LineProperties GetLineStyle(CodeLineData lineData, int lineIndex)
{
switch(lineData.CpuType) {
case CpuType.Spc: return _spcLine;
case CpuType.NecDsp:
case CpuType.Sa1:
case CpuType.Gsu:
case CpuType.Cx4:
return _coprocLine;
case CpuType.Gameboy:
if(_consoleType == ConsoleType.Snes) {
return _coprocLine;
}
break;
default: break;
}
return _defaultLine;
}
internal void SetConsoleType(ConsoleType consoleType)
{
_consoleType = consoleType;
}
}
}

View file

@ -0,0 +1,66 @@
<Window
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:m="clr-namespace:Mesen"
xmlns:vm="using:Mesen.Debugger.ViewModels"
xmlns:sys="using:System"
xmlns:v="using:Mesen.Views"
xmlns:dbg="using:Mesen.Debugger"
xmlns:c="using:Mesen.Controls"
xmlns:l="using:Mesen.Localization"
xmlns:i="using:Mesen.Interop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dvm="using:Mesen.Debugger.ViewModels"
xmlns:dc="using:Mesen.Debugger.Controls"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="300"
x:Class="Mesen.Debugger.Windows.TraceLoggerWindow"
Icon="/Assets/LogWindow.png"
Width="700" Height="500"
x:DataType="vm:TraceLoggerViewModel"
Title="{l:Translate wndTitle}"
>
<Design.DataContext>
<vm:TraceLoggerViewModel />
</Design.DataContext>
<DockPanel>
<TabControl Items="{CompiledBinding Tabs}" Padding="1" DockPanel.Dock="Bottom">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{l:Translate chkEnabled}" IsChecked="{Binding Options.Enabled}" />
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
<TextBlock VerticalAlignment="Center" Text="{l:Translate lblFormat}" />
<TextBox Grid.Column="1" Text="{Binding Options.Format}" />
<TextBlock VerticalAlignment="Center" Grid.Row="1" Text="{l:Translate lblCondition}" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Options.Condition}" />
</Grid>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<ScrollBar
DockPanel.Dock="Right"
Orientation="Vertical"
Minimum="0"
Maximum="{CompiledBinding MaxScrollPosition}"
VerticalAlignment="Stretch"
AllowAutoHide="False"
Value="{CompiledBinding ScrollPosition}"
/>
<dc:DisassemblyViewer
DataProvider="{CompiledBinding DataProvider}"
StyleProvider="{CompiledBinding StyleProvider}"
ScrollPosition="{CompiledBinding ScrollPosition}"
/>
</DockPanel>
</Window>

View file

@ -0,0 +1,87 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Threading;
using AvaloniaEdit;
using AvaloniaEdit.Highlighting;
using AvaloniaEdit.Highlighting.Xshd;
using Mesen.Debugger.Controls;
using Mesen.Debugger.ViewModels;
using Mesen.Interop;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Xml;
namespace Mesen.Debugger.Windows
{
public class TraceLoggerWindow : Window
{
private NotificationListener? _listener = null;
private TraceLoggerViewModel Model => ((TraceLoggerViewModel)DataContext!);
private int _refreshCounter = 0;
public TraceLoggerWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
if(Design.IsDesignMode) {
return;
}
_listener = new NotificationListener();
_listener.OnNotification += listener_OnNotification;
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
_listener?.Dispose();
}
private void listener_OnNotification(NotificationEventArgs e)
{
switch(e.NotificationType) {
case ConsoleNotificationType.GameLoaded:
Model.UpdateAvailableTabs();
break;
case ConsoleNotificationType.CodeBreak: {
Dispatcher.UIThread.Post(() => {
Model.UpdateLog();
});
break;
}
case ConsoleNotificationType.PpuFrameDone: {
_refreshCounter++;
if(_refreshCounter == 9) {
//Refresh every 9 frames, ~7fps
Dispatcher.UIThread.Post(() => {
Model.UpdateLog();
});
_refreshCounter = 0;
}
break;
}
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -20,10 +20,26 @@ namespace Mesen.Interop
[DllImport(DllPath)] public static extern void ResumeExecution();
[DllImport(DllPath)] public static extern void Step(CpuType cpuType, Int32 instructionCount, StepType type = StepType.Step);
[DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.LPUTF8Str)]string filename);
/*[DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.LPUTF8Str)]string filename);
[DllImport(DllPath)] public static extern void StopTraceLogger();
[DllImport(DllPath)] public static extern void SetTraceOptions(InteropTraceLoggerOptions options);
[DllImport(DllPath)] public static extern void ClearTraceLog();
[DllImport(DllPath)] public static extern void ClearTraceLog();*/
[DllImport(DllPath)] public static extern void SetTraceOptions(CpuType cpuType, InteropTraceLoggerOptions options);
[DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern UInt32 GetExecutionTraceWrapper(IntPtr output, UInt32 startOffset, UInt32 maxRowCount);
public static TraceRow[] GetExecutionTrace(UInt32 startOffset, UInt32 maxRowCount)
{
TraceRow[] rows = new TraceRow[maxRowCount];
GCHandle handle = GCHandle.Alloc(rows, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
UInt32 rowCount = DebugApi.GetExecutionTraceWrapper(ptr, startOffset, maxRowCount);
handle.Free();
Array.Resize(ref rows, (int)rowCount);
return rows;
}
[DllImport(DllPath, EntryPoint = "GetDebuggerLog")] private static extern IntPtr GetDebuggerLogWrapper();
public static string GetLog() { return Utf8Utilities.PtrToStringUtf8(DebugApi.GetDebuggerLogWrapper()).Replace("\n", Environment.NewLine); }
@ -49,9 +65,6 @@ namespace Mesen.Interop
[DllImport(DllPath)] public static extern int SearchDisassembly(CpuType type, [MarshalAs(UnmanagedType.LPUTF8Str)]string searchString, int startPosition, int endPosition, [MarshalAs(UnmanagedType.I1)]bool searchBackwards);
[DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern IntPtr GetExecutionTraceWrapper(UInt32 lineCount);
public static string GetExecutionTrace(UInt32 lineCount) { return Utf8Utilities.PtrToStringUtf8(DebugApi.GetExecutionTraceWrapper(lineCount)); }
[DllImport(DllPath, EntryPoint = "GetState")] private static extern void GetState(IntPtr state, CpuType cpuType);
public static T GetState<T>(CpuType cpuType) where T : struct, BaseState
@ -654,19 +667,9 @@ namespace Mesen.Interop
[Serializable]
public struct InteropTraceLoggerOptions
{
[MarshalAs(UnmanagedType.I1)] public bool LogCpu;
[MarshalAs(UnmanagedType.I1)] public bool LogSpc;
[MarshalAs(UnmanagedType.I1)] public bool LogNecDsp;
[MarshalAs(UnmanagedType.I1)] public bool LogSa1;
[MarshalAs(UnmanagedType.I1)] public bool LogGsu;
[MarshalAs(UnmanagedType.I1)] public bool LogCx4;
[MarshalAs(UnmanagedType.I1)] public bool LogGameboy;
[MarshalAs(UnmanagedType.I1)] public bool ShowExtraInfo;
[MarshalAs(UnmanagedType.I1)] public bool Enabled;
[MarshalAs(UnmanagedType.I1)] public bool IndentCode;
[MarshalAs(UnmanagedType.I1)] public bool UseLabels;
[MarshalAs(UnmanagedType.I1)] public bool UseWindowsEol;
[MarshalAs(UnmanagedType.I1)] public bool ExtendZeroPage;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)]
public byte[] Condition;
@ -843,4 +846,41 @@ namespace Mesen.Interop
public UInt64 MaxCycles;
public AddressInfo Address;
}
public unsafe struct TraceRow
{
public UInt32 ProgramCounter;
public CpuType Type;
public fixed byte ByteCode[4];
public byte ByteCodeSize;
public fixed byte LogOutput[500];
public unsafe string GetOutput()
{
fixed(byte* output = LogOutput) {
int i;
for(i = 0; i < 500; i++) {
if(output[i] == 0) {
break;
}
}
return UTF8Encoding.UTF8.GetString(output, i);
}
}
public unsafe string GetByteCode()
{
fixed(byte* output = ByteCode) {
StringBuilder sb = new StringBuilder();
int i;
for(i = 0; i < ByteCodeSize && i < 4; i++) {
sb.Append(ByteCode[i].ToString("X2") + " ");
}
return sb.ToString().Trim();
}
}
}
}

View file

@ -798,6 +798,13 @@
<Control ID="btnStop">Stop</Control>
</Form>
<Form ID="TraceLoggerWindow">
<Control ID="wndTitle">Trace Logger</Control>
<Control ID="lblFormat">Format: </Control>
<Control ID="lblCondition">Condition: </Control>
<Control ID="chkEnabled">Enabled</Control>
</Form>
<Form ID="EventViewerWindow">
<Control ID="wndTitle">Event Viewer</Control>

View file

@ -15,14 +15,14 @@
<ApplicationIcon>Assets\Icon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputPath>C:\Code\Mesen-S\bin\x64\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>C:\Code\Mesen-S\bin\x64\Release\</OutputPath>
<Optimize>false</Optimize>
<DefineConstants>DEBUG</DefineConstants>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\Accept.png" />
@ -215,6 +215,9 @@
<Compile Update="Debugger\Views\SnesPpuView.axaml.cs">
<DependentUpon>SnesPpuView.axaml</DependentUpon>
</Compile>
<Compile Update="Debugger\Windows\TraceLoggerWindow.axaml.cs">
<DependentUpon>TraceLoggerWindow.axaml</DependentUpon>
</Compile>
<Compile Update="Debugger\Windows\ProfilerWindow.axaml.cs">
<DependentUpon>ProfilerWindow.axaml</DependentUpon>
</Compile>

View file

@ -405,6 +405,11 @@
<Image Source="/Assets/Speed.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Trace Logger" Click="OnTraceLoggerClick">
<MenuItem.Icon>
<Image Source="/Assets/LogWindow.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Event Viewer" Click="OnEventViewerClick">
<MenuItem.Icon>
<Image Source="/Assets/EventViewer.png" />

View file

@ -95,6 +95,13 @@ namespace Mesen.Views
}.Show();
}
private void OnTraceLoggerClick(object sender, RoutedEventArgs e)
{
new TraceLoggerWindow {
DataContext = new TraceLoggerViewModel()
}.Show();
}
private void OnEventViewerClick(object sender, RoutedEventArgs e)
{
RomInfo romInfo = EmuApi.GetRomInfo();