mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
659 lines
22 KiB
C++
659 lines
22 KiB
C++
#include "pch.h"
|
|
#include <algorithm>
|
|
#include "Debugger/Disassembler.h"
|
|
#include "Debugger/DisassemblyInfo.h"
|
|
#include "Debugger/CdlManager.h"
|
|
#include "Debugger/Debugger.h"
|
|
#include "Debugger/LabelManager.h"
|
|
#include "Debugger/MemoryDumper.h"
|
|
#include "Debugger/CodeDataLogger.h"
|
|
#include "Debugger/DebugBreakHelper.h"
|
|
#include "Debugger/DebugUtilities.h"
|
|
#include "SNES/SnesCpuTypes.h"
|
|
#include "SNES/SpcTypes.h"
|
|
#include "SNES/Coprocessors/GSU/GsuTypes.h"
|
|
#include "SNES/Coprocessors/CX4/Cx4Types.h"
|
|
#include "Gameboy/GbTypes.h"
|
|
#include "NES/NesTypes.h"
|
|
#include "PCE/PceTypes.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#include "Utilities/FastString.h"
|
|
#include "Utilities/HexUtilities.h"
|
|
#include "Utilities/StringUtilities.h"
|
|
|
|
Disassembler::Disassembler(IConsole* console, Debugger* debugger)
|
|
{
|
|
_debugger = debugger;
|
|
_labelManager = debugger->GetLabelManager();
|
|
_console = console;
|
|
_settings = debugger->GetEmulator()->GetSettings();
|
|
_memoryDumper = _debugger->GetMemoryDumper();
|
|
|
|
for(int i = (int)MemoryType::SnesPrgRom; i < DebugUtilities::GetMemoryTypeCount(); i++) {
|
|
InitSource((MemoryType)i);
|
|
}
|
|
}
|
|
|
|
void Disassembler::InitSource(MemoryType type)
|
|
{
|
|
uint32_t size = _memoryDumper->GetMemorySize(type);
|
|
_sources[(int)type] = { vector<DisassemblyInfo>(size), size };
|
|
}
|
|
|
|
DisassemblerSource& Disassembler::GetSource(MemoryType type)
|
|
{
|
|
return _sources[(int)type];
|
|
}
|
|
|
|
uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuType type)
|
|
{
|
|
DisassemblerSource& src = GetSource(addrInfo.Type);
|
|
|
|
int returnSize = 0;
|
|
int32_t address = addrInfo.Address;
|
|
do {
|
|
DisassemblyInfo &disInfo = src.Cache[address];
|
|
if(!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags)) {
|
|
disInfo.Initialize(address, cpuFlags, type, addrInfo.Type, _memoryDumper);
|
|
for(int i = 1; i < disInfo.GetOpSize() && address + i < src.Cache.size() ; i++) {
|
|
//Clear any instructions that start in the middle of this one
|
|
//(can happen when resizing an instruction after X/M updates)
|
|
src.Cache[address + i] = DisassemblyInfo();
|
|
}
|
|
returnSize += disInfo.GetOpSize();
|
|
} else {
|
|
returnSize += disInfo.GetOpSize();
|
|
break;
|
|
}
|
|
|
|
if(!disInfo.CanDisassembleNextOp()) {
|
|
//Can't assume what follows is code, stop disassembling
|
|
break;
|
|
}
|
|
|
|
disInfo.UpdateCpuFlags(cpuFlags);
|
|
address += disInfo.GetOpSize();
|
|
} while(address >= 0 && address < (int32_t)src.Cache.size());
|
|
|
|
return returnSize;
|
|
}
|
|
|
|
void Disassembler::ResetPrgCache()
|
|
{
|
|
InitSource(MemoryType::SnesPrgRom);
|
|
InitSource(MemoryType::GbPrgRom);
|
|
InitSource(MemoryType::NesPrgRom);
|
|
InitSource(MemoryType::PcePrgRom);
|
|
}
|
|
|
|
void Disassembler::InvalidateCache(AddressInfo addrInfo, CpuType type)
|
|
{
|
|
if(addrInfo.Address >= 0) {
|
|
DisassemblerSource& src = GetSource(addrInfo.Type);
|
|
for(int i = 0; i < 4; i++) {
|
|
if(addrInfo.Address >= i) {
|
|
if(src.Cache[addrInfo.Address - i].IsInitialized()) {
|
|
src.Cache[addrInfo.Address - i].Reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<DisassemblyResult> Disassembler::Disassemble(CpuType cpuType, uint16_t bank)
|
|
{
|
|
constexpr int bytesPerRow = 8;
|
|
|
|
vector<DisassemblyResult> results;
|
|
results.reserve(20000);
|
|
|
|
DebugConfig& cfg = _settings->GetDebugConfig();
|
|
bool disUnident = cfg.DisassembleUnidentifiedData;
|
|
bool disData = cfg.DisassembleVerifiedData;
|
|
bool showUnident = cfg.ShowUnidentifiedData;
|
|
bool showData = cfg.ShowVerifiedData;
|
|
bool showJumpLabels = cfg.ShowJumpLabels;
|
|
|
|
bool inUnknownBlock = false;
|
|
bool inVerifiedBlock = false;
|
|
bool inUnmappedBlock = false;
|
|
LabelInfo labelInfo;
|
|
AddressInfo prevAddrInfo = {};
|
|
int byteCounter = 0;
|
|
|
|
AddressInfo relAddress = {};
|
|
relAddress.Type = DebugUtilities::GetCpuMemoryType(cpuType);
|
|
|
|
if(bank > GetMaxBank(cpuType)) {
|
|
return results;
|
|
}
|
|
|
|
int32_t bankStart = bank << 16;
|
|
int32_t bankEnd = (bank + 1) << 16;
|
|
bankEnd = std::min<int32_t>(bankEnd, (int32_t)_memoryDumper->GetMemorySize(relAddress.Type));
|
|
|
|
AddressInfo addrInfo = {};
|
|
|
|
auto pushEndBlock = [&]() {
|
|
if(inUnknownBlock || inVerifiedBlock) {
|
|
int flags = LineFlags::BlockEnd;
|
|
if(inVerifiedBlock) {
|
|
flags |= LineFlags::VerifiedData;
|
|
}
|
|
if((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) {
|
|
flags |= LineFlags::ShowAsData;
|
|
}
|
|
|
|
results[results.size() - 1].SetByteCount(bytesPerRow - byteCounter + 1);
|
|
results.push_back(DisassemblyResult(prevAddrInfo, relAddress.Address - 1, flags));
|
|
inUnknownBlock = false;
|
|
inVerifiedBlock = false;
|
|
byteCounter = 0;
|
|
}
|
|
};
|
|
|
|
auto pushUnmappedBlock = [&]() {
|
|
int32_t prevAddress = results.size() > 0 ? results[results.size() - 1].CpuAddress + 1 : bankStart;
|
|
results.push_back(DisassemblyResult(prevAddress, LineFlags::BlockStart | LineFlags::UnmappedMemory));
|
|
results.push_back(DisassemblyResult(prevAddress, LineFlags::UnmappedMemory | LineFlags::Empty));
|
|
results.push_back(DisassemblyResult(relAddress.Address - 1, LineFlags::BlockEnd | LineFlags::UnmappedMemory));
|
|
inUnmappedBlock = false;
|
|
};
|
|
|
|
for(int32_t i = bankStart; i < bankEnd; i++) {
|
|
relAddress.Address = i;
|
|
addrInfo = _console->GetAbsoluteAddress(relAddress);
|
|
|
|
if(addrInfo.Address < 0 || addrInfo.Type == MemoryType::SnesRegister) {
|
|
pushEndBlock();
|
|
inUnmappedBlock = true;
|
|
continue;
|
|
}
|
|
|
|
if(inUnmappedBlock) {
|
|
pushUnmappedBlock();
|
|
}
|
|
|
|
DisassemblerSource& src = GetSource(addrInfo.Type);
|
|
DisassemblyInfo disassemblyInfo = src.Cache[addrInfo.Address];
|
|
CodeDataLogger* cdl = _debugger->GetCdlManager()->GetCodeDataLogger(addrInfo.Type);
|
|
uint8_t opSize = 0;
|
|
|
|
bool isCode = cdl ? cdl->IsCode(addrInfo.Address) : false;
|
|
bool isData = cdl ? cdl->IsData(addrInfo.Address) : false;
|
|
|
|
if(disassemblyInfo.IsInitialized()) {
|
|
opSize = disassemblyInfo.GetOpSize();
|
|
} else if((isData && disData) || (!isData && !isCode && disUnident)) {
|
|
disassemblyInfo.Initialize(i, 0, cpuType, relAddress.Type, _memoryDumper);
|
|
opSize = disassemblyInfo.GetOpSize();
|
|
}
|
|
|
|
if(opSize > 0) {
|
|
pushEndBlock();
|
|
|
|
if(cdl && cdl->IsSubEntryPoint(addrInfo.Address)) {
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::SubStart | LineFlags::BlockStart | LineFlags::VerifiedCode | LineFlags::Empty));
|
|
}
|
|
|
|
if(_labelManager->GetLabelAndComment(addrInfo, labelInfo)) {
|
|
bool hasMultipleComment = labelInfo.Comment.find_first_of('\n') != string::npos;
|
|
if(hasMultipleComment) {
|
|
int16_t lineCount = 0;
|
|
for(char c : labelInfo.Comment) {
|
|
if(c == '\n') {
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment, lineCount));
|
|
lineCount++;
|
|
}
|
|
}
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment, lineCount));
|
|
}
|
|
|
|
if(labelInfo.Label.size()) {
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Label));
|
|
}
|
|
|
|
if(!hasMultipleComment && labelInfo.Comment.size()) {
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment));
|
|
} else {
|
|
results.push_back(DisassemblyResult(addrInfo, i));
|
|
}
|
|
} else if(showJumpLabels && cdl && (cdl->IsJumpTarget(addrInfo.Address) || cdl->IsSubEntryPoint(addrInfo.Address))) {
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Label));
|
|
results.push_back(DisassemblyResult(addrInfo, i));
|
|
} else {
|
|
results.push_back(DisassemblyResult(addrInfo, i));
|
|
}
|
|
|
|
if(disassemblyInfo.IsReturnInstruction()) {
|
|
//End of function
|
|
results.push_back(DisassemblyResult(i, LineFlags::VerifiedCode | LineFlags::BlockEnd | LineFlags::Empty));
|
|
}
|
|
|
|
//Move to the end of the instruction (but realign disassembly if another valid instruction is found)
|
|
//This can sometimes happen if the 2nd byte of BRK/COP is reused as the first byte of the next instruction
|
|
//Also required when disassembling unvalidated data as code (to realign once we find verified code)
|
|
MemoryType prevMemType = addrInfo.Type;
|
|
for(int j = 1; j < opSize && i + j < bankEnd; j++) {
|
|
relAddress.Address = i + 1;
|
|
addrInfo = _console->GetAbsoluteAddress(relAddress);
|
|
if(addrInfo.Type != prevMemType || src.Cache[addrInfo.Address].IsInitialized()) {
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
} else {
|
|
if(showData || showUnident) {
|
|
if((isData && inUnknownBlock) || (!isData && inVerifiedBlock)) {
|
|
pushEndBlock();
|
|
}
|
|
}
|
|
|
|
if(byteCounter > 0) {
|
|
//If showing as hex data, add a new data line every 8 bytes
|
|
byteCounter--;
|
|
if(byteCounter == 0) {
|
|
results[results.size() - 1].SetByteCount(bytesPerRow);
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0), bytesPerRow));
|
|
byteCounter = bytesPerRow;
|
|
}
|
|
} else if(!inUnknownBlock && !inVerifiedBlock) {
|
|
//If not in a block, start a new block based on the current byte's type (data vs unidentified)
|
|
bool showAsData = (isData && showData) || ((!isData && !isCode) && showUnident);
|
|
if(isData) {
|
|
inVerifiedBlock = true;
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockStart | LineFlags::VerifiedData | (showAsData ? LineFlags::ShowAsData : 0)));
|
|
} else {
|
|
inUnknownBlock = true;
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockStart | (showAsData ? LineFlags::ShowAsData : 0)));
|
|
}
|
|
|
|
if(showAsData) {
|
|
//If showing data as hex, add the first row of the block
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0)));
|
|
byteCounter = bytesPerRow;
|
|
} else {
|
|
//If not showing the data at all, display 1 empty line
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Empty | (isData ? LineFlags::VerifiedData : 0)));
|
|
}
|
|
}
|
|
prevAddrInfo = addrInfo;
|
|
}
|
|
}
|
|
|
|
relAddress.Address = bankEnd;
|
|
pushEndBlock();
|
|
if(inUnmappedBlock) {
|
|
pushUnmappedBlock();
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
void Disassembler::GetLineData(DisassemblyResult& row, CpuType type, MemoryType memType, CodeLineData& data)
|
|
{
|
|
data.Address = row.CpuAddress;
|
|
data.AbsoluteAddress = row.Address;
|
|
data.EffectiveAddress = {};
|
|
data.Value = 0;
|
|
data.Flags = row.Flags;
|
|
data.LineCpuType = type;
|
|
|
|
switch(row.Address.Type) {
|
|
default: break;
|
|
case MemoryType::GbPrgRom:
|
|
case MemoryType::SnesPrgRom:
|
|
case MemoryType::NesPrgRom:
|
|
case MemoryType::PcePrgRom:
|
|
data.Flags |= (uint8_t)LineFlags::PrgRom;
|
|
break;
|
|
|
|
case MemoryType::GbWorkRam:
|
|
case MemoryType::SnesWorkRam:
|
|
case MemoryType::NesWorkRam:
|
|
case MemoryType::PceWorkRam:
|
|
data.Flags |= (uint8_t)LineFlags::WorkRam;
|
|
break;
|
|
|
|
case MemoryType::GbCartRam:
|
|
case MemoryType::SnesSaveRam:
|
|
case MemoryType::NesSaveRam:
|
|
case MemoryType::PceSaveRam:
|
|
data.Flags |= (uint8_t)LineFlags::SaveRam;
|
|
break;
|
|
}
|
|
|
|
bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd | LineFlags::Empty)) != 0;
|
|
if(!isBlockStartEnd && row.Address.Address >= 0) {
|
|
if((data.Flags & LineFlags::ShowAsData)) {
|
|
FastString str(".db", 3);
|
|
for(int i = 0; i < row.GetByteCount(); i++) {
|
|
str.Write(" $", 2);
|
|
str.Write(HexUtilities::ToHexChar(_memoryDumper->GetMemoryValue(memType, row.CpuAddress + i)), 2);
|
|
}
|
|
data.OpSize = row.GetByteCount();
|
|
memcpy(data.Text, str.ToString(), str.GetSize() + 1);
|
|
} else if((data.Flags & LineFlags::Comment) && row.CommentLine >= 0) {
|
|
string comment = ";" + StringUtilities::Split(_labelManager->GetComment(row.Address), '\n')[row.CommentLine];
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
memcpy(data.Comment, comment.c_str(), std::min<int>((int)comment.size() + 1, 1000));
|
|
} else if(data.Flags & LineFlags::Label) {
|
|
string label = _labelManager->GetLabel(row.Address);
|
|
if(label.empty()) {
|
|
//Use address as the label
|
|
label = "$" + DebugUtilities::AddressToHex(type, row.CpuAddress);
|
|
if(_settings->GetDebugConfig().UseLowerCaseDisassembly) {
|
|
std::transform(label.begin(), label.end(), label.begin(), ::tolower);
|
|
}
|
|
}
|
|
label += ":";
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
memcpy(data.Text, label.c_str(), std::min<int>((int)label.size() + 1, 1000));
|
|
} else {
|
|
DisassemblerSource& src = GetSource(row.Address.Type);
|
|
DisassemblyInfo disInfo = src.Cache[row.Address.Address];
|
|
|
|
//Always use Sa1 as the cpu type when disassembling Sa1 address space
|
|
CpuType lineCpuType = type != CpuType::Sa1 && disInfo.IsInitialized() ? disInfo.GetCpuType() : type;
|
|
|
|
data.LineCpuType = lineCpuType;
|
|
CdlManager* cdlManager = _debugger->GetCdlManager();
|
|
switch(lineCpuType) {
|
|
case CpuType::Snes:
|
|
case CpuType::Sa1:
|
|
{
|
|
SnesCpuState state = (SnesCpuState&)_debugger->GetCpuStateRef(lineCpuType);
|
|
state.PC = (uint16_t)row.CpuAddress;
|
|
state.K = (row.CpuAddress >> 16);
|
|
|
|
CodeDataLogger* cdl = cdlManager->GetCodeDataLogger(row.Address.Type);
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, state.PS, lineCpuType, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= (!cdl || cdl->IsCode(data.AbsoluteAddress.Address)) ? LineFlags::VerifiedCode : LineFlags::UnexecutedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CpuType::Spc:
|
|
{
|
|
SpcState state = (SpcState&)_debugger->GetCpuStateRef(lineCpuType);
|
|
state.PC = (uint16_t)row.CpuAddress;
|
|
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, CpuType::Spc, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CpuType::Gsu:
|
|
{
|
|
GsuState state = (GsuState&)_debugger->GetCpuStateRef(lineCpuType);
|
|
state.R[15] = (uint16_t)row.CpuAddress;
|
|
state.ProgramBank = (row.CpuAddress >> 16);
|
|
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, CpuType::Gsu, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CpuType::NecDsp:
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, type, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
break;
|
|
|
|
case CpuType::Cx4:
|
|
{
|
|
Cx4State state = (Cx4State&)_debugger->GetCpuStateRef(lineCpuType);
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, type, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CpuType::Gameboy:
|
|
{
|
|
GbCpuState state = (GbCpuState&)_debugger->GetCpuStateRef(lineCpuType);
|
|
state.PC = (uint16_t)row.CpuAddress;
|
|
|
|
CodeDataLogger* cdl = cdlManager->GetCodeDataLogger(row.Address.Type);
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, CpuType::Gameboy, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= (!cdl || cdl->IsCode(data.AbsoluteAddress.Address)) ? LineFlags::VerifiedCode : LineFlags::UnexecutedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CpuType::Nes:
|
|
{
|
|
NesCpuState state = (NesCpuState&)_debugger->GetCpuStateRef(lineCpuType);
|
|
state.PC = (uint16_t)row.CpuAddress;
|
|
|
|
CodeDataLogger* cdl = cdlManager->GetCodeDataLogger(row.Address.Type);
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, CpuType::Nes, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= (!cdl || cdl->IsCode(data.AbsoluteAddress.Address)) ? LineFlags::VerifiedCode : LineFlags::UnexecutedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CpuType::Pce:
|
|
{
|
|
PceCpuState state = (PceCpuState&)_debugger->GetCpuStateRef(lineCpuType);
|
|
state.PC = (uint16_t)row.CpuAddress;
|
|
|
|
CodeDataLogger* cdl = cdlManager->GetCodeDataLogger(row.Address.Type);
|
|
if(!disInfo.IsInitialized()) {
|
|
disInfo = DisassemblyInfo(row.Address.Address, 0, CpuType::Pce, row.Address.Type, _memoryDumper);
|
|
} else {
|
|
data.Flags |= (!cdl || cdl->IsCode(data.AbsoluteAddress.Address)) ? LineFlags::VerifiedCode : LineFlags::UnexecutedCode;
|
|
}
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
|
|
if(data.EffectiveAddress.Address >= 0) {
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
string text;
|
|
disInfo.GetDisassembly(text, row.CpuAddress, _labelManager, _settings);
|
|
memcpy(data.Text, text.c_str(), std::min<int>((int)text.size() + 1, 1000));
|
|
|
|
disInfo.GetByteCode(data.ByteCode);
|
|
|
|
if(data.Flags & LineFlags::Comment) {
|
|
string comment = ";" + _labelManager->GetComment(row.Address);
|
|
memcpy(data.Comment, comment.c_str(), std::min<int>((int)comment.size() + 1, 1000));
|
|
} else {
|
|
data.Comment[0] = 0;
|
|
}
|
|
}
|
|
} else {
|
|
if(data.Flags & LineFlags::SubStart) {
|
|
string label = _labelManager->GetLabel(row.Address);
|
|
if(label.empty()) {
|
|
label = "sub start";
|
|
}
|
|
memcpy(data.Text, label.c_str(), label.size() + 1);
|
|
} else if(data.Flags & LineFlags::BlockStart) {
|
|
string label = (data.Flags & LineFlags::VerifiedData) ? "data" : "unidentified";
|
|
if(data.Flags & LineFlags::UnmappedMemory) {
|
|
label = "unmapped";
|
|
}
|
|
memcpy(data.Text, label.c_str(), label.size() + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t Disassembler::GetMatchingRow(vector<DisassemblyResult>& rows, uint32_t address, bool returnFirstRow)
|
|
{
|
|
int32_t i;
|
|
for(i = 0; i < (int32_t)rows.size(); i++) {
|
|
if(rows[i].CpuAddress == (int32_t)address) {
|
|
if(i + 1 >= rows.size() || rows[i + 1].CpuAddress != (int32_t)address || address == 0 || returnFirstRow) {
|
|
//Keep going down until the last instance of the matching address is found
|
|
//Except for address 0, to ensure scrolling to the very top is allowed
|
|
break;
|
|
}
|
|
} else if(rows[i].CpuAddress > (int32_t)address) {
|
|
while(i > 0 && (rows[i].CpuAddress > (int32_t)address || rows[i].CpuAddress < 0)) {
|
|
i--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return std::max(0, i);
|
|
}
|
|
|
|
uint32_t Disassembler::GetDisassemblyOutput(CpuType type, uint32_t address, CodeLineData output[], uint32_t rowCount)
|
|
{
|
|
uint16_t bank = address >> 16;
|
|
Timer timer;
|
|
vector<DisassemblyResult> rows = Disassemble(type, bank);
|
|
|
|
int32_t i = GetMatchingRow(rows, address, true);
|
|
|
|
if(i >= (int32_t)rows.size()) {
|
|
return 0;
|
|
}
|
|
|
|
MemoryType memType = DebugUtilities::GetCpuMemoryType(type);
|
|
uint32_t maxBank = (_memoryDumper->GetMemorySize(memType) - 1) >> 16;
|
|
|
|
int32_t row;
|
|
for(row = 0; row < (int32_t)rowCount; row++){
|
|
if(row + i >= rows.size()) {
|
|
if(bank < maxBank) {
|
|
bank++;
|
|
rows = Disassemble(type, bank);
|
|
if(rows.size() == 0) {
|
|
break;
|
|
}
|
|
i = -row;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
GetLineData(rows[row + i], type, memType, output[row]);
|
|
}
|
|
|
|
return row;
|
|
}
|
|
|
|
uint16_t Disassembler::GetMaxBank(CpuType cpuType)
|
|
{
|
|
AddressInfo relAddress = {};
|
|
relAddress.Type = DebugUtilities::GetCpuMemoryType(cpuType);
|
|
return (_memoryDumper->GetMemorySize(relAddress.Type) - 1) >> 16;
|
|
}
|
|
|
|
int32_t Disassembler::GetDisassemblyRowAddress(CpuType cpuType, uint32_t address, int32_t rowOffset)
|
|
{
|
|
uint16_t bank = address >> 16;
|
|
vector<DisassemblyResult> rows = Disassemble(cpuType, bank);
|
|
int32_t len = (int32_t)rows.size();
|
|
if(len == 0) {
|
|
return address;
|
|
}
|
|
|
|
uint16_t maxBank = GetMaxBank(cpuType);
|
|
int32_t i = GetMatchingRow(rows, address, false);
|
|
|
|
if(rowOffset > 0) {
|
|
while(len > 0) {
|
|
for(; i < len; i++) {
|
|
if(rowOffset <= 0 && rows[i].CpuAddress >= 0 && rows[i].CpuAddress != (int32_t)address) {
|
|
return rows[i].CpuAddress;
|
|
}
|
|
rowOffset--;
|
|
}
|
|
|
|
//End of bank, didn't find an appropriate row to jump to, try the next bank
|
|
if(bank == maxBank) {
|
|
//Reached bottom of last bank, return the bottom row
|
|
return rows[len - 1].CpuAddress >= 0 ? rows[len - 1].CpuAddress : address;
|
|
}
|
|
|
|
bank++;
|
|
rows = Disassemble(cpuType, bank);
|
|
len = (int32_t)rows.size();
|
|
i = 0;
|
|
}
|
|
} else if(rowOffset < 0) {
|
|
while(len > 0) {
|
|
for(; i >= 0; i--) {
|
|
if(rowOffset >= 0 && rows[i].CpuAddress >= 0 && rows[i].CpuAddress != (int32_t)address) {
|
|
return rows[i].CpuAddress;
|
|
}
|
|
rowOffset++;
|
|
}
|
|
|
|
//Start of bank, didn't find an appropriate row to jump to, try the previous bank
|
|
if(bank == 0) {
|
|
//Reached top of first bank, return the top row
|
|
return rows[0].CpuAddress >= 0 ? rows[0].CpuAddress : address;
|
|
}
|
|
|
|
bank--;
|
|
rows = Disassemble(cpuType, bank);
|
|
len = (int32_t)rows.size();
|
|
i = len - 1;
|
|
}
|
|
}
|
|
|
|
return address;
|
|
}
|