Debugger: Disassembly window, code data logger

This commit is contained in:
Sour 2019-02-27 19:49:26 -05:00
parent 4ee2c42663
commit 802bd75df1
32 changed files with 2044 additions and 458 deletions

134
Core/CodeDataLogger.cpp Normal file
View file

@ -0,0 +1,134 @@
#include "stdafx.h"
#include "CodeDataLogger.h"
CodeDataLogger::CodeDataLogger(uint32_t prgSize)
{
_prgSize = prgSize;
_cdlData = new uint8_t[prgSize];
Reset();
}
CodeDataLogger::~CodeDataLogger()
{
delete[] _cdlData;
}
void CodeDataLogger::Reset()
{
_codeSize = 0;
_dataSize = 0;
memset(_cdlData, 0, _prgSize);
}
bool CodeDataLogger::LoadCdlFile(string cdlFilepath)
{
ifstream cdlFile(cdlFilepath, ios::in | ios::binary);
if(cdlFile) {
cdlFile.seekg(0, std::ios::end);
size_t fileSize = (size_t)cdlFile.tellg();
cdlFile.seekg(0, std::ios::beg);
if(fileSize == _prgSize) {
Reset();
cdlFile.read((char*)_cdlData, _prgSize);
cdlFile.close();
CalculateStats();
return true;
}
}
return false;
}
void CodeDataLogger::CalculateStats()
{
_codeSize = 0;
_dataSize = 0;
for(int i = 0, len = _prgSize; i < len; i++) {
if(IsCode(i)) {
_codeSize++;
} else if(IsData(i)) {
_dataSize++;
}
}
}
bool CodeDataLogger::SaveCdlFile(string cdlFilepath)
{
ofstream cdlFile(cdlFilepath, ios::out | ios::binary);
if(cdlFile) {
cdlFile.write((char*)_cdlData, _prgSize);
cdlFile.close();
return true;
}
return false;
}
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) {
if(IsData(absoluteAddr)) {
//Remove the data flag from bytes that we are flagging as code
_cdlData[absoluteAddr] &= ~CdlFlags::Data;
_dataSize--;
}
_cdlData[absoluteAddr] |= flags;
_codeSize++;
} else if(flags & CdlFlags::Data) {
if(!IsCode(absoluteAddr)) {
_cdlData[absoluteAddr] |= flags;
_dataSize++;
}
} else {
_cdlData[absoluteAddr] |= flags;
}
}
}
}
CdlRatios CodeDataLogger::GetRatios()
{
CdlRatios ratios;
ratios.CodeRatio = (float)_codeSize / (float)_prgSize;
ratios.DataRatio = (float)_dataSize / (float)_prgSize;
ratios.PrgRatio = (float)(_codeSize + _dataSize) / (float)_prgSize;
return ratios;
}
bool CodeDataLogger::IsCode(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & CdlFlags::Code) != 0;
}
bool CodeDataLogger::IsJumpTarget(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & CdlFlags::JumpTarget) != 0;
}
bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & CdlFlags::SubEntryPoint) != 0;
}
bool CodeDataLogger::IsData(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & CdlFlags::Data) != 0;
}
uint8_t CodeDataLogger::GetCpuFlags(uint32_t absoluteAddr)
{
return _cdlData[absoluteAddr] & (CdlFlags::MemoryMode8 | CdlFlags::IndexMode8);
}
void CodeDataLogger::SetCdlData(uint8_t *cdlData, uint32_t length)
{
if(length <= _prgSize) {
memcpy(_cdlData, cdlData, length);
CalculateStats();
}
}

35
Core/CodeDataLogger.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "stdafx.h"
#include "DebugTypes.h"
class CodeDataLogger
{
private:
uint8_t *_cdlData = nullptr;
uint32_t _prgSize = 0;
uint32_t _codeSize = 0;
uint32_t _dataSize = 0;
void CalculateStats();
public:
CodeDataLogger(uint32_t prgSize);
~CodeDataLogger();
void Reset();
bool LoadCdlFile(string cdlFilepath);
bool SaveCdlFile(string cdlFilepath);
void SetFlags(int32_t absoluteAddr, uint8_t flags);
CdlRatios GetRatios();
bool IsCode(uint32_t absoluteAddr);
bool IsJumpTarget(uint32_t absoluteAddr);
bool IsSubEntryPoint(uint32_t absoluteAddr);
bool IsData(uint32_t absoluteAddr);
uint8_t GetCpuFlags(uint32_t absoluteAddr);
void SetCdlData(uint8_t *cdlData, uint32_t length);
};

View file

@ -52,15 +52,18 @@
<ClInclude Include="blargg_config.h" />
<ClInclude Include="blargg_endian.h" />
<ClInclude Include="blargg_source.h" />
<ClInclude Include="CodeDataLogger.h" />
<ClInclude Include="Console.h" />
<ClInclude Include="ControlDeviceState.h" />
<ClInclude Include="ControlManager.h" />
<ClInclude Include="Cpu.h" />
<ClInclude Include="RegisterHandlerB.h" />
<ClInclude Include="CpuTypes.h" />
<ClInclude Include="Debugger.h" />
<ClInclude Include="DebugHud.h" />
<ClInclude Include="DebugTypes.h" />
<ClInclude Include="DefaultVideoFilter.h" />
<ClInclude Include="Disassembler.h" />
<ClInclude Include="DisassemblyInfo.h" />
<ClInclude Include="DmaController.h" />
<ClInclude Include="DrawCommand.h" />
@ -88,6 +91,7 @@
<ClInclude Include="Ppu.h" />
<ClInclude Include="PpuTypes.h" />
<ClInclude Include="RamHandler.h" />
<ClInclude Include="RegisterHandlerA.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="SettingTypes.h" />
<ClInclude Include="SnesController.h" />
@ -108,6 +112,7 @@
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="BaseSoundManager.cpp" />
<ClCompile Include="BaseVideoFilter.cpp" />
<ClCompile Include="CodeDataLogger.cpp" />
<ClCompile Include="Console.cpp" />
<ClCompile Include="ControlManager.cpp" />
<ClCompile Include="Cpu.cpp" />
@ -115,11 +120,13 @@
<ClCompile Include="Debugger.cpp" />
<ClCompile Include="DebugHud.cpp" />
<ClCompile Include="DefaultVideoFilter.cpp" />
<ClCompile Include="Disassembler.cpp" />
<ClCompile Include="DisassemblyInfo.cpp" />
<ClCompile Include="DmaController.cpp" />
<ClCompile Include="InternalRegisters.cpp" />
<ClCompile Include="KeyManager.cpp" />
<ClCompile Include="MemoryDumper.cpp" />
<ClCompile Include="MemoryManager.cpp" />
<ClCompile Include="MessageManager.cpp" />
<ClCompile Include="NotificationManager.cpp" />
<ClCompile Include="Ppu.cpp" />

View file

@ -173,6 +173,18 @@
<ClInclude Include="FrameLimiter.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="Disassembler.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="CodeDataLogger.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="RegisterHandlerB.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="RegisterHandlerA.h">
<Filter>SNES</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -266,6 +278,15 @@
<ClCompile Include="ControlManager.cpp">
<Filter>SNES\Input</Filter>
</ClCompile>
<ClCompile Include="Disassembler.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="CodeDataLogger.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="MemoryManager.cpp">
<Filter>SNES</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -46,3 +46,55 @@ struct CdlRatios
float DataRatio;
float PrgRatio;
};
struct DisassemblyResult
{
AddressInfo Address;
int32_t CpuAddress;
uint8_t Flags;
DisassemblyResult(int32_t cpuAddress, uint8_t flags)
{
Flags = flags;
CpuAddress = cpuAddress;
Address.Address = -1;
}
DisassemblyResult(AddressInfo address, int32_t cpuAddress, uint8_t flags = 0)
{
Address = address;
CpuAddress = cpuAddress;
Flags = flags;
}
};
namespace LineFlags
{
enum LineFlags : uint8_t
{
None = 0,
PrgRom = 1,
WorkRam = 2,
SaveRam = 4,
VerifiedData = 8,
VerifiedCode = 16,
BlockStart = 32,
BlockEnd = 64,
SubStart = 128
};
}
struct CodeLineData
{
int32_t Address;
int32_t AbsoluteAddress;
uint8_t OpSize;
uint8_t Flags;
int32_t EffectiveAddress;
int32_t Value;
uint8_t ByteCode[4];
char Text[1000];
char Comment[1000];
};

View file

@ -3,12 +3,17 @@
#include "Console.h"
#include "Cpu.h"
#include "Ppu.h"
#include "BaseCartridge.h"
#include "MemoryManager.h"
#include "NotificationManager.h"
#include "CpuTypes.h"
#include "DisassemblyInfo.h"
#include "TraceLogger.h"
#include "MemoryDumper.h"
#include "CodeDataLogger.h"
#include "Disassembler.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/FolderUtilities.h"
Debugger::Debugger(shared_ptr<Console> console)
{
@ -17,51 +22,90 @@ Debugger::Debugger(shared_ptr<Console> console)
_ppu = console->GetPpu();
_memoryManager = console->GetMemoryManager();
_codeDataLogger.reset(new CodeDataLogger(console->GetCartridge()->DebugGetPrgRomSize()));
_disassembler.reset(new Disassembler(console, _codeDataLogger));
_traceLogger.reset(new TraceLogger(this, _memoryManager));
_memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, console->GetCartridge()));
_cpuStepCount = 0;
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_console->GetCartridge()->GetRomInfo().RomPath, false) + ".cdl");
_codeDataLogger->LoadCdlFile(cdlFile);
//TODO: Thread safety
uint32_t prgRomSize = console->GetCartridge()->DebugGetPrgRomSize();
AddressInfo addrInfo;
addrInfo.Type = SnesMemoryType::PrgRom;
for(uint32_t i = 0; i < prgRomSize; i++) {
if(_codeDataLogger->IsCode(i)) {
addrInfo.Address = (int32_t)i;
i += _disassembler->BuildCache(addrInfo, _codeDataLogger->GetCpuFlags(i)) - 1;
}
}
}
Debugger::~Debugger()
{
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_console->GetCartridge()->GetRomInfo().RomPath, false) + ".cdl");
_codeDataLogger->SaveCdlFile(cdlFile);
}
void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr);
CpuState state = _cpu->GetState();
if(type == MemoryOperationType::ExecOpCode) {
CpuState state = _cpu->GetState();
DisassemblyInfo disassemblyInfo(state, _memoryManager.get());
if(addressInfo.Address >= 0) {
if(addressInfo.Type == SnesMemoryType::PrgRom) {
uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
if(_prevOpCode == 0x20 || _prevOpCode == 0x5C || _prevOpCode == 0xDC || _prevOpCode == 0xFC) {
flags |= CdlFlags::SubEntryPoint;
}
_codeDataLogger->SetFlags(addressInfo.Address, flags);
}
_disassembler->BuildCache(addressInfo, state.PS);
}
DebugState debugState;
GetState(&debugState);
_traceLogger->LogEffectiveAddress(_cpu->GetLastOperand());
_traceLogger->Log(debugState, disassemblyInfo);
bool sendNotif = false;
if(value == 0x00) {
//break on BRK
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo);
_traceLogger->Log(debugState, disInfo);
_prevOpCode = value;
if(value == 0x00 || value == 0xCB) {
//break on BRK/WAI
_cpuStepCount = 1;
sendNotif = true;
}
if(_cpuStepCount > 0) {
_cpuStepCount--;
if(_cpuStepCount == 0) {
if(sendNotif) {
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak);
}
_disassembler->Disassemble();
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak);
while(_cpuStepCount == 0) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
}
}
}
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
}
} else {
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
}
}
}
void Debugger::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
if(type == MemoryOperationType::ExecOpCode) {
//_traceLogger->Trace
AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr);
if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) {
_disassembler->InvalidateCache(addressInfo);
}
}
@ -96,3 +140,8 @@ shared_ptr<MemoryDumper> Debugger::GetMemoryDumper()
{
return _memoryDumper;
}
shared_ptr<Disassembler> Debugger::GetDisassembler()
{
return _disassembler;
}

View file

@ -8,11 +8,12 @@ class Cpu;
class Ppu;
class BaseCartridge;
class MemoryManager;
class CodeDataLogger;
enum class MemoryOperationType;
class TraceLogger;
class MemoryDumper;
//class Disassembler;
class Disassembler;
struct DebugState
{
@ -32,9 +33,11 @@ private:
shared_ptr<TraceLogger> _traceLogger;
shared_ptr<MemoryDumper> _memoryDumper;
//unique_ptr<Disassembler> _disassembler;
shared_ptr<CodeDataLogger> _codeDataLogger;
shared_ptr<Disassembler> _disassembler;
atomic<int32_t> _cpuStepCount;
uint8_t _prevOpCode = 0;
public:
Debugger(shared_ptr<Console> console);
@ -51,4 +54,5 @@ public:
shared_ptr<TraceLogger> GetTraceLogger();
shared_ptr<MemoryDumper> GetMemoryDumper();
shared_ptr<Disassembler> GetDisassembler();
};

292
Core/Disassembler.cpp Normal file
View file

@ -0,0 +1,292 @@
#include "stdafx.h"
#include <algorithm>
#include "Disassembler.h"
#include "DisassemblyInfo.h"
#include "Cpu.h"
#include "MemoryManager.h"
#include "CpuTypes.h"
#include "Console.h"
#include "CodeDataLogger.h"
#include "BaseCartridge.h"
#include "../Utilities/HexUtilities.h"
Disassembler::Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogger> cdl)
{
_cdl = cdl;
_console = console.get();
_memoryManager = console->GetMemoryManager().get();
_prgRom = console->GetCartridge()->DebugGetPrgRom();
_prgRomSize = console->GetCartridge()->DebugGetPrgRomSize();
_sram = console->GetCartridge()->DebugGetSaveRam();
_sramSize = console->GetCartridge()->DebugGetSaveRamSize();
_wram = _memoryManager->DebugGetWorkRam();
_wramSize = MemoryManager::WorkRamSize;
_prgCache = vector<shared_ptr<DisassemblyInfo>>(_prgRomSize);
_sramCache = vector<shared_ptr<DisassemblyInfo>>(_sramSize);
_wramCache = vector<shared_ptr<DisassemblyInfo>>(_wramSize);
}
void Disassembler::GetSource(AddressInfo &info, uint8_t **source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache)
{
switch(info.Type) {
case SnesMemoryType::PrgRom:
*source = _prgRom;
*cache = &_prgCache;
size = _prgRomSize;
break;
case SnesMemoryType::WorkRam:
*source = _wram;
*cache = &_wramCache;
size = MemoryManager::WorkRamSize;
break;
case SnesMemoryType::SaveRam:
*source = _sram;
*cache = &_sramCache;
size = _sramSize;
break;
default:
throw std::runtime_error("Disassembler::GetSource() invalid memory type");
}
}
uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags)
{
uint8_t *source;
uint32_t sourceLength;
vector<shared_ptr<DisassemblyInfo>> *cache;
GetSource(addrInfo, &source, sourceLength, &cache);
if(addrInfo.Address >= 0) {
DisassemblyInfo *disInfo = (*cache)[addrInfo.Address].get();
if(!disInfo) {
shared_ptr<DisassemblyInfo> disassemblyInfo(new DisassemblyInfo(source+addrInfo.Address, cpuFlags));
(*cache)[addrInfo.Address] = disassemblyInfo;
_needDisassemble = true;
disInfo = disassemblyInfo.get();
}
return disInfo->GetOperandSize() + 1;
}
return 0;
}
void Disassembler::InvalidateCache(AddressInfo addrInfo)
{
uint8_t *source;
uint32_t sourceLength;
vector<shared_ptr<DisassemblyInfo>> *cache;
GetSource(addrInfo, &source, sourceLength, &cache);
if(addrInfo.Address >= 0) {
for(int i = 0; i < 4; i++) {
if(addrInfo.Address >= i) {
if((*cache)[addrInfo.Address - i]) {
_needDisassemble = true;
(*cache)[addrInfo.Address - i].reset();
}
}
}
}
}
void Disassembler::Disassemble()
{
if(!_needDisassemble) {
return;
}
_needDisassemble = false;
auto lock = _disassemblyLock.AcquireSafe();
_disassembly.clear();
uint8_t *source;
uint32_t sourceLength;
vector<shared_ptr<DisassemblyInfo>> *cache;
bool disassembleAll = false;
bool inUnknownBlock = false;
shared_ptr<DisassemblyInfo> disassemblyInfo;
shared_ptr<DisassemblyInfo> tmpInfo(new DisassemblyInfo());
AddressInfo addrInfo = {};
AddressInfo prevAddrInfo = {};
for(int32_t i = 0; i <= 0xFFFFFF; i++) {
prevAddrInfo = addrInfo;
addrInfo = _memoryManager->GetAbsoluteAddress(i);
if(addrInfo.Address < 0) {
continue;
}
GetSource(addrInfo, &source, sourceLength, &cache);
DisassemblyInfo *disassemblyInfo = (*cache)[addrInfo.Address].get();
uint8_t opSize = 0;
uint8_t opCode = (source + addrInfo.Address)[0];
bool needRealign = true;
if(!disassemblyInfo && disassembleAll) {
opSize = DisassemblyInfo::GetOperandSize(opCode, 0);
} else if(disassemblyInfo) {
opSize = disassemblyInfo->GetOperandSize();
needRealign = false;
}
if(disassemblyInfo || disassembleAll) {
if(inUnknownBlock) {
_disassembly.push_back(DisassemblyResult(prevAddrInfo, i - 1, LineFlags::BlockEnd));
inUnknownBlock = false;
}
if(addrInfo.Type == SnesMemoryType::PrgRom && _cdl->IsSubEntryPoint(addrInfo.Address)) {
_disassembly.push_back(DisassemblyResult(-1, LineFlags::SubStart | LineFlags::BlockStart | LineFlags::VerifiedCode));
}
_disassembly.push_back(DisassemblyResult(addrInfo, i));
if(needRealign) {
for(int j = 1; j < opSize; j++) {
if((*cache)[addrInfo.Address + j]) {
break;
}
i++;
}
} else {
i += opSize;
}
if(opCode == 0x60 || opCode == 0x6B) {
//End of function
_disassembly.push_back(DisassemblyResult(-1, LineFlags::VerifiedCode | LineFlags::BlockEnd));
}
} else {
if(!inUnknownBlock) {
inUnknownBlock = true;
_disassembly.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockStart));
_disassembly.push_back(DisassemblyResult(-1, LineFlags::None));
}
}
}
if(inUnknownBlock) {
_disassembly.push_back(DisassemblyResult(addrInfo, 0xFFFFFF, LineFlags::BlockEnd));
}
}
DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressInfo &info)
{
DisassemblyInfo* disassemblyInfo = nullptr;
switch(info.Type) {
case SnesMemoryType::PrgRom: disassemblyInfo = _prgCache[info.Address].get(); break;
case SnesMemoryType::WorkRam: disassemblyInfo = _wramCache[info.Address].get(); break;
case SnesMemoryType::SaveRam: disassemblyInfo = _sramCache[info.Address].get(); break;
}
if(disassemblyInfo) {
return *disassemblyInfo;
} else {
return DisassemblyInfo();
}
}
uint32_t Disassembler::GetLineCount()
{
auto lock = _disassemblyLock.AcquireSafe();
return (uint32_t)_disassembly.size();
}
uint32_t Disassembler::GetLineIndex(uint32_t cpuAddress)
{
auto lock = _disassemblyLock.AcquireSafe();
for(int i = 0; i < _disassembly.size(); i++) {
if(_disassembly[i].CpuAddress == cpuAddress) {
return i;
}
}
return 0;
}
bool Disassembler::GetLineData(uint32_t lineIndex, CodeLineData &data)
{
auto lock =_disassemblyLock.AcquireSafe();
if(lineIndex < _disassembly.size()) {
DisassemblyResult result = _disassembly[lineIndex];
data.Address = result.CpuAddress;
data.AbsoluteAddress = result.Address.Address;
data.Flags = result.Flags;
switch(result.Address.Type) {
case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break;
case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam; break;
case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break;
}
bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) != 0;
if(!isBlockStartEnd && result.Address.Address >= 0) {
shared_ptr<DisassemblyInfo> disInfo;
uint8_t *source;
uint32_t sourceLength;
vector<shared_ptr<DisassemblyInfo>> *cache;
GetSource(result.Address, &source, sourceLength, &cache);
disInfo = (*cache)[result.Address.Address];
if(!disInfo) {
disInfo.reset(new DisassemblyInfo(source + result.Address.Address, 0));
} else {
data.Flags |= (uint8_t)LineFlags::VerifiedCode;
}
string text;
disInfo->GetDisassembly(text, result.CpuAddress);
memcpy(data.Text, text.c_str(), std::min<int>((int)text.size(), 1000));
data.OpSize = disInfo->GetOperandSize() + 1;
data.EffectiveAddress = disInfo->GetEffectiveAddress();
data.Value = _memoryManager->Peek(result.CpuAddress);
disInfo->GetByteCode(data.ByteCode);
data.Comment[0] = 0;
} else {
if(data.Flags & LineFlags::SubStart) {
string label = "sub start";
memcpy(data.Text, label.c_str(), label.size() + 1);
} else if(data.Flags & LineFlags::BlockStart) {
string label = (data.Flags & LineFlags::VerifiedCode) ? "verified code" : "unidentified";
memcpy(data.Text, label.c_str(), label.size() + 1);
}
}
return true;
}
return false;
}
int32_t Disassembler::SearchCode(const char *searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards)
{
auto lock = _disassemblyLock.AcquireSafe();
int step = searchBackwards ? -1 : 1;
CodeLineData lineData = {};
for(int i = startPosition; i != endPosition; i += step) {
GetLineData(i, lineData);
string line = lineData.Text;
std::transform(line.begin(), line.end(), line.begin(), ::tolower);
if(line.find(searchString) != string::npos) {
return i;
}
//Continue search from start/end of document
if(!searchBackwards && i == _disassembly.size() - 1) {
i = 0;
} else if(searchBackwards && i == 0) {
i = (int32_t)(_disassembly.size() - 1);
}
}
return -1;
}

50
Core/Disassembler.h Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include "stdafx.h"
#include "DisassemblyInfo.h"
#include "DebugTypes.h"
#include "../Utilities/SimpleLock.h"
class MemoryManager;
class Console;
class CodeDataLogger;
struct CpuState;
class Disassembler
{
private:
MemoryManager *_memoryManager;
Console *_console;
shared_ptr<CodeDataLogger> _cdl;
vector<shared_ptr<DisassemblyInfo>> _prgCache;
vector<shared_ptr<DisassemblyInfo>> _wramCache;
vector<shared_ptr<DisassemblyInfo>> _sramCache;
SimpleLock _disassemblyLock;
vector<DisassemblyResult> _disassembly;
bool _needDisassemble = true;
uint8_t *_prgRom;
uint32_t _prgRomSize;
uint8_t *_wram;
uint32_t _wramSize;
uint8_t *_sram;
uint32_t _sramSize;
void GetSource(AddressInfo &info, uint8_t **source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache);
public:
Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogger> cdl);
uint32_t BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags);
void InvalidateCache(AddressInfo addrInfo);
void Disassemble();
DisassemblyInfo GetDisassemblyInfo(AddressInfo &info);
uint32_t GetLineCount();
uint32_t GetLineIndex(uint32_t cpuAddress);
bool GetLineData(uint32_t lineIndex, CodeLineData &data);
int32_t SearchCode(const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards);
};

View file

@ -10,21 +10,23 @@ DisassemblyInfo::DisassemblyInfo()
{
}
DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager)
DisassemblyInfo::DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags)
{
_flags = state.PS;
Initialize(opPointer, cpuFlags);
}
void DisassemblyInfo::Initialize(uint8_t *opPointer, uint8_t cpuFlags)
{
_flags = cpuFlags;
_effectiveAddress = -1;
uint32_t addr = (state.K << 16) | state.PC;
_byteCode[0] = memoryManager->Peek(addr);
_byteCode[0] = opPointer[0];
_addrMode = DisassemblyInfo::OpMode[_byteCode[0]];
_opSize = GetOperandSize() + 1;
_opSize = GetOperandSize(_addrMode, _flags) + 1;
for(int i = 1; i < _opSize; i++) {
_byteCode[i] = memoryManager->Peek((addr+i) & 0xFFFFFF);
_byteCode[i] = opPointer[i];
}
_emulationMode = state.EmulationMode;
}
void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr)
@ -41,7 +43,6 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr)
operand.Write(HexUtilities::ToHex(opAddr));
}
switch(_addrMode) {
case AddrMode::Abs: str.Write(operand); break;
case AddrMode::AbsJmp: str.Write(operand); break;
@ -54,7 +55,7 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr)
case AddrMode::AbsLng: str.Write(operand); break;
case AddrMode::AbsLngJmp: str.Write(operand); break;
case AddrMode::Acc: break;
case AddrMode::BlkMov: str.Write(operand[1], operand[2], " -> ", operand[3], operand[4]); break;
case AddrMode::BlkMov: str.Write(operand[1], operand[2], " -> "); str.Write(operand[3], operand[4]); break;
case AddrMode::DirIdxIndX: str.Write('(', operand, ",X)"); break;
case AddrMode::DirIdxX: str.Write(operand, ",X"); break;
case AddrMode::DirIdxY: str.Write(operand, ",Y"); break;
@ -105,7 +106,12 @@ uint32_t DisassemblyInfo::GetOperandAddress(uint32_t memoryAddr)
uint8_t DisassemblyInfo::GetOperandSize()
{
switch(_addrMode) {
return _opSize - 1;
}
uint8_t DisassemblyInfo::GetOperandSize(AddrMode addrMode, uint8_t flags)
{
switch(addrMode) {
case AddrMode::Acc:
case AddrMode::Imp:
case AddrMode::Stk:
@ -142,13 +148,23 @@ uint8_t DisassemblyInfo::GetOperandSize()
case AddrMode::AbsLng:
return 3;
case AddrMode::ImmX: return (_flags & ProcFlags::IndexMode8) ? 1 : 2;
case AddrMode::ImmM: return (_flags & ProcFlags::MemoryMode8) ? 1 : 2;
case AddrMode::ImmX: return (flags & ProcFlags::IndexMode8) ? 1 : 2;
case AddrMode::ImmM: return (flags & ProcFlags::MemoryMode8) ? 1 : 2;
}
throw std::runtime_error("Invalid mode");
}
uint8_t DisassemblyInfo::GetOperandSize(uint8_t opCode, uint8_t flags)
{
return GetOperandSize(DisassemblyInfo::OpMode[opCode], flags);
}
void DisassemblyInfo::GetByteCode(uint8_t copyBuffer[4])
{
memcpy(copyBuffer, _byteCode, _opSize);
}
void DisassemblyInfo::GetByteCode(string &out)
{
FastString str;

View file

@ -19,16 +19,22 @@ private:
uint8_t _opSize;
AddrMode _addrMode;
uint8_t _flags;
bool _emulationMode;
int32_t _effectiveAddress;
public:
DisassemblyInfo();
DisassemblyInfo(CpuState &state, MemoryManager *memoryManager);
DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags);
void Initialize(uint8_t * opPointer, uint8_t cpuFlags);
void GetDisassembly(string &out, uint32_t memoryAddr);
uint32_t GetOperandAddress(uint32_t memoryAddr);
uint8_t GetOperandSize();
static uint8_t GetOperandSize(AddrMode addrMode, uint8_t flags);
static uint8_t GetOperandSize(uint8_t opCode, uint8_t flags);
void GetByteCode(uint8_t copyBuffer[4]);
void GetByteCode(string &out);
void SetEffectiveAddress(int32_t effectiveAddress);

View file

@ -3,6 +3,7 @@
#include "../Core/Debugger.h"
#include "../Core/TraceLogger.h"
#include "../Core/MemoryDumper.h"
#include "../Core/Disassembler.h"
#include "../Core/DebugTypes.h"
extern shared_ptr<Console> _console;
@ -34,7 +35,11 @@ extern "C"
DllExport bool __stdcall IsExecutionStopped() { return GetDebugger()->IsExecutionStopped(); }
DllExport void __stdcall ResumeExecution() { GetDebugger()->Run(); }
DllExport void __stdcall Step(uint32_t count) { GetDebugger()->Step(count); }
//DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
DllExport void __stdcall GetDisassemblyLineData(uint32_t lineIndex, CodeLineData &data) { GetDebugger()->GetDisassembler()->GetLineData(lineIndex, data); }
DllExport uint32_t __stdcall GetDisassemblyLineCount() { return GetDebugger()->GetDisassembler()->GetLineCount(); }
DllExport uint32_t __stdcall GetDisassemblyLineIndex(uint32_t cpuAddress) { return GetDebugger()->GetDisassembler()->GetLineIndex(cpuAddress); }
DllExport int32_t __stdcall SearchDisassembly(const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards) { return GetDebugger()->GetDisassembler()->SearchCode(searchString, startPosition, endPosition, searchBackwards); }
DllExport void __stdcall SetTraceOptions(TraceLoggerOptions options) { GetDebugger()->GetTraceLogger()->SetOptions(options); }
DllExport void __stdcall StartTraceLogger(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); }

106
UI/Debugger/CodeLineData.cs Normal file
View file

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI.Debugger
{
public class CodeLineData
{
public string Text;
public Int32 Address;
public Int32 AbsoluteAddress;
public LineFlags Flags;
public byte OpSize;
public string ByteCode;
public string Comment;
public Int32 EffectiveAddress;
public Int32 Value;
public string GetEffectiveAddressString()
{
return "[" + EffectiveAddress.ToString("X6") + "]";
}
public int Indentation
{
get
{
if(Flags.HasFlag(LineFlags.BlockStart) || Flags.HasFlag(LineFlags.BlockEnd)) {
return 0;
} else {
return 10;
}
}
}
public CodeLineData()
{
}
public CodeLineData(InteropCodeLineData data)
{
this.Text = ConvertString(data.Text);
this.Comment = ConvertString(data.Comment);
this.OpSize = data.OpSize;
for(int i = 0; i < this.OpSize; i++) {
this.ByteCode += "$" + data.ByteCode[i].ToString("X2") + " ";
}
this.Address = data.Address;
this.AbsoluteAddress = data.AbsoluteAddress;
this.EffectiveAddress = data.EffectiveAddress;
this.Flags = (LineFlags)data.Flags;
this.Value = data.Value;
}
private string ConvertString(byte[] stringArray)
{
int length = 0;
for(int i = 0; i < 1000; i++) {
if(stringArray[i] == 0) {
length = i;
break;
}
}
return Encoding.UTF8.GetString(stringArray, 0, length);
}
}
public struct InteropCodeLineData
{
public Int32 Address;
public Int32 AbsoluteAddress;
public byte OpSize;
public byte Flags;
public Int32 EffectiveAddress;
public Int32 Value;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] ByteCode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)]
public byte[] Text;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)]
public byte[] Comment;
}
public enum LineFlags : byte
{
PrgRom = 1,
WorkRam = 2,
SaveRam = 4,
VerifiedData = 8,
VerifiedCode = 16,
BlockStart = 32,
BlockEnd = 64,
}
}

View file

@ -30,7 +30,15 @@ namespace Mesen.GUI.Config
public XmlColor CodeCommentColor = Color.Green;
public XmlColor CodeEffectiveAddressColor = Color.SteelBlue;
public XmlColor CodeVerifiedDataColor = Color.FromArgb(255, 252, 236);
public XmlColor CodeUnidentifiedDataColor = Color.FromArgb(255, 242, 242);
public XmlColor CodeUnexecutedCodeColor = Color.FromArgb(225, 244, 228);
public XmlColor CodeExecBreakpointColor = Color.FromArgb(140, 40, 40);
public XmlColor CodeWriteBreakpointColor = Color.FromArgb(40, 120, 80);
public XmlColor CodeReadBreakpointColor = Color.FromArgb(40, 40, 200);
public XmlColor CodeActiveStatementColor = Color.Yellow;
public DebugInfo()
{
}

View file

@ -15,6 +15,7 @@ namespace Mesen.GUI.Debugger.Controls
private Timer _tmrScroll;
private bool _scrollUp = false;
private bool _mouseDown = false;
private const int _buttonSize = 15;
@ -137,14 +138,13 @@ namespace Mesen.GUI.Debugger.Controls
e.Graphics.FillPolygon(Brushes.DimGray, new Point[] { new Point(left, bottom - arrowHeight), new Point(left + arrowWidth, bottom - arrowHeight), new Point(left + arrowWidth / 2, bottom) }, FillMode.Winding);
}
//private bool _mouseDown = false;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if(e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) {
this.UpdatePosition(e.Y);
//this._mouseDown = true;
this._mouseDown = true;
} else {
if(e.Y <= _buttonSize) {
if(this.Value > 0) {
@ -175,21 +175,20 @@ namespace Mesen.GUI.Debugger.Controls
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
//this._mouseDown = false;
this._mouseDown = false;
this._tmrScroll.Stop();
}
//TODO
/*
frmCodePreviewTooltip _codeTooltip = null;
int _lastPreviewScrollPosition = -1;
//frmCodePreviewTooltip _codeTooltip = null;
//int _lastPreviewScrollPosition = -1;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if(this._mouseDown) {
this.UpdatePosition(e.Y);
} else {
if(this.ColorProvider != null && e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) {
/*if(this.ColorProvider != null && e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) {
int scrollPosition = Math.Max(0, (e.Y - this.Top - _buttonSize) * this.Maximum / (this.Height - _buttonSize * 2));
if(_lastPreviewScrollPosition != scrollPosition) {
Point p = this.PointToScreen(new Point(this.ClientRectangle.Right, e.Y));
@ -206,12 +205,12 @@ namespace Mesen.GUI.Debugger.Controls
}
_lastPreviewScrollPosition = scrollPosition;
}
}
}*/
}
}
protected override void OnMouseLeave(EventArgs e)
//TODO
/*protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
if(_codeTooltip != null) {
@ -242,7 +241,7 @@ namespace Mesen.GUI.Debugger.Controls
private void UpdatePosition(int y)
{
this.Value = Math.Max(0 , y - HighlightHeight / 2 + HighlightOffset - this.Top - _buttonSize) * this.Maximum / (this.Height - _buttonSize * 2);
this.Value = (int)(Math.Max(0 , y - HighlightHeight / 2 + HighlightOffset - this.Top - _buttonSize) * (long)this.Maximum / (this.Height - _buttonSize * 2));
}
private int _value = 0;

View file

@ -0,0 +1,66 @@
namespace Mesen.GUI.Debugger.Controls
{
partial class ctrlDisassemblyView
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.ctrlCode = new Mesen.GUI.Debugger.Controls.ctrlScrollableTextbox();
this.SuspendLayout();
//
// ctrlCode
//
this.ctrlCode.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.ctrlCode.CodeHighlightingEnabled = true;
this.ctrlCode.Dock = System.Windows.Forms.DockStyle.Fill;
this.ctrlCode.HideSelection = false;
this.ctrlCode.Location = new System.Drawing.Point(0, 0);
this.ctrlCode.Name = "ctrlCode";
this.ctrlCode.ShowCompactPrgAddresses = false;
this.ctrlCode.ShowContentNotes = false;
this.ctrlCode.ShowLineNumberNotes = false;
this.ctrlCode.ShowMemoryValues = false;
this.ctrlCode.ShowScrollbars = true;
this.ctrlCode.ShowSingleContentLineNotes = true;
this.ctrlCode.ShowSingleLineLineNumberNotes = false;
this.ctrlCode.Size = new System.Drawing.Size(465, 398);
this.ctrlCode.TabIndex = 0;
//
// ctrlDisassemblyView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.ctrlCode);
this.Name = "ctrlDisassemblyView";
this.Size = new System.Drawing.Size(465, 398);
this.ResumeLayout(false);
}
#endregion
private ctrlScrollableTextbox ctrlCode;
}
}

View file

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Controls;
using Mesen.GUI.Config;
namespace Mesen.GUI.Debugger.Controls
{
public partial class ctrlDisassemblyView : BaseControl
{
private LineStyleProvider _styleProvider;
public ctrlDisassemblyView()
{
InitializeComponent();
_styleProvider = new LineStyleProvider();
ctrlCode.StyleProvider = _styleProvider;
ctrlCode.ShowContentNotes = true;
ctrlCode.ShowMemoryValues = true;
}
public void SetActiveAddress(int? address)
{
UpdateCode();
_styleProvider.ActiveAddress = address;
if(address.HasValue && address.Value >= 0) {
ctrlCode.ScrollToAddress(address.Value);
}
}
private void UpdateCode()
{
CodeDataProvider provider = new CodeDataProvider();
int centerLineIndex = ctrlCode.GetLineIndexAtPosition(0) + ctrlCode.GetNumberVisibleLines() / 2;
int centerLineAddress;
int scrollOffset = -1;
do {
//Save the address at the center of the debug view
centerLineAddress = provider.GetLineAddress(centerLineIndex);
centerLineIndex--;
scrollOffset++;
} while(centerLineAddress < 0 && centerLineIndex > 0);
ctrlCode.DataProvider = provider;
if(centerLineAddress >= 0) {
//Scroll to the same address as before, to prevent the code view from changing due to setting or banking changes, etc.
int lineIndex = provider.GetLineIndex((UInt32)centerLineAddress) + scrollOffset;
ctrlCode.ScrollToLineIndex(lineIndex, eHistoryType.None, false, true);
}
}
public class CodeDataProvider : ICodeDataProvider
{
private int _lineCount;
public CodeDataProvider()
{
_lineCount = (int)DebugApi.GetDisassemblyLineCount();
}
public CodeLineData GetCodeLineData(int lineIndex)
{
return DebugApi.GetDisassemblyLineData((UInt32)lineIndex);
}
public int GetLineAddress(int lineIndex)
{
return DebugApi.GetDisassemblyLineData((UInt32)lineIndex).Address;
}
public int GetLineCount()
{
return _lineCount;
}
public int GetLineIndex(uint cpuAddress)
{
return (int)DebugApi.GetDisassemblyLineIndex(cpuAddress);
}
public bool UseOptimizedSearch { get { return true; } }
public int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards)
{
return DebugApi.SearchDisassembly(searchString, startPosition, endPosition, searchBackwards);
}
}
public class LineStyleProvider : ctrlTextbox.ILineStyleProvider
{
public LineStyleProvider()
{
}
public int? ActiveAddress { get; set; }
public string GetLineComment(int lineNumber)
{
return null;
}
public static void ConfigureActiveStatement(LineProperties props)
{
props.FgColor = Color.Black;
props.TextBgColor = ConfigManager.Config.Debug.CodeActiveStatementColor;
props.Symbol |= LineSymbol.Arrow;
}
public LineProperties GetLineStyle(CodeLineData lineData, int lineNumber)
{
DebugInfo info = ConfigManager.Config.Debug;
LineProperties props = new LineProperties();
//TODO
//AddressInfo addressInfo = _code.GetAddressInfo(lineNumber);
//GetBreakpointLineProperties(props, cpuAddress, addressInfo);
bool isActiveStatement = ActiveAddress.HasValue && ActiveAddress.Value == lineData.Address;
if(isActiveStatement) {
ConfigureActiveStatement(props);
}
//TODO
/* else if(_code._code.UnexecutedAddresses.Contains(lineNumber)) {
props.LineBgColor = info.CodeUnexecutedCodeColor;
}*/
if(lineData.Flags.HasFlag(LineFlags.PrgRom)) {
props.AddressColor = Color.Gray;
} else if(lineData.Flags.HasFlag(LineFlags.WorkRam)) {
props.AddressColor = Color.DarkBlue;
} else if(lineData.Flags.HasFlag(LineFlags.SaveRam)) {
props.AddressColor = Color.DarkRed;
}
if(lineData.Flags.HasFlag(LineFlags.VerifiedData)) {
props.LineBgColor = info.CodeVerifiedDataColor;
} else if(!lineData.Flags.HasFlag(LineFlags.VerifiedCode)) {
props.LineBgColor = info.CodeUnidentifiedDataColor;
}
return props;
}
public static void GetBreakpointLineProperties(LineProperties props, int cpuAddress, AddressInfo addressInfo)
{
//TODO
/*DebugInfo config = ConfigManager.Config.Debug;
foreach(Breakpoint breakpoint in BreakpointManager.Breakpoints) {
if(breakpoint.Matches(cpuAddress, addressInfo)) {
Color fgColor = Color.White;
Color? bgColor = null;
Color bpColor = breakpoint.BreakOnExec ? config.CodeExecBreakpointColor : (breakpoint.BreakOnWrite ? config.CodeWriteBreakpointColor : config.CodeReadBreakpointColor);
Color outlineColor = bpColor;
LineSymbol symbol;
if(breakpoint.Enabled) {
bgColor = bpColor;
symbol = LineSymbol.Circle;
} else {
fgColor = Color.Black;
symbol = LineSymbol.CircleOutline;
}
if(breakpoint.MarkEvent) {
symbol |= LineSymbol.Mark;
}
if(!string.IsNullOrWhiteSpace(breakpoint.Condition)) {
symbol |= LineSymbol.Plus;
}
props.FgColor = fgColor;
props.TextBgColor = bgColor;
props.OutlineColor = outlineColor;
props.Symbol = symbol;
return;
}
}*/
}
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -61,7 +61,6 @@ namespace Mesen.GUI.Debugger.Controls
this.panelSearch.Location = new System.Drawing.Point(this.Width - this.panelSearch.Width - 20, -1);
this.ctrlTextbox.ShowLineNumbers = true;
this.ctrlTextbox.ShowLineInHex = true;
this.hScrollBar.ValueChanged += hScrollBar_ValueChanged;
this.vScrollBar.ValueChanged += vScrollBar_ValueChanged;
@ -164,35 +163,21 @@ namespace Mesen.GUI.Debugger.Controls
public ctrlTextbox.ILineStyleProvider StyleProvider { set { this.ctrlTextbox.StyleProvider = value; } }
public int GetLineIndex(int lineNumber)
public ICodeDataProvider DataProvider
{
return this.ctrlTextbox.GetLineIndex(lineNumber);
set
{
this.ctrlTextbox.DataProvider = value;
UpdateHorizontalScrollbar();
UpdateVerticalScrollbar();
}
}
public int GetLineIndexAtPosition(int yPos)
{
return this.ctrlTextbox.GetLineIndexAtPosition(yPos);
}
public string GetLineNoteAtLineIndex(int lineIndex)
{
if(lineIndex >= 0 && lineIndex < this.ctrlTextbox.LineNumberNotes.Length) {
return this.ctrlTextbox.LineNumberNotes[lineIndex];
} else {
return "";
}
}
public int GetLineNumber(int lineIndex)
{
return this.ctrlTextbox.GetLineNumber(lineIndex);
}
public int GetLineNumberAtPosition(int yPos)
{
return this.GetLineNumber(this.GetLineIndexAtPosition(yPos));
}
public int GetNumberVisibleLines()
{
return this.ctrlTextbox.GetNumberVisibleLines();
@ -203,9 +188,9 @@ namespace Mesen.GUI.Debugger.Controls
this.ctrlTextbox.ScrollToLineIndex(lineIndex, historyType, scrollToTop, forceScroll);
}
public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false)
public void ScrollToAddress(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false)
{
this.ctrlTextbox.ScrollToLineNumber(lineNumber, historyType, scrollToTop, forceScroll);
this.ctrlTextbox.ScrollToAddress(lineNumber, historyType, scrollToTop, forceScroll);
}
public void CopySelection(bool copyLineNumbers, bool copyContentNotes, bool copyComments)
@ -363,52 +348,6 @@ namespace Mesen.GUI.Debugger.Controls
this.ctrlTextbox.HorizontalScrollPosition = this.hScrollBar.Value;
}
public string[] Addressing { set { this.ctrlTextbox.Addressing = value; } }
public string[] Comments { set { this.ctrlTextbox.Comments = value; } }
public int[] LineIndentations{ set { this.ctrlTextbox.LineIndentations = value; } }
public string[] TextLines
{
set
{
this.ctrlTextbox.TextLines = value;
UpdateVerticalScrollbar();
UpdateHorizontalScrollbar();
}
}
public string[] TextLineNotes
{
set
{
this.ctrlTextbox.TextLineNotes = value;
}
}
public string[] CompareLines
{
set
{
this.ctrlTextbox.CompareLines = value;
}
}
public int[] LineNumbers
{
set
{
this.ctrlTextbox.LineNumbers = value;
}
}
public string[] LineNumberNotes
{
set
{
this.ctrlTextbox.LineNumberNotes = value;
}
}
public bool ShowSingleContentLineNotes
{
get { return this.ctrlTextbox.ShowSingleContentLineNotes; }
@ -417,16 +356,16 @@ namespace Mesen.GUI.Debugger.Controls
public bool ShowContentNotes
{
get { return this.ctrlTextbox.ShowContentNotes; }
set { this.ctrlTextbox.ShowContentNotes = value; }
get { return this.ctrlTextbox.ShowByteCode; }
set { this.ctrlTextbox.ShowByteCode = value; }
}
public bool ShowCompactPrgAddresses { get { return this.ctrlTextbox.ShowCompactPrgAddresses; } set { this.ctrlTextbox.ShowCompactPrgAddresses = value; } }
public bool ShowLineNumberNotes
{
get { return this.ctrlTextbox.ShowLineNumberNotes; }
set { this.ctrlTextbox.ShowLineNumberNotes = value; }
get { return this.ctrlTextbox.ShowAbsoluteAddreses; }
set { this.ctrlTextbox.ShowAbsoluteAddreses = value; }
}
public bool ShowSingleLineLineNumberNotes
@ -457,14 +396,6 @@ namespace Mesen.GUI.Debugger.Controls
public int SelectionStart { get { return this.ctrlTextbox.SelectionStart; } }
public int SelectionLength { get { return this.ctrlTextbox.SelectionLength; } }
public string Header
{
set
{
this.ctrlTextbox.Header = value;
}
}
public int MarginWidth { set { this.ctrlTextbox.MarginWidth = value; } }
public void OpenSearchBox(bool forceFocus = false)
@ -534,11 +465,6 @@ namespace Mesen.GUI.Debugger.Controls
}
}
public string GetLineContent(int lineIndex)
{
return this.ctrlTextbox.GetFullWidthString(lineIndex);
}
public void NavigateForward()
{
this.ctrlTextbox.NavigateForward();
@ -548,29 +474,5 @@ namespace Mesen.GUI.Debugger.Controls
{
this.ctrlTextbox.NavigateBackward();
}
public bool GetNoteRangeAtLocation(int yPos, out int rangeStart, out int rangeEnd)
{
rangeStart = -1;
rangeEnd = -1;
int lineIndex = GetLineIndexAtPosition(yPos);
while(lineIndex < LineCount - 2 && string.IsNullOrWhiteSpace(GetLineNoteAtLineIndex(lineIndex))) {
//Find the address of the next line with an address
lineIndex++;
}
if(Int32.TryParse(GetLineNoteAtLineIndex(lineIndex), NumberStyles.AllowHexSpecifier, null, out rangeStart)) {
while(lineIndex < LineCount - 2 && string.IsNullOrWhiteSpace(GetLineNoteAtLineIndex(lineIndex + 1))) {
//Find the next line with an address
lineIndex++;
}
if(Int32.TryParse(GetLineNoteAtLineIndex(lineIndex + 1), NumberStyles.AllowHexSpecifier, null, out rangeEnd)) {
rangeEnd--;
return true;
}
}
return false;
}
}
}

View file

@ -155,9 +155,8 @@
this.ctrlTextbox.Location = new System.Drawing.Point(0, 0);
this.ctrlTextbox.Name = "ctrlTextbox";
this.ctrlTextbox.ShowCompactPrgAddresses = false;
this.ctrlTextbox.ShowContentNotes = false;
this.ctrlTextbox.ShowLineInHex = false;
this.ctrlTextbox.ShowLineNumberNotes = false;
this.ctrlTextbox.ShowByteCode = false;
this.ctrlTextbox.ShowAbsoluteAddreses = false;
this.ctrlTextbox.ShowLineNumbers = true;
this.ctrlTextbox.ShowMemoryValues = false;
this.ctrlTextbox.ShowSingleContentLineNotes = true;

View file

@ -26,25 +26,22 @@ namespace Mesen.GUI.Debugger.Controls
private TextboxHistory _history = new TextboxHistory();
private string[] _contents = new string[0];
private string[] _contentNotes = new string[0];
private string[] _compareContents = null;
private int[] _lineNumbers = new int[0];
private string[] _lineNumberNotes = new string[0];
private Dictionary<int, int> _lineNumberIndex = new Dictionary<int,int>();
private ICodeDataProvider _dataProvider;
private bool _showLineNumbers = false;
private bool _showLineInHex = false;
private bool _showLineNumberNotes = false;
private bool _showAbsoluteAddresses = false;
private bool _showSingleLineLineNumberNotes = false;
private bool _showContentNotes = false;
private bool _showSingleLineContentNotes = true;
private bool _showCompactPrgAddresses = false;
private int _selectionStart = 0;
private int _selectionLength = 0;
private int _scrollPosition = 0;
private int _horizontalScrollPosition = 0;
private string _searchString = null;
private string _header = null;
private Font _noteFont = null;
private int _marginWidth = 9;
private int _extendedMarginWidth = 16;
@ -59,41 +56,12 @@ namespace Mesen.GUI.Debugger.Controls
this.DoubleBuffered = true;
this.UpdateFont();
}
public string[] Comments;
public string[] Addressing;
public int[] LineIndentations;
public string[] TextLines
public ICodeDataProvider DataProvider
{
set
{
int maxLength = 0;
_maxLineWidthIndex = 0;
_contents = value;
for(int i = 0, len = value.Length; i < len; i++) {
int length = _contents[i].Length + (Addressing != null ? Addressing[i].Length : 0);
if(Comments?[i].Length > 0) {
length = Math.Max(length, length > 0 ? CommentSpacingCharCount : 0) + Comments[i].Length;
}
if(length > maxLength) {
maxLength = length;
_maxLineWidthIndex = i;
}
}
UpdateHorizontalScrollWidth();
if(_lineNumbers.Length != _contents.Length) {
_lineNumbers = new int[_contents.Length];
_lineNumberIndex.Clear();
for(int i = _contents.Length - 1; i >=0; i--) {
_lineNumbers[i] = i;
_lineNumberIndex[i] = i;
}
}
this._dataProvider = value;
this.Invalidate();
}
}
@ -165,7 +133,7 @@ namespace Mesen.GUI.Debugger.Controls
}
}
public bool ShowContentNotes
public bool ShowByteCode
{
get { return _showContentNotes; }
set
@ -207,78 +175,24 @@ namespace Mesen.GUI.Debugger.Controls
}
}
public bool ShowLineNumberNotes
public bool ShowAbsoluteAddreses
{
get { return this._showLineNumberNotes; }
get { return this._showAbsoluteAddresses; }
set
{
this._showLineNumberNotes = value;
this._showAbsoluteAddresses = value;
this.Invalidate();
}
}
public string[] TextLineNotes
{
set
{
this._contentNotes = value;
this.Invalidate();
}
}
public string[] CompareLines
{
set
{
_compareContents = value;
}
}
public int LineCount
{
get
{
return _contents.Length;
return _dataProvider.GetLineCount();
}
}
public int[] LineNumbers
{
set
{
_lineNumbers = value;
_lineNumberIndex.Clear();
int i = 0;
foreach(int line in _lineNumbers) {
_lineNumberIndex[line] = i;
i++;
}
this.Invalidate();
}
}
public string[] LineNumberNotes
{
set
{
_lineNumberNotes = value;
this.Invalidate();
}
get
{
return _lineNumberNotes;
}
}
public string Header
{
set
{
this._header = value;
this.Invalidate();
}
}
public int MarginWidth
{
set
@ -306,8 +220,8 @@ namespace Mesen.GUI.Debugger.Controls
startPosition = this.SelectionStart;
endPosition = this.SelectionStart - searchOffset;
if(endPosition < 0) {
endPosition = _contents.Length - 1;
} else if(endPosition >= _contents.Length) {
endPosition = this.LineCount - 1;
} else if(endPosition >= this.LineCount) {
endPosition = 0;
}
@ -315,35 +229,45 @@ namespace Mesen.GUI.Debugger.Controls
startPosition = this.SelectionStart + searchOffset;
endPosition = this.SelectionStart;
if(startPosition < 0) {
startPosition = _contents.Length - 1;
} else if(startPosition >= _contents.Length) {
startPosition = this.LineCount - 1;
} else if(startPosition >= this.LineCount) {
startPosition = 0;
}
}
for(int i = startPosition; i != endPosition; i += searchOffset) {
string line = _contents[i].ToLowerInvariant();
if(line.Contains(this._searchString)) {
this.ScrollToLineIndex(i);
if(_dataProvider.UseOptimizedSearch) {
Int32 lineIndex = _dataProvider.GetNextResult(searchString, startPosition, endPosition, searchBackwards);
if(lineIndex >= 0) {
this.ScrollToLineIndex(lineIndex);
this.Invalidate();
return true;
}
return false;
} else {
for(int i = startPosition; i != endPosition; i += searchOffset) {
string line = _dataProvider.GetCodeLineData(i).Text.ToLowerInvariant();
if(line.Contains(this._searchString)) {
this.ScrollToLineIndex(i);
this.Invalidate();
return true;
}
//Continue search from start/end of document
if(!searchBackwards && i == this._contents.Length - 1) {
i = 0;
} else if(searchBackwards && i == 0) {
i = this._contents.Length - 1;
//Continue search from start/end of document
if(!searchBackwards && i == this.LineCount - 1) {
i = 0;
} else if(searchBackwards && i == 0) {
i = this.LineCount - 1;
}
}
this.Invalidate();
return _dataProvider.GetCodeLineData(_selectionStart).Text.ToLowerInvariant().Contains(this._searchString);
}
this.Invalidate();
return _contents[_selectionStart].ToLowerInvariant().Contains(this._searchString);
}
}
public interface ILineStyleProvider
{
LineProperties GetLineStyle(int cpuAddress, int lineIndex);
LineProperties GetLineStyle(CodeLineData lineData, int lineIndex);
string GetLineComment(int lineIndex);
}
@ -361,35 +285,12 @@ namespace Mesen.GUI.Debugger.Controls
public LineProperties GetLineStyle(int lineIndex)
{
if(StyleProvider != null) {
return StyleProvider.GetLineStyle(_lineNumbers[lineIndex], lineIndex);
return StyleProvider.GetLineStyle(_dataProvider.GetCodeLineData(lineIndex), lineIndex);
} else {
return null;
}
}
public int GetLineIndex(int lineNumber)
{
if(_lineNumberIndex.ContainsKey(lineNumber)) {
return _lineNumberIndex[lineNumber];
} else {
foreach(int line in _lineNumbers) {
if(line > lineNumber) {
return Math.Max(0, GetLineIndex(line) - 1);
}
}
}
return -1;
}
public int GetLineNumber(int lineIndex)
{
if(_lineNumbers.Length <= lineIndex) {
return 0;
} else {
return _lineNumbers[lineIndex];
}
}
public void ScrollToLineIndex(int lineIndex, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false)
{
bool scrolled = false;
@ -403,9 +304,10 @@ namespace Mesen.GUI.Debugger.Controls
//Line isn't currently visible, scroll it to the middle of the viewport
if(scrollToTop) {
int scrollPos = lineIndex;
while(scrollPos > 0 && _lineNumbers[scrollPos - 1] < 0 && string.IsNullOrWhiteSpace(_lineNumberNotes[scrollPos - 1])) {
CodeLineData lineData = _dataProvider.GetCodeLineData(scrollPos);
while(scrollPos > 0 && lineData.Address < 0 && lineData.AbsoluteAddress < 0) {
//Make sure any comment for the line is in scroll view
bool emptyLine = string.IsNullOrWhiteSpace(_contents[scrollPos]) && string.IsNullOrWhiteSpace(this.Comments[scrollPos]);
bool emptyLine = string.IsNullOrWhiteSpace(lineData.Text) && string.IsNullOrWhiteSpace(lineData.Comment);
if(emptyLine) {
//If there's a blank line, stop scrolling up
scrollPos++;
@ -413,7 +315,8 @@ namespace Mesen.GUI.Debugger.Controls
}
scrollPos--;
if(emptyLine || _contents[scrollPos].StartsWith("--") || _contents[scrollPos].StartsWith("__")) {
_dataProvider.GetCodeLineData(scrollPos);
if(emptyLine || lineData.Flags.HasFlag(LineFlags.BlockEnd) || lineData.Flags.HasFlag(LineFlags.BlockStart)) {
//Reached the start of a block, stop going back up
break;
}
@ -437,9 +340,9 @@ namespace Mesen.GUI.Debugger.Controls
}
}
public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false)
public void ScrollToAddress(int address, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false)
{
int lineIndex = this.GetLineIndex(lineNumber);
int lineIndex = _dataProvider.GetLineIndex((UInt32)address);
if(lineIndex >= 0) {
ScrollToLineIndex(lineIndex, historyType, scrollToTop, forceScroll);
}
@ -457,7 +360,7 @@ namespace Mesen.GUI.Debugger.Controls
private int GetMargin(Graphics g, bool getExtendedMargin)
{
int marginWidth = getExtendedMargin && this.ShowContentNotes && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth;
int marginWidth = getExtendedMargin && this.ShowByteCode && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth;
if(ShowCompactPrgAddresses) {
marginWidth += 4;
}
@ -466,18 +369,23 @@ namespace Mesen.GUI.Debugger.Controls
public int GetLineIndexAtPosition(int yPos)
{
if(this._dataProvider == null) {
return 0;
}
int lineIndex = this.ScrollPosition + this.GetLineAtPosition(yPos);
if(lineIndex > _contents.Length && _contents.Length != 0) {
lineIndex = _contents.Length - 1;
if(lineIndex > this.LineCount && this.LineCount != 0) {
lineIndex = this.LineCount - 1;
}
return lineIndex;
}
public string GetFullWidthString(int lineIndex)
{
string text = _contents[lineIndex] + Addressing?[lineIndex];
if(Comments?[lineIndex].Length > 0) {
return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + Comments[lineIndex];
CodeLineData lineData = _dataProvider.GetCodeLineData(lineIndex);
string text = lineData.Text + lineData.GetEffectiveAddressString();
if(lineData.Comment.Length > 0) {
return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + lineData.Comment;
}
return text;
}
@ -488,12 +396,13 @@ namespace Mesen.GUI.Debugger.Controls
using(Graphics g = Graphics.FromHwnd(this.Handle)) {
int marginLeft = this.GetMargin(g, true);
lineIndex = this.GetLineIndexAtPosition(position.Y);
if(lineIndex >= _contents.Length) {
if(lineIndex >= this.LineCount) {
return false;
}
int positionX = position.X - marginLeft;
positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0);
//TODO
//positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0);
if(positionX >= 0) {
float charWidth = g.MeasureString("W", this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
charIndex = (int)(positionX / charWidth);
@ -534,9 +443,6 @@ namespace Mesen.GUI.Debugger.Controls
private int GetLineAtPosition(int yPos)
{
if(!string.IsNullOrWhiteSpace(this._header)) {
yPos -= this.LineHeight;
}
return Math.Max(0, yPos / this.LineHeight);
}
@ -555,10 +461,10 @@ namespace Mesen.GUI.Debugger.Controls
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectionStart
{
get { return Math.Min(this._contents.Length - 1, Math.Max(0, _selectionStart)); }
get { return Math.Min(this.LineCount - 1, Math.Max(0, _selectionStart)); }
set
{
int selectionStart = Math.Max(0, Math.Min(this._contents.Length - 1, Math.Max(0, value)));
int selectionStart = Math.Max(0, Math.Min(this.LineCount - 1, Math.Max(0, value)));
_selectionStart = selectionStart;
@ -574,13 +480,13 @@ namespace Mesen.GUI.Debugger.Controls
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectionLength
{
get { return (this.SelectionStart + _selectionLength) > this._contents.Length - 1 ? this._contents.Length - this.SelectionStart - 1 : _selectionLength; }
get { return (this.SelectionStart + _selectionLength) > this.LineCount - 1 ? this.LineCount - this.SelectionStart - 1 : _selectionLength; }
set
{
_selectionLength = value;
if(this.SelectionStart + _selectionLength > this._contents.Length - 1) {
_selectionLength = this._contents.Length - this.SelectionStart - 1;
if(this.SelectionStart + _selectionLength > this.LineCount - 1) {
_selectionLength = this.LineCount - this.SelectionStart - 1;
}
if(value == 0) {
@ -616,14 +522,14 @@ namespace Mesen.GUI.Debugger.Controls
bool singleLineSelection = this.SelectionLength == 0;
if(singleLineSelection) {
if(this.SelectionStart + this.SelectionLength >= this._contents.Length - 1) {
if(this.SelectionStart + this.SelectionLength >= this.LineCount - 1) {
//End of document reached
break;
}
this.SelectedLine = this.SelectionStart + 1;
this.SelectionLength++;
} else if(this.SelectionStart + this.SelectionLength == this.SelectedLine) {
if(this.SelectionStart + this.SelectionLength >= this._contents.Length - 1) {
if(this.SelectionStart + this.SelectionLength >= this.LineCount - 1) {
//End of document reached
break;
}
@ -674,12 +580,12 @@ namespace Mesen.GUI.Debugger.Controls
public int CurrentLine
{
get { return _lineNumbers.Length > _selectionStart ? _lineNumbers[_selectionStart] : 0; }
get { return this.LineCount > _selectionStart ? _dataProvider.GetLineAddress(_selectionStart) : 0; }
}
public int LastSelectedLine
{
get { return _lineNumbers.Length > _selectionStart + this.SelectionLength ? _lineNumbers[_selectionStart + this.SelectionLength] : 0; }
get { return this.LineCount > _selectionStart + this.SelectionLength ? _dataProvider.GetLineAddress(_selectionStart + this.SelectionLength) : 0; }
}
[Browsable(false)]
@ -689,7 +595,7 @@ namespace Mesen.GUI.Debugger.Controls
get { return _scrollPosition; }
set
{
value = Math.Max(0, Math.Min(value, this._contents.Length-this.GetNumberVisibleLines()));
value = Math.Max(0, Math.Min(value, this.LineCount-this.GetNumberVisibleLines()));
_scrollPosition = value;
if(!_disableScrollPositionChangedEvent && this.ScrollPositionChanged != null) {
ScrollPositionChanged(this, null);
@ -721,7 +627,8 @@ namespace Mesen.GUI.Debugger.Controls
private void UpdateHorizontalScrollWidth()
{
if(LineIndentations != null && LineIndentations.Length > _maxLineWidthIndex) {
//TODO
/*if(LineIndentations != null && LineIndentations.Length > _maxLineWidthIndex) {
using(Graphics g = this.CreateGraphics()) {
_maxLineWidth = (LineIndentations != null ? LineIndentations[_maxLineWidthIndex] : 0) + g.MeasureString(GetFullWidthString(_maxLineWidthIndex), this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
HorizontalScrollWidth = (int)(Math.Max(0, HorizontalScrollFactor + _maxLineWidth - (this.Width - GetMargin(g, true))) / HorizontalScrollFactor);
@ -730,7 +637,7 @@ namespace Mesen.GUI.Debugger.Controls
_maxLineWidth = 0;
HorizontalScrollPosition = 0;
HorizontalScrollWidth = 0;
}
}*/
}
public void SetMessage(TextboxMessageInfo message)
@ -744,6 +651,10 @@ namespace Mesen.GUI.Debugger.Controls
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if(_dataProvider == null) {
return;
}
this.ScrollPosition = this.ScrollPosition;
UpdateHorizontalScrollWidth();
ScrollPositionChanged?.Invoke(this, e);
@ -755,17 +666,11 @@ namespace Mesen.GUI.Debugger.Controls
set { _showLineNumbers = value; }
}
public bool ShowLineInHex
{
get { return _showLineInHex; }
set { _showLineInHex = value; }
}
private int LineHeight
{
get
{
if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes || this.ShowContentNotes && !this.ShowSingleContentLineNotes) {
if(this.ShowAbsoluteAddreses && !this.ShowSingleLineLineNumberNotes || this.ShowByteCode && !this.ShowSingleContentLineNotes) {
return (int)(this.FontHeight * 1.60);
} else {
return this.FontHeight - 1;
@ -847,14 +752,14 @@ namespace Mesen.GUI.Debugger.Controls
{
StringBuilder sb = new StringBuilder();
for(int i = this.SelectionStart, end = this.SelectionStart + this.SelectionLength; i <= end; i++) {
string indent = "";
if(LineIndentations != null) {
indent = "".PadLeft(LineIndentations[i] / 10);
}
CodeLineData lineData = _dataProvider.GetCodeLineData(i);
string codeString = _contents[i].Trim();
if(codeString.StartsWith("__") || codeString.StartsWith("--")) {
codeString = "--------" + codeString.Substring(2, codeString.Length - 4) + "--------";
string indent = "";
indent = "".PadLeft(lineData.Indentation / 10);
string codeString = lineData.Text.Trim();
if(lineData.Flags.HasFlag(LineFlags.BlockEnd) || lineData.Flags.HasFlag(LineFlags.BlockStart)) {
codeString = "--------" + codeString + "--------";
}
int padding = Math.Max(CommentSpacingCharCount, codeString.Length);
@ -865,14 +770,14 @@ namespace Mesen.GUI.Debugger.Controls
codeString = codeString.PadRight(padding);
string line = indent + codeString;
if(copyContentNotes && _contentNotes[i].Length > 0) {
line = _contentNotes[i].PadRight(13) + line;
if(copyContentNotes && lineData.ByteCode.Length > 0) {
line = lineData.ByteCode.PadRight(13) + line;
}
if(copyLineNumbers && _lineNumbers[i] >= 0) {
line = _lineNumbers[i].ToString("X4") + " " + line;
if(copyLineNumbers && lineData.Address >= 0) {
line = lineData.Address.ToString("X4") + " " + line;
}
if(copyComments && !string.IsNullOrWhiteSpace(Comments[i])) {
line = line + Comments[i];
if(copyComments && !string.IsNullOrWhiteSpace(lineData.Comment)) {
line = line + lineData.Comment;
}
sb.AppendLine(line);
}
@ -891,19 +796,16 @@ namespace Mesen.GUI.Debugger.Controls
private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY, int lineHeight)
{
string codeString = _contents[currentLine];
string addressString = this.Addressing?[currentLine];
string commentString = this.Comments?[currentLine];
CodeLineData lineData = _dataProvider.GetCodeLineData(currentLine);
string codeString = lineData.Text;
float codeStringLength = g.MeasureString(codeString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
float addressStringLength = g.MeasureString(addressString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
//Adjust background color highlights based on number of spaces in front of content
int originalMargin = marginLeft;
marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0);
marginLeft += lineData.Indentation;
bool isBlockStart = codeString.StartsWith("__") && codeString.EndsWith("__");
bool isBlockEnd = codeString.StartsWith("--") && codeString.EndsWith("--");
bool isBlockStart = lineData.Flags.HasFlag(LineFlags.BlockStart);
bool isBlockEnd = lineData.Flags.HasFlag(LineFlags.BlockEnd);
Color? textColor = null;
LineProperties lineProperties = GetLineStyle(currentLine);
@ -953,28 +855,28 @@ namespace Mesen.GUI.Debugger.Controls
}
}
this.DrawLineText(g, currentLine, marginLeft, positionY, codeString, addressString, commentString, codeStringLength, addressStringLength, textColor, lineHeight);
this.DrawLineText(g, currentLine, marginLeft, positionY, lineData, codeStringLength, textColor, lineHeight);
}
private void DrawLineNumber(Graphics g, int currentLine, int marginLeft, int positionY, Color addressColor)
private void DrawLineNumber(Graphics g, CodeLineData lineData, int marginLeft, int positionY, Color addressColor)
{
using(Brush numberBrush = new SolidBrush(addressColor)) {
if(this.ShowLineNumberNotes && this.ShowSingleLineLineNumberNotes) {
if(this.ShowAbsoluteAddreses && this.ShowSingleLineLineNumberNotes) {
//Display line note instead of line number
string lineNumber;
if(string.IsNullOrEmpty(_lineNumberNotes[currentLine])) {
lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : "..";
if(lineData.AbsoluteAddress == 0) {
lineNumber = lineData.Address >= 0 ? lineData.Address.ToString("X6") : "..";
} else {
lineNumber = _lineNumberNotes[currentLine];
lineNumber = lineData.AbsoluteAddress.ToString("X6");
}
float width = g.MeasureString(lineNumber, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
g.DrawString(lineNumber, this.Font, numberBrush, marginLeft - width, positionY, StringFormat.GenericTypographic);
} else {
//Display line number
string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : "..";
string lineNumber = lineData.Address >= 0 ? lineData.Address.ToString("X6") : "..";
if(ShowCompactPrgAddresses) {
string lineNumberNote = _lineNumberNotes[currentLine];
string lineNumberNote = lineData.AbsoluteAddress.ToString("X6");
if(lineNumberNote.Length > 3) {
string compactView = lineNumberNote.Substring(0, lineNumberNote.Length - 3).TrimStart('0');
if(compactView.Length == 0) {
@ -987,34 +889,39 @@ namespace Mesen.GUI.Debugger.Controls
float width = g.MeasureString(lineNumber, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
g.DrawString(lineNumber, this.Font, numberBrush, marginLeft - width, positionY, StringFormat.GenericTypographic);
if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes) {
if(this.ShowAbsoluteAddreses && !this.ShowSingleLineLineNumberNotes) {
//Display line note below line number
width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont, int.MaxValue, StringFormat.GenericTypographic).Width;
g.DrawString(_lineNumberNotes[currentLine], _noteFont, numberBrush, marginLeft - width, positionY+this.Font.Size+3, StringFormat.GenericTypographic);
string absAddress = lineData.AbsoluteAddress.ToString("X6");
width = g.MeasureString(absAddress, _noteFont, int.MaxValue, StringFormat.GenericTypographic).Width;
g.DrawString(absAddress, _noteFont, numberBrush, marginLeft - width, positionY+this.Font.Size+3, StringFormat.GenericTypographic);
}
}
}
}
private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, string codeString, string addressString, string commentString, float codeStringLength, float addressStringLength, Color? textColor, int lineHeight)
private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, CodeLineData lineData, float codeStringLength, Color? textColor, int lineHeight)
{
string codeString = lineData.Text;
string addressString = lineData.EffectiveAddress>= 0 ? (" [" + lineData.EffectiveAddress.ToString("X6") + "]") : "";
float addressStringLength = g.MeasureString(addressString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
string commentString = lineData.Comment;
DebugInfo info = ConfigManager.Config.Debug;
if(codeString.StartsWith("__") && codeString.EndsWith("__") || codeString.StartsWith("--") && codeString.EndsWith("--")) {
if(lineData.Flags.HasFlag(LineFlags.BlockEnd) || lineData.Flags.HasFlag(LineFlags.BlockStart)) {
//Draw block start/end
g.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0);
string text = codeString.Substring(2, codeString.Length - 4);
float yOffset = codeString.StartsWith("__") ? 2 : -2;
if(text.Length > 0) {
SizeF size = g.MeasureString(text, this._noteFont, int.MaxValue, StringFormat.GenericTypographic);
float yOffset = lineData.Flags.HasFlag(LineFlags.BlockStart) ? 2 : -2;
if(codeString.Length > 0) {
SizeF size = g.MeasureString(codeString, this._noteFont, int.MaxValue, StringFormat.GenericTypographic);
float textLength = size.Width;
float textHeight = size.Height;
float positionX = (marginLeft + this.Width - textLength) / 2;
g.DrawLine(Pens.Black, marginLeft, yOffset + positionY + lineHeight / 2, marginLeft + this.Width, yOffset + positionY + lineHeight / 2);
yOffset = codeString.StartsWith("__") ? 3 : 2;
yOffset = lineData.Flags.HasFlag(LineFlags.BlockStart) ? 3 : 2;
g.FillRectangle(Brushes.White, positionX - 4, yOffset + positionY, textLength + 8, textHeight);
g.DrawRectangle(Pens.Black, positionX - 4, yOffset + positionY, textLength + 8, textHeight);
g.DrawString(text, this._noteFont, Brushes.Black, positionX, yOffset + positionY, StringFormat.GenericTypographic);
g.DrawString(codeString, this._noteFont, Brushes.Black, positionX, yOffset + positionY, StringFormat.GenericTypographic);
} else {
g.DrawLine(Pens.Black, marginLeft, yOffset + positionY + lineHeight / 2, marginLeft + this.Width, yOffset + positionY + lineHeight / 2);
}
@ -1069,31 +976,10 @@ namespace Mesen.GUI.Debugger.Controls
}
if(this.ShowMemoryValues && memoryAddress.Length > 0) {
int address = -1;
if(memoryAddress[0] == '$') {
Int32.TryParse(memoryAddress.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier, null, out address);
} else {
//Label
UInt32 arrayOffset = 0;
if(!string.IsNullOrWhiteSpace(arrayPosition)) {
memoryAddress = memoryAddress.Substring(0, memoryAddress.Length - arrayPosition.Length - 1);
arrayOffset = UInt32.Parse(arrayPosition);
}
//TODO
/*CodeLabel label = LabelManager.GetLabel(memoryAddress);
if(label != null) {
address = InteropEmu.DebugGetRelativeAddress(label.Address + arrayOffset, label.AddressType);
}*/
}
//TODO
/*
if(address >= 0) {
if(lineData.Value >= 0) {
colors.Add(defaultColor);
parts.Add(" = $" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, (UInt32)address).ToString("X2"));
parts.Add(" = $" + lineData.Value.ToString("X4"));
}
*/
}
//Display the rest of the line (used by trace logger)
@ -1128,11 +1014,10 @@ namespace Mesen.GUI.Debugger.Controls
}
}
if(this.ShowContentNotes && !this.ShowSingleContentLineNotes) {
g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3, StringFormat.GenericTypographic);
if(this.ShowByteCode && !this.ShowSingleContentLineNotes) {
g.DrawString(lineData.ByteCode, _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3, StringFormat.GenericTypographic);
}
this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY);
this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY);
}
}
string _lastSymbolComment = null;
@ -1237,25 +1122,6 @@ namespace Mesen.GUI.Debugger.Controls
}
}
private void DrawHighlightedCompareString(Graphics g, string lineText, int currentLine, int marginLeft, int positionY)
{
if(_compareContents != null && _compareContents.Length > currentLine) {
string compareText = _compareContents[currentLine];
if(compareText != lineText) {
StringBuilder sb = new StringBuilder();
for(int i = 0, len = lineText.Length; i < len; i++) {
if(lineText[i] == compareText[i]) {
sb.Append(" ");
} else {
sb.Append(lineText[i]);
}
}
g.DrawString(sb.ToString(), new Font(this.Font, FontStyle.Bold), Brushes.Red, marginLeft, positionY, StringFormat.GenericTypographic);
}
}
}
private void DrawHighlightedSearchString(Graphics g, string lineText, int marginLeft, int positionY)
{
@ -1288,6 +1154,7 @@ namespace Mesen.GUI.Debugger.Controls
private void DrawMargin(Graphics g, int currentLine, int marginLeft, int regularMargin, int positionY, int lineHeight)
{
CodeLineData lineData = _dataProvider.GetCodeLineData(currentLine);
LineProperties lineProperties = GetLineStyle(currentLine);
//Draw instruction progress here to avoid it being scrolled horizontally when window is small (or comments/etc exist)
@ -1300,14 +1167,14 @@ namespace Mesen.GUI.Debugger.Controls
if(this.ShowLineNumbers) {
//Show line number
Color lineNumberColor = lineProperties != null && lineProperties.AddressColor.HasValue ? lineProperties.AddressColor.Value : Color.Gray;
this.DrawLineNumber(g, currentLine, regularMargin, positionY, lineNumberColor);
this.DrawLineNumber(g, lineData, regularMargin, positionY, lineNumberColor);
}
if(this.ShowContentNotes && this.ShowSingleContentLineNotes) {
g.DrawString(_contentNotes[currentLine], this.Font, Brushes.Gray, regularMargin + 6, positionY, StringFormat.GenericTypographic);
if(this.ShowByteCode && this.ShowSingleContentLineNotes) {
g.DrawString(lineData.ByteCode, this.Font, Brushes.Gray, regularMargin + 6, positionY, StringFormat.GenericTypographic);
}
//Adjust background color highlights based on number of spaces in front of content
marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0);
marginLeft += lineData.Indentation;
if(lineProperties != null) {
this.DrawLineSymbols(g, positionY, lineProperties, lineHeight);
@ -1321,14 +1188,14 @@ namespace Mesen.GUI.Debugger.Controls
int endAddress = -1;
int end = this.SelectionStart + this.SelectionLength + 1;
while(endAddress < 0 && end < _lineNumbers.Length) {
endAddress = _lineNumbers[end];
while(endAddress < 0 && end < this.LineCount) {
endAddress = _dataProvider.GetLineAddress(end);
end++;
}
int start = this.SelectionStart;
while(startAddress < 0 && start < _lineNumbers.Length && start < end) {
startAddress = _lineNumbers[start];
while(startAddress < 0 && start < this.LineCount && start < end) {
startAddress = _dataProvider.GetLineAddress(start);
start++;
}
@ -1359,6 +1226,10 @@ namespace Mesen.GUI.Debugger.Controls
protected override void OnPaint(PaintEventArgs pe)
{
if(_dataProvider == null) {
return;
}
_lastSymbolComment = null;
int lineHeight = this.LineHeight;
pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
@ -1372,15 +1243,7 @@ namespace Mesen.GUI.Debugger.Controls
int currentLine = this.ScrollPosition;
int positionY = 0;
if(!string.IsNullOrWhiteSpace(this._header)) {
using(Brush lightGrayBrush = new SolidBrush(Color.FromArgb(240, 240, 240))) {
pe.Graphics.FillRectangle(lightGrayBrush, marginLeft, 0, Math.Max(_maxLineWidth, rect.Right), lineHeight);
}
pe.Graphics.DrawString(_header, this.Font, Brushes.Gray, marginLeft, positionY, StringFormat.GenericTypographic);
positionY += lineHeight;
}
while(positionY < rect.Bottom && currentLine < _contents.Length) {
while(positionY < rect.Bottom && currentLine < this.LineCount) {
this.DrawLine(pe.Graphics, currentLine, marginLeft, positionY, lineHeight);
positionY += lineHeight;
currentLine++;
@ -1403,8 +1266,8 @@ namespace Mesen.GUI.Debugger.Controls
}
currentLine = this.ScrollPosition;
positionY = string.IsNullOrWhiteSpace(this._header) ? 0 : lineHeight;
while(positionY < rect.Bottom && currentLine < _contents.Length) {
positionY = 0;
while(positionY < rect.Bottom && currentLine < this.LineCount) {
this.DrawMargin(pe.Graphics, currentLine, marginLeft, regularMargin, positionY, lineHeight);
positionY += lineHeight;
currentLine++;
@ -1456,4 +1319,15 @@ namespace Mesen.GUI.Debugger.Controls
{
public string Message;
}
public interface ICodeDataProvider
{
CodeLineData GetCodeLineData(int lineIndex);
int GetLineCount();
int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards);
bool UseOptimizedSearch { get; }
int GetLineAddress(int lineIndex);
int GetLineIndex(UInt32 address);
}
}

View file

@ -31,6 +31,7 @@ namespace Mesen.GUI.Debugger
} else {
BaseForm frm = null;
switch(window) {
case DebugWindow.Debugger: frm = new frmDebugger(); frm.Icon = Properties.Resources.Debugger; break;
case DebugWindow.TraceLogger: frm = new frmTraceLogger(); frm.Icon = Properties.Resources.LogWindow; break;
case DebugWindow.MemoryTools: frm = new frmMemoryTools(); frm.Icon = Properties.Resources.CheatCode; break;
}
@ -85,6 +86,7 @@ namespace Mesen.GUI.Debugger
{
//Only one of each of these windows can be opened at once, check if one is already opened
switch(window) {
case DebugWindow.Debugger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmDebugger));
case DebugWindow.TraceLogger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmTraceLogger));
}
@ -111,6 +113,7 @@ namespace Mesen.GUI.Debugger
public enum DebugWindow
{
Debugger,
MemoryTools,
TraceLogger
}

289
UI/Debugger/frmDebugger.Designer.cs generated Normal file
View file

@ -0,0 +1,289 @@
namespace Mesen.GUI.Debugger
{
partial class frmDebugger
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
if(this._notifListener != null) {
this._notifListener.Dispose();
this._notifListener = null;
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.ctrlDisassemblyView = new Mesen.GUI.Debugger.Controls.ctrlDisassemblyView();
this.ctrlMesenMenuStrip1 = new Mesen.GUI.Controls.ctrlMesenMenuStrip();
this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mnuContinue = new System.Windows.Forms.ToolStripMenuItem();
this.mnuBreak = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
this.mnuStepInto = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStepOver = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStepOut = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStepBack = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuReset = new System.Windows.Forms.ToolStripMenuItem();
this.mnuPowerCycle = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem24 = new System.Windows.Forms.ToolStripSeparator();
this.mnuToggleBreakpoint = new System.Windows.Forms.ToolStripMenuItem();
this.mnuDisableEnableBreakpoint = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuRunCpuCycle = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunPpuCycle = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunScanline = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunOneFrame = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator();
this.mnuBreakIn = new System.Windows.Forms.ToolStripMenuItem();
this.mnuBreakOn = new System.Windows.Forms.ToolStripMenuItem();
this.ctrlMesenMenuStrip1.SuspendLayout();
this.SuspendLayout();
//
// ctrlDisassemblyView
//
this.ctrlDisassemblyView.Dock = System.Windows.Forms.DockStyle.Left;
this.ctrlDisassemblyView.Location = new System.Drawing.Point(0, 24);
this.ctrlDisassemblyView.Name = "ctrlDisassemblyView";
this.ctrlDisassemblyView.Size = new System.Drawing.Size(559, 620);
this.ctrlDisassemblyView.TabIndex = 0;
//
// ctrlMesenMenuStrip1
//
this.ctrlMesenMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.debugToolStripMenuItem});
this.ctrlMesenMenuStrip1.Location = new System.Drawing.Point(0, 0);
this.ctrlMesenMenuStrip1.Name = "ctrlMesenMenuStrip1";
this.ctrlMesenMenuStrip1.Size = new System.Drawing.Size(852, 24);
this.ctrlMesenMenuStrip1.TabIndex = 1;
this.ctrlMesenMenuStrip1.Text = "ctrlMesenMenuStrip1";
//
// debugToolStripMenuItem
//
this.debugToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuContinue,
this.mnuBreak,
this.toolStripMenuItem3,
this.mnuStepInto,
this.mnuStepOver,
this.mnuStepOut,
this.mnuStepBack,
this.toolStripMenuItem1,
this.mnuReset,
this.mnuPowerCycle,
this.toolStripMenuItem24,
this.mnuToggleBreakpoint,
this.mnuDisableEnableBreakpoint,
this.toolStripMenuItem2,
this.mnuRunCpuCycle,
this.mnuRunPpuCycle,
this.mnuRunScanline,
this.mnuRunOneFrame,
this.toolStripMenuItem8,
this.mnuBreakIn,
this.mnuBreakOn});
this.debugToolStripMenuItem.Name = "debugToolStripMenuItem";
this.debugToolStripMenuItem.Size = new System.Drawing.Size(54, 20);
this.debugToolStripMenuItem.Text = "Debug";
//
// mnuContinue
//
this.mnuContinue.Image = global::Mesen.GUI.Properties.Resources.MediaPlay;
this.mnuContinue.Name = "mnuContinue";
this.mnuContinue.Size = new System.Drawing.Size(212, 22);
this.mnuContinue.Text = "Continue";
this.mnuContinue.Click += new System.EventHandler(this.mnuContinue_Click);
//
// mnuBreak
//
this.mnuBreak.Enabled = false;
this.mnuBreak.Image = global::Mesen.GUI.Properties.Resources.MediaPause;
this.mnuBreak.Name = "mnuBreak";
this.mnuBreak.ShortcutKeyDisplayString = "";
this.mnuBreak.Size = new System.Drawing.Size(212, 22);
this.mnuBreak.Text = "Break";
//
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.Size = new System.Drawing.Size(209, 6);
//
// mnuStepInto
//
this.mnuStepInto.Image = global::Mesen.GUI.Properties.Resources.StepInto;
this.mnuStepInto.Name = "mnuStepInto";
this.mnuStepInto.Size = new System.Drawing.Size(212, 22);
this.mnuStepInto.Text = "Step Into";
this.mnuStepInto.Click += new System.EventHandler(this.mnuStepInto_Click);
//
// mnuStepOver
//
this.mnuStepOver.Image = global::Mesen.GUI.Properties.Resources.StepOver;
this.mnuStepOver.Name = "mnuStepOver";
this.mnuStepOver.Size = new System.Drawing.Size(212, 22);
this.mnuStepOver.Text = "Step Over";
//
// mnuStepOut
//
this.mnuStepOut.Image = global::Mesen.GUI.Properties.Resources.StepOut;
this.mnuStepOut.Name = "mnuStepOut";
this.mnuStepOut.Size = new System.Drawing.Size(212, 22);
this.mnuStepOut.Text = "Step Out";
//
// mnuStepBack
//
this.mnuStepBack.Image = global::Mesen.GUI.Properties.Resources.StepBack;
this.mnuStepBack.Name = "mnuStepBack";
this.mnuStepBack.Size = new System.Drawing.Size(212, 22);
this.mnuStepBack.Text = "Step Back";
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(209, 6);
//
// mnuReset
//
this.mnuReset.Image = global::Mesen.GUI.Properties.Resources.Refresh;
this.mnuReset.Name = "mnuReset";
this.mnuReset.Size = new System.Drawing.Size(212, 22);
this.mnuReset.Text = "Reset";
//
// mnuPowerCycle
//
this.mnuPowerCycle.Image = global::Mesen.GUI.Properties.Resources.PowerCycle;
this.mnuPowerCycle.Name = "mnuPowerCycle";
this.mnuPowerCycle.Size = new System.Drawing.Size(212, 22);
this.mnuPowerCycle.Text = "Power Cycle";
//
// toolStripMenuItem24
//
this.toolStripMenuItem24.Name = "toolStripMenuItem24";
this.toolStripMenuItem24.Size = new System.Drawing.Size(209, 6);
//
// mnuToggleBreakpoint
//
this.mnuToggleBreakpoint.Image = global::Mesen.GUI.Properties.Resources.Breakpoint;
this.mnuToggleBreakpoint.Name = "mnuToggleBreakpoint";
this.mnuToggleBreakpoint.Size = new System.Drawing.Size(212, 22);
this.mnuToggleBreakpoint.Text = "Toggle Breakpoint";
//
// mnuDisableEnableBreakpoint
//
this.mnuDisableEnableBreakpoint.Image = global::Mesen.GUI.Properties.Resources.BreakpointDisabled;
this.mnuDisableEnableBreakpoint.Name = "mnuDisableEnableBreakpoint";
this.mnuDisableEnableBreakpoint.Size = new System.Drawing.Size(212, 22);
this.mnuDisableEnableBreakpoint.Text = "Disable/Enable Breakpoint";
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(209, 6);
//
// mnuRunCpuCycle
//
this.mnuRunCpuCycle.Image = global::Mesen.GUI.Properties.Resources.JumpTarget;
this.mnuRunCpuCycle.Name = "mnuRunCpuCycle";
this.mnuRunCpuCycle.Size = new System.Drawing.Size(212, 22);
this.mnuRunCpuCycle.Text = "Run one CPU cycle";
//
// mnuRunPpuCycle
//
this.mnuRunPpuCycle.Image = global::Mesen.GUI.Properties.Resources.RunPpuCycle;
this.mnuRunPpuCycle.Name = "mnuRunPpuCycle";
this.mnuRunPpuCycle.Size = new System.Drawing.Size(212, 22);
this.mnuRunPpuCycle.Text = "Run one PPU cycle";
//
// mnuRunScanline
//
this.mnuRunScanline.Image = global::Mesen.GUI.Properties.Resources.RunPpuScanline;
this.mnuRunScanline.Name = "mnuRunScanline";
this.mnuRunScanline.Size = new System.Drawing.Size(212, 22);
this.mnuRunScanline.Text = "Run one scanline";
//
// mnuRunOneFrame
//
this.mnuRunOneFrame.Image = global::Mesen.GUI.Properties.Resources.RunPpuFrame;
this.mnuRunOneFrame.Name = "mnuRunOneFrame";
this.mnuRunOneFrame.Size = new System.Drawing.Size(212, 22);
this.mnuRunOneFrame.Text = "Run one frame";
//
// toolStripMenuItem8
//
this.toolStripMenuItem8.Name = "toolStripMenuItem8";
this.toolStripMenuItem8.Size = new System.Drawing.Size(209, 6);
//
// mnuBreakIn
//
this.mnuBreakIn.Name = "mnuBreakIn";
this.mnuBreakIn.Size = new System.Drawing.Size(212, 22);
this.mnuBreakIn.Text = "Break in...";
//
// mnuBreakOn
//
this.mnuBreakOn.Name = "mnuBreakOn";
this.mnuBreakOn.Size = new System.Drawing.Size(212, 22);
this.mnuBreakOn.Text = "Break on...";
//
// frmDebugger
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(852, 644);
this.Controls.Add(this.ctrlDisassemblyView);
this.Controls.Add(this.ctrlMesenMenuStrip1);
this.Name = "frmDebugger";
this.Text = "frmDebugger";
this.ctrlMesenMenuStrip1.ResumeLayout(false);
this.ctrlMesenMenuStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Controls.ctrlDisassemblyView ctrlDisassemblyView;
private GUI.Controls.ctrlMesenMenuStrip ctrlMesenMenuStrip1;
private System.Windows.Forms.ToolStripMenuItem debugToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem mnuContinue;
private System.Windows.Forms.ToolStripMenuItem mnuBreak;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3;
private System.Windows.Forms.ToolStripMenuItem mnuStepInto;
private System.Windows.Forms.ToolStripMenuItem mnuStepOver;
private System.Windows.Forms.ToolStripMenuItem mnuStepOut;
private System.Windows.Forms.ToolStripMenuItem mnuStepBack;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem mnuReset;
private System.Windows.Forms.ToolStripMenuItem mnuPowerCycle;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem24;
private System.Windows.Forms.ToolStripMenuItem mnuToggleBreakpoint;
private System.Windows.Forms.ToolStripMenuItem mnuDisableEnableBreakpoint;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
private System.Windows.Forms.ToolStripMenuItem mnuRunCpuCycle;
private System.Windows.Forms.ToolStripMenuItem mnuRunPpuCycle;
private System.Windows.Forms.ToolStripMenuItem mnuRunScanline;
private System.Windows.Forms.ToolStripMenuItem mnuRunOneFrame;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8;
private System.Windows.Forms.ToolStripMenuItem mnuBreakIn;
private System.Windows.Forms.ToolStripMenuItem mnuBreakOn;
}
}

View file

@ -0,0 +1,94 @@
using Mesen.GUI.Config;
using Mesen.GUI.Forms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Debugger
{
public partial class frmDebugger : BaseForm
{
private NotificationListener _notifListener;
public frmDebugger()
{
InitializeComponent();
if(DesignMode) {
return;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_notifListener = new NotificationListener();
_notifListener.OnNotification += OnNotificationReceived;
InitShortcuts();
DebugApi.Step(10000);
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
if(this._notifListener != null) {
this._notifListener.Dispose();
this._notifListener = null;
}
}
private void InitShortcuts()
{
mnuReset.InitShortcut(this, nameof(DebuggerShortcutsConfig.Reset));
mnuPowerCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.PowerCycle));
mnuContinue.InitShortcut(this, nameof(DebuggerShortcutsConfig.Continue));
mnuBreak.InitShortcut(this, nameof(DebuggerShortcutsConfig.Break));
mnuBreakIn.InitShortcut(this, nameof(DebuggerShortcutsConfig.BreakIn));
mnuBreakOn.InitShortcut(this, nameof(DebuggerShortcutsConfig.BreakOn));
mnuStepBack.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepBack));
mnuStepOut.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepOut));
mnuStepInto.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepInto));
mnuStepOver.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepOver));
mnuRunCpuCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunCpuCycle));
mnuRunPpuCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuCycle));
mnuRunScanline.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuScanline));
mnuRunOneFrame.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuFrame));
}
private void OnNotificationReceived(NotificationEventArgs e)
{
switch(e.NotificationType) {
case ConsoleNotificationType.CodeBreak:
DebugState state = DebugApi.GetState();
int activeAddress = (int)((state.Cpu.K << 16) | state.Cpu.PC);
this.BeginInvoke((MethodInvoker)(() => {
ctrlDisassemblyView.SetActiveAddress(activeAddress);
}));
break;
}
}
private void mnuStepInto_Click(object sender, EventArgs e)
{
DebugApi.Step(1);
}
private void mnuContinue_Click(object sender, EventArgs e)
{
DebugApi.ResumeExecution();
}
}
}

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="ctrlMesenMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
</metadata>
</root>

View file

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
using Mesen.GUI.Controls;
using Mesen.GUI.Debugger.Controls;
using Mesen.GUI.Forms;
namespace Mesen.GUI.Debugger
@ -316,17 +317,13 @@ namespace Mesen.GUI.Debugger
}
}
lineContent.Add(content.TrimStart());
indent.Add(10);
}
this.BeginInvoke((Action)(() => {
txtTraceLog.ShowContentNotes = showByteCode;
txtTraceLog.ShowSingleContentLineNotes = showByteCode;
txtTraceLog.LineIndentations = indent.ToArray();
txtTraceLog.LineNumbers = programCounter.ToArray();
txtTraceLog.TextLineNotes = byteCode.ToArray();
txtTraceLog.TextLines = lineContent.ToArray();
txtTraceLog.DataProvider = new TraceLoggerCodeDataProvider(lineContent, programCounter, byteCode);
if(scrollToBottom) {
txtTraceLog.ScrollToLineIndex(txtTraceLog.LineCount - 1);
}
@ -477,4 +474,50 @@ namespace Mesen.GUI.Debugger
txtTraceLog.SelectAll();
}
}
public class TraceLoggerCodeDataProvider : ICodeDataProvider
{
private List<string> _textLines;
private List<Int32> _addresses;
private List<string> _byteCode;
public TraceLoggerCodeDataProvider(List<string> lineContent, List<int> programCounter, List<string> byteCode)
{
_addresses = programCounter;
_byteCode = byteCode;
_textLines = lineContent;
}
public CodeLineData GetCodeLineData(int lineIndex)
{
return new CodeLineData() {
Address = _addresses[lineIndex],
Text = _textLines[lineIndex],
ByteCode = _byteCode[lineIndex],
EffectiveAddress = -1
};
}
public bool UseOptimizedSearch { get { return false; } }
public int GetLineAddress(int lineIndex)
{
return _addresses[lineIndex];
}
public int GetLineCount()
{
return _textLines.Count;
}
public int GetLineIndex(uint address)
{
throw new NotImplementedException();
}
public int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards)
{
throw new NotImplementedException();
}
}
}

View file

@ -37,8 +37,8 @@
this.mnuRun100Instructions = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMemoryTools = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMain.SuspendLayout();
this.SuspendLayout();
//
@ -73,7 +73,7 @@
this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.Folder;
this.mnuOpen.Name = "mnuOpen";
this.mnuOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
this.mnuOpen.Size = new System.Drawing.Size(152, 22);
this.mnuOpen.Size = new System.Drawing.Size(146, 22);
this.mnuOpen.Text = "Open";
this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click);
//
@ -122,19 +122,11 @@
//
// mnuDebugger
//
this.mnuDebugger.Enabled = false;
this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Debugger;
this.mnuDebugger.Name = "mnuDebugger";
this.mnuDebugger.Size = new System.Drawing.Size(163, 22);
this.mnuDebugger.Text = "Debugger";
//
// mnuTraceLogger
//
this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow;
this.mnuTraceLogger.Name = "mnuTraceLogger";
this.mnuTraceLogger.Size = new System.Drawing.Size(163, 22);
this.mnuTraceLogger.Text = "Trace Logger";
this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click);
this.mnuDebugger.Click += new System.EventHandler(this.mnuDebugger_Click);
//
// mnuMemoryTools
//
@ -144,6 +136,14 @@
this.mnuMemoryTools.Text = "Memory Tools";
this.mnuMemoryTools.Click += new System.EventHandler(this.mnuMemoryTools_Click);
//
// mnuTraceLogger
//
this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow;
this.mnuTraceLogger.Name = "mnuTraceLogger";
this.mnuTraceLogger.Size = new System.Drawing.Size(163, 22);
this.mnuTraceLogger.Text = "Trace Logger";
this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click);
//
// frmMain
//
this.AllowDrop = true;

View file

@ -54,13 +54,15 @@ namespace Mesen.GUI.Forms
{
switch(e.NotificationType) {
case ConsoleNotificationType.CodeBreak:
this.BeginInvoke((Action)(() => {
DebugWindowManager.OpenDebugWindow(DebugWindow.TraceLogger);
}));
break;
}
}
private void mnuDebugger_Click(object sender, EventArgs e)
{
DebugWindowManager.OpenDebugWindow(DebugWindow.Debugger);
}
private void mnuTraceLogger_Click(object sender, EventArgs e)
{
DebugWindowManager.OpenDebugWindow(DebugWindow.TraceLogger);

View file

@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
using Mesen.GUI.Debugger;
using Mesen.GUI.Forms;
namespace Mesen.GUI
@ -25,6 +26,22 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void StopTraceLogger();
[DllImport(DllPath)] public static extern void SetTraceOptions(InteropTraceLoggerOptions options);
[DllImport(DllPath, EntryPoint = "GetDisassemblyLineData")] private static extern void GetDisassemblyLineDataWrapper(UInt32 lineIndex, ref InteropCodeLineData lineData);
public static CodeLineData GetDisassemblyLineData(UInt32 lineIndex)
{
InteropCodeLineData data = new InteropCodeLineData();
data.Comment = new byte[1000];
data.Text = new byte[1000];
data.ByteCode = new byte[4];
DebugApi.GetDisassemblyLineDataWrapper(lineIndex, ref data);
return new CodeLineData(data);
}
[DllImport(DllPath)] public static extern UInt32 GetDisassemblyLineCount();
[DllImport(DllPath)] public static extern UInt32 GetDisassemblyLineIndex(UInt32 cpuAddress);
[DllImport(DllPath)] public static extern int SearchDisassembly([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]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 Utf8Marshaler.PtrToStringUtf8(DebugApi.GetExecutionTraceWrapper(lineCount)); }
@ -62,6 +79,12 @@ namespace Mesen.GUI
CGRam,
}
public class AddressInfo
{
public Int32 Address;
public SnesMemoryType Type;
}
public struct CpuState
{
public UInt64 CycleCount;

View file

@ -42,7 +42,7 @@ namespace Mesen.GUI
}
} catch { }
} else {
this._identifier = new Guid("{A46606B7-2D1C-4CC5-A52F-43BCAF094AED}");
this._identifier = new Guid("{A46606B7-2D1C-4CC5-A52F-43BCAF094AEF}");
this._mutex = new Mutex(true, _identifier.ToString(), out _firstInstance);
}
}

View file

@ -279,6 +279,7 @@
<Compile Include="Controls\MyListView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Debugger\CodeLineData.cs" />
<Compile Include="Debugger\Controls\ComboBoxWithSeparator.cs">
<SubType>Component</SubType>
</Compile>
@ -288,6 +289,12 @@
<Compile Include="Debugger\Controls\ctrlDbgShortcuts.designer.cs">
<DependentUpon>ctrlDbgShortcuts.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlDisassemblyView.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Debugger\Controls\ctrlDisassemblyView.Designer.cs">
<DependentUpon>ctrlDisassemblyView.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlHexViewer.cs">
<SubType>UserControl</SubType>
</Compile>
@ -322,6 +329,12 @@
<DependentUpon>ctrlScrollableTextbox.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\FontDialogHelper.cs" />
<Compile Include="Debugger\frmDebugger.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\frmDebugger.Designer.cs">
<DependentUpon>frmDebugger.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\frmFadeSpeed.cs">
<SubType>Form</SubType>
</Compile>
@ -409,6 +422,7 @@
<Compile Include="ResourceExtractor.cs" />
<Compile Include="RuntimeChecker.cs" />
<Compile Include="SingleInstance.cs" />
<Compile Include="Utilities\HexConverter.cs" />
<Compile Include="Utilities\Md5Helper.cs" />
<Compile Include="Utilities\XmlColor.cs" />
<None Include="app.manifest" />
@ -436,6 +450,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlDbgShortcuts.resx">
<DependentUpon>ctrlDbgShortcuts.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlDisassemblyView.resx">
<DependentUpon>ctrlDisassemblyView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlHexViewer.resx">
<DependentUpon>ctrlHexViewer.cs</DependentUpon>
</EmbeddedResource>
@ -448,6 +465,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlScrollableTextbox.resx">
<DependentUpon>ctrlScrollableTextbox.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmDebugger.resx">
<DependentUpon>frmDebugger.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmFadeSpeed.resx">
<DependentUpon>frmFadeSpeed.cs</DependentUpon>
</EmbeddedResource>

View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI
{
class HexConverter
{
private static byte[] _hexLookup = new byte[256];
static HexConverter()
{
_hexLookup['0'] = 0;
_hexLookup['1'] = 1;
_hexLookup['2'] = 2;
_hexLookup['3'] = 3;
_hexLookup['4'] = 4;
_hexLookup['5'] = 5;
_hexLookup['6'] = 6;
_hexLookup['7'] = 7;
_hexLookup['8'] = 8;
_hexLookup['9'] = 9;
_hexLookup['a'] = 10;
_hexLookup['b'] = 11;
_hexLookup['c'] = 12;
_hexLookup['d'] = 13;
_hexLookup['e'] = 14;
_hexLookup['f'] = 15;
_hexLookup['A'] = 10;
_hexLookup['B'] = 11;
_hexLookup['C'] = 12;
_hexLookup['D'] = 13;
_hexLookup['E'] = 14;
_hexLookup['F'] = 15;
}
public static int FromHex(string hex)
{
int value = 0;
for(int i = 0; i < hex.Length; i++) {
value <<= 4;
value |= _hexLookup[hex[i]];
}
return value;
}
}
}