Mesen2/Core/SNES/Debugger/SnesDisUtils.cpp
2024-03-30 11:42:31 +09:00

387 lines
No EOL
16 KiB
C++

#include "pch.h"
#include "SNES/Debugger/SnesDisUtils.h"
#include "SNES/SnesCpuTypes.h"
#include "SNES/SnesCpu.h"
#include "SNES/SnesConsole.h"
#include "Shared/EmuSettings.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/LabelManager.h"
#include "SNES/Debugger/DummySnesCpu.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/FastString.h"
void SnesDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
{
FastString str(settings->GetDebugConfig().UseLowerCaseDisassembly);
uint8_t opCode = info.GetOpCode();
SnesAddrMode addrMode = SnesDisUtils::OpMode[opCode];
str.Write(SnesDisUtils::OpName[opCode]);
str.Write(' ');
uint32_t opAddr = SnesDisUtils::GetOperandAddress(info, memoryAddr);
uint32_t opSize = info.GetOpSize();
FastString operand(settings->GetDebugConfig().UseLowerCaseDisassembly);
if(opSize > 1) {
if(addrMode == SnesAddrMode::Rel || addrMode == SnesAddrMode::RelLng || opSize == 4) {
AddressInfo address { (int32_t)opAddr, MemoryType::SnesMemory };
string label = labelManager ? labelManager->GetLabel(address) : "";
if(label.size()) {
operand.Write(label, true);
} else {
operand.WriteAll('$', HexUtilities::ToHex24(opAddr));
}
} else if(opSize == 2) {
operand.WriteAll('$', HexUtilities::ToHex((uint8_t)opAddr));
} else if(opSize == 3) {
operand.WriteAll('$', HexUtilities::ToHex((uint16_t)opAddr));
}
}
switch(addrMode) {
case SnesAddrMode::Abs: str.Write(operand); break;
case SnesAddrMode::AbsJmp: str.Write(operand); break;
case SnesAddrMode::AbsIdxXInd: str.WriteAll('(', operand, ",X)"); break;
case SnesAddrMode::AbsIdxX: str.WriteAll(operand, ",X"); break;
case SnesAddrMode::AbsIdxY: str.WriteAll(operand, ",Y"); break;
case SnesAddrMode::AbsInd: str.WriteAll('(', operand, ')'); break;
case SnesAddrMode::AbsIndLng: str.WriteAll('[', operand, ']'); break;
case SnesAddrMode::AbsLngIdxX: str.WriteAll(operand, ",X"); break;
case SnesAddrMode::AbsLng: str.Write(operand); break;
case SnesAddrMode::AbsLngJmp: str.Write(operand); break;
case SnesAddrMode::Acc: break;
case SnesAddrMode::BlkMov: str.WriteAll('$', operand[1], operand[2], ','); str.WriteAll('$', operand[3], operand[4]); break;
case SnesAddrMode::DirIdxIndX: str.WriteAll('(', operand, ",X)"); break;
case SnesAddrMode::DirIdxX: str.WriteAll(operand, ",X"); break;
case SnesAddrMode::DirIdxY: str.WriteAll(operand, ",Y"); break;
case SnesAddrMode::DirIndIdxY: str.WriteAll("(", operand, "),Y"); break;
case SnesAddrMode::DirIndLngIdxY: str.WriteAll("[", operand, "],Y"); break;
case SnesAddrMode::DirIndLng: str.WriteAll("[", operand, "]"); break;
case SnesAddrMode::DirInd: str.WriteAll("(", operand, ")"); break;
case SnesAddrMode::Dir: str.Write(operand); break;
case SnesAddrMode::Imm8: case SnesAddrMode::Imm16: case SnesAddrMode::ImmX: case SnesAddrMode::ImmM:
str.WriteAll('#', operand);
break;
case SnesAddrMode::Imp: break;
case SnesAddrMode::RelLng: str.Write(operand); break;
case SnesAddrMode::Rel: str.Write(operand); break;
case SnesAddrMode::Stk: break;
case SnesAddrMode::StkRel: str.WriteAll(operand, ",S"); break;
case SnesAddrMode::StkRelIndIdxY: str.WriteAll('(', operand, ",S),Y"); break;
default: throw std::runtime_error("invalid address mode");
}
out += str.ToString();
}
uint32_t SnesDisUtils::GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAddr)
{
uint32_t opSize = info.GetOpSize();
uint32_t opAddr = 0;
uint8_t* byteCode = info.GetByteCode();
if(opSize == 2) {
opAddr = byteCode[1];
} else if(opSize == 3) {
opAddr = byteCode[1] | (byteCode[2] << 8);
} else if(opSize == 4) {
opAddr = byteCode[1] | (byteCode[2] << 8) | (byteCode[3] << 16);
}
SnesAddrMode addrMode = SnesDisUtils::OpMode[byteCode[0]];
if(addrMode == SnesAddrMode::Rel || addrMode == SnesAddrMode::RelLng) {
if(opSize == 2) {
opAddr = (memoryAddr & 0xFF0000) | (((int8_t)opAddr + memoryAddr + 2) & 0xFFFF);
} else {
opAddr = (memoryAddr & 0xFF0000) | (((int16_t)opAddr + memoryAddr + 3) & 0xFFFF);
}
}
return opAddr;
}
EffectiveAddressInfo SnesDisUtils::GetEffectiveAddress(DisassemblyInfo &info, SnesConsole *console, SnesCpuState &state, CpuType type)
{
SnesAddrMode opMode = SnesDisUtils::OpMode[info.GetOpCode()];
if(opMode == SnesAddrMode::Stk) {
//Show nothing for stack operations (push/pull)
return {};
}
bool showEffectiveAddress = HasEffectiveAddress(opMode);
DummySnesCpu dummyCpu(console, type);
state.PS &= ~(ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
state.PS |= info.GetFlags();
dummyCpu.SetDummyState(state);
dummyCpu.Exec();
bool isJump = SnesDisUtils::IsUnconditionalJump(info.GetOpCode()) || SnesDisUtils::IsConditionalJump(info.GetOpCode());
if(isJump) {
if(info.GetOpSize() == 3) {
//For 3-byte jumps, return the target address, and show no value
if(SnesDisUtils::IsUnconditionalJump(info.GetOpCode())) {
//Display the dummy cpu's current address - this allows indirect jumps to display the correct address based on K
return { (dummyCpu.GetState().K << 16) | dummyCpu.GetState().PC, 0, true };
} else {
return { dummyCpu.GetLastOperand(), 0, true };
}
} else {
//Relative or long jumps already show the final address in the disassembly, show nothing
return {};
}
}
//For everything else, return the last read/write address
uint32_t count = dummyCpu.GetOperationCount();
for(int i = count - 1; i > 0; i--) {
MemoryOperationInfo opInfo = dummyCpu.GetOperationInfo(i);
if(opInfo.Type != MemoryOperationType::ExecOperand) {
MemoryOperationInfo prevOpInfo = dummyCpu.GetOperationInfo(i - 1);
EffectiveAddressInfo result;
if(prevOpInfo.Type == opInfo.Type && prevOpInfo.Address == opInfo.Address - 1) {
//For 16-bit read/writes, return the first address
result.Address = prevOpInfo.Address;
result.Type = prevOpInfo.MemType;
result.ValueSize = 2;
} else if(opInfo.Type == MemoryOperationType::Write && prevOpInfo.Type == opInfo.Type && prevOpInfo.Address == opInfo.Address + 1) {
//For 16-bit RMW instructions, the 2nd byte is written first
result.Address = opInfo.Address;
result.Type = opInfo.MemType;
result.ValueSize = 2;
} else {
result.Address = opInfo.Address;
result.Type = opInfo.MemType;
result.ValueSize = 1;
}
result.ShowAddress = showEffectiveAddress;
return result;
}
}
return {};
}
bool SnesDisUtils::CanDisassembleNextOp(uint8_t opCode)
{
//Stop disassembling on PLP (because it can alter X/M flags)
return opCode != 0x28;
}
void SnesDisUtils::UpdateCpuFlags(uint8_t opCode, uint8_t* byteCode, uint8_t& cpuFlags)
{
if(opCode == 0xC2) {
//REP, update the flags and keep disassembling
uint8_t flags = byteCode[1];
cpuFlags &= ~flags;
} else if(opCode == 0xE2) {
//SEP, update the flags and keep disassembling
uint8_t flags = byteCode[1];
cpuFlags |= flags;
}
}
bool SnesDisUtils::HasEffectiveAddress(SnesAddrMode addrMode)
{
switch(addrMode) {
case SnesAddrMode::Acc:
case SnesAddrMode::Imp:
case SnesAddrMode::Stk:
case SnesAddrMode::Sig8:
case SnesAddrMode::Imm8:
case SnesAddrMode::Rel:
case SnesAddrMode::RelLng:
case SnesAddrMode::Imm16:
case SnesAddrMode::BlkMov:
case SnesAddrMode::AbsLngJmp:
case SnesAddrMode::AbsLng:
case SnesAddrMode::ImmX:
case SnesAddrMode::ImmM:
case SnesAddrMode::AbsJmp:
return false;
case SnesAddrMode::DirIdxIndX:
case SnesAddrMode::DirIdxX:
case SnesAddrMode::DirIdxY:
case SnesAddrMode::DirIndIdxY:
case SnesAddrMode::DirIndLngIdxY:
case SnesAddrMode::DirIndLng:
case SnesAddrMode::DirInd:
case SnesAddrMode::Dir:
case SnesAddrMode::StkRel:
case SnesAddrMode::StkRelIndIdxY:
case SnesAddrMode::Abs:
case SnesAddrMode::AbsIdxXInd:
case SnesAddrMode::AbsIdxX:
case SnesAddrMode::AbsIdxY:
case SnesAddrMode::AbsLngIdxX:
case SnesAddrMode::AbsInd:
case SnesAddrMode::AbsIndLng:
return true;
}
throw std::runtime_error("Invalid mode");
}
uint8_t SnesDisUtils::GetOpSize(SnesAddrMode addrMode, uint8_t flags)
{
if(addrMode == SnesAddrMode::ImmX) {
return (flags & ProcFlags::IndexMode8) ? 2 : 3;
} else if(addrMode == SnesAddrMode::ImmM) {
return (flags & ProcFlags::MemoryMode8) ? 2 : 3;
}
return SnesDisUtils::OpSize[(int)addrMode];
}
uint8_t SnesDisUtils::GetOpSize(uint8_t opCode, uint8_t flags)
{
return GetOpSize(SnesDisUtils::OpMode[opCode], flags);
}
bool SnesDisUtils::IsUnconditionalJump(uint8_t opCode)
{
switch(opCode) {
case 0x00: //BRK
case 0x02: //COP
case 0x20: //JSR
case 0x22: //JSR
case 0x40: //RTI
case 0x4C: //JMP
case 0x5C: //JMP
case 0x60: //RTS
case 0x6B: //RTL
case 0x6C: //JMP
case 0x7C: //JMP
case 0x80: //BRA
case 0x82: //BRL
case 0xDC: //JMP
case 0xFC: //JSR
return true;
default:
return false;
}
}
bool SnesDisUtils::IsConditionalJump(uint8_t opCode)
{
switch(opCode) {
case 0x10: //BPL
case 0x30: //BMI
case 0x50: //BVC
case 0x70: //BVS
case 0x90: //BCC
case 0xB0: //BCS
case 0xD0: //BNE
case 0xF0: //BEQ
return true;
default:
return false;
}
}
CdlFlags::CdlFlags SnesDisUtils::GetOpFlags(uint8_t opCode, uint32_t pc, uint32_t prevPc)
{
switch(opCode) {
case 0x00: //BRK
case 0x02: //COP
case 0x20: //JSR
case 0x22: //JSR
case 0xFC: //JSR
return CdlFlags::SubEntryPoint;
case 0x10: //BPL
case 0x30: //BMI
case 0x50: //BVC
case 0x70: //BVS
case 0x90: //BCC
case 0xB0: //BCS
case 0xD0: //BNE
case 0xF0: //BEQ
case 0x4C: //JMP
case 0x5C: //JMP
case 0x6C: //JMP
case 0x7C: //JMP
case 0x80: //BRA
case 0x82: //BRL
case 0xDC: //JMP
return pc != prevPc + SnesDisUtils::GetOpSize(opCode, 0) ? CdlFlags::JumpTarget : CdlFlags::None;
default:
return CdlFlags::None;
}
}
bool SnesDisUtils::IsJumpToSub(uint8_t opCode)
{
switch(opCode) {
case 0x00: //BRK
case 0x02: //COP
case 0x20: //JSR
case 0x22: //JSL
case 0xFC: //JSR
return true;
default:
return false;
}
}
bool SnesDisUtils::IsReturnInstruction(uint8_t opCode)
{
return opCode == 0x60 || opCode == 0x6B || opCode == 0x40;
}
uint8_t SnesDisUtils::OpSize[0x1F] = {
2, 2, 3, 0, 0, 3, 3, 3, 3, 3,
3, 4, 4, 3, 4, 1, 3, 2, 2, 2,
2, 2, 2, 2, 2, 1, 3, 2, 1, 2,
2
};
string SnesDisUtils::OpName[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
"BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", // 0
"BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", // 1
"JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", // 2
"BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", // 3
"RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", // 4
"BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", "CLI", "EOR", "PHY", "TCD", "JML", "EOR", "LSR", "EOR", // 5
"RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", // 6
"BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", // 7
"BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", // 8
"BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", // 9
"LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", // A
"BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", // B
"CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", // C
"BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", // D
"CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", // E
"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" // F
};
typedef SnesAddrMode M;
SnesAddrMode SnesDisUtils::OpMode[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
M::Imm8, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 0
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 1
M::Abs, M::DirIdxIndX, M::AbsLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 2
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 3
M::Stk, M::DirIdxIndX, M::Imm8, M::StkRel, M::BlkMov, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 4
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::BlkMov, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 5
M::Stk, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::AbsInd, M::Abs, M::Abs, M::AbsLng, // 6
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 7
M::Rel, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 8
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 9
M::ImmX, M::DirIdxIndX, M::ImmX, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // A
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxY, M::AbsLngIdxX, // B
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // C
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIndLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // D
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // E
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Imm16, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX // F
};