Debugger: Ability to add labels/symbols & comments to code (work in progress)

This commit is contained in:
Souryo 2016-11-21 22:34:47 -05:00
parent 1215b3e813
commit 4ffebdb4e0
28 changed files with 1231 additions and 270 deletions

View file

@ -55,11 +55,12 @@ struct State
class CPU : public Snapshotable
{
private:
public:
static const uint16_t NMIVector = 0xFFFA;
static const uint16_t ResetVector = 0xFFFC;
static const uint16_t IRQVector = 0xFFFE;
private:
static CPU* Instance;
typedef void(CPU::*Func)();

View file

@ -97,7 +97,7 @@ bool Debugger::LoadCdlFile(string cdlFilepath)
if(_codeDataLogger->LoadCdlFile(cdlFilepath)) {
for(int i = 0, len = _mapper->GetPrgSize(); i < len; i++) {
if(_codeDataLogger->IsCode(i)) {
i = _disassembler->BuildCache(i, -1, 0xFFFF, false) - 1;
i = _disassembler->BuildCache(i, -1, 0xFFFF, _codeDataLogger->IsSubEntryPoint(i)) - 1;
}
}
@ -127,6 +127,19 @@ CdlRatios Debugger::GetCdlRatios()
return _codeDataLogger->GetRatios();
}
void Debugger::SetLabel(uint32_t address, string label, string comment)
{
_codeLabels.erase(address);
if(!label.empty()) {
_codeLabels.emplace(address, label);
}
_codeComments.erase(address);
if(!comment.empty()) {
_codeComments.emplace(address, comment);
}
}
void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length)
{
_bpUpdateLock.AcquireSafe();
@ -449,10 +462,11 @@ string Debugger::GenerateOutput()
State cpuState = _cpu->GetState();
std::ostringstream output;
bool showEffectiveAddresses = CheckFlag(DebuggerFlags::ShowEffectiveAddresses);
bool showOnlyDiassembledCode = CheckFlag(DebuggerFlags::ShowOnlyDisassembledCode);
//Get code in internal RAM
output << _disassembler->GetCode(0x0000, 0x1FFF, 0x0000, PrgMemoryType::PrgRom, showEffectiveAddresses, cpuState, _memoryManager);
output << "2000:::--END OF INTERNAL RAM--\n";
output << _disassembler->GetCode(0x0000, 0x1FFF, 0x0000, PrgMemoryType::PrgRom, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _codeLabels, _codeComments);
output << "2000\x1\x1\x1--End of internal RAM--\n";
for(uint32_t i = 0x2000; i < 0x10000; i += 0x100) {
//Merge all sequential ranges into 1 chunk
@ -469,7 +483,7 @@ string Debugger::GenerateOutput()
romAddr += 0x100;
i+=0x100;
}
output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::PrgRom, showEffectiveAddresses, cpuState, _memoryManager);
output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::PrgRom, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _codeLabels, _codeComments);
} else if(ramAddr >= 0) {
startAddr = ramAddr;
endAddr = startAddr + 0xFF;
@ -478,7 +492,7 @@ string Debugger::GenerateOutput()
ramAddr += 0x100;
i += 0x100;
}
output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::WorkRam, showEffectiveAddresses, cpuState, _memoryManager);
output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::WorkRam, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _codeLabels, _codeComments);
}
}

View file

@ -4,9 +4,11 @@
#include <atomic>
#include <deque>
#include <unordered_set>
#include <unordered_map>
using std::atomic;
using std::deque;
using std::unordered_set;
using std::unordered_map;
#include "DebugState.h"
#include "Breakpoint.h"
@ -25,6 +27,7 @@ enum class DebuggerFlags
{
PpuPartialDraw = 1,
ShowEffectiveAddresses = 2,
ShowOnlyDisassembledCode = 4
};
class Debugger
@ -54,6 +57,9 @@ private:
vector<Breakpoint> _breakpoints[BreakpointTypeCount];
bool _hasBreakpoint[BreakpointTypeCount];
unordered_map<uint32_t, string> _codeLabels;
unordered_map<uint32_t, string> _codeComments;
deque<uint32_t> _callstackAbsolute;
deque<uint32_t> _callstackRelative;
@ -99,6 +105,7 @@ public:
bool CheckFlag(DebuggerFlags flag);
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
void SetLabel(uint32_t address, string label, string comment);
void GetFunctionEntryPoints(int32_t* entryPoints);
void GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative);

View file

@ -174,9 +174,25 @@ void Disassembler::InvalidateCache(uint16_t memoryAddr, int32_t absoluteRamAddr)
}
}
string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, State& cpuState, shared_ptr<MemoryManager> memoryManager)
vector<string> Disassembler::SplitComment(string input)
{
vector<string> result;
size_t index;
while((index = input.find('\n')) != string::npos) {
result.push_back(input.substr(0, index));
input = input.substr(index + 1, input.size() - index - 1);
}
result.push_back(input);
return result;
}
string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, bool showOnlyDiassembledCode, State& cpuState, shared_ptr<MemoryManager> memoryManager, unordered_map<uint32_t, string> &codeLabels, unordered_map<uint32_t, string> &codeComments) {
std::ostringstream output;
uint16_t resetVector = memoryManager->DebugReadWord(CPU::ResetVector);
uint16_t nmiVector = memoryManager->DebugReadWord(CPU::NMIVector);
uint16_t irqVector = memoryManager->DebugReadWord(CPU::IRQVector);
vector<shared_ptr<DisassemblyInfo>> *cache;
uint8_t *source;
uint32_t mask = 0xFFFFFFFF;
@ -194,32 +210,128 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo
uint32_t addr = startAddr;
uint32_t byteCount = 0;
bool skippingCode = false;
while(addr <= endAddr) {
shared_ptr<DisassemblyInfo> info;
if(info = (*cache)[addr&mask]) {
auto labelSearch = codeLabels.find(addr);
auto commentSearch = codeComments.find(addr);
string label = labelSearch != codeLabels.end() ? labelSearch->second : "";
string labelString = label.empty() ? "" : ("\x1\x1\x1" + labelSearch->second + ":\n");
string commentString = commentSearch != codeComments.end() ? commentSearch->second : "";
bool multilineComment = commentString.find_first_of('\n') != string::npos;
string singleLineComment = "";
string multiLineComment = "";
if(multilineComment) {
for(string &str : SplitComment(commentString)) {
multiLineComment += "\x1\x1\x1\x2\x2;" + str + "\n";
}
} else if(!commentString.empty()) {
singleLineComment = "\x2;" + commentString;
}
shared_ptr<DisassemblyInfo> info = (*cache)[addr&mask];
if(info) {
if(byteCount > 0) {
output << "\n";
byteCount = 0;
}
if(skippingCode) {
output << std::hex << std::uppercase << (memoryAddr - 1) << "\x1" << (addr - 1) << "\x1\x1";
if(showOnlyDiassembledCode) {
output << "----\n";
} else {
output << "__unknown block__\n";
}
skippingCode = false;
}
string effectiveAddress = showEffectiveAddresses ? info->GetEffectiveAddress(cpuState, memoryManager) : "";
output << std::hex << std::uppercase << memoryAddr << ":" << addr << ":" << info->ToString(memoryAddr) << "||" << effectiveAddress << "\n";
string effectiveAddress = showEffectiveAddresses ? info->GetEffectiveAddressString(cpuState, memoryManager, &codeLabels) : "";
if(info->IsSubEntryPoint()) {
if(label.empty()) {
output << "\x1\x1\x1\n\x1\x1\x1--sub start--\n";
} else {
output << "\x1\x1\x1\n\x1\x1\x1--" + label + "()--\n";
}
} else if(memoryAddr == resetVector) {
output << "\x1\x1\x1\n\x1\x1\x1--reset--\n";
} else if(memoryAddr == irqVector) {
output << "\x1\x1\x1\n\x1\x1\x1--irq--\n";
} else if(memoryAddr == nmiVector) {
output << "\x1\x1\x1\n\x1\x1\x1--nmi--\n";
}
output << multiLineComment;
output << labelString;
output << std::hex << std::uppercase << memoryAddr << "\x1" << addr << "\x1" << info->GetByteCode() << "\x1 " << info->ToString(memoryAddr, memoryManager, &codeLabels) << "\x2" << effectiveAddress;
output << singleLineComment;
output << "\n";
if(info->IsSubExitPoint()) {
output << "\x1\x1\x1__sub end__\n\x1\x1\x1\n";
}
addr += info->GetSize();
memoryAddr += info->GetSize();
} else {
if(byteCount >= 8) {
output << "\n";
byteCount = 0;
if(!skippingCode) {
output << std::hex << std::uppercase << memoryAddr << "\x1" << addr << "\x1\x1";
if(showOnlyDiassembledCode) {
output << "____\n\x1\x1\x1";
if(label.empty()) {
output << "[[unknown block]]\n";
} else {
output << "[[" << label << "]]\n";
if(!singleLineComment.empty()) {
output << "\x1\x1\x1" << singleLineComment << "\n";
} else {
output << multiLineComment;
}
}
} else {
output << "--unknown block--\n";
}
skippingCode = true;
}
if(byteCount == 0) {
output << std::hex << std::uppercase << memoryAddr << ":" << addr << "::" << ".db";
}
output << std::hex << " $" << std::setfill('0') << std::setw(2) << (short)source[addr&mask];
byteCount++;
if(!showOnlyDiassembledCode) {
if(byteCount >= 8 || !label.empty() || !commentString.empty()) {
output << "\n";
byteCount = 0;
}
if(byteCount == 0) {
output << multiLineComment;
output << labelString;
output << std::hex << std::uppercase << memoryAddr << "\x1" << addr << "\x1\x1" << ".db";
output << singleLineComment;
if(!label.empty() || !commentString.empty()) {
byteCount = 7;
}
}
output << std::hex << " $" << std::setfill('0') << std::setw(2) << (short)source[addr&mask];
byteCount++;
}
addr++;
memoryAddr++;
}
}
if(skippingCode) {
if(byteCount != 0) {
output << "\n";
}
output << std::hex << std::uppercase << (memoryAddr - 1) << "\x1" << (addr - 1) << "\x1\x1";
if(showOnlyDiassembledCode) {
output << "----\n";
} else {
output << "__unknown block__\n";
}
}
output << "\n";
return output.str();

View file

@ -1,6 +1,8 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include <unordered_map>
using std::unordered_map;
struct State;
class MemoryManager;
@ -18,6 +20,7 @@ private:
uint32_t _prgSize;
bool IsUnconditionalJump(uint8_t opCode);
vector<string> SplitComment(string input);
public:
Disassembler(uint8_t* internalRam, uint8_t* prgRom, uint32_t prgSize, uint8_t* prgRam, uint32_t prgRamSize);
@ -26,7 +29,7 @@ public:
uint32_t BuildCache(int32_t absoluteAddr, int32_t absoluteRamAddr, uint16_t memoryAddr, bool isSubEntryPoint);
void InvalidateCache(uint16_t memoryAddr, int32_t absoluteRamAddr);
string GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, State& cpuState, shared_ptr<MemoryManager> memoryManager);
string GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, bool showOnlyDiassembledCode, State& cpuState, shared_ptr<MemoryManager> memoryManager, unordered_map<uint32_t, string> &codeLabels, unordered_map<uint32_t, string> &codeComments);
shared_ptr<DisassemblyInfo> GetDisassemblyInfo(int32_t absoluteAddress, int32_t absoluteRamAddress, uint16_t memoryAddress);
};

View file

@ -6,108 +6,76 @@ string DisassemblyInfo::OPName[256];
AddrMode DisassemblyInfo::OPMode[256];
uint32_t DisassemblyInfo::OPSize[256];
void DisassemblyInfo::Initialize(uint32_t memoryAddr)
string DisassemblyInfo::ToString(uint32_t memoryAddr, shared_ptr<MemoryManager> memoryManager, std::unordered_map<uint32_t, string> *codeLabels)
{
_lastAddr = memoryAddr;
std::ostringstream output;
uint8_t opCode = *_opPointer;
_opSize = DisassemblyInfo::OPSize[opCode];
_opMode = DisassemblyInfo::OPMode[opCode];
//Output raw byte code
for(uint32_t i = 0; i < 3; i++) {
if(i < _opSize) {
output << "$" << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (short)*(_opPointer + i);
} else {
output << " ";
}
if(i != 2) {
output << " ";
}
}
output << ":";
output << DisassemblyInfo::OPName[opCode];
if(opCode == 0x40 || opCode == 0x60) {
//Make end of function/interrupt routines more obvious
output << " ---->";
}
if(DisassemblyInfo::OPName[opCode].empty()) {
output << "invalid opcode";
}
std::ostringstream nextByte;
std::ostringstream nextWord;
std::ostringstream addrString;
if(_opSize == 2) {
nextByte << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << (short)(*(_opPointer + 1));
_opAddr = *(_opPointer + 1);
} else if(_opSize == 3) {
nextWord << std::uppercase << std::hex << std::setw(4) << std::setfill('0') << (*(_opPointer + 1) | (*(_opPointer + 2) << 8));
_opAddr = *(_opPointer + 1) | (*(_opPointer + 2) << 8);
}
string operandValue;
if(codeLabels) {
auto result = codeLabels->find(memoryManager->ToAbsolutePrgAddress(_opAddr));
if(result != codeLabels->end()) {
operandValue = result->second;
}
}
if(operandValue.empty()) {
std::stringstream ss;
ss << "$" << std::uppercase << std::hex << std::setw(_opSize == 2 ? 2 : 4) << std::setfill('0') << (short)_opAddr;
operandValue = ss.str();
}
output << " ";
switch(_opMode) {
case AddrMode::Acc: output << " A"; break;
case AddrMode::Imm: output << "#" << operandValue; break;
case AddrMode::Ind: output << "(" << operandValue << ")"; break;
case AddrMode::IndX: output << "(" << operandValue << ",X)"; break;
case AddrMode::IndY:
case AddrMode::IndYW:
output << "(" << operandValue << "),Y";
break;
case AddrMode::Abs:
output << " $" << nextWord.str();
case AddrMode::Zero:
output << operandValue;
break;
case AddrMode::AbsX:
case AddrMode::AbsXW:
output << " $" << nextWord.str() << ",X";
case AddrMode::ZeroX:
output << operandValue << ",X";
break;
case AddrMode::AbsY:
case AddrMode::AbsYW:
output << " $" << nextWord.str() << ",Y";
break;
case AddrMode::Imm:
output << " #$" << nextByte.str();
break;
case AddrMode::Ind:
output << " ($" << nextWord.str() << ")";
break;
case AddrMode::IndX:
output << " ($" << nextByte.str() << ",X)";
break;
case AddrMode::IndY:
case AddrMode::IndYW:
output << " ($" << nextByte.str() << "),Y";
case AddrMode::ZeroY:
output << operandValue << ",Y";
break;
case AddrMode::Rel:
//TODO (not correct when banks are switched around in memory)
output << " $" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << ((int8_t)*(_opPointer + 1) + memoryAddr + 2);
break;
case AddrMode::Zero:
output << " $" << nextByte.str();
break;
case AddrMode::ZeroX:
output << " $" << nextByte.str() << ",X";
break;
case AddrMode::ZeroY:
output << " $" << nextByte.str() << ",Y";
break;
case AddrMode::Acc:
output << " A";
break;
default:
output << "$" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << ((int8_t)*(_opPointer + 1) + memoryAddr + 2);
break;
default: break;
}
if(_isSubEntryPoint) {
output << " <----";
}
_disassembly = output.str();
return output.str();
}
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint)
@ -115,72 +83,99 @@ DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint)
_opPointer = opPointer;
_isSubEntryPoint = isSubEntryPoint;
Initialize();
uint8_t opCode = *_opPointer;
_opSize = DisassemblyInfo::OPSize[opCode];
_opMode = DisassemblyInfo::OPMode[opCode];
_isSubExitPoint = opCode == 0x40 || opCode == 0x60;
//Raw byte code
std::stringstream byteCodeOutput;
for(uint32_t i = 0; i < 3; i++) {
if(i < _opSize) {
byteCodeOutput << "$" << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (short)*(_opPointer + i);
} else {
byteCodeOutput << " ";
}
if(i != 2) {
byteCodeOutput << " ";
}
}
_byteCode = byteCodeOutput.str();
}
void DisassemblyInfo::SetSubEntryPoint()
{
if(!_isSubEntryPoint) {
_isSubEntryPoint = true;
Initialize();
_isSubEntryPoint = true;
}
string DisassemblyInfo::GetEffectiveAddressString(State& cpuState, shared_ptr<MemoryManager> memoryManager, std::unordered_map<uint32_t, string> *codeLabels)
{
int32_t effectiveAddress = GetEffectiveAddress(cpuState, memoryManager);
if(effectiveAddress < 0) {
return "";
} else {
bool empty = true;
if(codeLabels) {
auto result = codeLabels->find(memoryManager->ToAbsolutePrgAddress(effectiveAddress));
if(result != codeLabels->end()) {
return " @ " + result->second;
}
}
std::stringstream ss;
ss << std::uppercase << std::setfill('0') << " @ $";
if(_opMode == AddrMode::ZeroX || _opMode == AddrMode::ZeroY) {
ss << std::setw(2) << std::hex << (uint16_t)effectiveAddress;
} else {
ss << std::setw(4) << std::hex << (uint16_t)effectiveAddress;
}
return ss.str();
}
}
string DisassemblyInfo::GetEffectiveAddress(State& cpuState, shared_ptr<MemoryManager> memoryManager)
int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, shared_ptr<MemoryManager> memoryManager)
{
std::stringstream ss;
ss << std::uppercase << std::setfill('0');
switch(_opMode) {
case AddrMode::ZeroX: ss << " @ $" << std::setw(2) << std::hex << (short)(uint8_t)(*(_opPointer + 1) + cpuState.X); break;
case AddrMode::ZeroY: ss << " @ $" << std::setw(2) << std::hex << (short)(uint8_t)(*(_opPointer + 1) + cpuState.Y); break;
case AddrMode::ZeroX: return (uint8_t)(*(_opPointer + 1) + cpuState.X); break;
case AddrMode::ZeroY: return (uint8_t)(*(_opPointer + 1) + cpuState.Y); break;
case AddrMode::IndX: {
uint8_t zeroAddr = *(_opPointer + 1) + cpuState.X;
uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8;
ss << " @ $" << std::setw(4) << std::hex << addr;
break;
return memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8;
}
case AddrMode::IndY:
case AddrMode::IndYW: {
uint8_t zeroAddr = *(_opPointer + 1);
uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8;
addr += cpuState.Y;
ss << " @ $" << std::setw(4) << std::hex << addr;
break;
return addr + cpuState.Y;
}
case AddrMode::Ind: {
uint8_t zeroAddr = *(_opPointer + 1);
uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8;
ss << " @ $" << std::setw(4) << std::hex << addr;
break;
return memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8;
}
case AddrMode::AbsX:
case AddrMode::AbsXW: {
uint16_t addr = (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.X;
ss << " @ $" << std::setw(4) << std::hex << addr;
break;
return (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.X;
}
case AddrMode::AbsY:
case AddrMode::AbsYW: {
uint16_t addr = (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.Y;
ss << " @ $" << std::setfill('0') << std::setw(4) << std::hex << addr;
break;
return (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.Y;
}
}
return ss.str();
return -1;
}
string DisassemblyInfo::ToString(uint32_t memoryAddr)
string DisassemblyInfo::GetByteCode()
{
if(memoryAddr != _lastAddr && _opMode == AddrMode::Rel) {
Initialize(memoryAddr);
}
return _disassembly;
return _byteCode;
}
uint32_t DisassemblyInfo::GetSize()
@ -188,3 +183,12 @@ uint32_t DisassemblyInfo::GetSize()
return _opSize;
}
bool DisassemblyInfo::IsSubEntryPoint()
{
return _isSubEntryPoint;
}
bool DisassemblyInfo::IsSubExitPoint()
{
return _isSubExitPoint;
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "stdafx.h"
#include <unordered_map>
#include "CPU.h"
class DisassemblyInfo
@ -10,22 +11,28 @@ public:
static uint32_t OPSize[256];
private:
string _disassembly;
string _byteCode;
uint8_t *_opPointer = nullptr;
bool _isSubEntryPoint = false;
bool _isSubExitPoint = false;
uint32_t _opSize = 0;
AddrMode _opMode;
uint32_t _lastAddr = 0;
private:
void Initialize(uint32_t memoryAddr = 0);
uint16_t _opAddr;
public:
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint);
void SetSubEntryPoint();
string GetEffectiveAddress(State& cpuState, shared_ptr<MemoryManager> memoryManager);
string ToString(uint32_t memoryAddr);
int32_t GetEffectiveAddress(State& cpuState, shared_ptr<MemoryManager> memoryManager);
string GetEffectiveAddressString(State& cpuState, shared_ptr<MemoryManager> memoryManager, std::unordered_map<uint32_t, string> *codeLabels);
string ToString(uint32_t memoryAddr, shared_ptr<MemoryManager> memoryManager, std::unordered_map<uint32_t, string> *codeLabels = nullptr);
string GetByteCode();
uint32_t GetSize();
bool IsSubEntryPoint();
bool IsSubExitPoint();
};

View file

@ -101,6 +101,11 @@ uint8_t MemoryManager::DebugRead(uint16_t addr)
return value;
}
uint16_t MemoryManager::DebugReadWord(uint16_t addr)
{
return DebugRead(addr) | (DebugRead(addr + 1) << 8);
}
void MemoryManager::ProcessCpuClock()
{
_mapper->ProcessCpuClock();
@ -179,6 +184,11 @@ void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value)
_mapper->WriteVRAM(addr, value);
}
uint32_t MemoryManager::ToAbsolutePrgAddress(uint16_t ramAddr)
{
return _mapper->ToAbsoluteAddress(ramAddr);
}
uint32_t MemoryManager::ToAbsoluteChrAddress(uint16_t vramAddr)
{
return _mapper->ToAbsoluteChrAddress(vramAddr);

View file

@ -39,6 +39,7 @@ class MemoryManager: public Snapshotable
void RegisterIODevice(IMemoryHandler *handler);
uint8_t DebugRead(uint16_t addr);
uint16_t DebugReadWord(uint16_t addr);
uint8_t DebugReadVRAM(uint16_t addr);
void DebugWrite(uint16_t addr, uint8_t value);
@ -53,6 +54,7 @@ class MemoryManager: public Snapshotable
uint8_t ReadVRAM(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::PpuRenderingRead);
void WriteVRAM(uint16_t addr, uint8_t value);
uint32_t ToAbsolutePrgAddress(uint16_t ramAddr);
uint32_t ToAbsoluteChrAddress(uint16_t vramAddr);
static uint8_t GetOpenBus(uint8_t mask = 0xFF);

View file

@ -42,11 +42,7 @@ void TraceLogger::Log(DebugState &state, shared_ptr<DisassemblyInfo> disassembly
State &cpuState = state.CPU;
PPUDebugState &ppuState = state.PPU;
string disassembly = disassemblyInfo->ToString(cpuState.DebugPC);
auto separatorPosition = disassembly.begin() + disassembly.find_first_of(':', 0);
string byteCode(disassembly.begin(), separatorPosition);
byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end());
string assemblyCode(separatorPosition + 1, disassembly.end());
string disassembly = disassemblyInfo->ToString(cpuState.DebugPC, _memoryManager);
//Roughly adjust PPU cycle & scanline to take into account the PPU already ran 3 cycles by the time we get here
short ppuCycle = (short)ppuState.Cycle - 3;
@ -66,7 +62,7 @@ void TraceLogger::Log(DebugState &state, shared_ptr<DisassemblyInfo> disassembly
_outputFile << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << std::right << (short)cpuState.DebugPC << " ";
if(_options.ShowByteCode) {
_outputFile << std::setfill(' ') << std::setw(10) << std::left << byteCode;
_outputFile << std::setfill(' ') << std::setw(10) << std::left << disassemblyInfo->GetByteCode();
}
int indentLevel = 0;
@ -75,7 +71,7 @@ void TraceLogger::Log(DebugState &state, shared_ptr<DisassemblyInfo> disassembly
_outputFile << std::string(indentLevel, ' ');
}
string codeString = assemblyCode + (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddress(state.CPU, _memoryManager) : "");
string codeString = disassembly + (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddressString(state.CPU, _memoryManager, nullptr) : "");
_outputFile << std::setfill(' ') << std::setw(32 - indentLevel) << std::left << codeString;
if(_options.ShowRegisters) {

View file

@ -12,7 +12,6 @@ namespace Mesen.GUI.Config
{
public class DebugViewInfo
{
public bool ShowOnlyDiassembledCode = true;
public bool ShowByteCode = false;
public bool ShowPrgAddresses = false;
public float FontSize = 13;
@ -22,6 +21,9 @@ namespace Mesen.GUI.Config
{
public DebugViewInfo LeftView;
public DebugViewInfo RightView;
public bool ShowOnlyDisassembledCode = true;
public bool SplitView = false;
public bool HexDisplay = true;

View file

@ -32,7 +32,6 @@
this.mnuShowNextStatement = new System.Windows.Forms.ToolStripMenuItem();
this.mnuSetNextStatement = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuShowOnlyDisassembledCode = new System.Windows.Forms.ToolStripMenuItem();
this.mnuShowLineNotes = new System.Windows.Forms.ToolStripMenuItem();
this.mnuShowCodeNotes = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
@ -54,7 +53,6 @@
this.mnuShowNextStatement,
this.mnuSetNextStatement,
this.toolStripMenuItem1,
this.mnuShowOnlyDisassembledCode,
this.mnuShowLineNotes,
this.mnuShowCodeNotes,
this.toolStripMenuItem2,
@ -87,16 +85,6 @@
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(255, 6);
//
// mnuShowOnlyDisassembledCode
//
this.mnuShowOnlyDisassembledCode.Checked = true;
this.mnuShowOnlyDisassembledCode.CheckOnClick = true;
this.mnuShowOnlyDisassembledCode.CheckState = System.Windows.Forms.CheckState.Checked;
this.mnuShowOnlyDisassembledCode.Name = "mnuShowOnlyDisassembledCode";
this.mnuShowOnlyDisassembledCode.Size = new System.Drawing.Size(258, 22);
this.mnuShowOnlyDisassembledCode.Text = "Show Only Disassembled Code";
this.mnuShowOnlyDisassembledCode.Click += new System.EventHandler(this.mnuShowOnlyDisassembledCode_Click);
//
// mnuShowLineNotes
//
this.mnuShowLineNotes.CheckOnClick = true;
@ -146,6 +134,7 @@
this.ctrlCodeViewer.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseUp);
this.ctrlCodeViewer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseMove);
this.ctrlCodeViewer.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseDown);
this.ctrlCodeViewer.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseDoubleClick);
this.ctrlCodeViewer.FontSizeChanged += new System.EventHandler(this.ctrlCodeViewer_FontSizeChanged);
//
// contextMenuMargin
@ -199,7 +188,6 @@
private System.Windows.Forms.ToolStripMenuItem mnuShowNextStatement;
private System.Windows.Forms.ToolStripMenuItem mnuSetNextStatement;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem mnuShowOnlyDisassembledCode;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
private System.Windows.Forms.ToolStripMenuItem mnuGoToLocation;
private System.Windows.Forms.ToolStripMenuItem mnuAddToWatch;

View file

@ -27,7 +27,6 @@ namespace Mesen.GUI.Debugger
public void SetConfig(DebugViewInfo config)
{
_config = config;
this.mnuShowOnlyDisassembledCode.Checked = config.ShowOnlyDiassembledCode;
this.mnuShowLineNotes.Checked = config.ShowPrgAddresses;
this.mnuShowCodeNotes.Checked = config.ShowByteCode;
this.FontSize = config.FontSize;
@ -38,7 +37,6 @@ namespace Mesen.GUI.Debugger
private void UpdateConfig()
{
_config.ShowOnlyDiassembledCode = this.mnuShowOnlyDisassembledCode.Checked;
_config.ShowPrgAddresses = this.mnuShowLineNotes.Checked;
_config.ShowByteCode = this.mnuShowCodeNotes.Checked;
_config.FontSize = this.FontSize;
@ -102,50 +100,17 @@ namespace Mesen.GUI.Debugger
List<string> lineNumberNotes = new List<string>();
List<string> codeNotes = new List<string>();
List<string> codeLines = new List<string>();
bool diassembledCodeOnly = mnuShowOnlyDisassembledCode.Checked;
bool skippingCode = false;
string[] lines = _code.Split('\n');
for(int i = 0, len = lines.Length - 1; i < len; i++) {
string line = lines[i];
string[] lineParts = line.Split(':');
if(skippingCode && (i == len - 1 || lineParts[3][0] != '.')) {
lineNumbers.Add(-1);
lineNumberNotes.Add("");
codeLines.Add("[code not disassembled]");
codeNotes.Add("");
int address = (int)ParseHexAddress(lineParts[0]);
if(i != len - 1 || lineParts[3][0] != '.') {
address--;
} else if(i == len - 1 && lineParts[3][0] == '.' && address >= 0xFFF8) {
address = 0xFFFF;
}
lineNumbers.Add(address);
lineNumberNotes.Add(lineParts[1]);
codeLines.Add("[code not disassembled]");
codeNotes.Add("");
skippingCode = false;
if(i == len - 1 && lineParts[3][0] == '.') {
break;
}
}
string[] lineParts = line.Split('\x1');
if(lineParts.Length >= 4) {
if(diassembledCodeOnly && lineParts[3][0] == '.') {
if(!skippingCode) {
lineNumbers.Add((int)ParseHexAddress(lineParts[0]));
lineNumberNotes.Add(lineParts[1]);
codeLines.Add("[code not disassembled]");
codeNotes.Add("");
skippingCode = true;
}
} else {
lineNumbers.Add((int)ParseHexAddress(lineParts[0]));
lineNumberNotes.Add(lineParts[1]);
codeLines.Add(lineParts[3]);
codeNotes.Add(lineParts[2]);
}
lineNumbers.Add(ParseHexAddress(lineParts[0]));
lineNumberNotes.Add(lineParts[1]);
codeNotes.Add(lineParts[2]);
codeLines.Add(lineParts[3]);
}
}
@ -160,9 +125,13 @@ namespace Mesen.GUI.Debugger
return false;
}
private UInt32 ParseHexAddress(string hexAddress)
private int ParseHexAddress(string hexAddress)
{
return UInt32.Parse(hexAddress, System.Globalization.NumberStyles.AllowHexSpecifier);
if(string.IsNullOrWhiteSpace(hexAddress)) {
return -1;
} else {
return (int)UInt32.Parse(hexAddress, System.Globalization.NumberStyles.AllowHexSpecifier);
}
}
public void HighlightBreakpoints()
@ -211,7 +180,19 @@ namespace Mesen.GUI.Debugger
string valueText = "$" + memoryValue.ToString("X");
toolTip.Show(valueText, ctrlCodeViewer, e.Location.X + 5, e.Location.Y - 20, 3000);
} else {
toolTip.Hide(ctrlCodeViewer);
CodeLabel label = LabelManager.GetLabel(word);
if(label == null) {
toolTip.Hide(ctrlCodeViewer);
} else {
Byte memoryValue = InteropEmu.DebugGetMemoryValue(label.Address);
toolTip.Show(
"Label: " + label.Label + Environment.NewLine +
"Address: $" + InteropEmu.DebugGetRelativeAddress(label.Address).ToString("X4") + Environment.NewLine +
"Value: $" + memoryValue.ToString("X2") + Environment.NewLine +
"Comment: " + (label.Comment.Contains(Environment.NewLine) ? (Environment.NewLine + label.Comment) : label.Comment)
, ctrlCodeViewer, e.Location.X + 5, e.Location.Y - 60 - label.Comment.Split('\n').Length * 14, 3000);
}
}
_previousLocation = e.Location;
}
@ -255,6 +236,26 @@ namespace Mesen.GUI.Debugger
}
}
}
private void ctrlCodeViewer_MouseDoubleClick(object sender, MouseEventArgs e)
{
int address = ctrlCodeViewer.GetLineNumberAtPosition(e.Y);
if(address >= 0 && e.Location.X > this.ctrlCodeViewer.CodeMargin / 2 && e.Location.X < this.ctrlCodeViewer.CodeMargin) {
UInt32 absoluteAddr = (UInt32)InteropEmu.DebugGetAbsoluteAddress((UInt32)address);
CodeLabel existingLabel = LabelManager.GetLabel(absoluteAddr);
CodeLabel newLabel = new CodeLabel() { Label = existingLabel?.Label, Comment = existingLabel?.Comment };
frmEditLabel frm = new frmEditLabel(absoluteAddr, newLabel);
if(frm.ShowDialog() == DialogResult.OK) {
if(string.IsNullOrWhiteSpace(newLabel.Label) && string.IsNullOrWhiteSpace(newLabel.Comment)) {
LabelManager.DeleteLabel(absoluteAddr);
} else {
LabelManager.SetLabel(absoluteAddr, newLabel.Label, newLabel.Comment);
}
}
}
}
#region Context Menu

View file

@ -53,7 +53,8 @@ namespace Mesen.GUI.Debugger.Controls
lstFunctions.ListViewItemSorter = null;
lstFunctions.Items.Clear();
for(int i = 0; entryPoints[i] >= 0; i++) {
ListViewItem item = lstFunctions.Items.Add("");
CodeLabel label = LabelManager.GetLabel((UInt32)entryPoints[i]);
ListViewItem item = lstFunctions.Items.Add(label?.Label);
Int32 relativeAddress = InteropEmu.DebugGetRelativeAddress((UInt32)entryPoints[i]);
if(relativeAddress >= 0) {

View file

@ -0,0 +1,113 @@
namespace Mesen.GUI.Debugger.Controls
{
partial class ctrlLabelList
{
/// <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.components = new System.ComponentModel.Container();
this.lstLabels = new System.Windows.Forms.ListView();
this.colFunctionLabel = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.colFunctionAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.colMemoryAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mnuDelete = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenu.SuspendLayout();
this.SuspendLayout();
//
// lstLabels
//
this.lstLabels.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.colFunctionLabel,
this.colFunctionAddress,
this.colMemoryAddress});
this.lstLabels.ContextMenuStrip = this.contextMenu;
this.lstLabels.Dock = System.Windows.Forms.DockStyle.Fill;
this.lstLabels.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lstLabels.FullRowSelect = true;
this.lstLabels.GridLines = true;
this.lstLabels.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.lstLabels.Location = new System.Drawing.Point(0, 0);
this.lstLabels.MultiSelect = false;
this.lstLabels.Name = "lstLabels";
this.lstLabels.Size = new System.Drawing.Size(275, 112);
this.lstLabels.TabIndex = 2;
this.lstLabels.UseCompatibleStateImageBehavior = false;
this.lstLabels.View = System.Windows.Forms.View.Details;
this.lstLabels.DoubleClick += new System.EventHandler(this.lstLabels_DoubleClick);
//
// colFunctionLabel
//
this.colFunctionLabel.Text = "Label";
this.colFunctionLabel.Width = 136;
//
// colFunctionAddress
//
this.colFunctionAddress.Text = "Address";
this.colFunctionAddress.Width = 62;
//
// colMemoryAddress
//
this.colMemoryAddress.Text = "ROM Addr.";
this.colMemoryAddress.Width = 71;
//
// contextMenu
//
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuDelete});
this.contextMenu.Name = "contextMenu";
this.contextMenu.Size = new System.Drawing.Size(153, 48);
this.contextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.mnuActions_Opening);
//
// mnuDelete
//
this.mnuDelete.Name = "mnuDelete";
this.mnuDelete.ShortcutKeys = System.Windows.Forms.Keys.Delete;
this.mnuDelete.Size = new System.Drawing.Size(152, 22);
this.mnuDelete.Text = "Delete";
this.mnuDelete.Click += new System.EventHandler(this.mnuDelete_Click);
//
// ctrlLabelList
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.lstLabels);
this.Name = "ctrlLabelList";
this.Size = new System.Drawing.Size(275, 112);
this.contextMenu.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView lstLabels;
private System.Windows.Forms.ColumnHeader colFunctionLabel;
private System.Windows.Forms.ColumnHeader colFunctionAddress;
private System.Windows.Forms.ColumnHeader colMemoryAddress;
private System.Windows.Forms.ContextMenuStrip contextMenu;
private System.Windows.Forms.ToolStripMenuItem mnuDelete;
}
}

View file

@ -0,0 +1,73 @@
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 System.Collections;
namespace Mesen.GUI.Debugger.Controls
{
public partial class ctrlLabelList : UserControl
{
public event EventHandler OnLabelSelected;
public ctrlLabelList()
{
InitializeComponent();
}
public void UpdateLabelList()
{
Int32[] entryPoints = InteropEmu.DebugGetFunctionEntryPoints();
lstLabels.BeginUpdate();
lstLabels.Items.Clear();
foreach(KeyValuePair<UInt32, CodeLabel> kvp in LabelManager.GetLabels()) {
if(kvp.Value.Label.Length > 0) {
ListViewItem item = lstLabels.Items.Add(kvp.Value.Label);
Int32 relativeAddress = InteropEmu.DebugGetRelativeAddress(kvp.Value.Address);
if(relativeAddress >= 0) {
item.SubItems.Add("$" + relativeAddress.ToString("X4"));
} else {
item.SubItems.Add("[n/a]");
item.ForeColor = Color.Gray;
item.Font = new Font(item.Font, FontStyle.Italic);
}
item.SubItems.Add("$" + kvp.Value.Address.ToString("X4"));
item.SubItems[1].Tag = kvp.Value.Address;
item.Tag = relativeAddress;
}
}
lstLabels.Sort();
lstLabels.EndUpdate();
}
private void lstLabels_DoubleClick(object sender, EventArgs e)
{
if(lstLabels.SelectedItems.Count > 0) {
Int32 relativeAddress = (Int32)lstLabels.SelectedItems[0].Tag;
if(relativeAddress >= 0) {
OnLabelSelected?.Invoke(relativeAddress, e);
}
}
}
private void mnuActions_Opening(object sender, CancelEventArgs e)
{
mnuDelete.Enabled = lstLabels.SelectedItems.Count > 0;
}
private void mnuDelete_Click(object sender, EventArgs e)
{
foreach(ListViewItem item in lstLabels.SelectedItems) {
LabelManager.DeleteLabel((UInt32)item.SubItems[1].Tag);
}
}
}
}

View file

@ -0,0 +1,123 @@
<?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="contextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -30,6 +30,12 @@ namespace Mesen.GUI.Debugger
remove { this.ctrlTextbox.MouseDown -= value; }
}
public new event MouseEventHandler MouseDoubleClick
{
add { this.ctrlTextbox.MouseDoubleClick += value; }
remove { this.ctrlTextbox.MouseDoubleClick -= value; }
}
public event EventHandler FontSizeChanged;
public ctrlScrollableTextbox()

View file

@ -36,6 +36,7 @@ namespace Mesen.GUI.Debugger
private string[] _contentNotes = new string[0];
private string[] _compareContents = null;
private int[] _lineNumbers = new int[0];
private int[] _lineMargins = new int[0];
private string[] _lineNumberNotes = new string[0];
private Dictionary<int, int> _lineNumberIndex = new Dictionary<int,int>();
private Dictionary<int, LineProperties> _lineProperties = new Dictionary<int,LineProperties>();
@ -61,7 +62,13 @@ namespace Mesen.GUI.Debugger
{
set
{
_contents = value;
_contents = new string[value.Length];
_lineMargins = new int[value.Length];
for(int i = 0, len = value.Length; i < len; i++) {
_contents[i] = value[i].TrimStart();
_lineMargins[i] = (value[i].Length - _contents[i].Length) * 10;
}
_lineNumbers = new int[_contents.Length];
_lineNumberIndex.Clear();
for(int i = _contents.Length - 1; i >=0; i--) {
@ -317,7 +324,10 @@ namespace Mesen.GUI.Debugger
}
if(positionX >= 0 && lineIndex < _contents.Length) {
string text = _contents[lineIndex];
string text = _contents[lineIndex].Replace("\x2", "");
//Adjust background color highlights based on number of spaces in front of content
positionX -= _lineMargins[lineIndex];
int previousWidth = 0;
for(int i = 0, len = text.Length; i < len; i++) {
int width = (int)g.MeasureString(text.Substring(0, i+1), this.Font).Width;
@ -337,7 +347,7 @@ namespace Mesen.GUI.Debugger
int charIndex;
int lineIndex;
if(this.GetCharIndex(position, out charIndex, out lineIndex)) {
string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : _contents[lineIndex];
string text = ((useCompareText && _compareContents != null) ? _compareContents[lineIndex] : _contents[lineIndex]).Replace("\x2", "");
List<char> wordDelimiters = new List<char>(new char[] { ' ', ',', '|', ';', '(', ')' });
if(wordDelimiters.Contains(text[charIndex])) {
return string.Empty;
@ -444,8 +454,8 @@ namespace Mesen.GUI.Debugger
private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY)
{
string[] lineContent = _contents[currentLine].Split(new string[] { "||" }, StringSplitOptions.None);
string codeString = lineContent.Length > 0 ? lineContent[0] : "";
string[] lineContent = _contents[currentLine].Split('\x2');
string codeString = lineContent[0].TrimStart();
string addressString = lineContent.Length > 1 ? lineContent[1] : "";
string commentString = lineContent.Length > 2 ? lineContent[2] : "";
@ -454,13 +464,7 @@ namespace Mesen.GUI.Debugger
if(this.ShowLineNumbers) {
//Show line number
string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : "..";
float width = g.MeasureString(lineNumber, this.Font).Width;
g.DrawString(lineNumber, this.Font, Brushes.Gray, marginLeft - width, positionY);
if(this.ShowLineNumberNotes) {
width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont).Width;
g.DrawString(_lineNumberNotes[currentLine], _noteFont, Brushes.Gray, marginLeft - width, positionY+this.Font.Size+3);
}
this.DrawLineNumber(g, currentLine, marginLeft, positionY);
}
if(currentLine == this.CursorPosition) {
@ -468,6 +472,9 @@ namespace Mesen.GUI.Debugger
g.FillRectangle(Brushes.AliceBlue, marginLeft, positionY, this.ClientRectangle.Width - marginLeft, this.LineHeight);
}
//Adjust background color highlights based on number of spaces in front of content
marginLeft += _lineMargins[currentLine];
Color textColor = Color.Black;
if(_lineProperties.ContainsKey(currentLine)) {
//Process background, foreground, outline color and line symbol
@ -476,59 +483,100 @@ namespace Mesen.GUI.Debugger
if(lineProperties.BgColor.HasValue) {
using(Brush bgBrush = new SolidBrush(lineProperties.BgColor.Value)) {
g.FillRectangle(bgBrush, marginLeft + 1, positionY + 1, codeStringLength, this.LineHeight-1);
g.FillRectangle(bgBrush, marginLeft, positionY + 1, codeStringLength, this.LineHeight-1);
}
}
if(lineProperties.OutlineColor.HasValue) {
using(Pen outlinePen = new Pen(lineProperties.OutlineColor.Value, 1)) {
g.DrawRectangle(outlinePen, marginLeft + 1, positionY + 1, codeStringLength, this.LineHeight-1);
g.DrawRectangle(outlinePen, marginLeft, positionY + 1, codeStringLength, this.LineHeight-1);
}
}
if(lineProperties.Symbol.HasFlag(LineSymbol.Circle)) {
using(Brush brush = new SolidBrush(lineProperties.OutlineColor.Value)) {
g.FillEllipse(brush, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3);
}
}
if(lineProperties.Symbol.HasFlag(LineSymbol.CircleOutline) && lineProperties.OutlineColor.HasValue) {
using(Pen pen = new Pen(lineProperties.OutlineColor.Value, 1)) {
g.DrawEllipse(pen, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3);
}
}
if(lineProperties.Symbol.HasFlag(LineSymbol.Arrow)) {
int arrowY = positionY + this.LineHeight / 2 + 1;
using(Pen pen = new Pen(Color.Black, this.LineHeight * 0.33f)) {
//Outline
g.DrawLine(pen, 3, arrowY, 3 + this.LineHeight * 0.25f, arrowY);
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, 3 + this.LineHeight * 0.75f, arrowY);
//Fill
pen.Width-=2f;
pen.Color = lineProperties.BgColor.Value;
pen.EndCap = System.Drawing.Drawing2D.LineCap.Square;
g.DrawLine(pen, 4, arrowY, 3 + this.LineHeight * 0.25f - 1, arrowY);
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, this.LineHeight * 0.75f + 1, arrowY);
}
}
this.DrawLineSymbols(g, positionY, lineProperties);
}
this.DrawLineText(g, currentLine, marginLeft, positionY, codeString, addressString, commentString, codeStringLength, addressStringLength, textColor);
}
private void DrawLineNumber(Graphics g, int currentLine, int marginLeft, int positionY)
{
string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : "..";
float width = g.MeasureString(lineNumber, this.Font).Width;
g.DrawString(lineNumber, this.Font, Brushes.Gray, marginLeft - width, positionY);
if(this.ShowLineNumberNotes) {
width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont).Width;
g.DrawString(_lineNumberNotes[currentLine], _noteFont, Brushes.Gray, marginLeft - width, positionY+this.Font.Size+3);
}
}
private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, string codeString, string addressString, string commentString, float codeStringLength, float addressStringLength, Color textColor)
{
using(Brush fgBrush = new SolidBrush(textColor)) {
g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY);
if(codeString.StartsWith("--") && codeString.EndsWith("--")) {
//Draw block start
string text = codeString.Substring(2, codeString.Length - 4);
float textLength = g.MeasureString(text, this._noteFont).Width;
g.DrawString(text, this._noteFont, fgBrush, (marginLeft + this.Width - textLength) / 2, positionY);
g.DrawLine(Pens.Black, marginLeft, positionY+this.LineHeight-2, marginLeft+this.Width, positionY+this.LineHeight-2);
} else if(codeString.StartsWith("__") && codeString.EndsWith("__")) {
//Draw block end
string text = codeString.Substring(2, codeString.Length - 4);
float textLength = g.MeasureString(text, this._noteFont).Width;
g.DrawString(text, this._noteFont, fgBrush, (marginLeft + this.Width - textLength) / 2, positionY + 4);
g.DrawLine(Pens.Black, marginLeft, positionY+2, marginLeft+this.Width, positionY+2);
} else if(codeString.StartsWith("[[") && codeString.EndsWith("]]")) {
//Draw small centered text
string text = codeString.Substring(2, codeString.Length - 4);
float textLength = g.MeasureString(text, this._noteFont).Width;
g.DrawString(text, new Font(this._noteFont, FontStyle.Italic), fgBrush, (marginLeft + this.Width - textLength) / 2, positionY + 2);
} else {
//Draw line content
g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY);
using(Brush addressBrush = new SolidBrush(Color.SteelBlue)) {
g.DrawString(addressString, this.Font, addressBrush, marginLeft + codeStringLength, positionY);
}
using(Brush commentBrush = new SolidBrush(Color.DarkGreen)) {
g.DrawString(commentString, this.Font, commentBrush, Math.Max(marginLeft + 220, marginLeft + codeStringLength + addressStringLength), positionY);
}
using(Brush addressBrush = new SolidBrush(Color.SteelBlue)) {
g.DrawString(addressString, this.Font, addressBrush, marginLeft + codeStringLength - 4, positionY);
}
using(Brush commentBrush = new SolidBrush(Color.DarkGreen)) {
g.DrawString(commentString, this.Font, commentBrush, codeString.Length == 0 && addressString.Length == 0 ? marginLeft : Math.Max(marginLeft + 220, marginLeft + codeStringLength + addressStringLength), positionY);
}
if(this.ShowContentNotes) {
g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3);
if(this.ShowContentNotes) {
g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3);
}
this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY);
this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY);
}
}
}
private void DrawLineSymbols(Graphics g, int positionY, LineProperties lineProperties)
{
if(lineProperties.Symbol.HasFlag(LineSymbol.Circle)) {
using(Brush brush = new SolidBrush(lineProperties.OutlineColor.Value)) {
g.FillEllipse(brush, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3);
}
}
if(lineProperties.Symbol.HasFlag(LineSymbol.CircleOutline) && lineProperties.OutlineColor.HasValue) {
using(Pen pen = new Pen(lineProperties.OutlineColor.Value, 1)) {
g.DrawEllipse(pen, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3);
}
}
if(lineProperties.Symbol.HasFlag(LineSymbol.Arrow)) {
int arrowY = positionY + this.LineHeight / 2 + 1;
using(Pen pen = new Pen(Color.Black, this.LineHeight * 0.33f)) {
//Outline
g.DrawLine(pen, 3, arrowY, 3 + this.LineHeight * 0.25f, arrowY);
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, 3 + this.LineHeight * 0.75f, arrowY);
//Fill
pen.Width-=2f;
pen.Color = lineProperties.BgColor.Value;
pen.EndCap = System.Drawing.Drawing2D.LineCap.Square;
g.DrawLine(pen, 4, arrowY, 3 + this.LineHeight * 0.25f - 1, arrowY);
pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, this.LineHeight * 0.75f + 1, arrowY);
}
this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY);
this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY);
}
}

View file

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI.Debugger
{
public class CodeLabel
{
public UInt32 Address;
public string Label;
public string Comment;
}
public class LabelManager
{
private static Dictionary<UInt32, CodeLabel> _labels = new Dictionary<uint, CodeLabel>();
private static Dictionary<string, CodeLabel> _reverseLookup = new Dictionary<string, CodeLabel>();
public static event EventHandler OnLabelUpdated;
public static CodeLabel GetLabel(UInt32 address)
{
return _labels.ContainsKey(address) ? _labels[address] : null;
}
public static CodeLabel GetLabel(string label)
{
return _reverseLookup.ContainsKey(label) ? _reverseLookup[label] : null;
}
public static Dictionary<UInt32, CodeLabel> GetLabels()
{
return _labels;
}
public static bool SetLabel(UInt32 address, string label, string comment)
{
if(_reverseLookup.ContainsKey(label) && _reverseLookup[label].Address != address) {
//Label already exists
return false;
}
if(_labels.ContainsKey(address)) {
_reverseLookup.Remove(_labels[address].Label);
}
_labels[address] = new CodeLabel() { Address = address, Label = label, Comment = comment };
if(label.Length > 0) {
_reverseLookup[label] = _labels[address];
}
InteropEmu.DebugSetLabel(address, label, comment);
OnLabelUpdated?.Invoke(null, null);
return true;
}
public static void DeleteLabel(UInt32 address)
{
if(_labels.ContainsKey(address)) {
_reverseLookup.Remove(_labels[address].Label);
}
if(_labels.Remove(address)) {
InteropEmu.DebugSetLabel(address, string.Empty, string.Empty);
OnLabelUpdated?.Invoke(null, null);
}
}
}
}

View file

@ -43,6 +43,7 @@
this.ctrlDebuggerCodeSplit = new Mesen.GUI.Debugger.ctrlDebuggerCode();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.grpLabels = new System.Windows.Forms.GroupBox();
this.ctrlLabelList = new Mesen.GUI.Debugger.Controls.ctrlLabelList();
this.grpFunctions = new System.Windows.Forms.GroupBox();
this.ctrlFunctionList = new Mesen.GUI.Debugger.Controls.ctrlFunctionList();
this.tableLayoutPanel10 = new System.Windows.Forms.TableLayoutPanel();
@ -111,6 +112,7 @@
this.lblChrAnalysisResult = new System.Windows.Forms.ToolStripStatusLabel();
this.ctrlPpuMemoryMapping = new Mesen.GUI.Debugger.Controls.ctrlMemoryMapping();
this.ctrlCpuMemoryMapping = new Mesen.GUI.Debugger.Controls.ctrlMemoryMapping();
this.mnuShowOnlyDisassembledCode = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenuCode.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
this.splitContainer.Panel1.SuspendLayout();
@ -118,6 +120,7 @@
this.splitContainer.SuspendLayout();
this.tlpTop.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.grpLabels.SuspendLayout();
this.grpFunctions.SuspendLayout();
this.tableLayoutPanel10.SuspendLayout();
this.grpWatch.SuspendLayout();
@ -250,13 +253,23 @@
//
// grpLabels
//
this.grpLabels.Controls.Add(this.ctrlLabelList);
this.grpLabels.Dock = System.Windows.Forms.DockStyle.Fill;
this.grpLabels.Location = new System.Drawing.Point(3, 196);
this.grpLabels.Name = "grpLabels";
this.grpLabels.Size = new System.Drawing.Size(304, 188);
this.grpLabels.TabIndex = 6;
this.grpLabels.TabStop = false;
this.grpLabels.Text = "Labels and Comments";
this.grpLabels.Text = "Labels";
//
// ctrlLabelList
//
this.ctrlLabelList.Dock = System.Windows.Forms.DockStyle.Fill;
this.ctrlLabelList.Location = new System.Drawing.Point(3, 16);
this.ctrlLabelList.Name = "ctrlLabelList";
this.ctrlLabelList.Size = new System.Drawing.Size(298, 169);
this.ctrlLabelList.TabIndex = 0;
this.ctrlLabelList.OnLabelSelected += new System.EventHandler(this.ctrlLabelList_OnLabelSelected);
//
// grpFunctions
//
@ -393,12 +406,12 @@
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.Size = new System.Drawing.Size(100, 6);
this.toolStripMenuItem3.Size = new System.Drawing.Size(149, 6);
//
// mnuClose
//
this.mnuClose.Name = "mnuClose";
this.mnuClose.Size = new System.Drawing.Size(103, 22);
this.mnuClose.Size = new System.Drawing.Size(152, 22);
this.mnuClose.Text = "Close";
this.mnuClose.Click += new System.EventHandler(this.mnuClose_Click);
//
@ -598,7 +611,8 @@
this.mnuShowPpuMemoryMapping,
this.toolStripMenuItem6,
this.mnuPpuPartialDraw,
this.mnuShowEffectiveAddresses});
this.mnuShowEffectiveAddresses,
this.mnuShowOnlyDisassembledCode});
this.mnuOptions.Name = "mnuOptions";
this.mnuOptions.Size = new System.Drawing.Size(61, 20);
this.mnuOptions.Text = "Options";
@ -607,7 +621,7 @@
//
this.mnuSplitView.CheckOnClick = true;
this.mnuSplitView.Name = "mnuSplitView";
this.mnuSplitView.Size = new System.Drawing.Size(228, 22);
this.mnuSplitView.Size = new System.Drawing.Size(237, 22);
this.mnuSplitView.Text = "Split View";
this.mnuSplitView.Click += new System.EventHandler(this.mnuSplitView_Click);
//
@ -618,7 +632,7 @@
this.mnuDecreaseFontSize,
this.mnuResetFontSize});
this.fontSizeToolStripMenuItem.Name = "fontSizeToolStripMenuItem";
this.fontSizeToolStripMenuItem.Size = new System.Drawing.Size(228, 22);
this.fontSizeToolStripMenuItem.Size = new System.Drawing.Size(237, 22);
this.fontSizeToolStripMenuItem.Text = "Text Size";
//
// mnuIncreaseFontSize
@ -651,13 +665,13 @@
// toolStripMenuItem5
//
this.toolStripMenuItem5.Name = "toolStripMenuItem5";
this.toolStripMenuItem5.Size = new System.Drawing.Size(225, 6);
this.toolStripMenuItem5.Size = new System.Drawing.Size(234, 6);
//
// mnuShowCpuMemoryMapping
//
this.mnuShowCpuMemoryMapping.CheckOnClick = true;
this.mnuShowCpuMemoryMapping.Name = "mnuShowCpuMemoryMapping";
this.mnuShowCpuMemoryMapping.Size = new System.Drawing.Size(228, 22);
this.mnuShowCpuMemoryMapping.Size = new System.Drawing.Size(237, 22);
this.mnuShowCpuMemoryMapping.Text = "Show CPU Memory Mapping";
this.mnuShowCpuMemoryMapping.CheckedChanged += new System.EventHandler(this.mnuShowCpuMemoryMapping_CheckedChanged);
//
@ -665,20 +679,20 @@
//
this.mnuShowPpuMemoryMapping.CheckOnClick = true;
this.mnuShowPpuMemoryMapping.Name = "mnuShowPpuMemoryMapping";
this.mnuShowPpuMemoryMapping.Size = new System.Drawing.Size(228, 22);
this.mnuShowPpuMemoryMapping.Size = new System.Drawing.Size(237, 22);
this.mnuShowPpuMemoryMapping.Text = "Show PPU Memory Mapping";
this.mnuShowPpuMemoryMapping.CheckedChanged += new System.EventHandler(this.mnuShowPpuMemoryMapping_CheckedChanged);
//
// toolStripMenuItem6
//
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
this.toolStripMenuItem6.Size = new System.Drawing.Size(225, 6);
this.toolStripMenuItem6.Size = new System.Drawing.Size(234, 6);
//
// mnuPpuPartialDraw
//
this.mnuPpuPartialDraw.CheckOnClick = true;
this.mnuPpuPartialDraw.Name = "mnuPpuPartialDraw";
this.mnuPpuPartialDraw.Size = new System.Drawing.Size(228, 22);
this.mnuPpuPartialDraw.Size = new System.Drawing.Size(237, 22);
this.mnuPpuPartialDraw.Text = "Draw Partial Frame";
this.mnuPpuPartialDraw.Click += new System.EventHandler(this.mnuPpuPartialDraw_Click);
//
@ -686,9 +700,9 @@
//
this.mnuShowEffectiveAddresses.CheckOnClick = true;
this.mnuShowEffectiveAddresses.Name = "mnuShowEffectiveAddresses";
this.mnuShowEffectiveAddresses.Size = new System.Drawing.Size(228, 22);
this.mnuShowEffectiveAddresses.Size = new System.Drawing.Size(237, 22);
this.mnuShowEffectiveAddresses.Text = "Show Effective Addresses";
this.mnuShowEffectiveAddresses.Click += new System.EventHandler(this.mnuShowEffectiveAddresses_Click);
this.mnuShowEffectiveAddresses.CheckedChanged += new System.EventHandler(this.mnuShowEffectiveAddresses_CheckedChanged);
//
// toolsToolStripMenuItem
//
@ -852,6 +866,14 @@
this.ctrlCpuMemoryMapping.Text = "ctrlMemoryMapping1";
this.ctrlCpuMemoryMapping.Visible = false;
//
// mnuShowOnlyDisassembledCode
//
this.mnuShowOnlyDisassembledCode.CheckOnClick = true;
this.mnuShowOnlyDisassembledCode.Name = "mnuShowOnlyDisassembledCode";
this.mnuShowOnlyDisassembledCode.Size = new System.Drawing.Size(237, 22);
this.mnuShowOnlyDisassembledCode.Text = "Show Only Disassembled Code";
this.mnuShowOnlyDisassembledCode.CheckedChanged += new System.EventHandler(this.mnuShowOnlyDisassembledCode_CheckedChanged);
//
// frmDebugger
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -875,6 +897,7 @@
this.splitContainer.ResumeLayout(false);
this.tlpTop.ResumeLayout(false);
this.tableLayoutPanel1.ResumeLayout(false);
this.grpLabels.ResumeLayout(false);
this.grpFunctions.ResumeLayout(false);
this.tableLayoutPanel10.ResumeLayout(false);
this.grpWatch.ResumeLayout(false);
@ -971,5 +994,7 @@
private System.Windows.Forms.GroupBox grpLabels;
private System.Windows.Forms.GroupBox grpFunctions;
private Controls.ctrlFunctionList ctrlFunctionList;
private Controls.ctrlLabelList ctrlLabelList;
private System.Windows.Forms.ToolStripMenuItem mnuShowOnlyDisassembledCode;
}
}

View file

@ -35,6 +35,9 @@ namespace Mesen.GUI.Debugger
this.mnuShowEffectiveAddresses.Checked = ConfigManager.Config.DebugInfo.ShowEffectiveAddresses;
this.mnuShowCpuMemoryMapping.Checked = ConfigManager.Config.DebugInfo.ShowCpuMemoryMapping;
this.mnuShowPpuMemoryMapping.Checked = ConfigManager.Config.DebugInfo.ShowPpuMemoryMapping;
this.mnuShowOnlyDisassembledCode.Checked = ConfigManager.Config.DebugInfo.ShowOnlyDisassembledCode;
LabelManager.OnLabelUpdated += LabelManager_OnLabelUpdated;
_lastCodeWindow = ctrlDebuggerCode;
@ -94,6 +97,9 @@ namespace Mesen.GUI.Debugger
if(mnuShowEffectiveAddresses.Checked) {
flags |= DebuggerFlags.ShowEffectiveAddresses;
}
if(mnuShowOnlyDisassembledCode.Checked) {
flags |= DebuggerFlags.ShowOnlyDisassembledCode;
}
InteropEmu.DebugSetFlags(flags);
}
@ -129,13 +135,13 @@ namespace Mesen.GUI.Debugger
return mnuSplitView.Checked;
}
private void UpdateDebugger()
private void UpdateDebugger(bool updateActiveAddress = true)
{
ctrlFunctionList.UpdateFunctionList();
UpdateDebuggerFlags();
if(InteropEmu.DebugIsCodeChanged()) {
string code = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(InteropEmu.DebugGetCode());
string code = InteropEmu.DebugGetCode();
ctrlDebuggerCode.Code = code;
ctrlDebuggerCodeSplit.Code = code;
}
@ -149,8 +155,10 @@ namespace Mesen.GUI.Debugger
_lastCodeWindow = ctrlDebuggerCode;
}
ctrlDebuggerCode.SelectActiveAddress(state.CPU.DebugPC);
ctrlDebuggerCodeSplit.SetActiveAddress(state.CPU.DebugPC);
if(updateActiveAddress) {
ctrlDebuggerCode.SelectActiveAddress(state.CPU.DebugPC);
ctrlDebuggerCodeSplit.SetActiveAddress(state.CPU.DebugPC);
}
RefreshBreakpoints();
ctrlConsoleStatus.UpdateStatus(ref state);
@ -282,7 +290,7 @@ namespace Mesen.GUI.Debugger
ConfigManager.Config.DebugInfo.SplitView = this.mnuSplitView.Checked;
ConfigManager.ApplyChanges();
UpdateDebugger();
UpdateDebugger(false);
}
private void mnuMemoryViewer_Click(object sender, EventArgs e)
@ -423,11 +431,18 @@ namespace Mesen.GUI.Debugger
ConfigManager.ApplyChanges();
}
private void mnuShowEffectiveAddresses_Click(object sender, EventArgs e)
private void mnuShowEffectiveAddresses_CheckedChanged(object sender, EventArgs e)
{
ConfigManager.Config.DebugInfo.ShowEffectiveAddresses = mnuShowEffectiveAddresses.Checked;
ConfigManager.ApplyChanges();
UpdateDebugger();
UpdateDebugger(false);
}
private void mnuShowOnlyDisassembledCode_CheckedChanged(object sender, EventArgs e)
{
ConfigManager.Config.DebugInfo.ShowOnlyDisassembledCode = mnuShowOnlyDisassembledCode.Checked;
ConfigManager.ApplyChanges();
UpdateDebugger(false);
}
private void mnuShowCpuMemoryMapping_CheckedChanged(object sender, EventArgs e)
@ -458,5 +473,16 @@ namespace Mesen.GUI.Debugger
{
_lastCodeWindow.ScrollToLineNumber((Int32)relativeAddress);
}
private void LabelManager_OnLabelUpdated(object sender, EventArgs e)
{
UpdateDebugger(false);
ctrlLabelList.UpdateLabelList();
}
private void ctrlLabelList_OnLabelSelected(object relativeAddress, EventArgs e)
{
_lastCodeWindow.ScrollToLineNumber((Int32)relativeAddress);
}
}
}

125
GUI.NET/Debugger/frmEditLabel.Designer.cs generated Normal file
View file

@ -0,0 +1,125 @@
namespace Mesen.GUI.Debugger
{
partial class frmEditLabel
{
/// <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 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.txtComment = new System.Windows.Forms.TextBox();
this.lblLabel = new System.Windows.Forms.Label();
this.lblComment = new System.Windows.Forms.Label();
this.txtLabel = new System.Windows.Forms.TextBox();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 165);
this.baseConfigPanel.Size = new System.Drawing.Size(352, 29);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.Controls.Add(this.txtComment, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.lblLabel, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.lblComment, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.txtLabel, 1, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(352, 165);
this.tableLayoutPanel1.TabIndex = 2;
//
// txtComment
//
this.txtComment.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtComment.Location = new System.Drawing.Point(63, 29);
this.txtComment.Multiline = true;
this.txtComment.Name = "txtComment";
this.txtComment.Size = new System.Drawing.Size(286, 133);
this.txtComment.TabIndex = 3;
//
// lblLabel
//
this.lblLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblLabel.AutoSize = true;
this.lblLabel.Location = new System.Drawing.Point(3, 6);
this.lblLabel.Name = "lblLabel";
this.lblLabel.Size = new System.Drawing.Size(36, 13);
this.lblLabel.TabIndex = 0;
this.lblLabel.Text = "Label:";
//
// lblComment
//
this.lblComment.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblComment.AutoSize = true;
this.lblComment.Location = new System.Drawing.Point(3, 89);
this.lblComment.Name = "lblComment";
this.lblComment.Size = new System.Drawing.Size(54, 13);
this.lblComment.TabIndex = 1;
this.lblComment.Text = "Comment:";
//
// txtLabel
//
this.txtLabel.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtLabel.Location = new System.Drawing.Point(63, 3);
this.txtLabel.Name = "txtLabel";
this.txtLabel.Size = new System.Drawing.Size(286, 20);
this.txtLabel.TabIndex = 2;
//
// frmEditLabel
//
this.AcceptButton = null;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(352, 194);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "frmEditLabel";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Label";
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.Controls.SetChildIndex(this.tableLayoutPanel1, 0);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.TextBox txtComment;
private System.Windows.Forms.Label lblLabel;
private System.Windows.Forms.Label lblComment;
private System.Windows.Forms.TextBox txtLabel;
}
}

View file

@ -0,0 +1,54 @@
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;
using Mesen.GUI.Forms;
namespace Mesen.GUI.Debugger
{
public partial class frmEditLabel : BaseConfigForm
{
UInt32 _address;
public frmEditLabel(UInt32 address, CodeLabel label)
{
InitializeComponent();
_address = address;
this.Text = "Edit Label: $" + address.ToString("X4");
Entity = label;
AddBinding("Label", txtLabel);
AddBinding("Comment", txtComment);
}
protected override bool ValidateInput()
{
CodeLabel existingLabel = LabelManager.GetLabel(txtLabel.Text);
return (existingLabel == null || existingLabel.Address == _address)
&& !txtComment.Text.Contains('\x1') && !txtComment.Text.Contains('\x2')
&& !txtLabel.Text.Contains('\x1') && !txtLabel.Text.Contains('\x2');
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
txtLabel.Text = txtLabel.Text.Replace("$", "");
base.OnFormClosed(e);
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if(keyData == (Keys.Control | Keys.Enter)) {
this.DialogResult = DialogResult.OK;
this.Close();
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
}

View file

@ -0,0 +1,123 @@
<?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>
</root>

View file

@ -278,6 +278,12 @@
<Compile Include="Debugger\Controls\ctrlAddressList.Designer.cs">
<DependentUpon>ctrlAddressList.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlLabelList.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Debugger\Controls\ctrlLabelList.Designer.cs">
<DependentUpon>ctrlLabelList.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlFunctionList.cs">
<SubType>UserControl</SubType>
</Compile>
@ -343,6 +349,12 @@
<Compile Include="Debugger\Controls\ctrlWatch.Designer.cs">
<DependentUpon>ctrlWatch.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\frmEditLabel.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\frmEditLabel.Designer.cs">
<DependentUpon>frmEditLabel.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\frmBreakpoint.cs">
<SubType>Form</SubType>
</Compile>
@ -379,6 +391,7 @@
<Compile Include="Debugger\frmTraceLogger.Designer.cs">
<DependentUpon>frmTraceLogger.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\LabelManager.cs" />
<Compile Include="Forms\BaseConfigForm.Designer.cs">
<DependentUpon>BaseConfigForm.cs</DependentUpon>
</Compile>
@ -582,6 +595,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlAddressList.resx">
<DependentUpon>ctrlAddressList.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlLabelList.resx">
<DependentUpon>ctrlLabelList.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlFunctionList.resx">
<DependentUpon>ctrlFunctionList.cs</DependentUpon>
</EmbeddedResource>
@ -612,6 +628,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlWatch.resx">
<DependentUpon>ctrlWatch.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmEditLabel.resx">
<DependentUpon>frmEditLabel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmBreakpoint.resx">
<DependentUpon>frmBreakpoint.cs</DependentUpon>
</EmbeddedResource>

View file

@ -166,6 +166,7 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void DebugSetFlags(DebuggerFlags flags);
[DllImport(DLLPath)] public static extern void DebugGetState(ref DebugState state);
[DllImport(DLLPath)] public static extern void DebugSetBreakpoints([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]InteropBreakpoint[] breakpoints, UInt32 length);
[DllImport(DLLPath)] public static extern void DebugSetLabel(UInt32 address, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string label, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string comment);
[DllImport(DLLPath)] public static extern void DebugStep(UInt32 count);
[DllImport(DLLPath)] public static extern void DebugPpuStep(UInt32 count);
[DllImport(DLLPath)] public static extern void DebugStepCycles(UInt32 count);
@ -173,9 +174,9 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void DebugStepOver();
[DllImport(DLLPath)] public static extern void DebugRun();
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsCodeChanged();
[DllImport(DLLPath)] public static extern IntPtr DebugGetCode();
[DllImport(DLLPath)] public static extern Byte DebugGetMemoryValue(UInt32 addr);
[DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 addr);
[DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 absoluteAddr);
[DllImport(DLLPath)] public static extern Int32 DebugGetAbsoluteAddress(UInt32 relativeAddr);
[DllImport(DLLPath)] public static extern void DebugSetNextStatement(UInt16 addr);
[DllImport(DLLPath)] public static extern Int32 DebugEvaluateExpression([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string expression, out EvalResultType resultType);
@ -187,6 +188,9 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void DebugGetCdlRatios(ref CdlRatios ratios);
[DllImport(DLLPath)] public static extern void DebugResetCdlLog();
[DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper();
public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); }
[DllImport(DLLPath, EntryPoint="DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer);
public static byte[] DebugGetMemoryState(DebugMemoryType type)
{
@ -735,7 +739,8 @@ namespace Mesen.GUI
{
None = 0,
PpuPartialDraw = 1,
ShowEffectiveAddresses = 2
ShowEffectiveAddresses = 2,
ShowOnlyDisassembledCode = 4,
}
public struct InteropRomInfo

View file

@ -31,6 +31,7 @@ extern "C"
DllExport void __stdcall DebugGetState(DebugState *state) { GetDebugger()->GetState(state); }
DllExport void __stdcall DebugSetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->SetBreakpoints(breakpoints, length); }
DllExport void __stdcall DebugSetLabel(uint32_t address, char* label, char* comment) { GetDebugger()->SetLabel(address, label, comment); }
DllExport void __stdcall DebugRun() { GetDebugger()->Run(); }
DllExport void __stdcall DebugStep(uint32_t count) { GetDebugger()->Step(count); }
@ -55,6 +56,7 @@ extern "C"
DllExport uint8_t __stdcall DebugGetMemoryValue(uint32_t addr) { return GetDebugger()->GetMemoryValue(addr); }
DllExport int32_t __stdcall DebugGetRelativeAddress(uint32_t addr) { return GetDebugger()->GetRelativeAddress(addr); }
DllExport int32_t __stdcall DebugGetAbsoluteAddress(uint32_t addr) { return GetDebugger()->GetAbsoluteAddress(addr); }
DllExport bool __stdcall DebugLoadCdlFile(char* cdlFilepath) { return GetDebugger()->LoadCdlFile(cdlFilepath); }
DllExport bool __stdcall DebugSaveCdlFile(char* cdlFilepath) { return GetDebugger()->SaveCdlFile(cdlFilepath); }