From ad548465c6f06c6a9f57092e4e7b8ed226048f09 Mon Sep 17 00:00:00 2001 From: Sour Date: Thu, 27 Mar 2025 18:03:25 +0900 Subject: [PATCH] Debugger: NES - Added new "break on..." options Invalid VRAM access, Invalid OAM write, DMA controller read, Unstable opcodes --- Core/Debugger/DebugTypes.h | 7 ++++-- Core/Debugger/Debugger.cpp | 27 +++++++++++--------- Core/NES/Debugger/NesDebugger.cpp | 7 ++++-- Core/NES/Debugger/NesDisUtils.cpp | 5 ++++ Core/NES/Debugger/NesDisUtils.h | 1 + Core/NES/NesCpu.cpp | 4 +-- Core/NES/NesPpu.cpp | 12 ++++++--- Core/Shared/SettingTypes.h | 7 ++++-- UI/Config/Debugger/DebugConfig.cs | 16 ++++++++---- UI/Config/Debugger/NesDebuggerConfig.cs | 9 ++++--- UI/Debugger/Views/DebuggerOptionsView.axaml | 28 ++++++++++++++++----- UI/Debugger/Windows/DebuggerWindow.axaml | 2 +- UI/Interop/DebugApi.cs | 7 ++++-- UI/Localization/resources.en.xml | 26 ++++++++++++------- 14 files changed, 109 insertions(+), 49 deletions(-) diff --git a/Core/Debugger/DebugTypes.h b/Core/Debugger/DebugTypes.h index c92b90d6..7b922910 100644 --- a/Core/Debugger/DebugTypes.h +++ b/Core/Debugger/DebugTypes.h @@ -325,12 +325,15 @@ enum class BreakSource GbOamCorruption, NesBreakOnDecayedOamRead, - NesBreakOnPpu2000ScrollGlitch, - NesBreakOnPpu2006ScrollGlitch, + NesBreakOnPpuScrollGlitch, BreakOnUnofficialOpCode, + BreakOnUnstableOpCode, NesBusConflict, NesBreakOnCpuCrash, NesBreakOnExtOutputMode, + NesInvalidVramAccess, + NesInvalidOamWrite, + NesDmaInputRead, PceBreakOnInvalidVramAddress, diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index 9ad4aac6..bab28da0 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -789,19 +789,22 @@ bool Debugger::IsDebugWindowOpened(CpuType cpuType) bool Debugger::IsBreakOptionEnabled(BreakSource src) { + DebugConfig& cfg = _settings->GetDebugConfig(); switch(src) { - case BreakSource::GbDisableLcdOutsideVblank: return _settings->GetDebugConfig().GbBreakOnDisableLcdOutsideVblank; - case BreakSource::GbInvalidVramAccess: return _settings->GetDebugConfig().GbBreakOnInvalidVramAccess; - case BreakSource::GbInvalidOamAccess: return _settings->GetDebugConfig().GbBreakOnInvalidOamAccess; - case BreakSource::NesBreakOnDecayedOamRead: return _settings->GetDebugConfig().NesBreakOnDecayedOamRead; - case BreakSource::NesBreakOnPpu2000ScrollGlitch: return _settings->GetDebugConfig().NesBreakOnPpu2000ScrollGlitch; - case BreakSource::NesBreakOnPpu2006ScrollGlitch: return _settings->GetDebugConfig().NesBreakOnPpu2006ScrollGlitch; - case BreakSource::NesBusConflict: return _settings->GetDebugConfig().NesBreakOnBusConflict; - case BreakSource::NesBreakOnCpuCrash: return _settings->GetDebugConfig().NesBreakOnCpuCrash; - case BreakSource::NesBreakOnExtOutputMode: return _settings->GetDebugConfig().NesBreakOnExtOutputMode; - case BreakSource::PceBreakOnInvalidVramAddress: return _settings->GetDebugConfig().PceBreakOnInvalidVramAddress; - case BreakSource::GbaInvalidOpCode: return _settings->GetDebugConfig().GbaBreakOnInvalidOpCode; - case BreakSource::GbaUnalignedMemoryAccess: return _settings->GetDebugConfig().GbaBreakOnUnalignedMemAccess; + case BreakSource::GbDisableLcdOutsideVblank: return cfg.GbBreakOnDisableLcdOutsideVblank; + case BreakSource::GbInvalidVramAccess: return cfg.GbBreakOnInvalidVramAccess; + case BreakSource::GbInvalidOamAccess: return cfg.GbBreakOnInvalidOamAccess; + case BreakSource::NesBreakOnDecayedOamRead: return cfg.NesBreakOnDecayedOamRead; + case BreakSource::NesBreakOnPpuScrollGlitch: return cfg.NesBreakOnPpuScrollGlitch; + case BreakSource::NesBusConflict: return cfg.NesBreakOnBusConflict; + case BreakSource::NesBreakOnCpuCrash: return cfg.NesBreakOnCpuCrash; + case BreakSource::NesBreakOnExtOutputMode: return cfg.NesBreakOnExtOutputMode; + case BreakSource::NesInvalidVramAccess: return cfg.NesBreakOnInvalidVramAccess; + case BreakSource::NesInvalidOamWrite: return cfg.NesBreakOnInvalidOamWrite; + case BreakSource::NesDmaInputRead: return cfg.NesBreakOnDmaInputRead; + case BreakSource::PceBreakOnInvalidVramAddress: return cfg.PceBreakOnInvalidVramAddress; + case BreakSource::GbaInvalidOpCode: return cfg.GbaBreakOnInvalidOpCode; + case BreakSource::GbaUnalignedMemoryAccess: return cfg.GbaBreakOnUnalignedMemAccess; } return true; } diff --git a/Core/NES/Debugger/NesDebugger.cpp b/Core/NES/Debugger/NesDebugger.cpp index 2a52eeff..c0e6ac42 100644 --- a/Core/NES/Debugger/NesDebugger.cpp +++ b/Core/NES/Debugger/NesDebugger.cpp @@ -10,7 +10,6 @@ #include "Debugger/MemoryDumper.h" #include "Debugger/MemoryAccessCounter.h" #include "Debugger/ExpressionEvaluator.h" -#include "Debugger/CodeDataLogger.h" #include "NES/NesHeader.h" #include "NES/NesConsole.h" #include "NES/NesCpu.h" @@ -135,6 +134,8 @@ void NesDebugger::ProcessInstruction() _step->Break(BreakSource::BreakOnBrk); } else if(_settings->GetDebugConfig().NesBreakOnUnofficialOpCode && NesDisUtils::IsOpUnofficial(opCode)) { _step->Break(BreakSource::BreakOnUnofficialOpCode); + } else if(_settings->GetDebugConfig().NesBreakOnUnstableOpCode && NesDisUtils::IsOpUnstable(opCode)) { + _step->Break(BreakSource::BreakOnUnstableOpCode); } } @@ -189,7 +190,9 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType if(operation.Type == MemoryOperationType::DmaRead) { bool isDmcDma = _cpu->IsDmcDma(); _eventManager->AddEvent(isDmcDma ? DebugEventType::DmcDmaRead : DebugEventType::DmaRead, operation); - if(isDmcDma && addressInfo.Type == MemoryType::NesPrgRom && addressInfo.Address >= 0) { + if((addr == 0x4016 || addr == 0x4017) && _settings->CheckDebuggerFlag(DebuggerFlags::NesDebuggerEnabled) && _settings->GetDebugConfig().NesBreakOnDmaInputRead) { + _debugger->BreakImmediately(CpuType::Nes, BreakSource::NesDmaInputRead); + } else if(isDmcDma && addressInfo.Type == MemoryType::NesPrgRom && addressInfo.Address >= 0) { _codeDataLogger->SetData(addressInfo.Address); } } else if(operation.Type != MemoryOperationType::DummyRead && addressInfo.Type == MemoryType::NesPrgRom && addressInfo.Address >= 0) { diff --git a/Core/NES/Debugger/NesDisUtils.cpp b/Core/NES/Debugger/NesDisUtils.cpp index 8c699e44..05a3537a 100644 --- a/Core/NES/Debugger/NesDisUtils.cpp +++ b/Core/NES/Debugger/NesDisUtils.cpp @@ -239,6 +239,11 @@ bool NesDisUtils::IsOpUnofficial(uint8_t opCode) return _isUnofficial[opCode]; } +bool NesDisUtils::IsOpUnstable(uint8_t opCode) +{ + return opCode == 0xAB || opCode == 0x8B; +} + bool NesDisUtils::IsUnconditionalJump(uint8_t opCode) { switch(opCode) { diff --git a/Core/NES/Debugger/NesDisUtils.h b/Core/NES/Debugger/NesDisUtils.h index b51b3ce0..20b62cc6 100644 --- a/Core/NES/Debugger/NesDisUtils.h +++ b/Core/NES/Debugger/NesDisUtils.h @@ -22,6 +22,7 @@ public: static char const* const GetOpName(uint8_t opCode); static NesAddrMode GetOpMode(uint8_t opCode); static bool IsOpUnofficial(uint8_t opCode); + static bool IsOpUnstable(uint8_t opCode); static bool IsUnconditionalJump(uint8_t opCode); static bool IsConditionalJump(uint8_t opCode); static CdlFlags::CdlFlags GetOpFlags(uint8_t opCode, uint16_t pc, uint16_t prevPc); diff --git a/Core/NES/NesCpu.cpp b/Core/NES/NesCpu.cpp index aabcd890..5c60cd80 100644 --- a/Core/NES/NesCpu.cpp +++ b/Core/NES/NesCpu.cpp @@ -433,7 +433,7 @@ void NesCpu::ProcessPendingDma(uint16_t readAddress) assert(_needHalt || _needDummyRead); processCycle(); if(!skipDummyReads) { - _memoryManager->Read(readAddress, MemoryOperationType::DummyRead); + _memoryManager->Read(readAddress, MemoryOperationType::DmaRead); } EndCpuCycle(true); } @@ -451,7 +451,7 @@ void NesCpu::ProcessPendingDma(uint16_t readAddress) //Align to read cycle before starting sprite DMA (or align to perform DMC read) processCycle(); if(!skipDummyReads) { - _memoryManager->Read(readAddress, MemoryOperationType::DummyRead); + _memoryManager->Read(readAddress, MemoryOperationType::DmaRead); } EndCpuCycle(true); } diff --git a/Core/NES/NesPpu.cpp b/Core/NES/NesPpu.cpp index 64845dcd..1c2e9853 100644 --- a/Core/NES/NesPpu.cpp +++ b/Core/NES/NesPpu.cpp @@ -389,6 +389,10 @@ template uint8_t NesPpu::ReadRam(uint16_t addr) openBusMask = 0x00; } + if(_scanline < 240 && IsRenderingEnabled()) { + _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesInvalidVramAccess); + } + _ignoreVramRead = 6; _needStateUpdate = true; _needVideoRamIncrement = true; @@ -441,6 +445,7 @@ template void NesPpu::WriteRam(uint16_t addr, uint8_t value) //"Writes to OAMDATA during rendering (on the pre-render line and the visible lines 0-239, provided either sprite or background rendering is enabled) do not modify values in OAM, //but do perform a glitchy increment of OAMADDR, bumping only the high 6 bits" _spriteRamAddr = (_spriteRamAddr + 4) & 0xFF; + _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesInvalidOamWrite); } break; @@ -489,6 +494,7 @@ template void NesPpu::WriteRam(uint16_t addr, uint8_t value) } else { //During rendering, the value written is ignored, and instead the address' LSB is used (not confirmed, based on Visual NES) _mapper->WriteVram(_ppuBusAddress & 0x3FFF, _ppuBusAddress & 0xFF); + _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesInvalidVramAccess); } } _needStateUpdate = true; @@ -510,7 +516,7 @@ template void NesPpu::ProcessTmpAddrScrollGlitch(uint16_t normalAddr if(_cycle == 257 && _console->GetNesConfig().EnablePpu2000ScrollGlitch && _scanline < 240 && IsRenderingEnabled()) { //Use open bus to set some parts of V (glitch that occurs when writing to $2000/$2005/$2006 on cycle 257) _videoRamAddr = (_videoRamAddr & ~mask) | (value & mask); - _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpu2000ScrollGlitch); + _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpuScrollGlitch); } } @@ -1461,10 +1467,10 @@ template void NesPpu::UpdateState() //When a $2006 address update lands on the Y or X increment, the written value is bugged and is ANDed with the incremented value if(_cycle == 257) { _videoRamAddr &= _updateVramAddr; - _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpu2006ScrollGlitch); + _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpuScrollGlitch); } else if(_cycle > 0 && (_cycle & 0x07) == 0 && (_cycle <= 256 || _cycle > 320)) { _videoRamAddr = (_updateVramAddr & ~0x41F) | (_videoRamAddr & _updateVramAddr & 0x41F); - _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpu2006ScrollGlitch); + _emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpuScrollGlitch); } else { _videoRamAddr = _updateVramAddr; } diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h index 9dd99dbf..d5166740 100644 --- a/Core/Shared/SettingTypes.h +++ b/Core/Shared/SettingTypes.h @@ -812,12 +812,15 @@ struct DebugConfig bool NesBreakOnBrk = false; bool NesBreakOnUnofficialOpCode = false; + bool NesBreakOnUnstableOpCode = false; bool NesBreakOnCpuCrash = false; bool NesBreakOnBusConflict = false; bool NesBreakOnDecayedOamRead = false; - bool NesBreakOnPpu2000ScrollGlitch = false; - bool NesBreakOnPpu2006ScrollGlitch = false; + bool NesBreakOnPpuScrollGlitch = false; bool NesBreakOnExtOutputMode = false; + bool NesBreakOnInvalidVramAccess = false; + bool NesBreakOnInvalidOamWrite = false; + bool NesBreakOnDmaInputRead = false; bool PceBreakOnBrk = false; bool PceBreakOnUnofficialOpCode = false; diff --git a/UI/Config/Debugger/DebugConfig.cs b/UI/Config/Debugger/DebugConfig.cs index 06f18deb..b7e317cc 100644 --- a/UI/Config/Debugger/DebugConfig.cs +++ b/UI/Config/Debugger/DebugConfig.cs @@ -73,12 +73,15 @@ namespace Mesen.Config NesBreakOnBrk = Debugger.Nes.BreakOnBrk, NesBreakOnUnofficialOpCode = Debugger.Nes.BreakOnUnofficialOpCode, + NesBreakOnUnstableOpCode = Debugger.Nes.BreakOnUnstableOpCode, NesBreakOnCpuCrash = Debugger.Nes.BreakOnCpuCrash, NesBreakOnBusConflict = Debugger.Nes.BreakOnBusConflict, NesBreakOnDecayedOamRead = Debugger.Nes.BreakOnDecayedOamRead, - NesBreakOnPpu2000ScrollGlitch = Debugger.Nes.BreakOnPpu2000ScrollGlitch, - NesBreakOnPpu2006ScrollGlitch = Debugger.Nes.BreakOnPpu2006ScrollGlitch, - NesBreakOnExtOutputMode = Debugger.Nes.NesBreakOnExtOutputMode, + NesBreakOnPpuScrollGlitch = Debugger.Nes.BreakOnPpuScrollGlitch, + NesBreakOnExtOutputMode = Debugger.Nes.BreakOnExtOutputMode, + NesBreakOnInvalidVramAccess = Debugger.Nes.BreakOnInvalidVramAccess, + NesBreakOnInvalidOamWrite = Debugger.Nes.BreakOnInvalidOamWrite, + NesBreakOnDmaInputRead = Debugger.Nes.BreakOnDmaInputRead, PceBreakOnBrk = Debugger.Pce.BreakOnBrk, PceBreakOnUnofficialOpCode = Debugger.Pce.BreakOnUnofficialOpCode, @@ -136,12 +139,15 @@ namespace Mesen.Config [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnBrk; [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnUnofficialOpCode; + [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnUnstableOpCode; [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnCpuCrash; [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnBusConflict; [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnDecayedOamRead; - [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnPpu2000ScrollGlitch; - [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnPpu2006ScrollGlitch; + [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnPpuScrollGlitch; [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnExtOutputMode; + [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnInvalidVramAccess; + [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnInvalidOamWrite; + [MarshalAs(UnmanagedType.I1)] public bool NesBreakOnDmaInputRead; [MarshalAs(UnmanagedType.I1)] public bool PceBreakOnBrk; [MarshalAs(UnmanagedType.I1)] public bool PceBreakOnUnofficialOpCode; diff --git a/UI/Config/Debugger/NesDebuggerConfig.cs b/UI/Config/Debugger/NesDebuggerConfig.cs index 4e78ed33..def98566 100644 --- a/UI/Config/Debugger/NesDebuggerConfig.cs +++ b/UI/Config/Debugger/NesDebuggerConfig.cs @@ -13,12 +13,15 @@ namespace Mesen.Config { [Reactive] public bool BreakOnBrk { get; set; } = false; [Reactive] public bool BreakOnUnofficialOpCode { get; set; } = false; + [Reactive] public bool BreakOnUnstableOpCode { get; set; } = false; [Reactive] public bool BreakOnCpuCrash { get; set; } = false; [Reactive] public bool BreakOnBusConflict { get; set; } = false; [Reactive] public bool BreakOnDecayedOamRead { get; set; } = false; - [Reactive] public bool BreakOnPpu2000ScrollGlitch { get; set; } = false; - [Reactive] public bool BreakOnPpu2006ScrollGlitch { get; set; } = false; - [Reactive] public bool NesBreakOnExtOutputMode { get; set; } = true; + [Reactive] public bool BreakOnPpuScrollGlitch { get; set; } = false; + [Reactive] public bool BreakOnExtOutputMode { get; set; } = true; + [Reactive] public bool BreakOnInvalidVramAccess { get; set; } = false; + [Reactive] public bool BreakOnInvalidOamWrite { get; set; } = false; + [Reactive] public bool BreakOnDmaInputRead { get; set; } = false; } } diff --git a/UI/Debugger/Views/DebuggerOptionsView.axaml b/UI/Debugger/Views/DebuggerOptionsView.axaml index 0e00764a..caec69e4 100644 --- a/UI/Debugger/Views/DebuggerOptionsView.axaml +++ b/UI/Debugger/Views/DebuggerOptionsView.axaml @@ -22,9 +22,12 @@ + - + @@ -127,6 +130,11 @@ IsChecked="{Binding Config.Nes.BreakOnUnofficialOpCode}" Content="{l:Translate chkBreakOnUnofficialOpCode}" /> + + + diff --git a/UI/Debugger/Windows/DebuggerWindow.axaml b/UI/Debugger/Windows/DebuggerWindow.axaml index 6757855a..649bd42d 100644 --- a/UI/Debugger/Windows/DebuggerWindow.axaml +++ b/UI/Debugger/Windows/DebuggerWindow.axaml @@ -154,7 +154,7 @@ - + diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index cd43fb1f..8e660ad4 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -1557,12 +1557,15 @@ namespace Mesen.Interop GbOamCorruption, NesBreakOnDecayedOamRead, - NesBreakOnPpu2000ScrollGlitch, - NesBreakOnPpu2006ScrollGlitch, + NesBreakOnPpuScrollGlitch, BreakOnUnofficialOpCode, + BreakOnUnstableOpCode, NesBusConflict, NesBreakOnCpuCrash, NesBreakOnExtOutputMode, + NesInvalidVramAccess, + NesInvalidOamWrite, + NesDmaInputRead, PceBreakOnInvalidVramAddress, diff --git a/UI/Localization/resources.en.xml b/UI/Localization/resources.en.xml index 188710e7..88b0c394 100644 --- a/UI/Localization/resources.en.xml +++ b/UI/Localization/resources.en.xml @@ -1354,21 +1354,25 @@ Power/reset Unofficial opcodes + Unstable opcodes + Breaks on "ANE #imm" ($8B) and "LAX #imm" ($AB) Both opcodes are unstable on hardware and their results are usually unreliable. CPU crash Bus conflict Decayed OAM read - PPU $2000/5/6 scroll glitch - PPU $2006 scroll glitch + PPU scroll glitch + Invalid VRAM access + Invalid OAM write + DMA controller read PPU EXT output mode - Break on invalid OAM access - Break on invalid VRAM access - Break on LCD disable outside vblank - Break on invalid instructions - Break on LD B, B (nop) - Break on OAM corruption + Invalid OAM access + Invalid VRAM access + LCD disable outside vblank + Invalid instructions + LD B, B (nop) + OAM corruption - Break on invalid VRAM address + Invalid VRAM address Break on invalid instructions Break on MOV R11, R11 (nop) @@ -2945,9 +2949,13 @@ E $2000/5/6 scroll glitch $2006 scroll glitch Unofficial OP + Unstable OP Bus conflict CPU crashed PPU EXT output mode + Invalid VRAM access + Invalid OAM access + DMA controller read Invalid VRAM address (>= $8000)