mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
Trace logger (missing file logging)
This commit is contained in:
parent
a0a06e15a4
commit
078546329f
66 changed files with 2430 additions and 1307 deletions
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
517
Core/Debugger/BaseTraceLogger.h
Normal file
517
Core/Debugger/BaseTraceLogger.h
Normal 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());
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
36
Core/Debugger/ITraceLogger.h
Normal file
36
Core/Debugger/ITraceLogger.h
Normal 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; }
|
||||
};
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
42
Core/Debugger/TraceLogFileSaver.h
Normal file
42
Core/Debugger/TraceLogFileSaver.h
Normal 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");
|
||||
}
|
||||
}*/
|
||||
};
|
|
@ -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);
|
|
@ -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);
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
75
Core/Gameboy/Debugger/GbTraceLogger.cpp
Normal file
75
Core/Gameboy/Debugger/GbTraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
27
Core/Gameboy/Debugger/GbTraceLogger.h
Normal file
27
Core/Gameboy/Debugger/GbTraceLogger.h
Normal 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; }
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
60
Core/NES/Debugger/NesTraceLogger.cpp
Normal file
60
Core/NES/Debugger/NesTraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
27
Core/NES/Debugger/NesTraceLogger.h
Normal file
27
Core/NES/Debugger/NesTraceLogger.h
Normal 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; }
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
128
Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp
Normal file
128
Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
29
Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.h
Normal file
29
Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.h
Normal 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; }
|
||||
};
|
100
Core/SNES/Debugger/TraceLogger/GsuTraceLogger.cpp
Normal file
100
Core/SNES/Debugger/TraceLogger/GsuTraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
29
Core/SNES/Debugger/TraceLogger/GsuTraceLogger.h
Normal file
29
Core/SNES/Debugger/TraceLogger/GsuTraceLogger.h
Normal 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; }
|
||||
};
|
105
Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp
Normal file
105
Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
31
Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.h
Normal file
31
Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.h
Normal 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; }
|
||||
};
|
67
Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.cpp
Normal file
67
Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
29
Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h
Normal file
29
Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h
Normal 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; }
|
||||
};
|
61
Core/SNES/Debugger/TraceLogger/SpcTraceLogger.cpp
Normal file
61
Core/SNES/Debugger/TraceLogger/SpcTraceLogger.cpp
Normal 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()
|
||||
};
|
||||
}
|
29
Core/SNES/Debugger/TraceLogger/SpcTraceLogger.h
Normal file
29
Core/SNES/Debugger/TraceLogger/SpcTraceLogger.h
Normal 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; }
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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();
|
||||
|
|
80
NewUI/Config/Debugger/TraceLoggerConfig.cs
Normal file
80
NewUI/Config/Debugger/TraceLoggerConfig.cs
Normal 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]"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
35
NewUI/Config/Debugger/TraceLoggerCpuConfig.cs
Normal file
35
NewUI/Config/Debugger/TraceLoggerCpuConfig.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
178
NewUI/Debugger/ViewModels/TraceLoggerViewModel.cs
Normal file
178
NewUI/Debugger/ViewModels/TraceLoggerViewModel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
NewUI/Debugger/Windows/TraceLoggerWindow.axaml
Normal file
66
NewUI/Debugger/Windows/TraceLoggerWindow.axaml
Normal 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>
|
87
NewUI/Debugger/Windows/TraceLoggerWindow.axaml.cs
Normal file
87
NewUI/Debugger/Windows/TraceLoggerWindow.axaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue