2019-02-27 19:49:26 -05:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Disassembler.h"
|
|
|
|
#include "DisassemblyInfo.h"
|
|
|
|
#include "Cpu.h"
|
2019-04-06 17:38:14 -04:00
|
|
|
#include "Spc.h"
|
2019-07-14 21:45:12 -04:00
|
|
|
#include "NecDsp.h"
|
2019-04-28 22:19:52 -04:00
|
|
|
#include "Debugger.h"
|
2019-02-27 19:49:26 -05:00
|
|
|
#include "MemoryManager.h"
|
2019-04-28 22:19:52 -04:00
|
|
|
#include "LabelManager.h"
|
2019-02-27 19:49:26 -05:00
|
|
|
#include "CpuTypes.h"
|
2019-05-24 21:47:06 -04:00
|
|
|
#include "MemoryDumper.h"
|
2019-02-27 19:49:26 -05:00
|
|
|
#include "Console.h"
|
|
|
|
#include "CodeDataLogger.h"
|
2019-04-28 22:19:52 -04:00
|
|
|
#include "DebugBreakHelper.h"
|
2019-02-27 19:49:26 -05:00
|
|
|
#include "BaseCartridge.h"
|
2019-05-24 21:47:06 -04:00
|
|
|
#include "EmuSettings.h"
|
2019-07-18 16:14:32 -04:00
|
|
|
#include "../Utilities/FastString.h"
|
2019-02-27 19:49:26 -05:00
|
|
|
#include "../Utilities/HexUtilities.h"
|
2019-04-28 22:19:52 -04:00
|
|
|
#include "../Utilities/StringUtilities.h"
|
2019-02-27 19:49:26 -05:00
|
|
|
|
2019-04-28 22:19:52 -04:00
|
|
|
Disassembler::Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogger> cdl, Debugger* debugger)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
_cdl = cdl;
|
2019-04-28 22:19:52 -04:00
|
|
|
_debugger = debugger;
|
|
|
|
_labelManager = debugger->GetLabelManager();
|
2019-02-27 19:49:26 -05:00
|
|
|
_console = console.get();
|
2019-04-07 12:25:14 -04:00
|
|
|
_spc = console->GetSpc().get();
|
2019-05-24 21:47:06 -04:00
|
|
|
_memoryDumper = _debugger->GetMemoryDumper().get();
|
2019-02-27 19:49:26 -05:00
|
|
|
_memoryManager = console->GetMemoryManager().get();
|
|
|
|
|
|
|
|
_prgRom = console->GetCartridge()->DebugGetPrgRom();
|
|
|
|
_prgRomSize = console->GetCartridge()->DebugGetPrgRomSize();
|
|
|
|
_sram = console->GetCartridge()->DebugGetSaveRam();
|
|
|
|
_sramSize = console->GetCartridge()->DebugGetSaveRamSize();
|
|
|
|
_wram = _memoryManager->DebugGetWorkRam();
|
|
|
|
_wramSize = MemoryManager::WorkRamSize;
|
2019-04-06 17:38:14 -04:00
|
|
|
|
|
|
|
_spcRam = console->GetSpc()->GetSpcRam();
|
|
|
|
_spcRamSize = Spc::SpcRamSize;
|
|
|
|
_spcRom = console->GetSpc()->GetSpcRom();
|
|
|
|
_spcRomSize = Spc::SpcRomSize;
|
|
|
|
|
2019-07-14 21:45:12 -04:00
|
|
|
_necDspProgramRom = console->GetCartridge()->GetDsp() ? console->GetCartridge()->GetDsp()->DebugGetProgramRom() : nullptr;
|
|
|
|
_necDspProgramRomSize = console->GetCartridge()->GetDsp() ? console->GetCartridge()->GetDsp()->DebugGetProgramRomSize() : 0;
|
|
|
|
|
2019-04-05 00:02:43 -04:00
|
|
|
_prgCache = vector<DisassemblyInfo>(_prgRomSize);
|
|
|
|
_sramCache = vector<DisassemblyInfo>(_sramSize);
|
|
|
|
_wramCache = vector<DisassemblyInfo>(_wramSize);
|
2019-04-06 17:38:14 -04:00
|
|
|
_spcRamCache = vector<DisassemblyInfo>(_spcRamSize);
|
|
|
|
_spcRomCache = vector<DisassemblyInfo>(_spcRomSize);
|
2019-07-14 21:45:12 -04:00
|
|
|
_necDspRomCache = vector<DisassemblyInfo>(_necDspProgramRomSize);
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-04-05 00:02:43 -04:00
|
|
|
void Disassembler::GetSource(AddressInfo &info, uint8_t **source, uint32_t &size, vector<DisassemblyInfo> **cache)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
switch(info.Type) {
|
|
|
|
case SnesMemoryType::PrgRom:
|
|
|
|
*source = _prgRom;
|
|
|
|
*cache = &_prgCache;
|
|
|
|
size = _prgRomSize;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SnesMemoryType::WorkRam:
|
|
|
|
*source = _wram;
|
|
|
|
*cache = &_wramCache;
|
|
|
|
size = MemoryManager::WorkRamSize;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SnesMemoryType::SaveRam:
|
|
|
|
*source = _sram;
|
|
|
|
*cache = &_sramCache;
|
|
|
|
size = _sramSize;
|
|
|
|
break;
|
2019-04-06 17:38:14 -04:00
|
|
|
|
|
|
|
case SnesMemoryType::SpcRam:
|
|
|
|
*source = _spcRam;
|
|
|
|
*cache = &_spcRamCache;
|
|
|
|
size = _spcRamSize;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SnesMemoryType::SpcRom:
|
|
|
|
*source = _spcRom;
|
|
|
|
*cache = &_spcRomCache;
|
|
|
|
size = _spcRomSize;
|
|
|
|
break;
|
2019-02-27 19:49:26 -05:00
|
|
|
|
2019-07-14 21:45:12 -04:00
|
|
|
case SnesMemoryType::DspProgramRom:
|
|
|
|
*source = _necDspProgramRom;
|
|
|
|
*cache = &_necDspRomCache;
|
|
|
|
size = _necDspProgramRomSize;
|
|
|
|
break;
|
|
|
|
|
2019-02-27 19:49:26 -05:00
|
|
|
default:
|
|
|
|
throw std::runtime_error("Disassembler::GetSource() invalid memory type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-06 17:38:14 -04:00
|
|
|
uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuType type)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
uint8_t *source;
|
|
|
|
uint32_t sourceLength;
|
2019-04-05 00:02:43 -04:00
|
|
|
vector<DisassemblyInfo> *cache;
|
2019-02-27 19:49:26 -05:00
|
|
|
GetSource(addrInfo, &source, sourceLength, &cache);
|
|
|
|
|
2019-05-14 19:11:00 -04:00
|
|
|
int returnSize = 0;
|
|
|
|
int32_t address = addrInfo.Address;
|
2019-07-03 00:44:45 -04:00
|
|
|
while(address >= 0 && address < (int32_t)cache->size()) {
|
2019-05-14 19:11:00 -04:00
|
|
|
DisassemblyInfo &disInfo = (*cache)[address];
|
|
|
|
if(!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags)) {
|
|
|
|
disInfo.Initialize(source+address, cpuFlags, type);
|
2019-04-07 12:25:14 -04:00
|
|
|
_needDisassemble[(int)type] = true;
|
2019-06-24 11:46:59 -04:00
|
|
|
returnSize += disInfo.GetOpSize();
|
|
|
|
} else {
|
|
|
|
returnSize += disInfo.GetOpSize();
|
|
|
|
break;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
2019-05-14 19:11:00 -04:00
|
|
|
|
|
|
|
if(disInfo.UpdateCpuFlags(cpuFlags)) {
|
|
|
|
address += disInfo.GetOpSize();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
2019-05-14 19:11:00 -04:00
|
|
|
return returnSize;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-04-29 20:40:52 -04:00
|
|
|
void Disassembler::ResetPrgCache()
|
|
|
|
{
|
|
|
|
_prgCache = vector<DisassemblyInfo>(_prgRomSize);
|
|
|
|
_needDisassemble[(int)CpuType::Cpu] = true;
|
|
|
|
}
|
|
|
|
|
2019-06-24 11:46:59 -04:00
|
|
|
void Disassembler::InvalidateCache(AddressInfo addrInfo, CpuType type)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
uint8_t *source;
|
|
|
|
uint32_t sourceLength;
|
2019-04-05 00:02:43 -04:00
|
|
|
vector<DisassemblyInfo> *cache;
|
2019-02-27 19:49:26 -05:00
|
|
|
GetSource(addrInfo, &source, sourceLength, &cache);
|
|
|
|
|
|
|
|
if(addrInfo.Address >= 0) {
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
if(addrInfo.Address >= i) {
|
2019-04-05 00:02:43 -04:00
|
|
|
if((*cache)[addrInfo.Address - i].IsInitialized()) {
|
|
|
|
(*cache)[addrInfo.Address - i].Reset();
|
2019-06-24 11:46:59 -04:00
|
|
|
_needDisassemble[(int)type] = true;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
void Disassembler::Disassemble(CpuType cpuType)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
2019-04-07 12:25:14 -04:00
|
|
|
if(!_needDisassemble[(int)cpuType]) {
|
2019-02-27 19:49:26 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
_needDisassemble[(int)cpuType] = false;
|
2019-02-27 19:49:26 -05:00
|
|
|
|
|
|
|
auto lock = _disassemblyLock.AcquireSafe();
|
2019-04-07 12:25:14 -04:00
|
|
|
|
|
|
|
bool isSpc = cpuType == CpuType::Spc;
|
|
|
|
vector<DisassemblyResult> &results = isSpc ? _spcDisassembly : _disassembly;
|
|
|
|
int32_t maxAddr = isSpc ? 0xFFFF : 0xFFFFFF;
|
|
|
|
results.clear();
|
2019-02-27 19:49:26 -05:00
|
|
|
|
|
|
|
uint8_t *source;
|
|
|
|
uint32_t sourceLength;
|
2019-04-05 00:02:43 -04:00
|
|
|
vector<DisassemblyInfo> *cache;
|
2019-02-27 19:49:26 -05:00
|
|
|
|
2019-05-24 21:47:06 -04:00
|
|
|
bool disUnident = _console->GetSettings()->CheckDebuggerFlag(DebuggerFlags::DisassembleUnidentifiedData);
|
|
|
|
bool disData = _console->GetSettings()->CheckDebuggerFlag(DebuggerFlags::DisassembleVerifiedData);
|
|
|
|
bool showUnident = _console->GetSettings()->CheckDebuggerFlag(DebuggerFlags::ShowUnidentifiedData);
|
|
|
|
bool showData = _console->GetSettings()->CheckDebuggerFlag(DebuggerFlags::ShowVerifiedData);
|
|
|
|
|
2019-02-27 19:49:26 -05:00
|
|
|
bool inUnknownBlock = false;
|
2019-05-24 21:47:06 -04:00
|
|
|
bool inVerifiedBlock = false;
|
|
|
|
|
2019-02-27 19:49:26 -05:00
|
|
|
AddressInfo addrInfo = {};
|
|
|
|
AddressInfo prevAddrInfo = {};
|
2019-04-28 22:19:52 -04:00
|
|
|
string label;
|
|
|
|
string comment;
|
2019-05-24 21:47:06 -04:00
|
|
|
int byteCounter = 0;
|
2019-04-07 12:25:14 -04:00
|
|
|
for(int32_t i = 0; i <= maxAddr; i++) {
|
2019-02-27 19:49:26 -05:00
|
|
|
prevAddrInfo = addrInfo;
|
2019-04-07 12:25:14 -04:00
|
|
|
addrInfo = isSpc ? _spc->GetAbsoluteAddress(i) : _memoryManager->GetAbsoluteAddress(i);
|
2019-02-27 19:49:26 -05:00
|
|
|
|
|
|
|
if(addrInfo.Address < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetSource(addrInfo, &source, sourceLength, &cache);
|
|
|
|
|
2019-04-05 00:02:43 -04:00
|
|
|
DisassemblyInfo disassemblyInfo = (*cache)[addrInfo.Address];
|
2019-02-27 19:49:26 -05:00
|
|
|
|
|
|
|
uint8_t opSize = 0;
|
|
|
|
uint8_t opCode = (source + addrInfo.Address)[0];
|
|
|
|
bool needRealign = true;
|
2019-05-24 21:47:06 -04:00
|
|
|
|
|
|
|
bool isCode = addrInfo.Type == SnesMemoryType::PrgRom ? _cdl->IsCode(addrInfo.Address) : false;
|
|
|
|
bool isData = addrInfo.Type == SnesMemoryType::PrgRom ? _cdl->IsData(addrInfo.Address) : false;
|
|
|
|
|
|
|
|
if(disassemblyInfo.IsInitialized()) {
|
2019-04-06 17:38:14 -04:00
|
|
|
opSize = disassemblyInfo.GetOpSize();
|
2019-02-27 19:49:26 -05:00
|
|
|
needRealign = false;
|
2019-05-24 21:47:06 -04:00
|
|
|
} else if((isData && disData) || (!isData && !isCode && disUnident)) {
|
|
|
|
opSize = DisassemblyInfo::GetOpSize(opCode, 0, cpuType);
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-05-24 21:47:06 -04:00
|
|
|
if(opSize > 0) {
|
|
|
|
if(inUnknownBlock || inVerifiedBlock) {
|
|
|
|
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
|
|
|
results.push_back(DisassemblyResult(prevAddrInfo, i - 1, flags));
|
2019-02-27 19:49:26 -05:00
|
|
|
inUnknownBlock = false;
|
2019-05-24 21:47:06 -04:00
|
|
|
inVerifiedBlock = false;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
2019-05-24 21:47:06 -04:00
|
|
|
byteCounter = 0;
|
2019-02-27 19:49:26 -05:00
|
|
|
|
|
|
|
if(addrInfo.Type == SnesMemoryType::PrgRom && _cdl->IsSubEntryPoint(addrInfo.Address)) {
|
2019-04-28 22:19:52 -04:00
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::SubStart | LineFlags::BlockStart | LineFlags::VerifiedCode));
|
|
|
|
}
|
|
|
|
|
|
|
|
_labelManager->GetLabelAndComment(addrInfo, label, comment);
|
|
|
|
|
|
|
|
bool hasMultipleComment = comment.find_first_of('\n') != string::npos;
|
|
|
|
if(hasMultipleComment) {
|
|
|
|
int16_t lineCount = 0;
|
|
|
|
for(char c : comment) {
|
|
|
|
if(c == '\n') {
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment, lineCount));
|
|
|
|
lineCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment, lineCount));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(label.size()) {
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Label));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!hasMultipleComment && comment.size()) {
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment));
|
|
|
|
} else {
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i));
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if(needRealign) {
|
2019-07-01 18:35:21 -04:00
|
|
|
for(int j = 1, max = (int)(*cache).size(); j < opSize && addrInfo.Address + j < max; j++) {
|
2019-04-05 00:02:43 -04:00
|
|
|
if((*cache)[addrInfo.Address + j].IsInitialized()) {
|
2019-02-27 19:49:26 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
} else {
|
2019-04-06 17:38:14 -04:00
|
|
|
i += opSize - 1;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
if(DisassemblyInfo::IsReturnInstruction(opCode, cpuType)) {
|
2019-02-27 19:49:26 -05:00
|
|
|
//End of function
|
2019-04-07 12:25:14 -04:00
|
|
|
results.push_back(DisassemblyResult(-1, LineFlags::VerifiedCode | LineFlags::BlockEnd));
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
} else {
|
2019-05-24 21:47:06 -04:00
|
|
|
if(showData || showUnident) {
|
|
|
|
if((isData && inUnknownBlock) || (!isData && inVerifiedBlock)) {
|
|
|
|
if(isData && inUnknownBlock) {
|
|
|
|
//In an unknown block and the next byte is data, end the block
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockEnd | (showUnident ? LineFlags::ShowAsData : 0)));
|
|
|
|
} else if(!isData && inVerifiedBlock) {
|
|
|
|
//In a verified data block and the next byte is unknown, end the block
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockEnd | LineFlags::VerifiedData | (showData ? LineFlags::ShowAsData : 0)));
|
|
|
|
}
|
|
|
|
inUnknownBlock = false;
|
|
|
|
inVerifiedBlock = false;
|
|
|
|
byteCounter = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(byteCounter > 0) {
|
|
|
|
//If showing as hex data, add a new data line every 8 bytes
|
|
|
|
byteCounter--;
|
|
|
|
if(byteCounter == 0) {
|
|
|
|
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0)));
|
|
|
|
byteCounter = 8;
|
|
|
|
}
|
|
|
|
} else if(!inUnknownBlock && !inVerifiedBlock) {
|
|
|
|
//If not in a block, start a new block based on the current byte's type (data vs unidentified)
|
2019-07-02 20:18:57 -04:00
|
|
|
bool showAsData = (isData && showData) || ((!isData && !isCode) && showUnident);
|
2019-05-24 21:47:06 -04:00
|
|
|
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 = 8;
|
|
|
|
} else {
|
|
|
|
//If not showing the data at all, display 1 empty line
|
|
|
|
results.push_back(DisassemblyResult(-1, LineFlags::None | (isData ? LineFlags::VerifiedData : 0)));
|
|
|
|
}
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-24 21:47:06 -04:00
|
|
|
if(inUnknownBlock || inVerifiedBlock) {
|
|
|
|
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
2019-07-18 16:14:32 -04:00
|
|
|
results.push_back(DisassemblyResult(addrInfo, maxAddr, flags));
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressInfo &info)
|
|
|
|
{
|
2019-04-05 00:02:43 -04:00
|
|
|
DisassemblyInfo disassemblyInfo;
|
2019-02-27 19:49:26 -05:00
|
|
|
switch(info.Type) {
|
2019-03-31 14:50:12 -04:00
|
|
|
default: break;
|
2019-04-05 00:02:43 -04:00
|
|
|
case SnesMemoryType::PrgRom: disassemblyInfo = _prgCache[info.Address]; break;
|
|
|
|
case SnesMemoryType::WorkRam: disassemblyInfo = _wramCache[info.Address]; break;
|
|
|
|
case SnesMemoryType::SaveRam: disassemblyInfo = _sramCache[info.Address]; break;
|
2019-04-06 17:38:14 -04:00
|
|
|
case SnesMemoryType::SpcRam: disassemblyInfo = _spcRamCache[info.Address]; break;
|
|
|
|
case SnesMemoryType::SpcRom: disassemblyInfo = _spcRomCache[info.Address]; break;
|
2019-07-14 21:45:12 -04:00
|
|
|
case SnesMemoryType::DspProgramRom: disassemblyInfo = _necDspRomCache[info.Address]; break;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-04-05 00:02:43 -04:00
|
|
|
if(disassemblyInfo.IsInitialized()) {
|
|
|
|
return disassemblyInfo;
|
2019-02-27 19:49:26 -05:00
|
|
|
} else {
|
|
|
|
return DisassemblyInfo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-28 22:19:52 -04:00
|
|
|
void Disassembler::RefreshDisassembly(CpuType type)
|
|
|
|
{
|
|
|
|
DebugBreakHelper helper(_debugger);
|
|
|
|
_needDisassemble[(int)type] = true;
|
|
|
|
Disassemble(type);
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
uint32_t Disassembler::GetLineCount(CpuType type)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
auto lock = _disassemblyLock.AcquireSafe();
|
2019-04-07 12:25:14 -04:00
|
|
|
vector<DisassemblyResult> &source = type == CpuType::Cpu ? _disassembly : _spcDisassembly;
|
|
|
|
return (uint32_t)source.size();
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
uint32_t Disassembler::GetLineIndex(CpuType type, uint32_t cpuAddress)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
auto lock = _disassemblyLock.AcquireSafe();
|
2019-04-07 12:25:14 -04:00
|
|
|
vector<DisassemblyResult> &source = type == CpuType::Cpu ? _disassembly : _spcDisassembly;
|
2019-03-30 21:47:30 -04:00
|
|
|
uint32_t lastAddress = 0;
|
2019-04-07 12:25:14 -04:00
|
|
|
for(size_t i = 1; i < source.size(); i++) {
|
2019-04-28 22:19:52 -04:00
|
|
|
if(source[i].CpuAddress < 0 || (source[i].Flags & LineFlags::SubStart) | (source[i].Flags & LineFlags::Label) || ((source[i].Flags & LineFlags::Comment) && source[i].CommentLine >= 0)) {
|
2019-03-30 21:47:30 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
if(cpuAddress == (uint32_t)source[i].CpuAddress) {
|
2019-03-31 20:03:22 -04:00
|
|
|
return (uint32_t)i;
|
2019-04-07 12:25:14 -04:00
|
|
|
} else if(cpuAddress >= lastAddress && cpuAddress < (uint32_t)source[i].CpuAddress) {
|
2019-03-31 17:47:32 -04:00
|
|
|
return (uint32_t)i - 1;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
2019-03-30 21:47:30 -04:00
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
lastAddress = source[i].CpuAddress;
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &data)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
auto lock =_disassemblyLock.AcquireSafe();
|
2019-05-24 21:47:06 -04:00
|
|
|
bool isSpc = type == CpuType::Spc;
|
|
|
|
vector<DisassemblyResult> &source = isSpc ? _spcDisassembly : _disassembly;
|
|
|
|
int32_t maxAddr = isSpc ? 0xFFFF : 0xFFFFFF;
|
2019-04-07 12:25:14 -04:00
|
|
|
if(lineIndex < source.size()) {
|
|
|
|
DisassemblyResult result = source[lineIndex];
|
2019-04-28 22:19:52 -04:00
|
|
|
data.Address = -1;
|
|
|
|
data.AbsoluteAddress = -1;
|
2019-02-27 19:49:26 -05:00
|
|
|
data.Flags = result.Flags;
|
|
|
|
|
|
|
|
switch(result.Address.Type) {
|
2019-03-31 14:50:12 -04:00
|
|
|
default: break;
|
2019-02-27 19:49:26 -05:00
|
|
|
case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break;
|
|
|
|
case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam; break;
|
|
|
|
case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) != 0;
|
|
|
|
if(!isBlockStartEnd && result.Address.Address >= 0) {
|
2019-05-24 21:47:06 -04:00
|
|
|
if((data.Flags & LineFlags::ShowAsData)) {
|
2019-07-18 16:14:32 -04:00
|
|
|
FastString str(".db", 3);
|
|
|
|
SnesMemoryType memType = isSpc ? SnesMemoryType::SpcMemory : SnesMemoryType::CpuMemory;
|
|
|
|
int nextAddr = lineIndex < source.size() - 2 ? source[lineIndex+1].CpuAddress : (maxAddr + 1);
|
2019-05-24 21:47:06 -04:00
|
|
|
for(int i = 0; i < 8 && result.CpuAddress+i < nextAddr; i++) {
|
2019-07-18 16:14:32 -04:00
|
|
|
str.Write(" $", 2);
|
|
|
|
str.Write(HexUtilities::ToHexChar(_memoryDumper->GetMemoryValue(memType, result.CpuAddress + i)), 2);
|
2019-05-24 21:47:06 -04:00
|
|
|
}
|
|
|
|
data.Address = result.CpuAddress;
|
|
|
|
data.AbsoluteAddress = result.Address.Address;
|
2019-07-18 16:14:32 -04:00
|
|
|
memcpy(data.Text, str.ToString(), str.GetSize());
|
2019-05-24 21:47:06 -04:00
|
|
|
} else if((data.Flags & LineFlags::Comment) && result.CommentLine >= 0) {
|
2019-04-28 22:19:52 -04:00
|
|
|
string comment = ";" + StringUtilities::Split(_labelManager->GetComment(result.Address), '\n')[result.CommentLine];
|
|
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
|
|
memcpy(data.Comment, comment.c_str(), std::min<int>((int)comment.size(), 1000));
|
|
|
|
} else if(data.Flags & LineFlags::Label) {
|
|
|
|
string label = _labelManager->GetLabel(result.Address) + ":";
|
|
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
|
|
memcpy(data.Text, label.c_str(), std::min<int>((int)label.size(), 1000));
|
|
|
|
} else {
|
|
|
|
uint8_t *source;
|
|
|
|
uint32_t sourceLength;
|
|
|
|
vector<DisassemblyInfo> *cache;
|
|
|
|
GetSource(result.Address, &source, sourceLength, &cache);
|
|
|
|
DisassemblyInfo disInfo = (*cache)[result.Address.Address];
|
|
|
|
|
|
|
|
data.Address = result.CpuAddress;
|
|
|
|
data.AbsoluteAddress = result.Address.Address;
|
|
|
|
|
|
|
|
if(type == CpuType::Cpu) {
|
|
|
|
CpuState state = _console->GetCpu()->GetState();
|
|
|
|
state.PC = (uint16_t)result.CpuAddress;
|
|
|
|
state.K = (result.CpuAddress >> 16);
|
|
|
|
|
|
|
|
if(!disInfo.IsInitialized()) {
|
|
|
|
disInfo = DisassemblyInfo(source + result.Address.Address, state.PS, CpuType::Cpu);
|
|
|
|
} else {
|
|
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
|
|
}
|
2019-02-27 19:49:26 -05:00
|
|
|
|
2019-05-24 21:47:06 -04:00
|
|
|
data.OpSize = disInfo.GetOpSize();
|
2019-04-28 22:19:52 -04:00
|
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_console, &state);
|
2019-02-27 19:49:26 -05:00
|
|
|
|
2019-04-28 22:19:52 -04:00
|
|
|
if(data.EffectiveAddress >= 0) {
|
2019-05-24 21:47:06 -04:00
|
|
|
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryManager, data.ValueSize);
|
2019-04-28 22:19:52 -04:00
|
|
|
} else {
|
|
|
|
data.ValueSize = 0;
|
|
|
|
}
|
2019-04-07 12:25:14 -04:00
|
|
|
} else {
|
2019-04-28 22:19:52 -04:00
|
|
|
SpcState state = _console->GetSpc()->GetState();
|
|
|
|
state.PC = (uint16_t)result.CpuAddress;
|
2019-05-24 21:47:06 -04:00
|
|
|
|
|
|
|
if(!disInfo.IsInitialized()) {
|
|
|
|
disInfo = DisassemblyInfo(source + result.Address.Address, 0, CpuType::Spc);
|
|
|
|
} else {
|
|
|
|
data.Flags |= LineFlags::VerifiedCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.OpSize = disInfo.GetOpSize();
|
2019-04-28 22:19:52 -04:00
|
|
|
data.EffectiveAddress = disInfo.GetEffectiveAddress(_console, &state);
|
|
|
|
if(data.EffectiveAddress >= 0) {
|
|
|
|
data.Value = _spc->DebugRead(data.EffectiveAddress);
|
|
|
|
data.ValueSize = 1;
|
|
|
|
} else {
|
|
|
|
data.ValueSize = 0;
|
|
|
|
}
|
2019-04-07 12:25:14 -04:00
|
|
|
}
|
2019-02-28 16:53:04 -05:00
|
|
|
|
2019-04-28 22:19:52 -04:00
|
|
|
string text;
|
|
|
|
disInfo.GetDisassembly(text, result.CpuAddress, _labelManager.get());
|
|
|
|
memcpy(data.Text, text.c_str(), std::min<int>((int)text.size(), 1000));
|
|
|
|
|
|
|
|
disInfo.GetByteCode(data.ByteCode);
|
|
|
|
|
|
|
|
if(data.Flags & LineFlags::Comment) {
|
|
|
|
string comment = ";" + _labelManager->GetComment(result.Address);
|
|
|
|
memcpy(data.Comment, comment.c_str(), std::min<int>((int)comment.size(), 1000));
|
2019-04-07 17:57:30 -04:00
|
|
|
} else {
|
2019-04-28 22:19:52 -04:00
|
|
|
data.Comment[0] = 0;
|
2019-04-07 17:57:30 -04:00
|
|
|
}
|
2019-02-28 16:53:04 -05:00
|
|
|
}
|
2019-02-27 19:49:26 -05:00
|
|
|
} else {
|
|
|
|
if(data.Flags & LineFlags::SubStart) {
|
2019-04-28 22:19:52 -04:00
|
|
|
string label = _labelManager->GetLabel(result.Address);
|
|
|
|
if(label.empty()) {
|
|
|
|
label = "sub start";
|
|
|
|
}
|
2019-02-27 19:49:26 -05:00
|
|
|
memcpy(data.Text, label.c_str(), label.size() + 1);
|
|
|
|
} else if(data.Flags & LineFlags::BlockStart) {
|
2019-05-24 21:47:06 -04:00
|
|
|
string label = (data.Flags & LineFlags::VerifiedData) ? "data" : "unidentified";
|
2019-02-27 19:49:26 -05:00
|
|
|
memcpy(data.Text, label.c_str(), label.size() + 1);
|
|
|
|
}
|
2019-05-24 21:47:06 -04:00
|
|
|
|
|
|
|
if(data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) {
|
|
|
|
if(!(data.Flags & LineFlags::ShowAsData)) {
|
|
|
|
//For hidden blocks, give the start/end lines an address
|
|
|
|
data.Address = result.CpuAddress;
|
|
|
|
data.AbsoluteAddress = result.Address.Address;
|
|
|
|
}
|
|
|
|
}
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-07 12:25:14 -04:00
|
|
|
int32_t Disassembler::SearchDisassembly(CpuType type, const char *searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards)
|
2019-02-27 19:49:26 -05:00
|
|
|
{
|
|
|
|
auto lock = _disassemblyLock.AcquireSafe();
|
2019-04-07 12:25:14 -04:00
|
|
|
vector<DisassemblyResult> &source = type == CpuType::Cpu ? _disassembly : _spcDisassembly;
|
2019-02-27 19:49:26 -05:00
|
|
|
int step = searchBackwards ? -1 : 1;
|
|
|
|
CodeLineData lineData = {};
|
|
|
|
for(int i = startPosition; i != endPosition; i += step) {
|
2019-04-07 12:25:14 -04:00
|
|
|
GetLineData(type, i, lineData);
|
2019-02-27 19:49:26 -05:00
|
|
|
string line = lineData.Text;
|
|
|
|
std::transform(line.begin(), line.end(), line.begin(), ::tolower);
|
|
|
|
|
|
|
|
if(line.find(searchString) != string::npos) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Continue search from start/end of document
|
2019-04-07 12:25:14 -04:00
|
|
|
if(!searchBackwards && i == (int)(source.size() - 1)) {
|
2019-02-27 19:49:26 -05:00
|
|
|
i = 0;
|
|
|
|
} else if(searchBackwards && i == 0) {
|
2019-04-07 12:25:14 -04:00
|
|
|
i = (int32_t)(source.size() - 1);
|
2019-02-27 19:49:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|