mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
Debugger: Implemented step back for all main CPUs
This commit is contained in:
parent
d97093ea89
commit
c3dd78b5ab
33 changed files with 368 additions and 62 deletions
|
@ -22,6 +22,7 @@
|
|||
<ClInclude Include="Debugger\Base6502Assembler.h" />
|
||||
<ClInclude Include="Debugger\CdlManager.h" />
|
||||
<ClInclude Include="Debugger\DisassemblySearch.h" />
|
||||
<ClInclude Include="Debugger\StepBackManager.h" />
|
||||
<ClInclude Include="SNES\DSP\DspInterpolation.h" />
|
||||
<ClInclude Include="SNES\DSP\Dsp.h" />
|
||||
<ClInclude Include="NES\Debugger\NesCodeDataLogger.h" />
|
||||
|
@ -685,6 +686,7 @@
|
|||
<ClCompile Include="Debugger\ExpressionEvaluator.Pce.cpp" />
|
||||
<ClCompile Include="Debugger\ExpressionEvaluator.Snes.cpp" />
|
||||
<ClCompile Include="Debugger\ExpressionEvaluator.Spc.cpp" />
|
||||
<ClCompile Include="Debugger\StepBackManager.cpp" />
|
||||
<ClCompile Include="Gameboy\Debugger\DummyGbCpu.cpp" />
|
||||
<ClCompile Include="Gameboy\Debugger\GbTraceLogger.cpp" />
|
||||
<ClCompile Include="Gameboy\Debugger\GbPpuTools.cpp" />
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<None Include="Core.ruleset" />
|
||||
<ClInclude Include="Shared\EventType.h" />
|
||||
<ClInclude Include="Shared\FirmwareHelper.h" />
|
||||
<ClInclude Include="Shared\MemoryOperationType.h" />
|
||||
<ClInclude Include="Shared\MemoryType.h" />
|
||||
<ClInclude Include="Shared\RomFinder.h" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClCompile Include="Debugger\BaseEventManager.cpp">
|
||||
|
@ -2597,6 +2592,24 @@
|
|||
<ClInclude Include="SNES\DSP\DspTypes.h">
|
||||
<Filter>SNES\DSP</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger\StepBackManager.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Shared\RomFinder.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Shared\MemoryType.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Shared\MemoryOperationType.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Shared\FirmwareHelper.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Shared\EventType.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Shared\Video\RotateFilter.cpp">
|
||||
|
@ -2719,6 +2732,9 @@
|
|||
<ClCompile Include="SNES\DSP\DspVoice.cpp">
|
||||
<Filter>SNES\DSP</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\StepBackManager.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="PCE">
|
||||
|
|
|
@ -343,6 +343,7 @@ enum class StepType
|
|||
SpecificScanline,
|
||||
RunToNmi,
|
||||
RunToIrq,
|
||||
StepBack
|
||||
};
|
||||
|
||||
struct StepRequest
|
||||
|
|
|
@ -166,8 +166,21 @@ uint64_t Debugger::GetCpuCycleCount()
|
|||
template<CpuType type>
|
||||
void Debugger::ProcessInstruction()
|
||||
{
|
||||
_debuggers[(int)type].Debugger->IgnoreBreakpoints = false;
|
||||
_debuggers[(int)type].Debugger->AllowChangeProgramCounter = true;
|
||||
IDebugger* debugger = _debuggers[(int)type].Debugger.get();
|
||||
if(debugger->CheckStepBack()) {
|
||||
//Step back target reached, break at the current instruction
|
||||
debugger->GetStepRequest()->Break(BreakSource::CpuStep);
|
||||
|
||||
//Reset prev op code flag to prevent debugger code from incorrectly flagging
|
||||
//an instruction as the start of a function, etc. after loading the state
|
||||
debugger->ResetPrevOpCode();
|
||||
} else if(debugger->IsStepBack()) {
|
||||
//While step back is running, don't process instructions
|
||||
return;
|
||||
}
|
||||
|
||||
debugger->IgnoreBreakpoints = false;
|
||||
debugger->AllowChangeProgramCounter = true;
|
||||
|
||||
switch(type) {
|
||||
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessInstruction(); break;
|
||||
|
@ -181,10 +194,10 @@ void Debugger::ProcessInstruction()
|
|||
case CpuType::Pce: GetDebugger<type, PceDebugger>()->ProcessInstruction(); break;
|
||||
}
|
||||
|
||||
_debuggers[(int)type].Debugger->AllowChangeProgramCounter = false;
|
||||
debugger->AllowChangeProgramCounter = false;
|
||||
|
||||
if(_scriptManager->HasCpuMemoryCallbacks()) {
|
||||
MemoryOperationInfo memOp = _debuggers[(int)type].Debugger->InstructionProgress.LastMemOperation;
|
||||
MemoryOperationInfo memOp = debugger->InstructionProgress.LastMemOperation;
|
||||
AddressInfo relAddr = { (int32_t)memOp.Address, memOp.MemType };
|
||||
uint8_t value = (uint8_t)memOp.Value;
|
||||
_scriptManager->ProcessMemoryOperation(relAddr, value, MemoryOperationType::ExecOpCode, type, true);
|
||||
|
@ -194,6 +207,10 @@ void Debugger::ProcessInstruction()
|
|||
template<CpuType type, typename T>
|
||||
void Debugger::ProcessMemoryRead(uint32_t addr, T& value, MemoryOperationType opType)
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessRead(addr, value, opType); break;
|
||||
case CpuType::Spc: GetDebugger<type, SpcDebugger>()->ProcessRead(addr, value, opType); break;
|
||||
|
@ -214,6 +231,10 @@ void Debugger::ProcessMemoryRead(uint32_t addr, T& value, MemoryOperationType op
|
|||
template<CpuType type, typename T>
|
||||
void Debugger::ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType opType)
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessWrite(addr, value, opType); break;
|
||||
case CpuType::Spc: GetDebugger<type, SpcDebugger>()->ProcessWrite(addr, value, opType); break;
|
||||
|
@ -234,6 +255,10 @@ void Debugger::ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType o
|
|||
template<CpuType type>
|
||||
void Debugger::ProcessIdleCycle()
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_debuggers[(int)type].Debugger->InstructionProgress.LastMemOperation.Type = MemoryOperationType::Idle;
|
||||
|
||||
switch(type) {
|
||||
|
@ -245,6 +270,10 @@ void Debugger::ProcessIdleCycle()
|
|||
template<CpuType type, typename T>
|
||||
void Debugger::ProcessPpuRead(uint16_t addr, T& value, MemoryType memoryType, MemoryOperationType opType)
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessPpuRead(addr, value, memoryType); break;
|
||||
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessPpuRead(addr, value, memoryType); break;
|
||||
|
@ -261,6 +290,10 @@ void Debugger::ProcessPpuRead(uint16_t addr, T& value, MemoryType memoryType, Me
|
|||
template<CpuType type, typename T>
|
||||
void Debugger::ProcessPpuWrite(uint16_t addr, T& value, MemoryType memoryType)
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessPpuWrite(addr, value, memoryType); break;
|
||||
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessPpuWrite(addr, value, memoryType); break;
|
||||
|
@ -277,6 +310,10 @@ void Debugger::ProcessPpuWrite(uint16_t addr, T& value, MemoryType memoryType)
|
|||
template<CpuType type>
|
||||
void Debugger::ProcessPpuCycle()
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessPpuCycle(); break;
|
||||
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessPpuCycle(); break;
|
||||
|
@ -359,6 +396,10 @@ void Debugger::ProcessPredictiveBreakpoint(CpuType sourceCpu, BreakpointManager*
|
|||
template<CpuType type>
|
||||
void Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
|
||||
{
|
||||
if(_debuggers[(int)type].Debugger->IsStepBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_debuggers[(int)type].Debugger->ProcessInterrupt(originalPc, currentPc, forNmi);
|
||||
ProcessEvent(forNmi ? EventType::Nmi : EventType::Irq);
|
||||
}
|
||||
|
@ -476,6 +517,7 @@ void Debugger::Run()
|
|||
{
|
||||
for(int i = 0; i <= (int)DebugUtilities::GetLastCpuType(); i++) {
|
||||
if(_debuggers[i].Debugger) {
|
||||
_debuggers[i].Debugger->ResetStepBackCache();
|
||||
_debuggers[i].Debugger->Run();
|
||||
}
|
||||
}
|
||||
|
@ -499,12 +541,19 @@ void Debugger::Step(CpuType cpuType, int32_t stepCount, StepType type, BreakSour
|
|||
IDebugger* debugger = _debuggers[(int)cpuType].Debugger.get();
|
||||
|
||||
if(debugger) {
|
||||
if(type != StepType::StepBack) {
|
||||
debugger->ResetStepBackCache();
|
||||
} else {
|
||||
debugger->StepBack();
|
||||
}
|
||||
|
||||
debugger->Step(stepCount, type);
|
||||
debugger->GetStepRequest()->SetBreakSource(source);
|
||||
}
|
||||
|
||||
for(int i = 0; i <= (int)DebugUtilities::GetLastCpuType(); i++) {
|
||||
if(_debuggers[i].Debugger && _debuggers[i].Debugger.get() != debugger) {
|
||||
_debuggers[i].Debugger->ResetStepBackCache();
|
||||
_debuggers[i].Debugger->Run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "pch.h"
|
||||
#include "Debugger/DebuggerFeatures.h"
|
||||
#include "Debugger/DebugTypes.h"
|
||||
#include "Debugger/StepBackManager.h"
|
||||
|
||||
enum class StepType;
|
||||
class BreakpointManager;
|
||||
|
@ -15,10 +16,12 @@ struct BaseState;
|
|||
enum class EventType;
|
||||
enum class MemoryOperationType;
|
||||
|
||||
//TODO rename/refactor to BaseDebugger
|
||||
class IDebugger
|
||||
{
|
||||
protected:
|
||||
unique_ptr<StepRequest> _step;
|
||||
unique_ptr<StepBackManager> _stepBackManager = unique_ptr<StepBackManager>(new StepBackManager(nullptr, nullptr));
|
||||
|
||||
public:
|
||||
bool IgnoreBreakpoints = false;
|
||||
|
@ -28,6 +31,12 @@ public:
|
|||
virtual ~IDebugger() = default;
|
||||
|
||||
StepRequest* GetStepRequest() { return _step.get(); }
|
||||
bool CheckStepBack() { return _stepBackManager->CheckStepBack(); }
|
||||
bool IsStepBack() { return _stepBackManager->IsRewinding(); }
|
||||
void ResetStepBackCache() { return _stepBackManager->ResetCache(); }
|
||||
void StepBack() { return _stepBackManager->StepBack(); }
|
||||
|
||||
virtual void ResetPrevOpCode() {}
|
||||
|
||||
virtual void Step(int32_t stepCount, StepType type) = 0;
|
||||
virtual void Reset() = 0;
|
||||
|
|
|
@ -174,21 +174,23 @@ int LuaApi::GetLibrary(lua_State *lua)
|
|||
GenerateEnumDefinition<AccessCounterType>(lua, "counterType");
|
||||
GenerateEnumDefinition<CpuType>(lua, "cpuType");
|
||||
GenerateEnumDefinition<ScriptDrawSurface>(lua, "drawSurface");
|
||||
GenerateEnumDefinition<EventType>(lua, "eventType");
|
||||
GenerateEnumDefinition<StepType>(lua, "stepType");
|
||||
GenerateEnumDefinition<EventType>(lua, "eventType", { EventType::LastValue });
|
||||
GenerateEnumDefinition<StepType>(lua, "stepType", { StepType::StepBack });
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LuaApi::GenerateEnumDefinition(lua_State* lua, string enumName)
|
||||
void LuaApi::GenerateEnumDefinition(lua_State* lua, string enumName, unordered_set<T> excludedValues)
|
||||
{
|
||||
lua_pushstring(lua, enumName.c_str());
|
||||
lua_newtable(lua);
|
||||
for(auto& entry : magic_enum::enum_entries<T>()) {
|
||||
string name = string(entry.second);
|
||||
name[0] = ::tolower(name[0]);
|
||||
LuaPushIntValue(lua, name, (int)entry.first);
|
||||
if(excludedValues.find(entry.first) == excludedValues.end()) {
|
||||
string name = string(entry.second);
|
||||
name[0] = ::tolower(name[0]);
|
||||
LuaPushIntValue(lua, name, (int)entry.first);
|
||||
}
|
||||
}
|
||||
lua_settable(lua, -3);
|
||||
}
|
||||
|
|
|
@ -97,5 +97,5 @@ private:
|
|||
static ScriptingContext* _context;
|
||||
|
||||
static std::pair<unique_ptr<BaseVideoFilter>, FrameInfo> GetRenderedFrame();
|
||||
template<typename T> static void GenerateEnumDefinition(lua_State* lua, string enumName);
|
||||
template<typename T> static void GenerateEnumDefinition(lua_State* lua, string enumName, unordered_set<T> excludedValues = {});
|
||||
};
|
||||
|
|
95
Core/Debugger/StepBackManager.cpp
Normal file
95
Core/Debugger/StepBackManager.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include "pch.h"
|
||||
#include "Debugger/StepBackManager.h"
|
||||
#include "Debugger/IDebugger.h"
|
||||
#include "Shared/Emulator.h"
|
||||
#include "Shared/SaveStateManager.h"
|
||||
#include "Shared/NotificationManager.h"
|
||||
#include "Shared/RewindManager.h"
|
||||
|
||||
StepBackManager::StepBackManager(Emulator* emu, IDebugger* debugger)
|
||||
{
|
||||
_emu = emu;
|
||||
_debugger = debugger;
|
||||
}
|
||||
|
||||
void StepBackManager::StepBack()
|
||||
{
|
||||
if(!_active) {
|
||||
_targetClock = _debugger->GetCpuCycleCount();
|
||||
_active = true;
|
||||
_allowRetry = true;
|
||||
_stateClockLimit = StepBackManager::DefaultClockLimit;
|
||||
}
|
||||
}
|
||||
|
||||
bool StepBackManager::CheckStepBack()
|
||||
{
|
||||
if(!_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t clock = _debugger->GetCpuCycleCount();
|
||||
|
||||
if(!_emu->GetRewindManager()->IsStepBack()) {
|
||||
if(_cache.size() > 1) {
|
||||
//Check to see if previous instruction is already in cache
|
||||
if(_cache.back().Clock == _targetClock) {
|
||||
//End of cache is the current instruction, remove it first
|
||||
_cache.pop_back();
|
||||
if(_cache.size()) {
|
||||
//If cache isn't empty, load the last state
|
||||
_emu->GetRewindManager()->SetIgnoreLoadState(true);
|
||||
_emu->Deserialize(_cache.back().SaveState, SaveStateManager::FileFormatVersion, true);
|
||||
_emu->GetRewindManager()->SetIgnoreLoadState(false);
|
||||
|
||||
_emu->GetRewindManager()->StopRewinding(true);
|
||||
_active = false;
|
||||
_prevClock = clock;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
//On mismatch, clear cache and rewind normally instead
|
||||
_cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//Start rewinding on next instruction after StepBack() is called
|
||||
_cache.clear();
|
||||
_emu->GetRewindManager()->StartRewinding(true);
|
||||
clock = _debugger->GetCpuCycleCount();
|
||||
}
|
||||
|
||||
if(clock < _targetClock && _targetClock - clock < _stateClockLimit) {
|
||||
//Create a save state every instruction for the last X clocks
|
||||
_cache.push_back({});
|
||||
_cache.back().Clock = clock;
|
||||
_emu->Serialize(_cache.back().SaveState, true, 0);
|
||||
}
|
||||
|
||||
if(clock >= _targetClock) {
|
||||
//If the CPU is back to where it was before step back, check if the cache contains data
|
||||
if(_cache.size() > 0) {
|
||||
//If it does, load the last state
|
||||
_emu->GetRewindManager()->SetIgnoreLoadState(true);
|
||||
_emu->Deserialize(_cache.back().SaveState, SaveStateManager::FileFormatVersion, true);
|
||||
_emu->GetRewindManager()->SetIgnoreLoadState(false);
|
||||
} else if(_allowRetry && clock > _prevClock && (clock - _prevClock) > StepBackManager::DefaultClockLimit) {
|
||||
//Cache is empty, this can happen when a single instruction takes more than X clocks (e.g block transfers, dma)
|
||||
//In this case, re-run the step back process again but start recordings state earlier
|
||||
_emu->GetRewindManager()->StopRewinding(true);
|
||||
_emu->GetRewindManager()->StartRewinding(true);
|
||||
_stateClockLimit = (clock - _prevClock) + StepBackManager::DefaultClockLimit;
|
||||
_allowRetry = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
//Stop rewinding, even if the target was not found
|
||||
_emu->GetRewindManager()->StopRewinding(true);
|
||||
_active = false;
|
||||
_prevClock = clock;
|
||||
return true;
|
||||
}
|
||||
|
||||
_prevClock = clock;
|
||||
return false;
|
||||
}
|
36
Core/Debugger/StepBackManager.h
Normal file
36
Core/Debugger/StepBackManager.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
|
||||
class Emulator;
|
||||
class IDebugger;
|
||||
|
||||
struct StepBackCacheEntry
|
||||
{
|
||||
stringstream SaveState;
|
||||
uint64_t Clock;
|
||||
};
|
||||
|
||||
class StepBackManager
|
||||
{
|
||||
private:
|
||||
static constexpr uint64_t DefaultClockLimit = 600; //Default to 600 clocks to avoid retry when NES sprite DMA occurs (~512 cycles)
|
||||
|
||||
Emulator* _emu = nullptr;
|
||||
IDebugger* _debugger = nullptr;
|
||||
|
||||
vector<StepBackCacheEntry> _cache;
|
||||
uint64_t _targetClock = 0;
|
||||
uint64_t _prevClock = 0;
|
||||
bool _active = false;
|
||||
bool _allowRetry = false;
|
||||
uint64_t _stateClockLimit = StepBackManager::DefaultClockLimit;
|
||||
|
||||
public:
|
||||
StepBackManager(Emulator* emu, IDebugger* debugger);
|
||||
|
||||
void StepBack();
|
||||
bool CheckStepBack();
|
||||
|
||||
void ResetCache() { _cache.clear(); }
|
||||
bool IsRewinding() { return _active; }
|
||||
};
|
|
@ -18,6 +18,7 @@
|
|||
#include "Debugger/MemoryDumper.h"
|
||||
#include "Debugger/CodeDataLogger.h"
|
||||
#include "Debugger/BaseEventManager.h"
|
||||
#include "Debugger/StepBackManager.h"
|
||||
#include "Utilities/Patches/IpsPatcher.h"
|
||||
#include "Utilities/HexUtilities.h"
|
||||
#include "Gameboy/Debugger/GbAssembler.h"
|
||||
|
@ -56,6 +57,7 @@ GbDebugger::GbDebugger(Debugger* debugger)
|
|||
_traceLogger.reset(new GbTraceLogger(debugger, this, _ppu));
|
||||
_ppuTools.reset(new GbPpuTools(debugger, debugger->GetEmulator()));
|
||||
|
||||
_stepBackManager.reset(new StepBackManager(_emu, this));
|
||||
_eventManager.reset(new GbEventManager(debugger, _gameboy->GetCpu(), _ppu));
|
||||
_callstackManager.reset(new CallstackManager(debugger, _gameboy));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, this, CpuType::Gameboy, _eventManager.get()));
|
||||
|
@ -79,7 +81,7 @@ GbDebugger::~GbDebugger()
|
|||
void GbDebugger::Reset()
|
||||
{
|
||||
_callstackManager->Clear();
|
||||
_prevOpCode = 0;
|
||||
ResetPrevOpCode();
|
||||
}
|
||||
|
||||
void GbDebugger::ProcessInstruction()
|
||||
|
@ -277,7 +279,7 @@ void GbDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool
|
|||
|
||||
//If a call/return occurred just before IRQ, it needs to be processed now
|
||||
ProcessCallStackUpdates(ret, originalPc);
|
||||
_prevOpCode = 0;
|
||||
ResetPrevOpCode();
|
||||
|
||||
_debugger->InternalProcessInterrupt(
|
||||
CpuType::Gameboy, *this, *_step.get(),
|
||||
|
@ -326,6 +328,7 @@ DebuggerFeatures GbDebugger::GetSupportedFeatures()
|
|||
features.RunToNmi = false;
|
||||
features.StepOver = true;
|
||||
features.StepOut = true;
|
||||
features.StepBack = true;
|
||||
features.CallStack = true;
|
||||
features.ChangeProgramCounter = AllowChangeProgramCounter;
|
||||
|
||||
|
@ -351,6 +354,16 @@ uint32_t GbDebugger::GetProgramCounter(bool getInstPc)
|
|||
return getInstPc ? _prevProgramCounter : _cpu->GetState().PC;
|
||||
}
|
||||
|
||||
uint64_t GbDebugger::GetCpuCycleCount()
|
||||
{
|
||||
return _gameboy->GetCycleCount();
|
||||
}
|
||||
|
||||
void GbDebugger::ResetPrevOpCode()
|
||||
{
|
||||
_prevOpCode = 0;
|
||||
}
|
||||
|
||||
BaseEventManager* GbDebugger::GetEventManager()
|
||||
{
|
||||
return _eventManager.get();
|
||||
|
|
|
@ -76,6 +76,8 @@ public:
|
|||
|
||||
void SetProgramCounter(uint32_t addr) override;
|
||||
uint32_t GetProgramCounter(bool getInstPc) override;
|
||||
uint64_t GetCpuCycleCount() override;
|
||||
void ResetPrevOpCode();
|
||||
|
||||
DebuggerFeatures GetSupportedFeatures() override;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "Debugger/CodeDataLogger.h"
|
||||
#include "Debugger/ScriptManager.h"
|
||||
#include "Debugger/Debugger.h"
|
||||
#include "Debugger/StepBackManager.h"
|
||||
#include "Debugger/MemoryDumper.h"
|
||||
#include "Debugger/MemoryAccessCounter.h"
|
||||
#include "Debugger/ExpressionEvaluator.h"
|
||||
|
@ -73,6 +74,7 @@ NesDebugger::NesDebugger(Debugger* debugger)
|
|||
|
||||
_ppuTools.reset(new NesPpuTools(debugger, debugger->GetEmulator(), console));
|
||||
|
||||
_stepBackManager.reset(new StepBackManager(_emu, this));
|
||||
_eventManager.reset(new NesEventManager(debugger, console));
|
||||
_callstackManager.reset(new CallstackManager(debugger, console));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, this, CpuType::Nes, _eventManager.get()));
|
||||
|
@ -94,7 +96,7 @@ void NesDebugger::Reset()
|
|||
{
|
||||
_enableBreakOnUninitRead = true;
|
||||
_callstackManager->Clear();
|
||||
_prevOpCode = 0xFF;
|
||||
ResetPrevOpCode();
|
||||
}
|
||||
|
||||
uint64_t NesDebugger::GetCpuCycleCount()
|
||||
|
@ -102,6 +104,11 @@ uint64_t NesDebugger::GetCpuCycleCount()
|
|||
return _cpu->GetState().CycleCount;
|
||||
}
|
||||
|
||||
void NesDebugger::ResetPrevOpCode()
|
||||
{
|
||||
_prevOpCode = 0xFF;
|
||||
}
|
||||
|
||||
void NesDebugger::ProcessInstruction()
|
||||
{
|
||||
NesCpuState& state = _cpu->GetState();
|
||||
|
@ -306,7 +313,7 @@ void NesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool
|
|||
|
||||
//If a call/return occurred just before IRQ, it needs to be processed now
|
||||
ProcessCallStackUpdates(ret, originalPc);
|
||||
_prevOpCode = 0xFF;
|
||||
ResetPrevOpCode();
|
||||
|
||||
_debugger->InternalProcessInterrupt(
|
||||
CpuType::Nes, *this, *_step.get(),
|
||||
|
@ -378,6 +385,7 @@ DebuggerFeatures NesDebugger::GetSupportedFeatures()
|
|||
features.RunToNmi = true;
|
||||
features.StepOver = true;
|
||||
features.StepOut = true;
|
||||
features.StepBack = true;
|
||||
features.CallStack = true;
|
||||
features.CpuCycleStep = true;
|
||||
features.ChangeProgramCounter = AllowChangeProgramCounter;
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
void Reset() override;
|
||||
|
||||
uint64_t GetCpuCycleCount() override;
|
||||
void ResetPrevOpCode() override;
|
||||
|
||||
void ProcessInstruction();
|
||||
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Debugger/MemoryAccessCounter.h"
|
||||
#include "Debugger/ExpressionEvaluator.h"
|
||||
#include "Debugger/CodeDataLogger.h"
|
||||
#include "Debugger/StepBackManager.h"
|
||||
#include "PCE/PceConsole.h"
|
||||
#include "PCE/PceCpu.h"
|
||||
#include "PCE/PceVdc.h"
|
||||
|
@ -59,6 +60,7 @@ PceDebugger::PceDebugger(Debugger* debugger)
|
|||
_cdlFile = _codeDataLogger->GetCdlFilePath(_console->GetRomFormat() == RomFormat::PceCdRom ? "PceCdromBios.cdl" : _emu->GetRomInfo().RomFile.GetFileName());
|
||||
_codeDataLogger->LoadCdlFile(_cdlFile, _settings->GetDebugConfig().AutoResetCdl);
|
||||
|
||||
_stepBackManager.reset(new StepBackManager(_emu, this));
|
||||
_eventManager.reset(new PceEventManager(debugger, console));
|
||||
_callstackManager.reset(new CallstackManager(debugger, console));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, this, CpuType::Pce, _eventManager.get()));
|
||||
|
@ -80,7 +82,7 @@ void PceDebugger::Reset()
|
|||
{
|
||||
_enableBreakOnUninitRead = true;
|
||||
_callstackManager->Clear();
|
||||
_prevOpCode = 0x01;
|
||||
ResetPrevOpCode();
|
||||
}
|
||||
|
||||
uint64_t PceDebugger::GetCpuCycleCount()
|
||||
|
@ -88,6 +90,11 @@ uint64_t PceDebugger::GetCpuCycleCount()
|
|||
return _cpu->GetState().CycleCount;
|
||||
}
|
||||
|
||||
void PceDebugger::ResetPrevOpCode()
|
||||
{
|
||||
_prevOpCode = 0x01;
|
||||
}
|
||||
|
||||
void PceDebugger::ProcessInstruction()
|
||||
{
|
||||
PceCpuState& state = _cpu->GetState();
|
||||
|
@ -285,7 +292,7 @@ void PceDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool
|
|||
|
||||
//If a call/return occurred just before IRQ, it needs to be processed now
|
||||
ProcessCallStackUpdates(ret, originalPc);
|
||||
_prevOpCode = 0x01;
|
||||
ResetPrevOpCode();
|
||||
|
||||
_debugger->InternalProcessInterrupt(
|
||||
CpuType::Pce, *this, *_step.get(),
|
||||
|
@ -341,6 +348,7 @@ DebuggerFeatures PceDebugger::GetSupportedFeatures()
|
|||
features.RunToNmi = false;
|
||||
features.StepOver = true;
|
||||
features.StepOut = true;
|
||||
features.StepBack = true;
|
||||
features.CallStack = true;
|
||||
features.ChangeProgramCounter = AllowChangeProgramCounter;
|
||||
features.CpuCycleStep = true;
|
||||
|
|
|
@ -68,6 +68,7 @@ public:
|
|||
void Reset() override;
|
||||
|
||||
uint64_t GetCpuCycleCount() override;
|
||||
void ResetPrevOpCode() override;
|
||||
|
||||
void ProcessInstruction();
|
||||
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#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"
|
||||
|
@ -75,7 +76,8 @@ SnesDebugger::SnesDebugger(Debugger* debugger, CpuType cpuType)
|
|||
} 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, console));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, this, cpuType, _eventManager.get()));
|
||||
|
@ -107,7 +109,7 @@ void SnesDebugger::Reset()
|
|||
{
|
||||
_enableBreakOnUninitRead = true;
|
||||
_callstackManager->Clear();
|
||||
_prevOpCode = 0xFF;
|
||||
ResetPrevOpCode();
|
||||
}
|
||||
|
||||
void SnesDebugger::ProcessConfigChange()
|
||||
|
@ -129,6 +131,11 @@ uint64_t SnesDebugger::GetCpuCycleCount()
|
|||
return GetCpuState().CycleCount;
|
||||
}
|
||||
|
||||
void SnesDebugger::ResetPrevOpCode()
|
||||
{
|
||||
_prevOpCode = 0xFF;
|
||||
}
|
||||
|
||||
void SnesDebugger::ProcessInstruction()
|
||||
{
|
||||
SnesCpuState& state = GetCpuState();
|
||||
|
@ -354,7 +361,7 @@ void SnesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, boo
|
|||
|
||||
//If a call/return occurred just before IRQ, it needs to be processed now
|
||||
ProcessCallStackUpdates(ret, originalPc, GetCpuState().PS);
|
||||
_prevOpCode = 0xFF;
|
||||
ResetPrevOpCode();
|
||||
|
||||
_debugger->InternalProcessInterrupt(
|
||||
_cpuType, *this, *_step.get(),
|
||||
|
@ -427,6 +434,7 @@ DebuggerFeatures SnesDebugger::GetSupportedFeatures()
|
|||
features.RunToNmi = true;
|
||||
features.StepOver = true;
|
||||
features.StepOut = true;
|
||||
features.StepBack = true;
|
||||
features.CallStack = true;
|
||||
features.ChangeProgramCounter = AllowChangeProgramCounter;
|
||||
features.CpuCycleStep = true;
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
void ProcessConfigChange() override;
|
||||
|
||||
uint64_t GetCpuCycleCount() override;
|
||||
void ResetPrevOpCode() override;
|
||||
|
||||
void ProcessInstruction();
|
||||
void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type);
|
||||
|
|
|
@ -85,7 +85,7 @@ void RewindManager::ProcessNotification(ConsoleNotificationType type, void * par
|
|||
ClearBuffer();
|
||||
}
|
||||
} else if(type == ConsoleNotificationType::StateLoaded) {
|
||||
if(_rewindState == RewindState::Stopped) {
|
||||
if(_rewindState == RewindState::Stopped && !_ignoreLoadState) {
|
||||
//A save state was loaded by the user, mark as the end of the current "segment" (for history viewer)
|
||||
_currentHistory.EndOfSegment = true;
|
||||
AddHistoryBlock();
|
||||
|
@ -111,10 +111,10 @@ void RewindManager::AddHistoryBlock()
|
|||
|
||||
void RewindManager::PopHistory()
|
||||
{
|
||||
if(_history.empty() && _currentHistory.FrameCount <= 0) {
|
||||
if(_history.empty() && _currentHistory.FrameCount <= 0 && !IsStepBack()) {
|
||||
StopRewinding();
|
||||
} else {
|
||||
if(_currentHistory.FrameCount <= 0) {
|
||||
if(_currentHistory.FrameCount <= 0 && !IsStepBack()) {
|
||||
_currentHistory = _history.back();
|
||||
_history.pop_back();
|
||||
}
|
||||
|
@ -131,22 +131,30 @@ void RewindManager::PopHistory()
|
|||
void RewindManager::Start(bool forDebugger)
|
||||
{
|
||||
if(_rewindState == RewindState::Stopped && _settings->GetRewindBufferSize() > 0) {
|
||||
auto lock = _emu->AcquireLock();
|
||||
|
||||
_rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
|
||||
_videoHistoryBuilder.clear();
|
||||
_videoHistory.clear();
|
||||
_audioHistoryBuilder.clear();
|
||||
_audioHistory.clear();
|
||||
_historyBackup.clear();
|
||||
|
||||
PopHistory();
|
||||
_emu->GetSoundMixer()->StopAudio(true);
|
||||
_settings->SetFlag(EmulationFlags::MaximumSpeed);
|
||||
_settings->SetFlag(EmulationFlags::Rewind);
|
||||
if(forDebugger) {
|
||||
InternalStart(forDebugger);
|
||||
} else {
|
||||
auto lock = _emu->AcquireLock();
|
||||
InternalStart(forDebugger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RewindManager::InternalStart(bool forDebugger)
|
||||
{
|
||||
_rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
|
||||
_videoHistoryBuilder.clear();
|
||||
_videoHistory.clear();
|
||||
_audioHistoryBuilder.clear();
|
||||
_audioHistory.clear();
|
||||
_historyBackup.clear();
|
||||
|
||||
PopHistory();
|
||||
_emu->GetSoundMixer()->StopAudio(true);
|
||||
_settings->SetFlag(EmulationFlags::MaximumSpeed);
|
||||
_settings->SetFlag(EmulationFlags::Rewind);
|
||||
}
|
||||
|
||||
void RewindManager::ForceStop()
|
||||
{
|
||||
if(_rewindState != RewindState::Stopped) {
|
||||
|
@ -374,3 +382,8 @@ bool RewindManager::SendAudio(int16_t* soundBuffer, uint32_t sampleCount)
|
|||
{
|
||||
return ProcessAudio(soundBuffer, sampleCount);
|
||||
}
|
||||
|
||||
void RewindManager::SetIgnoreLoadState(bool ignore)
|
||||
{
|
||||
_ignoreLoadState = ignore;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ private:
|
|||
EmuSettings* _settings = nullptr;
|
||||
|
||||
bool _hasHistory = false;
|
||||
bool _ignoreLoadState = false;
|
||||
|
||||
deque<RewindData> _history;
|
||||
deque<RewindData> _historyBackup;
|
||||
|
@ -56,6 +57,7 @@ private:
|
|||
void PopHistory();
|
||||
|
||||
void Start(bool forDebugger);
|
||||
void InternalStart(bool forDebugger);
|
||||
void Stop();
|
||||
void ForceStop();
|
||||
|
||||
|
@ -85,4 +87,5 @@ public:
|
|||
|
||||
void SendFrame(RenderedFrame& frame, bool forRewind);
|
||||
bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount);
|
||||
void SetIgnoreLoadState(bool ignore);
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 227 B |
|
@ -1,6 +1,7 @@
|
|||
using Mesen.ViewModels;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Mesen.Debugger.StatusViews
|
||||
{
|
||||
|
@ -10,6 +11,18 @@ namespace Mesen.Debugger.StatusViews
|
|||
[Reactive] public UInt64 ElapsedCycles { get; set; }
|
||||
[Reactive] public UInt64 CycleCount { get; set; }
|
||||
|
||||
private bool _needUpdate = false;
|
||||
|
||||
public BaseConsoleStatusViewModel()
|
||||
{
|
||||
PropertyChanged += BaseConsoleStatusViewModel_PropertyChanged;
|
||||
}
|
||||
|
||||
private void BaseConsoleStatusViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
_needUpdate = true;
|
||||
}
|
||||
|
||||
public void UpdateCycleCount(UInt64 newCycleCount)
|
||||
{
|
||||
if(newCycleCount > CycleCount) {
|
||||
|
@ -20,7 +33,22 @@ namespace Mesen.Debugger.StatusViews
|
|||
CycleCount = newCycleCount;
|
||||
}
|
||||
|
||||
public abstract void UpdateUiState();
|
||||
public abstract void UpdateConsoleState();
|
||||
public void UpdateUiState()
|
||||
{
|
||||
InternalUpdateUiState();
|
||||
_needUpdate = false;
|
||||
}
|
||||
|
||||
public void UpdateConsoleState()
|
||||
{
|
||||
if(_needUpdate) {
|
||||
//Only update emulator state if user manually changed the state in the UI
|
||||
//Otherwise this causes issues when a state is loaded (e.g step back)
|
||||
InternalUpdateConsoleState();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void InternalUpdateUiState();
|
||||
protected abstract void InternalUpdateConsoleState();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
{
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
Cx4State cpu = DebugApi.GetCpuState<Cx4State>(CpuType.Cx4);
|
||||
|
||||
|
@ -95,7 +95,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
FlagIrq = cpu.IrqFlag;
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
Cx4State cpu = DebugApi.GetCpuState<Cx4State>(CpuType.Cx4);
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
);
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
GbState state = DebugApi.GetConsoleState<GbState>(ConsoleType.Gameboy);
|
||||
|
||||
|
@ -94,7 +94,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
StackPreview = sb.ToString();
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
GbCpuState cpu = DebugApi.GetCpuState<GbCpuState>(CpuType.Gameboy);
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
);
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
GsuState cpu = DebugApi.GetCpuState<GsuState>(CpuType.Gsu);
|
||||
|
||||
|
@ -118,7 +118,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
FlagIrq = cpu.SFR.Irq;
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
GsuState cpu = DebugApi.GetCpuState<GsuState>(CpuType.Gsu);
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
{
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
NecDspState cpu = DebugApi.GetCpuState<NecDspState>(CpuType.NecDsp);
|
||||
|
||||
|
@ -80,7 +80,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
RegB_S1 = cpu.FlagsB.Sign1;
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
NecDspState cpu = DebugApi.GetCpuState<NecDspState>(CpuType.NecDsp);
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
});
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
NesCpuState cpu = DebugApi.GetCpuState<NesCpuState>(CpuType.Nes);
|
||||
NesPpuState ppu = DebugApi.GetPpuState<NesPpuState>(CpuType.Nes);
|
||||
|
@ -139,7 +139,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
IntensifyBlue = ppu.Mask.IntensifyBlue;
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
NesCpuState cpu = DebugApi.GetCpuState<NesCpuState>(CpuType.Nes);
|
||||
NesPpuState ppu = DebugApi.GetPpuState<NesPpuState>(CpuType.Nes);
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
});
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
PceCpuState cpu = DebugApi.GetCpuState<PceCpuState>(CpuType.Pce);
|
||||
PceVideoState video = DebugApi.GetPpuState<PceVideoState>(CpuType.Pce);
|
||||
|
@ -80,7 +80,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
FrameCount = video.Vdc.FrameCount;
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
PceCpuState cpu = DebugApi.GetCpuState<PceCpuState>(CpuType.Pce);
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
);
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
SnesCpuState cpu = DebugApi.GetCpuState<SnesCpuState>(_cpuType);
|
||||
SnesPpuState ppu = DebugApi.GetPpuState<SnesPpuState>(CpuType.Snes);
|
||||
|
@ -115,7 +115,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
Scanline = ppu.Scanline;
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
SnesCpuState cpu = DebugApi.GetCpuState<SnesCpuState>(_cpuType);
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
);
|
||||
}
|
||||
|
||||
public override void UpdateUiState()
|
||||
protected override void InternalUpdateUiState()
|
||||
{
|
||||
SpcState cpu = DebugApi.GetCpuState<SpcState>(CpuType.Spc);
|
||||
|
||||
|
@ -78,7 +78,7 @@ namespace Mesen.Debugger.StatusViews
|
|||
StackPreview = sb.ToString();
|
||||
}
|
||||
|
||||
public override void UpdateConsoleState()
|
||||
protected override void InternalUpdateConsoleState()
|
||||
{
|
||||
SpcState cpu = DebugApi.GetCpuState<SpcState>(CpuType.Spc);
|
||||
|
||||
|
|
|
@ -66,8 +66,7 @@ namespace Mesen.Debugger.Utilities
|
|||
ActionType = ActionType.StepBack,
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.StepBack),
|
||||
IsVisible = () => DebugApi.GetDebuggerFeatures(getCpuType()).StepBack,
|
||||
IsEnabled = () => false,
|
||||
OnClick = () => { } //TODO
|
||||
OnClick = () => Step(getCpuType(), StepType.StepBack, 1)
|
||||
},
|
||||
|
||||
new ContextMenuSeparator() { IsVisible = () => DebugApi.GetDebuggerFeatures(getCpuType()).CpuCycleStep },
|
||||
|
|
|
@ -217,7 +217,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
DebuggerShortcut.StepInto,
|
||||
DebuggerShortcut.StepOver,
|
||||
DebuggerShortcut.StepOut,
|
||||
//DebuggerShortcut.StepBack,
|
||||
DebuggerShortcut.StepBack,
|
||||
DebuggerShortcut.RunCpuCycle,
|
||||
DebuggerShortcut.RunPpuCycle,
|
||||
DebuggerShortcut.RunPpuScanline,
|
||||
|
|
|
@ -1236,6 +1236,7 @@ namespace Mesen.Interop
|
|||
SpecificScanline,
|
||||
RunToNmi,
|
||||
RunToIrq,
|
||||
StepBack
|
||||
}
|
||||
|
||||
public enum BreakSource
|
||||
|
|
|
@ -19,6 +19,8 @@ using Mesen.Debugger.Utilities;
|
|||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using Mesen.Debugger.Windows;
|
||||
using Avalonia.Input.Platform;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mesen.Windows
|
||||
{
|
||||
|
@ -46,6 +48,14 @@ namespace Mesen.Windows
|
|||
{
|
||||
DataContext = new MainWindowViewModel();
|
||||
|
||||
List<KeyGesture> gestures = AvaloniaLocator.Current.GetRequiredService<PlatformHotkeyConfiguration>().OpenContextMenu;
|
||||
for(int i = gestures.Count - 1; i >= 0; i--) {
|
||||
if(gestures[i].Key == Key.F10 && gestures[i].KeyModifiers == KeyModifiers.Shift) {
|
||||
//Disable Shift-F10 shortcut to open context menu - interferes with default shortcut for step back
|
||||
gestures.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
EmuApi.InitDll();
|
||||
|
||||
Directory.CreateDirectory(ConfigManager.HomeFolder);
|
||||
|
|
Loading…
Add table
Reference in a new issue