Debugger: Improved CA/CC65 integration

-Auto-detect all function entry points
-Auto-detect all jump targets
-Fixed a bug where jump targets were being disassembled when loading a CDL file, potentially causing data to be shown as code
-Fixed some valid data block being shown as unidentified data
This commit is contained in:
Sour 2019-01-02 22:40:21 -05:00
parent a3a91ede59
commit 2c9d6aabd7
14 changed files with 277 additions and 150 deletions

View file

@ -151,6 +151,11 @@ bool CodeDataLogger::IsCode(uint32_t absoluteAddr)
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Code) == (uint8_t)CdlPrgFlags::Code; return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Code) == (uint8_t)CdlPrgFlags::Code;
} }
bool CodeDataLogger::IsJumpTarget(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::JumpTarget) == (uint8_t)CdlPrgFlags::JumpTarget;
}
bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr) bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr)
{ {
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::SubEntryPoint) == (uint8_t)CdlPrgFlags::SubEntryPoint; return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::SubEntryPoint) == (uint8_t)CdlPrgFlags::SubEntryPoint;

View file

@ -11,9 +11,16 @@ enum class CdlPrgFlags
None = 0x00, None = 0x00,
Code = 0x01, Code = 0x01,
Data = 0x02, Data = 0x02,
IndirectCode = 0x10,
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
//Repurposed to mean the address is the target of a jump instruction
JumpTarget = 0x10,
IndirectData = 0x20, IndirectData = 0x20,
PcmData = 0x40, PcmData = 0x40,
//Unused bit in original CDL spec
//Used to denote that the byte is the start of function (sub)
SubEntryPoint = 0x80 SubEntryPoint = 0x80
}; };
@ -74,6 +81,7 @@ public:
CdlRatios GetRatios(); CdlRatios GetRatios();
bool IsCode(uint32_t absoluteAddr); bool IsCode(uint32_t absoluteAddr);
bool IsJumpTarget(uint32_t absoluteAddr);
bool IsSubEntryPoint(uint32_t absoluteAddr); bool IsSubEntryPoint(uint32_t absoluteAddr);
bool IsData(uint32_t absoluteAddr); bool IsData(uint32_t absoluteAddr);
bool IsRead(uint32_t absoluteAddr); bool IsRead(uint32_t absoluteAddr);

View file

@ -44,5 +44,5 @@ void CodeRunner::WriteRAM(uint16_t addr, uint8_t value)
DisassemblyInfo CodeRunner::GetDisassemblyInfo(uint16_t cpuAddress) DisassemblyInfo CodeRunner::GetDisassemblyInfo(uint16_t cpuAddress)
{ {
return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false, false); return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false);
} }

View file

@ -765,14 +765,18 @@ bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uin
bool isSubEntryPoint = _lastInstruction == 0x20; //Previous instruction was a JSR bool isSubEntryPoint = _lastInstruction == 0x20; //Previous instruction was a JSR
if(absoluteAddr >= 0) { if(absoluteAddr >= 0) {
bool isJumpTarget = _disassembler->IsJump(_lastInstruction);
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code); _codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code);
if(isSubEntryPoint) { if(isSubEntryPoint) {
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::SubEntryPoint); _codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::SubEntryPoint);
_functionEntryPoints.emplace(absoluteAddr); _functionEntryPoints.emplace(absoluteAddr);
} else if(isJumpTarget) {
//Only mark as jump target if not marked as sub entry point
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::JumpTarget);
} }
} }
_disassembler->BuildCache(addressInfo, addr, isSubEntryPoint, false); _disassembler->BuildCache(addressInfo, addr, isSubEntryPoint, true);
ProcessStepConditions(addr); ProcessStepConditions(addr);
@ -1142,11 +1146,6 @@ const char* Debugger::GetCode(uint32_t &length)
} }
} }
void Debugger::GetJumpTargets(bool* jumpTargets)
{
_disassembler->GetJumpTargets(jumpTargets);
}
int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type) int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type)
{ {
switch(type) { switch(type) {

View file

@ -170,8 +170,6 @@ public:
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length); void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
void ProcessMarkedBreakpoints(BreakpointType type, OperationInfo &operationInfo);
shared_ptr<LabelManager> GetLabelManager(); shared_ptr<LabelManager> GetLabelManager();
void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount); void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount);
@ -222,8 +220,6 @@ public:
void GenerateCodeOutput(); void GenerateCodeOutput();
const char* GetCode(uint32_t &length); const char* GetCode(uint32_t &length);
void GetJumpTargets(bool* jumpTargets);
int32_t GetRelativeAddress(uint32_t addr, AddressType type); int32_t GetRelativeAddress(uint32_t addr, AddressType type);
int32_t GetAbsoluteAddress(uint32_t addr); int32_t GetAbsoluteAddress(uint32_t addr);
int32_t GetAbsoluteChrAddress(uint32_t addr); int32_t GetAbsoluteChrAddress(uint32_t addr);

View file

@ -146,12 +146,30 @@ bool Disassembler::IsUnofficialOpCode(uint8_t opCode)
bool Disassembler::IsJump(uint8_t opCode) bool Disassembler::IsJump(uint8_t opCode)
{ {
return opCode == 0x10 || opCode == 0x30|| opCode == 0x50 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x20; return (
opCode == 0x10 || //BPL
opCode == 0x30 || //BMI
opCode == 0x50 || //BVC
opCode == 0x70 || //BVS
opCode == 0x90 || //BCC
opCode == 0xB0 || //BCS
opCode == 0xD0 || //BNE
opCode == 0xF0 || //BEQ
opCode == 0x4C || //JMP (Absolute)
opCode == 0x6C || //JMP (Indirect)
opCode == 0x20 //JSR
);
} }
bool Disassembler::IsUnconditionalJump(uint8_t opCode) bool Disassembler::IsUnconditionalJump(uint8_t opCode)
{ {
return opCode == 0x40 || opCode == 0x60 || opCode == 0x6C || opCode == 0x4C || opCode == 0x20; return (
opCode == 0x40 || //RTI
opCode == 0x60 || //RTS
opCode == 0x6C || //JMP (Indirect)
opCode == 0x4C || //JMP (Absolute)
opCode == 0x20 //JSR
);
} }
void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache) void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache)
@ -188,7 +206,7 @@ void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &si
} }
uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bool isSubEntryPoint, bool isJumpTarget) uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bool isSubEntryPoint, bool processJumps)
{ {
uint32_t mask = info.Type == AddressType::InternalRam ? 0x7FF : 0xFFFFFFFF; uint32_t mask = info.Type == AddressType::InternalRam ? 0x7FF : 0xFFFFFFFF;
@ -203,9 +221,8 @@ uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bo
if(!disInfo) { if(!disInfo) {
while(absoluteAddr < (int32_t)size && !(*cache)[absoluteAddr]) { while(absoluteAddr < (int32_t)size && !(*cache)[absoluteAddr]) {
bool isJump = IsUnconditionalJump(source[absoluteAddr]); bool isJump = IsUnconditionalJump(source[absoluteAddr]);
disInfo = new DisassemblyInfo(source+absoluteAddr, isSubEntryPoint, isJumpTarget); disInfo = new DisassemblyInfo(source+absoluteAddr, isSubEntryPoint);
isSubEntryPoint = false; isSubEntryPoint = false;
isJumpTarget = false;
(*cache)[absoluteAddr] = shared_ptr<DisassemblyInfo>(disInfo); (*cache)[absoluteAddr] = shared_ptr<DisassemblyInfo>(disInfo);
@ -219,22 +236,17 @@ uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bo
if(isSubEntryPoint) { if(isSubEntryPoint) {
disInfo->SetSubEntryPoint(); disInfo->SetSubEntryPoint();
} }
if(isJumpTarget) {
disInfo->SetJumpTarget();
}
uint8_t opCode = source[absoluteAddr]; uint8_t opCode = source[absoluteAddr];
if(IsJump(opCode)) { if(processJumps && IsJump(opCode)) {
uint16_t jumpDest = disInfo->GetOpAddr(cpuAddress); uint16_t jumpDest = disInfo->GetJumpDestination(cpuAddress, _memoryManager);
if(jumpDest != cpuAddress) { if(jumpDest != cpuAddress) {
AddressTypeInfo addressInfo; AddressTypeInfo addressInfo;
_debugger->GetAbsoluteAddressAndType(jumpDest, &addressInfo); _debugger->GetAbsoluteAddressAndType(jumpDest, &addressInfo);
const uint8_t jsrCode = 0x20; constexpr uint8_t jsrCode = 0x20;
if(addressInfo.Address >= 0) { if(addressInfo.Address >= 0) {
BuildCache(addressInfo, jumpDest, opCode == jsrCode, true); BuildCache(addressInfo, jumpDest, opCode == jsrCode, false);
} else {
disInfo->SetJumpTarget();
} }
} }
} }
@ -539,7 +551,7 @@ string Disassembler::GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uin
isVerifiedData = addressInfo.Type == AddressType::PrgRom && cdl->IsData(addr&mask); isVerifiedData = addressInfo.Type == AddressType::PrgRom && cdl->IsData(addr&mask);
if(!info && ((disassembleUnidentifiedData && !isVerifiedData) || (disassembleVerifiedData && isVerifiedData))) { if(!info && ((disassembleUnidentifiedData && !isVerifiedData) || (disassembleVerifiedData && isVerifiedData))) {
dataType = isVerifiedData ? DataType::VerifiedData : DataType::UnidentifiedData; dataType = isVerifiedData ? DataType::VerifiedData : DataType::UnidentifiedData;
tmpInfo->Initialize(source + (addr & mask), false, false); tmpInfo->Initialize(source + (addr & mask), false);
info = tmpInfo; info = tmpInfo;
} else if(info) { } else if(info) {
dataType = DataType::VerifiedCode; dataType = DataType::VerifiedCode;
@ -686,14 +698,3 @@ DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressTypeInfo &info)
return DisassemblyInfo(); return DisassemblyInfo();
} }
} }
void Disassembler::GetJumpTargets(bool* jumpTargets)
{
memset(jumpTargets, 0, _disassembleCache.size());
for(size_t i = 0, len = _disassembleCache.size(); i < len; i++) {
shared_ptr<DisassemblyInfo> disInfo = _disassembleCache[i];
if(disInfo && disInfo->IsJumpTarget()) {
jumpTargets[i] = true;
}
}
}

View file

@ -28,8 +28,6 @@ private:
vector<shared_ptr<DisassemblyInfo>> _disassembleSaveRamCache; vector<shared_ptr<DisassemblyInfo>> _disassembleSaveRamCache;
vector<shared_ptr<DisassemblyInfo>> _disassembleMemoryCache; vector<shared_ptr<DisassemblyInfo>> _disassembleMemoryCache;
bool IsJump(uint8_t opCode);
bool IsUnconditionalJump(uint8_t opCode);
void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1, DataType dataType = DataType::VerifiedCode, char memoryType = ' '); void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1, DataType dataType = DataType::VerifiedCode, char memoryType = ' ');
void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, DataType dataType, bool isIndented, char memoryType = ' '); void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, DataType dataType, bool isIndented, char memoryType = ' ');
void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector); void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector);
@ -43,16 +41,16 @@ public:
void BuildOpCodeTables(bool useLowerCase); void BuildOpCodeTables(bool useLowerCase);
void Reset(); void Reset();
uint32_t BuildCache(AddressTypeInfo &info, uint16_t memoryAddr, bool isSubEntryPoint, bool isJumpTarget); uint32_t BuildCache(AddressTypeInfo &info, uint16_t memoryAddr, bool isSubEntryPoint, bool processJumps);
void InvalidateCache(AddressTypeInfo &info); void InvalidateCache(AddressTypeInfo &info);
bool IsUnofficialOpCode(uint8_t opCode); bool IsUnofficialOpCode(uint8_t opCode);
bool IsJump(uint8_t opCode);
bool IsUnconditionalJump(uint8_t opCode);
string GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uint16_t memoryAddr, State& cpuState, shared_ptr<MemoryManager> memoryManager, shared_ptr<LabelManager> labelManager); string GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uint16_t memoryAddr, State& cpuState, shared_ptr<MemoryManager> memoryManager, shared_ptr<LabelManager> labelManager);
DisassemblyInfo GetDisassemblyInfo(AddressTypeInfo &info); DisassemblyInfo GetDisassemblyInfo(AddressTypeInfo &info);
void GetJumpTargets(bool* jumpTargets);
void RebuildPrgRomCache(uint32_t absoluteAddr, int32_t length); void RebuildPrgRomCache(uint32_t absoluteAddr, int32_t length);
}; };

View file

@ -33,9 +33,9 @@ DisassemblyInfo::DisassemblyInfo()
{ {
} }
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget) DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint)
{ {
Initialize(opPointer, isSubEntryPoint, isJumpTarget); Initialize(opPointer, isSubEntryPoint);
} }
void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage) void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage)
@ -148,10 +148,9 @@ uint16_t DisassemblyInfo::GetOpAddr(uint16_t memoryAddr)
return opAddr; return opAddr;
} }
void DisassemblyInfo::Initialize(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget) void DisassemblyInfo::Initialize(uint8_t* opPointer, bool isSubEntryPoint)
{ {
_isSubEntryPoint = isSubEntryPoint; _isSubEntryPoint = isSubEntryPoint;
_isJumpTarget = isJumpTarget;
uint8_t opCode = *opPointer; uint8_t opCode = *opPointer;
_opSize = DisassemblyInfo::OPSize[opCode]; _opSize = DisassemblyInfo::OPSize[opCode];
@ -169,11 +168,6 @@ void DisassemblyInfo::SetSubEntryPoint()
_isSubEntryPoint = true; _isSubEntryPoint = true;
} }
void DisassemblyInfo::SetJumpTarget()
{
_isJumpTarget = true;
}
int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryManager) int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryManager)
{ {
int32_t address = -1; int32_t address = -1;
@ -192,6 +186,22 @@ int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryMa
} }
} }
int32_t DisassemblyInfo::GetJumpDestination(uint16_t pc, MemoryManager* memoryManager)
{
int32_t address = -1;
if(_opMode == AddrMode::Rel || _opMode == AddrMode::Abs) {
address = GetOpAddr(pc);
} else if(_opMode == AddrMode::Ind) {
address = GetIndirectJumpDestination(memoryManager);
}
if(address >= 0 && address <= 0xFFFF) {
return memoryManager->DebugRead(address);
} else {
return -1;
}
}
uint16_t DisassemblyInfo::GetIndirectJumpDestination(MemoryManager* memoryManager) uint16_t DisassemblyInfo::GetIndirectJumpDestination(MemoryManager* memoryManager)
{ {
uint16_t addr = _byteCode[1] | (_byteCode[2] << 8); uint16_t addr = _byteCode[1] | (_byteCode[2] << 8);
@ -319,8 +329,3 @@ bool DisassemblyInfo::IsSubExitPoint()
{ {
return _isSubExitPoint; return _isSubExitPoint;
} }
bool DisassemblyInfo::IsJumpTarget()
{
return _isJumpTarget;
}

View file

@ -17,7 +17,6 @@ public:
private: private:
uint8_t _byteCode[3]; uint8_t _byteCode[3];
bool _isJumpTarget = false;
bool _isSubEntryPoint = false; bool _isSubEntryPoint = false;
bool _isSubExitPoint = false; bool _isSubExitPoint = false;
uint32_t _opSize = 0; uint32_t _opSize = 0;
@ -25,18 +24,17 @@ private:
public: public:
DisassemblyInfo(); DisassemblyInfo();
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget); DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint);
void Initialize(uint8_t * opPointer, bool isSubEntryPoint, bool isJumpTarget); void Initialize(uint8_t * opPointer, bool isSubEntryPoint);
void SetSubEntryPoint(); void SetSubEntryPoint();
void SetJumpTarget();
int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager); int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager);
void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager); void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager);
int32_t GetMemoryValue(State& cpuState, MemoryManager* memoryManager); int32_t GetMemoryValue(State& cpuState, MemoryManager* memoryManager);
int32_t GetJumpDestination(uint16_t pc, MemoryManager* memoryManager);
uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager); uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager);
void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage); void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage);
void GetByteCode(string &out); void GetByteCode(string &out);
@ -45,6 +43,5 @@ public:
bool IsSubEntryPoint(); bool IsSubEntryPoint();
bool IsSubExitPoint(); bool IsSubExitPoint();
bool IsJumpTarget();
}; };

View file

@ -39,7 +39,7 @@ namespace Mesen.GUI.Debugger
private static Regex _segmentRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+)", RegexOptions.Compiled); private static Regex _segmentRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+)", RegexOptions.Compiled);
private static Regex _segmentPrgRomRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+),.*ooffs=([0-9]+)", RegexOptions.Compiled); private static Regex _segmentPrgRomRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+),.*ooffs=([0-9]+)", RegexOptions.Compiled);
private static Regex _lineRegex = new Regex("^line\tid=([0-9]+),.*file=([0-9]+),.*line=([0-9]+)(,.*type=([0-9]+)){0,1}(,.*span=([0-9]+)){0,1}", RegexOptions.Compiled); private static Regex _lineRegex = new Regex("^line\tid=([0-9]+),.*file=([0-9]+),.*line=([0-9]+)(,.*type=([0-9]+)){0,1}(,.*span=([0-9+]+)){0,1}", RegexOptions.Compiled);
private static Regex _fileRegex = new Regex("^file\tid=([0-9]+),.*name=\"([^\"]+)\"", RegexOptions.Compiled); private static Regex _fileRegex = new Regex("^file\tid=([0-9]+),.*name=\"([^\"]+)\"", RegexOptions.Compiled);
private static Regex _spanRegex = new Regex("^span\tid=([0-9]+),.*seg=([0-9]+),.*start=([0-9]+),.*size=([0-9]+)(,.*type=([0-9]+)){0,1}", RegexOptions.Compiled); private static Regex _spanRegex = new Regex("^span\tid=([0-9]+),.*seg=([0-9]+),.*start=([0-9]+),.*size=([0-9]+)(,.*type=([0-9]+)){0,1}", RegexOptions.Compiled);
private static Regex _symbolRegex = new Regex("^sym\tid=([0-9]+).*name=\"([0-9a-zA-Z@_-]+)\"(,.*def=([0-9+]+)){0,1}(,.*ref=([0-9+]+)){0,1}(,.*val=0x([0-9a-fA-F]+)){0,1}(,.*seg=([0-9]+)){0,1}(,.*exp=([0-9]+)){0,1}", RegexOptions.Compiled); private static Regex _symbolRegex = new Regex("^sym\tid=([0-9]+).*name=\"([0-9a-zA-Z@_-]+)\"(,.*def=([0-9+]+)){0,1}(,.*ref=([0-9+]+)){0,1}(,.*val=0x([0-9a-fA-F]+)){0,1}(,.*seg=([0-9]+)){0,1}(,.*exp=([0-9]+)){0,1}", RegexOptions.Compiled);
@ -51,6 +51,7 @@ namespace Mesen.GUI.Debugger
private static Regex _cPreviousLinesRegex = new Regex("^\\s*//(.*)", RegexOptions.Compiled); private static Regex _cPreviousLinesRegex = new Regex("^\\s*//(.*)", RegexOptions.Compiled);
private Dictionary<int, LineInfo> _linesByPrgAddress = new Dictionary<int, LineInfo>(); private Dictionary<int, LineInfo> _linesByPrgAddress = new Dictionary<int, LineInfo>();
private Dictionary<int, LineInfo[]> _linesByFile = new Dictionary<int, LineInfo[]>();
private Dictionary<string, int> _prgAddressByLine = new Dictionary<string, int>(); private Dictionary<string, int> _prgAddressByLine = new Dictionary<string, int>();
public Dictionary<int, FileInfo> Files { get { return _files; } } public Dictionary<int, FileInfo> Files { get { return _files; } }
@ -66,6 +67,17 @@ namespace Mesen.GUI.Debugger
return -1; return -1;
} }
private int GetPrgAddress(SpanInfo span)
{
SegmentInfo segment;
if(_segments.TryGetValue(span.SegmentID, out segment)) {
if(!segment.IsRam && span.Size != segment.Size) {
return span.Offset + segment.FileOffset - _headerSize;
}
}
return -1;
}
public LineInfo GetSourceCodeLineInfo(int prgRomAddress) public LineInfo GetSourceCodeLineInfo(int prgRomAddress)
{ {
LineInfo line; LineInfo line;
@ -110,24 +122,49 @@ namespace Mesen.GUI.Debugger
return null; return null;
} }
private SpanInfo GetSymbolDefinitionSpan(SymbolInfo symbol)
{
foreach(int definition in symbol.Definitions) {
LineInfo definitionLine;
FileInfo file;
if(_lines.TryGetValue(definition, out definitionLine)) {
if(_files.TryGetValue(definitionLine.FileID, out file)) {
int lineNumber = definitionLine.LineNumber;
while(!(definitionLine?.SpanIDs.Count > 0) && lineNumber < file.Data.Length - 1) {
//Definition line contains no code, try the next line
lineNumber++;
definitionLine = _linesByFile[file.ID][lineNumber];
}
if(definitionLine != null && definitionLine.SpanIDs.Count > 0) {
SpanInfo span;
if(_spans.TryGetValue(definitionLine.SpanIDs[0], out span)) {
return span;
}
}
}
}
}
return null;
}
private SymbolInfo GetMatchingSymbol(SymbolInfo symbol, int rangeStart, int rangeEnd) private SymbolInfo GetMatchingSymbol(SymbolInfo symbol, int rangeStart, int rangeEnd)
{ {
foreach(int reference in symbol.References) { foreach(int reference in symbol.References) {
LineInfo line = _lines[reference]; LineInfo line = _lines[reference];
if(line.SpanID == null) {
continue;
}
SpanInfo span = _spans[line.SpanID.Value]; foreach(int spanID in line.SpanIDs) {
SegmentInfo seg = _segments[span.SegmentID]; SpanInfo span = _spans[spanID];
SegmentInfo seg = _segments[span.SegmentID];
if(!seg.IsRam) { if(!seg.IsRam) {
int spanPrgOffset = seg.FileOffset - _headerSize + span.Offset; int spanPrgOffset = seg.FileOffset - _headerSize + span.Offset;
if(rangeStart < spanPrgOffset + span.Size && rangeEnd >= spanPrgOffset) { if(rangeStart < spanPrgOffset + span.Size && rangeEnd >= spanPrgOffset) {
if(symbol.ExportSymbolID != null && symbol.Address == null) { if(symbol.ExportSymbolID != null && symbol.Address == null) {
return _symbols[symbol.ExportSymbolID.Value]; return _symbols[symbol.ExportSymbolID.Value];
} else { } else {
return symbol; return symbol;
}
} }
} }
} }
@ -246,9 +283,23 @@ namespace Mesen.GUI.Debugger
FileID = Int32.Parse(match.Groups[2].Value), FileID = Int32.Parse(match.Groups[2].Value),
LineNumber = Int32.Parse(match.Groups[3].Value) - 1, LineNumber = Int32.Parse(match.Groups[3].Value) - 1,
Type = match.Groups[5].Success ? (LineType)Int32.Parse(match.Groups[5].Value) : LineType.Assembly, Type = match.Groups[5].Success ? (LineType)Int32.Parse(match.Groups[5].Value) : LineType.Assembly,
SpanID = match.Groups[7].Success ? (int?)Int32.Parse(match.Groups[7].Value) : null
}; };
if(line.LineNumber < 0) {
line.LineNumber = 0;
}
if(match.Groups[7].Success) {
string[] spanIDs = match.Groups[7].Value.Split('+');
line.SpanIDs = new List<int>(spanIDs.Length);
for(int i = spanIDs.Length - 1; i >= 0; i--) {
//Read them backwards to get them in order
line.SpanIDs.Add(Int32.Parse(spanIDs[i]));
}
} else {
line.SpanIDs = new List<int>();
}
_usedFileIds.Add(line.FileID); _usedFileIds.Add(line.FileID);
_lines.Add(line.ID, line); _lines.Add(line.ID, line);
return true; return true;
@ -402,10 +453,13 @@ namespace Mesen.GUI.Debugger
count++; count++;
} }
int address = GetSymbolAddressInfo(symbol).Address; AddressTypeInfo addressInfo = GetSymbolAddressInfo(symbol);
CodeLabel label = this.CreateLabel(address, segment.IsRam); if(addressInfo != null) {
if(label != null) { int address = addressInfo.Address;
label.Label = newName; CodeLabel label = this.CreateLabel(address, segment.IsRam);
if(label != null) {
label.Label = newName;
}
} }
} }
} catch { } catch {
@ -419,11 +473,11 @@ namespace Mesen.GUI.Debugger
foreach(KeyValuePair<int, LineInfo> kvp in _lines) { foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
try { try {
LineInfo line = kvp.Value; LineInfo line = kvp.Value;
if(line.SpanID == null) { if(line.SpanIDs.Count == 0) {
continue; continue;
} }
SpanInfo span = _spans[line.SpanID.Value]; SpanInfo span = _spans[line.SpanIDs[0]];
SegmentInfo segment = _segments[span.SegmentID]; SegmentInfo segment = _segments[span.SegmentID];
if(_files[line.FileID].Data == null) { if(_files[line.FileID].Data == null) {
@ -479,6 +533,19 @@ namespace Mesen.GUI.Debugger
private void LoadFileData(string path) private void LoadFileData(string path)
{ {
Dictionary<int, int> maxLineCountByFile = new Dictionary<int, int>();
foreach(LineInfo line in _lines.Values) {
int currentMax;
if(maxLineCountByFile.TryGetValue(line.FileID, out currentMax)) {
if(currentMax < line.LineNumber) {
maxLineCountByFile[line.FileID] = line.LineNumber;
}
} else {
maxLineCountByFile[line.FileID] = line.LineNumber;
}
}
foreach(FileInfo file in _files.Values) { foreach(FileInfo file in _files.Values) {
if(_usedFileIds.Contains(file.ID)) { if(_usedFileIds.Contains(file.ID)) {
try { try {
@ -497,6 +564,14 @@ namespace Mesen.GUI.Debugger
if(File.Exists(sourceFile)) { if(File.Exists(sourceFile)) {
file.Data = File.ReadAllLines(sourceFile); file.Data = File.ReadAllLines(sourceFile);
} }
LineInfo[] fileInfos = new LineInfo[maxLineCountByFile[file.ID] + 1];
foreach(LineInfo line in _lines.Values) {
if(line.FileID == file.ID) {
fileInfos[line.LineNumber] = line;
}
}
_linesByFile[file.ID] = fileInfos;
} catch { } catch {
_errorCount++; _errorCount++;
} }
@ -504,6 +579,74 @@ namespace Mesen.GUI.Debugger
} }
} }
private void BuildCdlData()
{
int prgSize = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom);
if(prgSize <= 0) {
return;
}
byte[] cdlFile = new byte[prgSize];
//Mark data/code regions
foreach(SpanInfo span in _spans.Values) {
int prgAddress = GetPrgAddress(span);
if(prgAddress >= 0 && prgAddress < prgSize) {
for(int i = 0; i < span.Size; i++) {
if(cdlFile[prgAddress + i] != (byte)CdlPrgFlags.Data && !span.IsData && span.Size <= 3) {
cdlFile[prgAddress + i] = (byte)CdlPrgFlags.Code;
} else if(span.IsData) {
cdlFile[prgAddress + i] = (byte)CdlPrgFlags.Data;
} else if(cdlFile[prgAddress + i] == 0) {
//Mark bytes as tentative data, until we know that the bytes are actually code
cdlFile[prgAddress + i] = 0x04;
}
}
}
}
for(int i = 0; i < cdlFile.Length; i++) {
if(cdlFile[i] == 0x04) {
//Mark all bytes marked as tentative data as data
cdlFile[i] = (byte)CdlPrgFlags.Data;
}
}
//Find/identify functions and jump targets
byte[] prgRomContent = InteropEmu.DebugGetMemoryState(DebugMemoryType.PrgRom);
foreach(SymbolInfo symbol in _symbols.Values) {
LineInfo line;
if(!symbol.SegmentID.HasValue) {
//This is a constant, ignore it
continue;
}
foreach(int reference in symbol.References) {
if(_lines.TryGetValue(reference, out line) && line.SpanIDs.Count > 0) {
SpanInfo span;
if(_spans.TryGetValue(line.SpanIDs[0], out span) && !span.IsData && span.Size <= 3) {
int referencePrgAddr = GetPrgAddress(span);
if(referencePrgAddr >= 0 && referencePrgAddr < prgRomContent.Length) {
byte opCode = prgRomContent[referencePrgAddr];
if(opCode == 0x20 || opCode == 0x10 || opCode == 0x30 || opCode == 0x50 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x20 || opCode == 0x4C || opCode == 0x6C) {
//This symbol is used with a JSR/jump instruction, so it's either a function or jump target
bool isJsr = opCode == 0x20;
SpanInfo definitionSpan = GetSymbolDefinitionSpan(symbol);
if(definitionSpan != null) {
int definitionPrgAddr = GetPrgAddress(definitionSpan);
if(definitionPrgAddr >= 0 && definitionPrgAddr < prgRomContent.Length) {
cdlFile[definitionPrgAddr] |= (byte)(isJsr ? CdlPrgFlags.SubEntryPoint : CdlPrgFlags.JumpTarget);
break;
}
}
}
}
}
}
}
}
InteropEmu.DebugSetCdlData(cdlFile);
}
public void Import(string path, bool silent = false) public void Import(string path, bool silent = false)
{ {
RomInfo romInfo = InteropEmu.GetRomInfo(); RomInfo romInfo = InteropEmu.GetRomInfo();
@ -537,51 +680,30 @@ namespace Mesen.GUI.Debugger
LoadFileData(basePath); LoadFileData(basePath);
int prgSize = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom); BuildCdlData();
if(prgSize > 0) {
byte[] cdlFile = new byte[prgSize];
foreach(KeyValuePair<int, SpanInfo> kvp in _spans) {
SegmentInfo segment;
if(_segments.TryGetValue(kvp.Value.SegmentID, out segment)) {
if(!segment.IsRam && kvp.Value.Size != segment.Size) {
int prgAddress = kvp.Value.Offset + segment.FileOffset - _headerSize;
if(prgAddress >= 0 && prgAddress < prgSize) {
for(int i = 0; i < kvp.Value.Size; i++) {
if(cdlFile[prgAddress + i] == 0 && !kvp.Value.IsData && kvp.Value.Size <= 3) {
cdlFile[prgAddress + i] = (byte)0x01;
} else if(kvp.Value.IsData) {
cdlFile[prgAddress + i] = (byte)0x02;
}
}
}
}
}
}
InteropEmu.DebugSetCdlData(cdlFile);
}
foreach(LineInfo line in _lines.Values) { foreach(LineInfo line in _lines.Values) {
if(line.SpanID == null) { foreach(int spanID in line.SpanIDs) {
continue; SpanInfo span;
} if(_spans.TryGetValue(spanID, out span)) {
SegmentInfo segment;
if(_segments.TryGetValue(span.SegmentID, out segment) && !segment.IsRam) {
for(int i = 0; i < span.Size; i++) {
int prgAddress = segment.FileOffset - _headerSize + span.Offset + i;
FileInfo file = _files[line.FileID]; LineInfo existingLine;
SpanInfo span = _spans[line.SpanID.Value]; if(_linesByPrgAddress.TryGetValue(prgAddress, out existingLine) && existingLine.Type == LineType.External) {
SegmentInfo segment = _segments[span.SegmentID]; //Give priority to lines that come from C files
if(!segment.IsRam) { continue;
for(int i = 0; i < span.Size; i++) { }
int prgAddress = segment.FileOffset - _headerSize + span.Offset + i;
LineInfo existingLine; _linesByPrgAddress[prgAddress] = line;
if(_linesByPrgAddress.TryGetValue(prgAddress, out existingLine) && existingLine.Type == LineType.External) { if(i == 0 && spanID == line.SpanIDs[0]) {
//Give priority to lines that come from C files //Mark the first byte of the first span representing this line as the PRG address for this line of code
continue; FileInfo file = _files[line.FileID];
} _prgAddressByLine[file.ID.ToString() + "_" + line.LineNumber.ToString()] = prgAddress;
}
_linesByPrgAddress[prgAddress] = line; }
if(i == 0) {
_prgAddressByLine[file.ID.ToString() + "_" + line.LineNumber.ToString()] = prgAddress;
} }
} }
} }
@ -606,7 +728,7 @@ namespace Mesen.GUI.Debugger
labels.AddRange(_saveRamLabels.Values); labels.AddRange(_saveRamLabels.Values);
labelCount += _ramLabels.Count + _workRamLabels.Count + _saveRamLabels.Count; labelCount += _ramLabels.Count + _workRamLabels.Count + _saveRamLabels.Count;
} }
LabelManager.SetLabels(labels, !silent); LabelManager.SetLabels(labels, true);
if(!silent) { if(!silent) {
if(_errorCount > 0) { if(_errorCount > 0) {
@ -660,7 +782,7 @@ namespace Mesen.GUI.Debugger
{ {
public int ID; public int ID;
public int FileID; public int FileID;
public int? SpanID; public List<int> SpanIDs;
public LineType Type; public LineType Type;
public int LineNumber; public int LineNumber;

View file

@ -145,10 +145,10 @@ namespace Mesen.GUI.Debugger
public static void CreateAutomaticJumpLabels() public static void CreateAutomaticJumpLabels()
{ {
bool[] jumpTargets = InteropEmu.DebugGetJumpTargets(); byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
List<CodeLabel> labelsToAdd = new List<CodeLabel>(); List<CodeLabel> labelsToAdd = new List<CodeLabel>();
for(int i = 0; i < jumpTargets.Length; i++) { for(int i = 0; i < cdlData.Length; i++) {
if(jumpTargets[i] && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) { if((cdlData[i] & (byte)CdlPrgFlags.JumpTarget) != 0 && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) {
labelsToAdd.Add(new CodeLabel() { Flags = CodeLabelFlags.AutoJumpLabel, Address = (uint)i, AddressType = AddressType.PrgRom, Label = "L" + i.ToString("X4"), Comment = "" }); labelsToAdd.Add(new CodeLabel() { Flags = CodeLabelFlags.AutoJumpLabel, Address = (uint)i, AddressType = AddressType.PrgRom, Label = "L" + i.ToString("X4"), Comment = "" });
} }
} }

View file

@ -156,7 +156,7 @@ namespace Mesen.GUI.Debugger
_resultCount = 0; _resultCount = 0;
HashSet<int> entryPoints = new HashSet<int>(InteropEmu.DebugGetFunctionEntryPoints()); HashSet<int> entryPoints = new HashSet<int>(InteropEmu.DebugGetFunctionEntryPoints());
bool[] isJumpTargets = InteropEmu.DebugGetJumpTargets(); byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
List<SearchResultInfo> searchResults = new List<SearchResultInfo>(); List<SearchResultInfo> searchResults = new List<SearchResultInfo>();
@ -201,7 +201,7 @@ namespace Mesen.GUI.Debugger
SearchResultType resultType = SearchResultType.Data; SearchResultType resultType = SearchResultType.Data;
if(addressInfo?.Type == AddressType.PrgRom && entryPoints.Contains(addressInfo.Address)) { if(addressInfo?.Type == AddressType.PrgRom && entryPoints.Contains(addressInfo.Address)) {
resultType = SearchResultType.Function; resultType = SearchResultType.Function;
} else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < isJumpTargets.Length && isJumpTargets[addressInfo.Address]) { } else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < cdlData.Length && (cdlData[addressInfo.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) {
resultType = SearchResultType.JumpTarget; resultType = SearchResultType.JumpTarget;
} else if(isConstant) { } else if(isConstant) {
resultType = SearchResultType.Constant; resultType = SearchResultType.Constant;
@ -226,7 +226,7 @@ namespace Mesen.GUI.Debugger
SearchResultType resultType = SearchResultType.Data; SearchResultType resultType = SearchResultType.Data;
if(label.AddressType == AddressType.PrgRom && entryPoints.Contains((int)label.Address)) { if(label.AddressType == AddressType.PrgRom && entryPoints.Contains((int)label.Address)) {
resultType = SearchResultType.Function; resultType = SearchResultType.Function;
} else if(label.AddressType == AddressType.PrgRom && label.Address < isJumpTargets.Length && isJumpTargets[label.Address]) { } else if(label.AddressType == AddressType.PrgRom && label.Address < cdlData.Length && (cdlData[label.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) {
resultType = SearchResultType.JumpTarget; resultType = SearchResultType.JumpTarget;
} }

View file

@ -368,21 +368,6 @@ namespace Mesen.GUI
return assembledCode; return assembledCode;
} }
[DllImport(DLLPath, EntryPoint = "DebugGetJumpTargets")] private static extern void DebugGetJumpTargetsWrapper(IntPtr isJumpTargets);
public static bool[] DebugGetJumpTargets()
{
bool[] isJumpTarget = new bool[InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom)];
GCHandle hJumpTarget = GCHandle.Alloc(isJumpTarget, GCHandleType.Pinned);
try {
InteropEmu.DebugGetJumpTargetsWrapper(hJumpTarget.AddrOfPinnedObject());
} finally {
hJumpTarget.Free();
}
return isJumpTarget;
}
[DllImport(DLLPath, EntryPoint = "DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer); [DllImport(DLLPath, EntryPoint = "DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer);
public static byte[] DebugGetMemoryState(DebugMemoryType type) public static byte[] DebugGetMemoryState(DebugMemoryType type)
{ {
@ -608,6 +593,12 @@ namespace Mesen.GUI
} }
[DllImport(DLLPath, EntryPoint = "DebugGetCdlData")] private static extern void DebugGetCdlDataWrapper(UInt32 offset, UInt32 length, DebugMemoryType type, IntPtr counts); [DllImport(DLLPath, EntryPoint = "DebugGetCdlData")] private static extern void DebugGetCdlDataWrapper(UInt32 offset, UInt32 length, DebugMemoryType type, IntPtr counts);
public static byte[] DebugGetPrgCdlData()
{
return DebugGetCdlData(0, (uint)InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom), DebugMemoryType.PrgRom);
}
public static byte[] DebugGetCdlData(UInt32 offset, UInt32 length, DebugMemoryType type) public static byte[] DebugGetCdlData(UInt32 offset, UInt32 length, DebugMemoryType type)
{ {
byte[] cdlData = new byte[length]; byte[] cdlData = new byte[length];
@ -1180,9 +1171,16 @@ namespace Mesen.GUI
None = 0x00, None = 0x00,
Code = 0x01, Code = 0x01,
Data = 0x02, Data = 0x02,
IndirectCode = 0x10,
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
//Repurposed to mean the address is the target of a jump instruction
JumpTarget = 0x10,
IndirectData = 0x20, IndirectData = 0x20,
PcmData = 0x40, PcmData = 0x40,
//Unused bit in original CDL spec
//Used to denote that the byte is the start of function (sub)
SubEntryPoint = 0x80 SubEntryPoint = 0x80
} }

View file

@ -67,8 +67,6 @@ extern "C"
DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); } DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); }
DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); } DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
DllExport void __stdcall DebugGetJumpTargets(bool* isJumpTarget) { GetDebugger()->GetJumpTargets(isJumpTarget); }
DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); } DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); }
DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); } DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); }