Files
SourMesen e391fd8f9a Code: Add auto-formatting rules for C++ (clang-format) and enforce in CI via "clang-format" and "dotnet format" (#78)
* Code: Add auto-formatting rules for C++ projects (clang-format) and enforce code style via "clang-format" and "dotnet format"

* fix ui format warnings
2026-04-30 06:03:55 -07:00

627 lines
21 KiB
C++

#include "pch.h"
#include "SNES/SnesCpu.h"
#include "SNES/Coprocessors/SA1/Sa1.h"
#include "SNES/SnesMemoryManager.h"
#include "SNES/BaseCartridge.h"
#include "SNES/SnesConsole.h"
#include "SNES/Spc.h"
#include "SNES/SnesPpu.h"
#include "SNES/MemoryMappings.h"
#include "SNES/Input/SnesController.h"
#include "SNES/Debugger/DummySnesCpu.h"
#include "SNES/Debugger/SnesDisUtils.h"
#include "SNES/Debugger/SnesCodeDataLogger.h"
#include "SNES/Debugger/SnesAssembler.h"
#include "SNES/Debugger/SnesDebugger.h"
#include "SNES/Debugger/SnesEventManager.h"
#include "SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h"
#include "SNES/Debugger/SnesPpuTools.h"
#include "Debugger/CdlManager.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/MemoryDumper.h"
#include "Debugger/MemoryAccessCounter.h"
#include "Debugger/ExpressionEvaluator.h"
#include "Debugger/ScriptManager.h"
#include "Debugger/Debugger.h"
#include "Debugger/CodeDataLogger.h"
#include "Debugger/StepBackManager.h"
#include "Shared/SettingTypes.h"
#include "Shared/BaseControlManager.h"
#include "Shared/EmuSettings.h"
#include "Shared/Emulator.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/FolderUtilities.h"
#include "Utilities/Patches/IpsPatcher.h"
#include "Utilities/CRC32.h"
#include "Shared/MemoryOperationType.h"
SnesDebugger::SnesDebugger(Debugger* debugger, CpuType cpuType) : IDebugger(debugger->GetEmulator())
{
_cpuType = cpuType;
_cpuMemType = _cpuType == CpuType::Snes ? MemoryType::SnesMemory : MemoryType::Sa1Memory;
_debugger = debugger;
_emu = debugger->GetEmulator();
SnesConsole* console = (SnesConsole*)debugger->GetConsole();
_console = console;
_disassembler = debugger->GetDisassembler();
_memoryAccessCounter = debugger->GetMemoryAccessCounter();
_cpu = console->GetCpu();
_sa1 = console->GetCartridge()->GetSa1();
_settings = debugger->GetEmulator()->GetSettings();
_memoryManager = console->GetMemoryManager();
_cart = console->GetCartridge();
_spc = console->GetSpc();
_ppu = console->GetPpu();
_traceLogger.reset(new SnesCpuTraceLogger(debugger, this, cpuType, _ppu, _memoryManager));
_ppuTools.reset(new SnesPpuTools(debugger, debugger->GetEmulator()));
if(_cpuType == CpuType::Snes) {
_memoryMappings = _memoryManager->GetMemoryMappings();
} else {
_memoryMappings = _sa1->GetMemoryMappings();
}
if(cpuType == CpuType::Snes) {
uint32_t crc32 = CRC32::GetCRC((uint8_t*)_emu->GetMemory(MemoryType::SnesPrgRom).Memory, _emu->GetMemory(MemoryType::SnesPrgRom).Size);
_codeDataLogger.reset(new SnesCodeDataLogger(debugger, MemoryType::SnesPrgRom, console->GetCartridge()->DebugGetPrgRomSize(), CpuType::Snes, crc32));
_cdl = _codeDataLogger.get();
_cdlFile = _codeDataLogger->GetCdlFilePath(_console->GetCartridge()->GetGameboy() ? "SgbFirmware.cdl" : _emu->GetRomInfo().RomFile.GetFileName());
_codeDataLogger->LoadCdlFile(_cdlFile, _settings->GetDebugConfig().AutoResetCdl);
} else {
_cdl = (SnesCodeDataLogger*)_debugger->GetCdlManager()->GetCodeDataLogger(MemoryType::SnesPrgRom);
}
_stepBackManager.reset(new StepBackManager(_emu, this));
_eventManager.reset(new SnesEventManager(debugger, _cpu, console->GetPpu(), _memoryManager, console->GetDmaController()));
_callstackManager.reset(new CallstackManager(debugger, this));
_breakpointManager.reset(new BreakpointManager(debugger, this, cpuType, _eventManager.get()));
_step.reset(new StepRequest());
_assembler.reset(new SnesAssembler(_debugger->GetLabelManager()));
_dummyCpu.reset(new DummySnesCpu(_console, _cpuType));
}
SnesDebugger::~SnesDebugger()
{
if(_codeDataLogger) {
_codeDataLogger->SaveCdlFile(_cdlFile);
}
}
void SnesDebugger::Init()
{
_spcTraceLogger = _debugger->GetTraceLogger(CpuType::Spc);
_dspTraceLogger = _debugger->GetTraceLogger(CpuType::NecDsp);
}
void SnesDebugger::OnBeforeBreak(CpuType cpuType)
{
//Don't catch up audio when the SPC debugger pauses - this causes a stack overflow
if(cpuType != CpuType::Spc) {
_console->RunAudio();
}
}
void SnesDebugger::Reset()
{
_callstackManager->Clear();
ResetPrevOpCode();
}
void SnesDebugger::ProcessConfigChange()
{
_debuggerEnabled = _settings->CheckDebuggerFlag(_cpuType == CpuType::Snes ? DebuggerFlags::SnesDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled);
_predictiveBreakpoints = _settings->GetDebugConfig().UsePredictiveBreakpoints;
_runSpc = _spcTraceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled);
_runCoprocessors =
(_dspTraceLogger && _dspTraceLogger->IsEnabled()) ||
_settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled) ||
_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled);
_needCoprocessors = _runSpc || _runCoprocessors;
}
uint64_t SnesDebugger::GetCpuCycleCount(bool forProfiler)
{
if(forProfiler && _cpuType == CpuType::Snes) {
return _memoryManager->GetMasterClock();
} else {
return GetCpuState().CycleCount;
}
}
void SnesDebugger::ResetPrevOpCode()
{
_prevOpCode = 0xFF;
}
void SnesDebugger::ProcessInstruction()
{
SnesCpuState& state = GetCpuState();
uint32_t pc = (state.K << 16) | state.PC;
AddressInfo addressInfo = GetAbsoluteAddress(pc);
uint8_t opCode = _memoryMappings->Peek(pc);
MemoryOperationInfo operation(pc, opCode, MemoryOperationType::ExecOpCode, _cpuMemType);
InstructionProgress.LastMemOperation = operation;
InstructionProgress.StartCycle = state.CycleCount;
if(addressInfo.Address >= 0) {
uint8_t cpuFlags = state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
if(addressInfo.Type == MemoryType::SnesPrgRom) {
_cdl->SetCode(addressInfo.Address, SnesDisUtils::GetOpFlags(_prevOpCode, pc, _prevProgramCounter) | cpuFlags);
}
if(_traceLogger->IsEnabled() || _debuggerEnabled) {
_disassembler->BuildCache(addressInfo, cpuFlags, _cpuType);
}
}
ProcessCallStackUpdates(addressInfo, pc, state.PS, state.SP);
if(_step->BreakAddress == (int32_t)pc && _step->BreakStackPointer == state.SP && (SnesDisUtils::IsReturnInstruction(_prevOpCode) || _prevOpCode == 0x44 || _prevOpCode == 0x54)) {
//RTS/RTL/RTI found, if we're on the expected return address, break immediately (for step over/step out)
//Also used for MVN/MVP
_step->Break(BreakSource::CpuStep);
}
_prevOpCode = opCode;
_prevProgramCounter = pc;
_prevStackPointer = state.SP;
_step->ProcessCpuExec();
if(_debuggerEnabled) {
//Break on BRK/STP/WDM/COP
switch(opCode) {
case 0x00:
if(_settings->GetDebugConfig().SnesBreakOnBrk) {
_step->Break(BreakSource::BreakOnBrk);
}
break;
case 0x02:
if(_settings->GetDebugConfig().SnesBreakOnCop) {
_step->Break(BreakSource::BreakOnCop);
}
break;
case 0x42:
if(_settings->GetDebugConfig().SnesBreakOnWdm) {
_step->Break(BreakSource::BreakOnWdm);
}
break;
case 0xDB:
if(_settings->GetDebugConfig().SnesBreakOnStp) {
_step->Break(BreakSource::BreakOnStp);
}
break;
}
}
if(_step->StepCount != 0 && _breakpointManager->HasBreakpoints() && _predictiveBreakpoints) {
_dummyCpu->SetDummyState(state);
_dummyCpu->Exec();
for(uint32_t i = 1; i < _dummyCpu->GetOperationCount(); i++) {
MemoryOperationInfo memOp = _dummyCpu->GetOperationInfo(i);
if(_breakpointManager->HasBreakpointForType(memOp.Type)) {
AddressInfo absAddr = GetAbsoluteAddress(memOp.Address);
_debugger->ProcessPredictiveBreakpoint(_cpuType, _breakpointManager.get(), memOp, absAddr);
}
}
}
_debugger->ProcessBreakConditions(_cpuType, *_step.get(), _breakpointManager.get(), operation, addressInfo);
}
void SnesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, _cpuMemType);
InstructionProgress.LastMemOperation = operation;
SnesCpuState& state = GetCpuState();
if(IsRegister(addr)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType);
_traceLogger->Log(state, disInfo, operation, addressInfo);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
if(_step->ProcessCpuCycle()) {
_debugger->SleepUntilResume(_cpuType, BreakSource::CpuStep, &operation);
}
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Type == MemoryType::SnesPrgRom && addressInfo.Address >= 0) {
_cdl->SetCode(addressInfo.Address, (state.PS & (SnesCdlFlags::IndexMode8 | SnesCdlFlags::MemoryMode8)));
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation, addressInfo);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
_step->ProcessCpuCycle();
_debugger->ProcessBreakConditions(_cpuType, *_step.get(), _breakpointManager.get(), operation, addressInfo);
} else {
if(addressInfo.Type == MemoryType::SnesPrgRom && addressInfo.Address >= 0) {
_cdl->SetData(addressInfo.Address);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation, addressInfo);
}
ReadResult result = _memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
if(result != ReadResult::Normal) {
//Memory access was a read on an uninitialized memory address
if(result == ReadResult::FirstUninitRead) {
//Only warn the first time
_debugger->Log(string(_cpuType == CpuType::Sa1 ? "[SA1]" : "[CPU]") + " Uninitialized memory read: $" + HexUtilities::ToHex24(addr));
}
if(_debuggerEnabled && _settings->GetDebugConfig().BreakOnUninitRead) {
_step->Break(BreakSource::BreakOnUninitMemoryRead);
}
}
if(type != MemoryOperationType::DmaRead) {
_step->ProcessCpuCycle();
}
_debugger->ProcessBreakConditions(_cpuType, *_step.get(), _breakpointManager.get(), operation, addressInfo);
}
}
void SnesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, _cpuMemType);
InstructionProgress.LastMemOperation = operation;
if(addressInfo.Address >= 0 && (addressInfo.Type == MemoryType::SnesWorkRam || addressInfo.Type == MemoryType::SnesSaveRam)) {
_disassembler->InvalidateCache(addressInfo, _cpuType);
}
if(IsRegister(addr)) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation, addressInfo);
}
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
if(type != MemoryOperationType::DmaWrite) {
_step->ProcessCpuCycle();
}
_debugger->ProcessBreakConditions(_cpuType, *_step.get(), _breakpointManager.get(), operation, addressInfo);
}
void SnesDebugger::ProcessIdleCycle()
{
if(_step->ProcessCpuCycle()) {
_debugger->SleepUntilResume(_cpuType, BreakSource::CpuStep);
}
}
AddressInfo SnesDebugger::GetAbsoluteAddress(uint32_t addr)
{
if(IsRegister(addr)) {
return { (int32_t)(addr & 0xFFFF), MemoryType::SnesRegister };
} else {
return _memoryMappings->GetAbsoluteAddress(addr);
}
}
void SnesDebugger::Run()
{
_step.reset(new StepRequest());
}
void SnesDebugger::Step(int32_t stepCount, StepType type)
{
StepRequest step(type);
switch(type) {
case StepType::Step: step.StepCount = stepCount; break;
case StepType::StepOut:
step.BreakAddress = _callstackManager->GetReturnAddress();
step.BreakStackPointer = _callstackManager->GetReturnStackPointer();
break;
case StepType::StepOver:
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC || _prevOpCode == 0x00 || _prevOpCode == 0x02 || _prevOpCode == 0x44 || _prevOpCode == 0x54) {
//JSR, JSL, BRK, COP, MVP, MVN
step.BreakAddress = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + SnesDisUtils::GetOpSize(_prevOpCode, 0)) & 0xFFFF);
step.BreakStackPointer = _prevStackPointer;
} else {
//For any other instruction, step over is the same as step into
step.StepCount = 1;
}
break;
case StepType::CpuCycleStep: step.CpuCycleStepCount = stepCount; break;
case StepType::PpuStep: step.PpuStepCount = stepCount; break;
case StepType::PpuScanline: step.PpuStepCount = 341 * stepCount; break;
case StepType::PpuFrame: step.PpuStepCount = 341 * (_ppu->GetVblankEndScanline() + 1) * stepCount; break;
case StepType::SpecificScanline: step.BreakScanline = stepCount; break;
}
_step.reset(new StepRequest(step));
}
StepBackConfig SnesDebugger::GetStepBackConfig()
{
if(_cpuType == CpuType::Snes) {
return {
_memoryManager->GetMasterClock(),
1364,
1364u * (_ppu->GetVblankEndScanline() + 1)
};
} else {
return IDebugger::GetStepBackConfig();
}
}
void SnesDebugger::DrawPartialFrame()
{
_ppu->DebugSendFrame();
}
void SnesDebugger::ProcessCallStackUpdates(AddressInfo& destAddr, uint32_t destPc, uint8_t cpuFlags, uint16_t sp)
{
if(SnesDisUtils::IsJumpToSub(_prevOpCode)) {
//JSR, JSL
uint8_t opSize = SnesDisUtils::GetOpSize(_prevOpCode, cpuFlags);
uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF);
AddressInfo srcAddress = _memoryMappings->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo retAddress = _memoryMappings->GetAbsoluteAddress(returnPc);
_callstackManager->Push(srcAddress, _prevProgramCounter, destAddr, destPc, retAddress, returnPc, _prevStackPointer, StackFrameFlags::None);
} else if(SnesDisUtils::IsReturnInstruction(_prevOpCode)) {
//RTS, RTL, RTI
_callstackManager->Pop(destAddr, destPc, sp);
}
}
void SnesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
AddressInfo ret = _memoryMappings->GetAbsoluteAddress(originalPc);
AddressInfo dest = _memoryMappings->GetAbsoluteAddress(currentPc);
if(dest.Type == MemoryType::SnesPrgRom && dest.Address >= 0) {
_cdl->SetCode(dest.Address, CdlFlags::SubEntryPoint);
}
//This assumes that the CPU is never running in emulation mode,
//which is almost always true. This doesn't need to be perfect since
//it only has minor impacts on the debugger (with step out/over)
uint16_t originalSp = GetCpuState().SP + 4;
_prevStackPointer = originalSp;
//If a call/return occurred just before IRQ, it needs to be processed now
ProcessCallStackUpdates(ret, originalPc, GetCpuState().PS, originalSp);
ResetPrevOpCode();
_debugger->InternalProcessInterrupt(_cpuType, *this, *_step.get(), ret, originalPc, dest, currentPc, ret, originalPc, originalSp, forNmi);
}
void SnesDebugger::ProcessPpuRead(uint16_t addr, uint8_t value, MemoryType memoryType)
{
MemoryOperationInfo operation(addr, value, MemoryOperationType::Read, memoryType);
AddressInfo addressInfo { addr, memoryType };
_debugger->ProcessBreakConditions(CpuType::Snes, *_step.get(), _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock());
}
void SnesDebugger::ProcessPpuWrite(uint16_t addr, uint8_t value, MemoryType memoryType)
{
MemoryOperationInfo operation(addr, value, MemoryOperationType::Write, memoryType);
AddressInfo addressInfo { addr, memoryType };
_debugger->ProcessBreakConditions(CpuType::Snes, *_step.get(), _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _console->GetMasterClock());
}
void SnesDebugger::ProcessPpuCycle()
{
if(_ppuTools->HasOpenedViewer()) {
_ppuTools->UpdateViewers(_ppu->GetScanline(), _ppu->GetCycle());
}
if(_step->HasRequest) {
if(_step->HasScanlineBreakRequest() && _ppu->GetScanline() == _step->BreakScanline && _memoryManager->GetHClock() == 0) {
_debugger->SleepUntilResume(CpuType::Snes, _step->GetBreakSource());
} else if(_step->PpuStepCount > 0) {
_step->PpuStepCount--;
if(_step->PpuStepCount == 0) {
_debugger->SleepUntilResume(CpuType::Snes, _step->GetBreakSource());
}
}
}
//Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs)
if(_needCoprocessors) {
if(_runSpc) {
_spc->Run();
}
if(_runCoprocessors) {
_cart->RunCoprocessors();
}
}
}
SnesCpuState& SnesDebugger::GetCpuState()
{
if(_cpuType == CpuType::Snes) {
return _cpu->GetState();
} else {
return _sa1->GetCpuState();
}
}
bool SnesDebugger::IsRegister(uint32_t addr)
{
return _cpuType == CpuType::Snes && _memoryManager->IsRegister(addr);
}
DebuggerFeatures SnesDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.RunToIrq = true;
features.RunToNmi = true;
features.StepOver = true;
features.StepOut = true;
features.StepBack = true;
features.CallStack = true;
features.ChangeProgramCounter = AllowChangeProgramCounter;
features.CpuCycleStep = true;
features.CpuVectors[0] = { "NMI", 0xFFEA };
features.CpuVectors[1] = { "IRQ", 0xFFEE };
features.CpuVectors[2] = { "Reset", 0xFFFC };
features.CpuVectors[3] = { "BRK", 0xFFE6 };
features.CpuVectors[4] = { "COP", 0xFFE4 };
features.CpuVectors[5] = { "NMI (6502)", 0xFFFA };
features.CpuVectors[6] = { "IRQ (6502)", 0xFFFE };
features.CpuVectors[7] = { "COP (6502)", 0xFFF4 };
features.CpuVectorCount = 8;
return features;
}
void SnesDebugger::SetProgramCounter(uint32_t addr, bool updateDebuggerOnly)
{
if(!updateDebuggerOnly) {
GetCpuState().PC = (uint16_t)addr;
GetCpuState().K = (uint8_t)(addr >> 16);
}
_prevOpCode = _memoryMappings->Peek(addr);
_prevProgramCounter = addr;
_prevStackPointer = GetCpuState().SP;
}
uint32_t SnesDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : ((GetCpuState().K << 16) | GetCpuState().PC);
}
uint8_t SnesDebugger::GetCpuFlags(uint32_t addr)
{
return GetCpuState().PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
}
CallstackManager* SnesDebugger::GetCallstackManager()
{
return _callstackManager.get();
}
ITraceLogger* SnesDebugger::GetTraceLogger()
{
return _traceLogger.get();
}
BreakpointManager* SnesDebugger::GetBreakpointManager()
{
return _breakpointManager.get();
}
IAssembler* SnesDebugger::GetAssembler()
{
return _assembler.get();
}
BaseEventManager* SnesDebugger::GetEventManager()
{
return _eventManager.get();
}
PpuTools* SnesDebugger::GetPpuTools()
{
return _ppuTools.get();
}
ISerializable* SnesDebugger::GetSerializableCpu()
{
if(_cpuType == CpuType::Snes) {
return _cpu;
} else {
return _sa1;
}
}
BaseState& SnesDebugger::GetState()
{
return GetCpuState();
}
void SnesDebugger::GetPpuState(BaseState& state)
{
(SnesPpuState&)state = _ppu->GetStateRef();
}
void SnesDebugger::SetPpuState(BaseState& srcState)
{
SnesPpuState& dstState = _ppu->GetStateRef();
dstState = (SnesPpuState&)srcState;
}
bool SnesDebugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption)
{
vector<uint8_t> output;
uint8_t* prgRom = _debugger->GetMemoryDumper()->GetMemoryBuffer(MemoryType::SnesPrgRom);
uint32_t prgRomSize = _debugger->GetMemoryDumper()->GetMemorySize(MemoryType::SnesPrgRom);
vector<uint8_t> rom = vector<uint8_t>(prgRom, prgRom + prgRomSize);
if(saveAsIps) {
vector<uint8_t> originalRom;
_emu->GetRomInfo().RomFile.ReadFile(originalRom);
output = IpsPatcher::CreatePatch(originalRom, rom);
} else {
if(stripOption != CdlStripOption::StripNone) {
_cdl->StripData(rom.data(), stripOption);
//Preserve rom header regardless of CDL file contents
SnesCartInformation header = _cart->GetHeader();
memcpy(rom.data() + _cart->GetHeaderOffset(), &header, sizeof(SnesCartInformation));
}
output = rom;
}
ofstream file(filename, ios::out | ios::binary);
if(file) {
file.write((char*)output.data(), output.size());
file.close();
return true;
}
return false;
}
void SnesDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8])
{
BaseControlManager* controlManager = _console->GetControlManager();
for(int i = 0; i < 8; i++) {
shared_ptr<SnesController> controller = std::dynamic_pointer_cast<SnesController>(controlManager->GetControlDeviceByIndex(i));
if(controller && inputOverrides[i].HasPressedButton()) {
controller->SetBitValue(SnesController::Buttons::A, inputOverrides[i].A);
controller->SetBitValue(SnesController::Buttons::B, inputOverrides[i].B);
controller->SetBitValue(SnesController::Buttons::X, inputOverrides[i].X);
controller->SetBitValue(SnesController::Buttons::Y, inputOverrides[i].Y);
controller->SetBitValue(SnesController::Buttons::L, inputOverrides[i].L);
controller->SetBitValue(SnesController::Buttons::R, inputOverrides[i].R);
controller->SetBitValue(SnesController::Buttons::Select, inputOverrides[i].Select);
controller->SetBitValue(SnesController::Buttons::Start, inputOverrides[i].Start);
controller->SetBitValue(SnesController::Buttons::Up, inputOverrides[i].Up);
controller->SetBitValue(SnesController::Buttons::Down, inputOverrides[i].Down);
controller->SetBitValue(SnesController::Buttons::Left, inputOverrides[i].Left);
controller->SetBitValue(SnesController::Buttons::Right, inputOverrides[i].Right);
}
}
controlManager->RefreshHubState();
}