Debugger: Added freeze address option in memory viewer

This commit is contained in:
Sour 2022-09-25 19:36:31 -04:00
parent 00133c4643
commit 1df7d9aeac
28 changed files with 219 additions and 89 deletions

View file

@ -22,6 +22,7 @@
<ClInclude Include="Debugger\Base6502Assembler.h" />
<ClInclude Include="Debugger\CdlManager.h" />
<ClInclude Include="Debugger\DisassemblySearch.h" />
<ClInclude Include="Debugger\FrozenAddressManager.h" />
<ClInclude Include="Debugger\StepBackManager.h" />
<ClInclude Include="SNES\DSP\DspInterpolation.h" />
<ClInclude Include="SNES\DSP\Dsp.h" />

View file

@ -2610,6 +2610,9 @@
<ClInclude Include="Shared\EventType.h">
<Filter>Shared</Filter>
</ClInclude>
<ClInclude Include="Debugger\FrozenAddressManager.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Shared\Video\RotateFilter.cpp">

View file

@ -237,10 +237,12 @@ 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)
bool Debugger::ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType opType)
{
if(_debuggers[(int)type].Debugger->IsStepBack()) {
return;
return true;
} else if(_debuggers[(int)type].Debugger->GetFrozenAddressManager().IsFrozenAddress(addr)) {
return false;
}
switch(type) {
@ -258,6 +260,8 @@ void Debugger::ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType o
if(_scriptManager->HasCpuMemoryCallbacks()) {
ProcessScripts<type>(addr, value, opType);
}
return true;
}
template<CpuType type>
@ -879,6 +883,11 @@ void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption str
}
}
FrozenAddressManager* Debugger::GetFrozenAddressManager(CpuType cpuType)
{
return &_debuggers[(int)cpuType].Debugger->GetFrozenAddressManager();
}
ITraceLogger* Debugger::GetTraceLogger(CpuType cpuType)
{
if(_debuggers[(int)cpuType].Debugger) {
@ -993,15 +1002,15 @@ template void Debugger::ProcessMemoryRead<CpuType::Gameboy>(uint32_t addr, uint8
template void Debugger::ProcessMemoryRead<CpuType::Nes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Pce>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Snes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Sa1>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Spc>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Gsu>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::NecDsp>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Cx4>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Gameboy>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Nes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Pce>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Snes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Sa1>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Spc>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Gsu>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::NecDsp>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Cx4>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Gameboy>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Nes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Pce>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessIdleCycle<CpuType::Snes>();
template void Debugger::ProcessIdleCycle<CpuType::Sa1>();

View file

@ -35,6 +35,7 @@ class IAssembler;
class IDebugger;
class ITraceLogger;
class TraceLogFileSaver;
class FrozenAddressManager;
struct TraceRow;
struct BaseState;
@ -104,7 +105,7 @@ public:
template<CpuType type> void ProcessInstruction();
template<CpuType type, typename T> void ProcessMemoryRead(uint32_t addr, T& value, MemoryOperationType opType);
template<CpuType type, typename T> void ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType opType);
template<CpuType type, typename T> bool ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType opType);
template<CpuType type> void ProcessIdleCycle();
template<CpuType type> void ProcessHaltedCpu();
template<CpuType type, typename T> void ProcessPpuRead(uint16_t addr, T& value, MemoryType memoryType, MemoryOperationType opType);
@ -158,7 +159,7 @@ public:
bool HasCpuType(CpuType cpuType);
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
void SetInputOverrides(uint32_t index, DebugControllerState state);
void GetAvailableInputOverrides(uint8_t* availableIndexes);
@ -181,6 +182,7 @@ public:
IConsole* GetConsole() { return _console; }
Emulator* GetEmulator() { return _emu; }
FrozenAddressManager* GetFrozenAddressManager(CpuType cpuType);
ITraceLogger* GetTraceLogger(CpuType cpuType);
PpuTools* GetPpuTools(CpuType cpuType);
BaseEventManager* GetEventManager(CpuType cpuType);

View file

@ -0,0 +1,34 @@
#pragma once
#include "pch.h"
class FrozenAddressManager
{
protected:
unordered_set<uint32_t> _frozenAddresses;
public:
void UpdateFrozenAddresses(uint32_t start, uint32_t end, bool freeze)
{
if(freeze) {
for(uint32_t i = start; i <= end; i++) {
_frozenAddresses.emplace(i);
}
} else {
for(uint32_t i = start; i <= end; i++) {
_frozenAddresses.erase(i);
}
}
}
bool IsFrozenAddress(uint32_t addr)
{
return _frozenAddresses.size() > 0 && _frozenAddresses.find(addr) != _frozenAddresses.end();
}
void GetFrozenState(uint32_t start, uint32_t end, bool* outState)
{
for(uint32_t i = start; i <= end; i++) {
outState[i - start] = _frozenAddresses.find(i) != _frozenAddresses.end();
}
}
};

View file

@ -3,6 +3,7 @@
#include "Debugger/DebuggerFeatures.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/StepBackManager.h"
#include "Debugger/FrozenAddressManager.h"
enum class StepType;
class BreakpointManager;
@ -23,6 +24,8 @@ protected:
unique_ptr<StepRequest> _step;
unique_ptr<StepBackManager> _stepBackManager = unique_ptr<StepBackManager>(new StepBackManager(nullptr, nullptr));
FrozenAddressManager _frozenAddressManager;
public:
bool IgnoreBreakpoints = false;
bool AllowChangeProgramCounter = false;
@ -36,6 +39,8 @@ public:
void ResetStepBackCache() { return _stepBackManager->ResetCache(); }
void StepBack() { return _stepBackManager->StepBack(); }
FrozenAddressManager& GetFrozenAddressManager() { return _frozenAddressManager; }
virtual void ResetPrevOpCode() {}
virtual void Step(int32_t stepCount, StepType type) = 0;

View file

@ -196,11 +196,12 @@ uint8_t GbMemoryManager::ReadDma(uint16_t addr)
template<MemoryOperationType type>
void GbMemoryManager::Write(uint16_t addr, uint8_t value)
{
_emu->ProcessMemoryWrite<CpuType::Gameboy>(addr, value, type);
if(_state.IsWriteRegister[addr >> 8]) {
WriteRegister(addr, value);
} else if(_writes[addr >> 8]) {
_writes[addr >> 8][(uint8_t)addr] = value;
if(_emu->ProcessMemoryWrite<CpuType::Gameboy>(addr, value, type)) {
if(_state.IsWriteRegister[addr >> 8]) {
WriteRegister(addr, value);
} else if(_writes[addr >> 8]) {
_writes[addr >> 8][(uint8_t)addr] = value;
}
}
}

View file

@ -130,8 +130,9 @@ uint8_t NesMemoryManager::Read(uint16_t addr, MemoryOperationType operationType)
void NesMemoryManager::Write(uint16_t addr, uint8_t value, MemoryOperationType operationType)
{
_emu->ProcessMemoryWrite<CpuType::Nes>(addr, value, operationType);
_ramWriteHandlers[addr]->WriteRam(addr, value);
if(_emu->ProcessMemoryWrite<CpuType::Nes>(addr, value, operationType)) {
_ramWriteHandlers[addr]->WriteRam(addr, value);
}
}
void NesMemoryManager::DebugWrite(uint16_t addr, uint8_t value, bool disableSideEffects)

View file

@ -309,9 +309,10 @@ void PceMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
void PceMemoryManager::WriteVdc(uint16_t addr, uint8_t value)
{
_emu->ProcessMemoryWrite<CpuType::Pce>(addr, value, MemoryOperationType::Write);
_vpc->StVdcWrite(addr, value);
Exec(); //CPU is delayed by 1 CPU cycle when reading/writing to VDC/VCE
if(_emu->ProcessMemoryWrite<CpuType::Pce>(addr, value, MemoryOperationType::Write)) {
_vpc->StVdcWrite(addr, value);
Exec(); //CPU is delayed by 1 CPU cycle when reading/writing to VDC/VCE
}
}
uint8_t PceMemoryManager::DebugRead(uint16_t addr)

View file

@ -122,19 +122,19 @@ __forceinline uint8_t PceMemoryManager::Read(uint16_t addr, MemoryOperationType
__forceinline void PceMemoryManager::Write(uint16_t addr, uint8_t value, MemoryOperationType type)
{
_emu->ProcessMemoryWrite<CpuType::Pce>(addr, value, type);
uint8_t bank = _state.Mpr[(addr & 0xE000) >> 13];
if(_mapper && _mapper->IsBankMapped(bank)) {
_mapper->Write(bank, addr, value);
}
addr &= 0x1FFF;
if(bank != 0xFF) {
if(_writeBanks[bank]) {
_writeBanks[bank][addr] = value;
if(_emu->ProcessMemoryWrite<CpuType::Pce>(addr, value, type)) {
uint8_t bank = _state.Mpr[(addr & 0xE000) >> 13];
if(_mapper && _mapper->IsBankMapped(bank)) {
_mapper->Write(bank, addr, value);
}
addr &= 0x1FFF;
if(bank != 0xFF) {
if(_writeBanks[bank]) {
_writeBanks[bank][addr] = value;
}
} else {
WriteRegister(addr, value);
}
} else {
WriteRegister(addr, value);
}
}

View file

@ -271,10 +271,11 @@ uint8_t Cx4::ReadCx4(uint32_t addr)
void Cx4::WriteCx4(uint32_t addr, uint8_t value)
{
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
_emu->ProcessMemoryWrite<CpuType::Cx4>(addr, value, MemoryOperationType::Write);
handler->Write(addr, value);
if(_emu->ProcessMemoryWrite<CpuType::Cx4>(addr, value, MemoryOperationType::Write)) {
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
handler->Write(addr, value);
}
}
}

View file

@ -258,13 +258,14 @@ uint8_t Gsu::ReadGsu(uint32_t addr, MemoryOperationType opType)
void Gsu::WriteGsu(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
IMemoryHandler *handler = _mappings.GetHandler(addr);
if(handler) {
handler->Write(addr, value);
} else {
LogDebug("[Debug] GSU - Missing write handler: " + HexUtilities::ToHex(addr));
if(_emu->ProcessMemoryWrite<CpuType::Gsu>(addr, value, opType)) {
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
handler->Write(addr, value);
} else {
LogDebug("[Debug] GSU - Missing write handler: " + HexUtilities::ToHex(addr));
}
}
_emu->ProcessMemoryWrite<CpuType::Gsu>(addr, value, opType);
}
void Gsu::InitProgramCache(uint16_t cacheAddr)

View file

@ -441,14 +441,15 @@ void Sa1::ProcessInterrupts()
void Sa1::WriteSa1(uint32_t addr, uint8_t value, MemoryOperationType type)
{
IMemoryHandler *handler = _mappings.GetHandler(addr);
_emu->ProcessMemoryWrite<CpuType::Sa1>(addr, value, type);
if(handler) {
_lastAccessMemType = handler->GetMemoryType();
_openBus = value;
handler->Write(addr, value);
} else {
LogDebug("[Debug] Write SA1 - missing handler: $" + HexUtilities::ToHex(addr));
if(_emu->ProcessMemoryWrite<CpuType::Sa1>(addr, value, type)) {
IMemoryHandler *handler = _mappings.GetHandler(addr);
if(handler) {
_lastAccessMemType = handler->GetMemoryType();
_openBus = value;
handler->Write(addr, value);
} else {
LogDebug("[Debug] Write SA1 - missing handler: $" + HexUtilities::ToHex(addr));
}
}
}

View file

@ -65,9 +65,10 @@ void RegisterHandlerB::Write(uint32_t addr, uint8_t value)
} if(addr >= 0x2180 && addr <= 0x2183) {
switch(addr & 0xFFFF) {
case 0x2180:
_emu->ProcessMemoryWrite<CpuType::Snes>(0x7E0000 | _wramPosition, value, MemoryOperationType::Write);
_workRam[_wramPosition] = value;
_wramPosition = (_wramPosition + 1) & 0x1FFFF;
if(_emu->ProcessMemoryWrite<CpuType::Snes>(0x7E0000 | _wramPosition, value, MemoryOperationType::Write)) {
_workRam[_wramPosition] = value;
_wramPosition = (_wramPosition + 1) & 0x1FFFF;
}
break;
case 0x2181: _wramPosition = (_wramPosition & 0x1FF00) | value; break;

View file

@ -315,14 +315,14 @@ void SnesMemoryManager::PeekBlock(uint32_t addr, uint8_t *dest)
void SnesMemoryManager::Write(uint32_t addr, uint8_t value, MemoryOperationType type)
{
IncrementMasterClockValue(_cpuSpeed);
_emu->ProcessMemoryWrite<CpuType::Snes>(addr, value, type);
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
handler->Write(addr, value);
_memTypeBusA = handler->GetMemoryType();
} else {
LogDebug("[Debug] Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
if(_emu->ProcessMemoryWrite<CpuType::Snes>(addr, value, type)) {
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
handler->Write(addr, value);
_memTypeBusA = handler->GetMemoryType();
} else {
LogDebug("[Debug] Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
}
}
}
@ -330,27 +330,27 @@ void SnesMemoryManager::WriteDma(uint32_t addr, uint8_t value, bool forBusA)
{
_cpu->DetectNmiSignalEdge();
IncMasterClock4();
_emu->ProcessMemoryWrite<CpuType::Snes>(addr, value, MemoryOperationType::DmaWrite);
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
if(forBusA && handler == _registerHandlerB.get() && (addr & 0xFF00) == 0x2100) {
//Trying to write to bus B using bus A does nothing
} else if(handler == _registerHandlerA.get()) {
uint16_t regAddr = addr & 0xFFFF;
if(regAddr == 0x420B || regAddr == 0x420C || (regAddr >= 0x4300 && regAddr <= 0x437F)) {
//Trying to write to the DMA controller with DMA does nothing
if(_emu->ProcessMemoryWrite<CpuType::Snes>(addr, value, MemoryOperationType::DmaWrite)) {
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
if(forBusA && handler == _registerHandlerB.get() && (addr & 0xFF00) == 0x2100) {
//Trying to write to bus B using bus A does nothing
} else if(handler == _registerHandlerA.get()) {
uint16_t regAddr = addr & 0xFFFF;
if(regAddr == 0x420B || regAddr == 0x420C || (regAddr >= 0x4300 && regAddr <= 0x437F)) {
//Trying to write to the DMA controller with DMA does nothing
} else {
handler->Write(addr, value);
}
} else {
handler->Write(addr, value);
if(handler != _registerHandlerB.get()) {
_memTypeBusA = handler->GetMemoryType();
}
}
} else {
handler->Write(addr, value);
if(handler != _registerHandlerB.get()) {
_memTypeBusA = handler->GetMemoryType();
}
LogDebug("[Debug] Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
}
} else {
LogDebug("[Debug] Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
}
}

View file

@ -264,8 +264,9 @@ void Spc::Write(uint16_t addr, uint8_t value, MemoryOperationType type)
//Writes always affect the underlying RAM
if(_state.WriteEnabled) {
_emu->ProcessMemoryWrite<CpuType::Spc>(addr, value, type);
_ram[addr] = value;
if(_emu->ProcessMemoryWrite<CpuType::Spc>(addr, value, type)) {
_ram[addr] = value;
}
}
switch(addr) {

View file

@ -274,11 +274,12 @@ public:
}
}
template<CpuType type, typename T> __forceinline void ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType opType)
template<CpuType type, typename T> __forceinline bool ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType opType)
{
if(_debugger) {
_debugger->ProcessMemoryWrite<type>(addr, value, opType);
return _debugger->ProcessMemoryWrite<type>(addr, value, opType);
}
return true;
}
template<CpuType type> __forceinline void ProcessIdleCycle()

View file

@ -19,6 +19,7 @@
#include "Core/Debugger/BaseEventManager.h"
#include "Core/Debugger/ITraceLogger.h"
#include "Core/Debugger/TraceLogFileSaver.h"
#include "Core/Debugger/FrozenAddressManager.h"
#include "Core/Gameboy/GbTypes.h"
#include "Utilities/StringUtilities.h"
@ -121,6 +122,9 @@ extern "C"
string logString = WithDebugger(string, GetLog());
StringUtilities::CopyToBuffer(logString, outBuffer, maxLength);
}
DllExport void __stdcall UpdateFrozenAddresses(CpuType cpuType, uint32_t start, uint32_t end, bool freeze) { return WithDebugger(void, GetFrozenAddressManager(cpuType)->UpdateFrozenAddresses(start, end, freeze)); }
DllExport void __stdcall GetFrozenState(CpuType cpuType, uint32_t start, uint32_t end, bool* outState) { return WithDebugger(void, GetFrozenAddressManager(cpuType)->GetFrozenState(start, end, outState)); }
DllExport void __stdcall SetMemoryState(MemoryType type, uint8_t* buffer, int32_t length) { WithDebugger(void, GetMemoryDumper()->SetMemoryState(type, buffer, length)); }
DllExport uint32_t __stdcall GetMemorySize(MemoryType type) { return WithDebugger(uint32_t, GetMemoryDumper()->GetMemorySize(type)); }

View file

@ -235,8 +235,8 @@ namespace Mesen.Config
Add(new() { Shortcut = DebuggerShortcut.PaletteViewer_ViewInMemoryViewer, KeyBinding = new(Key.F1) });
//Memory Tools
//Add(new() { Shortcut = eDebuggerShortcut.MemoryViewer_Freeze, KeyBinding = new(KeyModifiers.Control, Key.Q) });
//Add(new() { Shortcut = eDebuggerShortcut.MemoryViewer_Unfreeze, KeyBinding = new(KeyModifiers.Control, Key.W) });
Add(new() { Shortcut = DebuggerShortcut.MemoryViewer_Freeze, KeyBinding = new(KeyModifiers.Control, Key.Q) });
Add(new() { Shortcut = DebuggerShortcut.MemoryViewer_Unfreeze, KeyBinding = new(KeyModifiers.Control, Key.W) });
Add(new() { Shortcut = DebuggerShortcut.MemoryViewer_AddToWatch, KeyBinding = new() });
Add(new() { Shortcut = DebuggerShortcut.MemoryViewer_EditBreakpoint, KeyBinding = new() });
Add(new() { Shortcut = DebuggerShortcut.MemoryViewer_EditLabel, KeyBinding = new() });

View file

@ -42,6 +42,8 @@ namespace Mesen.Config
[Reactive] public HighlightConfig CodeHighlight { get; set; } = new() { Highlight = false, ColorCode = Colors.DarkSeaGreen.ToUint32() };
[Reactive] public HighlightConfig DataHighlight { get; set; } = new() { Highlight = false, ColorCode = Colors.LightSteelBlue.ToUint32() };
[Reactive] public HighlightConfig FrozenHighlight { get; set; } = new() { Highlight = true, ColorCode = Colors.Magenta.ToUint32() };
[Reactive] public HighlightConfig NesPcmDataHighlight { get; set; } = new() { Highlight = false, ColorCode = Colors.Khaki.ToUint32() };
[Reactive] public HighlightConfig NesDrawnChrRomHighlight { get; set; } = new() { Highlight = false, ColorCode = Colors.Thistle.ToUint32() };

View file

@ -26,6 +26,7 @@ namespace Mesen.Debugger
private byte[] _data = Array.Empty<byte>();
private long _firstByteIndex = 0;
private TblByteCharConverter? _tblConverter = null;
private byte[]? _frozenAddresses = null;
public HexEditorDataProvider(MemoryType memoryType, HexEditorConfig cfg, TblByteCharConverter? tblConverter)
{
@ -74,6 +75,12 @@ namespace Mesen.Debugger
_counters = DebugApi.GetMemoryAccessCounts((UInt32)firstByteIndex, (UInt32)visibleByteCount, _memoryType);
if(_cfg.FrozenHighlight.Highlight && _memoryType.IsRelativeMemory() && !_memoryType.IsPpuMemory()) {
_frozenAddresses = DebugApi.GetFrozenState(_cpuType, (UInt32)firstByteIndex, (UInt32)(firstByteIndex + visibleByteCount));
} else {
_frozenAddresses = null;
}
_cdlData = null;
if(_memoryType.SupportsCdl()) {
if(_cfg.DataHighlight.Highlight || _cfg.CodeHighlight.Highlight || (_cpuType == CpuType.Nes && (_cfg.NesDrawnChrRomHighlight.Highlight || _cfg.NesPcmDataHighlight.Highlight))) {
@ -185,6 +192,10 @@ namespace Mesen.Debugger
_byteInfo.ForeColor = Color.FromArgb(alpha, 0, 0, 0);
}
if(_frozenAddresses != null && _frozenAddresses[index] != 0) {
_byteInfo.ForeColor = _cfg.FrozenHighlight.Color;
}
_byteInfo.Value = _data[index];
return _byteInfo;

View file

@ -753,5 +753,10 @@ namespace Mesen.Debugger.Utilities
[IconFile("Settings")]
GameConfig,
[IconFile("MediaStop")]
FreezeMemory,
[IconFile("MediaPlay")]
UnfreezeMemory,
}
}

View file

@ -24,6 +24,7 @@ namespace Mesen.Debugger.ViewModels
public int[] AvailableWidths => new int[] { 4, 8, 16, 32, 48, 64, 80, 96, 112, 128 };
[Reactive] public bool ShowFrozenAddressesOption { get; set; }
[Reactive] public bool ShowNesPcmDataOption { get; set; }
[Reactive] public bool ShowNesDrawnChrRomOption { get; set; }
@ -41,6 +42,7 @@ namespace Mesen.Debugger.ViewModels
public void UpdateAvailableOptions()
{
ShowFrozenAddressesOption = Config.MemoryType.SupportsFreezeAddress();
ShowNesPcmDataOption = Config.MemoryType.ToCpuType() == CpuType.Nes;
ShowNesDrawnChrRomOption = Config.MemoryType.ToCpuType() == CpuType.Nes && DebugApi.GetMemorySize(MemoryType.NesChrRom) > 0;
}

View file

@ -96,6 +96,12 @@
Checked="{CompiledBinding Config.LabelHighlight.Highlight}"
Color="{CompiledBinding Config.LabelHighlight.Color}"
/>
<dc:ColorCheckbox
Text="{l:Translate chkHighlightFrozenAddresses}"
Checked="{CompiledBinding Config.FrozenHighlight.Highlight}"
Color="{CompiledBinding Config.FrozenHighlight.Color}"
IsVisible="{CompiledBinding ShowFrozenAddressesOption}"
/>
<dc:ColorCheckbox
Text="{l:Translate chkHighlightNesPcmData}"
Checked="{CompiledBinding Config.NesPcmDataHighlight.Highlight}"

View file

@ -151,6 +151,9 @@ namespace Mesen.Debugger.Windows
new ContextMenuSeparator(),
GetViewInDebuggerAction(),
GetViewInMemoryAction(),
new ContextMenuSeparator() { IsVisible = () => _model.Config.MemoryType.SupportsFreezeAddress() },
GetFreezeAction(ActionType.FreezeMemory, DebuggerShortcut.MemoryViewer_Freeze),
GetFreezeAction(ActionType.UnfreezeMemory, DebuggerShortcut.MemoryViewer_Unfreeze),
new ContextMenuSeparator(),
new ContextMenuAction() {
ActionType = ActionType.Copy,
@ -318,7 +321,7 @@ namespace Mesen.Debugger.Windows
}
});
_model.ToolbarItems = _model.AddDisposables(new List<ContextMenuAction>() {
_model.ToolbarItems = _model.AddDisposables(new List<ContextMenuAction>() {
GetImportAction(),
GetExportAction(),
});
@ -544,6 +547,22 @@ namespace Mesen.Debugger.Windows
};
}
private ContextMenuAction GetFreezeAction(ActionType action, DebuggerShortcut shortcut)
{
DebugConfig cfg = ConfigManager.Config.Debug;
return new ContextMenuAction() {
ActionType = action,
HintText = () => GetAddressRange(),
IsVisible = () => _model.Config.MemoryType.SupportsFreezeAddress(),
OnClick = () => {
UInt32 start = (UInt32)_editor.SelectionStart;
UInt32 end = (UInt32)(_editor.SelectionStart + Math.Max(1, _editor.SelectionLength) - 1);
DebugApi.UpdateFrozenAddresses(_model.Config.MemoryType.ToCpuType(), start, end, action == ActionType.FreezeMemory);
},
Shortcut = () => cfg.Shortcuts.Get(shortcut)
};
}
private ContextMenuAction GetMarkSelectionAction()
{
return MarkSelectionHelper.GetAction(

View file

@ -212,6 +212,15 @@ namespace Mesen.Interop
[DllImport(DllPath)] public static extern void SetMemoryValues(MemoryType type, UInt32 address, [In] byte[] data, Int32 length);
[DllImport(DllPath)] public static extern void SetMemoryState(MemoryType type, [In] byte[] buffer, Int32 length);
[DllImport(DllPath)] public static extern void UpdateFrozenAddresses(CpuType cpuType, UInt32 start, UInt32 end, [MarshalAs(UnmanagedType.I1)] bool freeze);
[DllImport(DllPath)] private static extern void GetFrozenState(CpuType type, UInt32 start, UInt32 end, [In, Out] byte[] outState);
public static byte[] GetFrozenState(CpuType cpuType, UInt32 start, UInt32 end)
{
byte[] outState = new byte[end - start + 1];
DebugApi.GetFrozenState(cpuType, start, end, outState);
return outState;
}
[DllImport(DllPath)] public static extern AddressInfo GetAbsoluteAddress(AddressInfo relAddress);
[DllImport(DllPath)] public static extern AddressInfo GetRelativeAddress(AddressInfo absAddress, CpuType cpuType);

View file

@ -262,6 +262,11 @@ namespace Mesen.Interop
return true;
}
public static bool SupportsFreezeAddress(this MemoryType memType)
{
return memType.IsRelativeMemory() && !memType.IsPpuMemory();
}
public static string GetShortName(this MemoryType memType)
{
return memType switch {

View file

@ -1160,6 +1160,7 @@
<Control ID="chkHighlightData">Data</Control>
<Control ID="lblUnidentified">Unidentified</Control>
<Control ID="chkHighlightLabel">Labels</Control>
<Control ID="chkHighlightFrozenAddresses">Frozen addresses</Control>
<Control ID="chkHighlightNesPcmData">DPCM Data</Control>
<Control ID="chkHighlightNesDrawnChrRom">Drawn tiles (PPU)</Control>
<Control ID="chkHighlightBreakpoints">Breakpoints</Control>
@ -3112,8 +3113,11 @@
<Value ID="ExportMovie">Export Movie</Value>
<Value ID="CreateSaveState">Create Save State</Value>
<Value ID="ResumeGameplay">Resume Gameplay</Value>
<Value ID="GameConfig">Game Settings</Value>
<Value ID="FreezeMemory">Freeze</Value>
<Value ID="UnfreezeMemory">Unfreeze</Value>
</Enum>
</Enums>
</Resources>