Debugger: Disassembly window, code data logger
This commit is contained in:
parent
4ee2c42663
commit
802bd75df1
32 changed files with 2044 additions and 458 deletions
134
Core/CodeDataLogger.cpp
Normal file
134
Core/CodeDataLogger.cpp
Normal 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
35
Core/CodeDataLogger.h
Normal 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);
|
||||
};
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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];
|
||||
};
|
|
@ -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)
|
||||
{
|
||||
if(type == MemoryOperationType::ExecOpCode) {
|
||||
AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr);
|
||||
CpuState state = _cpu->GetState();
|
||||
DisassemblyInfo disassemblyInfo(state, _memoryManager.get());
|
||||
|
||||
if(type == MemoryOperationType::ExecOpCode) {
|
||||
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) {
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -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
292
Core/Disassembler.cpp
Normal 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
50
Core/Disassembler.h
Normal 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);
|
||||
};
|
|
@ -10,21 +10,23 @@ DisassemblyInfo::DisassemblyInfo()
|
|||
{
|
||||
}
|
||||
|
||||
DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager)
|
||||
DisassemblyInfo::DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags)
|
||||
{
|
||||
_flags = state.PS;
|
||||
_effectiveAddress = -1;
|
||||
|
||||
uint32_t addr = (state.K << 16) | state.PC;
|
||||
_byteCode[0] = memoryManager->Peek(addr);
|
||||
_addrMode = DisassemblyInfo::OpMode[_byteCode[0]];
|
||||
_opSize = GetOperandSize() + 1;
|
||||
|
||||
for(int i = 1; i < _opSize; i++) {
|
||||
_byteCode[i] = memoryManager->Peek((addr+i) & 0xFFFFFF);
|
||||
Initialize(opPointer, cpuFlags);
|
||||
}
|
||||
|
||||
_emulationMode = state.EmulationMode;
|
||||
void DisassemblyInfo::Initialize(uint8_t *opPointer, uint8_t cpuFlags)
|
||||
{
|
||||
_flags = cpuFlags;
|
||||
_effectiveAddress = -1;
|
||||
|
||||
_byteCode[0] = opPointer[0];
|
||||
_addrMode = DisassemblyInfo::OpMode[_byteCode[0]];
|
||||
_opSize = GetOperandSize(_addrMode, _flags) + 1;
|
||||
|
||||
for(int i = 1; i < _opSize; i++) {
|
||||
_byteCode[i] = opPointer[i];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
106
UI/Debugger/CodeLineData.cs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -30,6 +30,14 @@ 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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
66
UI/Debugger/Controls/ctrlDisassemblyView.Designer.cs
generated
Normal file
66
UI/Debugger/Controls/ctrlDisassemblyView.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
192
UI/Debugger/Controls/ctrlDisassemblyView.cs
Normal file
192
UI/Debugger/Controls/ctrlDisassemblyView.cs
Normal 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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
UI/Debugger/Controls/ctrlDisassemblyView.resx
Normal file
120
UI/Debugger/Controls/ctrlDisassemblyView.resx
Normal 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>
|
|
@ -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,9 +163,14 @@ 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)
|
||||
|
@ -174,25 +178,6 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -60,40 +57,11 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
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,75 +175,21 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
return _dataProvider.GetLineCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,14 +229,23 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 = _contents[i].ToLowerInvariant();
|
||||
string line = _dataProvider.GetCodeLineData(i).Text.ToLowerInvariant();
|
||||
if(line.Contains(this._searchString)) {
|
||||
this.ScrollToLineIndex(i);
|
||||
this.Invalidate();
|
||||
|
@ -330,20 +253,21 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
}
|
||||
|
||||
//Continue search from start/end of document
|
||||
if(!searchBackwards && i == this._contents.Length - 1) {
|
||||
if(!searchBackwards && i == this.LineCount - 1) {
|
||||
i = 0;
|
||||
} else if(searchBackwards && i == 0) {
|
||||
i = this._contents.Length - 1;
|
||||
i = this.LineCount - 1;
|
||||
}
|
||||
}
|
||||
this.Invalidate();
|
||||
return _contents[_selectionStart].ToLowerInvariant().Contains(this._searchString);
|
||||
return _dataProvider.GetCodeLineData(_selectionStart).Text.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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
289
UI/Debugger/frmDebugger.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
94
UI/Debugger/frmDebugger.cs
Normal file
94
UI/Debugger/frmDebugger.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
126
UI/Debugger/frmDebugger.resx
Normal file
126
UI/Debugger/frmDebugger.resx
Normal 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>
|
|
@ -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,16 +317,12 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
22
UI/Forms/frmMain.Designer.cs
generated
22
UI/Forms/frmMain.Designer.cs
generated
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
20
UI/UI.csproj
20
UI/UI.csproj
|
@ -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>
|
||||
|
|
49
UI/Utilities/HexConverter.cs
Normal file
49
UI/Utilities/HexConverter.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue