diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index e851e228..c71097ab 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -256,6 +256,10 @@ void Debugger::SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOpe _debuggers[(int)sourceCpu].Debugger->IgnoreBreakpoints = true; } + if(_settings->CheckDebuggerFlag(DebuggerFlags::DrawPartialFrame)) { + _debuggers[(int)sourceCpu].Debugger->DrawPartialFrame(); + } + //Only trigger code break event if the pause was caused by user action BreakEvent evt = {}; evt.SourceCpu = sourceCpu; diff --git a/Core/Debugger/IDebugger.h b/Core/Debugger/IDebugger.h index c796201f..4568ffdc 100644 --- a/Core/Debugger/IDebugger.h +++ b/Core/Debugger/IDebugger.h @@ -31,6 +31,8 @@ public: virtual void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) {} + virtual void DrawPartialFrame() { } + virtual DebuggerFeatures GetSupportedFeatures() { return {}; } virtual uint32_t GetProgramCounter(bool getInstPc) = 0; diff --git a/Core/Gameboy/Debugger/GbDebugger.cpp b/Core/Gameboy/Debugger/GbDebugger.cpp index d94281ba..db010d8e 100644 --- a/Core/Gameboy/Debugger/GbDebugger.cpp +++ b/Core/Gameboy/Debugger/GbDebugger.cpp @@ -253,6 +253,11 @@ void GbDebugger::Step(int32_t stepCount, StepType type) _step.reset(new StepRequest(step)); } +void GbDebugger::DrawPartialFrame() +{ + _ppu->DebugSendFrame(); +} + void GbDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) { AddressInfo src = _gameboy->GetAbsoluteAddress(_prevProgramCounter); diff --git a/Core/Gameboy/Debugger/GbDebugger.h b/Core/Gameboy/Debugger/GbDebugger.h index 5b45a471..6798e6a2 100644 --- a/Core/Gameboy/Debugger/GbDebugger.h +++ b/Core/Gameboy/Debugger/GbDebugger.h @@ -66,7 +66,9 @@ public: void Run() override; void Step(int32_t stepCount, StepType type) override; - + + void DrawPartialFrame() override; + void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption); void SetProgramCounter(uint32_t addr) override; diff --git a/Core/Gameboy/GbPpu.cpp b/Core/Gameboy/GbPpu.cpp index dcf0776d..aed5c063 100644 --- a/Core/Gameboy/GbPpu.cpp +++ b/Core/Gameboy/GbPpu.cpp @@ -653,12 +653,8 @@ void GbPpu::SendFrame() _isFirstFrame = false; RenderedFrame frame(_currentBuffer, GbConstants::ScreenWidth, GbConstants::ScreenHeight, 1.0, _state.FrameCount, _gameboy->GetControlManager()->GetPortStates()); -#ifdef LIBRETRO - _emu->GetVideoDecoder()->UpdateFrame(frame, true, false); -#else bool rewinding = _emu->GetRewindManager()->IsRewinding(); _emu->GetVideoDecoder()->UpdateFrame(frame, rewinding, rewinding); -#endif _emu->ProcessEndOfFrame(); _gameboy->ProcessEndOfFrame(); @@ -666,6 +662,26 @@ void GbPpu::SendFrame() _currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0]; } +void GbPpu::DebugSendFrame() +{ + if(_gameboy->IsSgb()) { + return; + } + + int lastPixel = std::max(0, _state.IrqMode == PpuMode::HBlank ? 160 : _drawnPixels); + int offset = std::max(0, (int)(lastPixel + 1 + _state.Scanline * GbConstants::ScreenWidth)); + int pixelsToClear = GbConstants::PixelCount - offset; + if(pixelsToClear > 0) { + memset(_currentBuffer + offset, 0, pixelsToClear * sizeof(uint16_t)); + } + + RenderedFrame frame(_currentBuffer, GbConstants::ScreenWidth, GbConstants::ScreenHeight, 1.0, _state.FrameCount); + + _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); + //Send twice to prevent LCD blending behavior + _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); +} + void GbPpu::UpdatePalette() { if(!_gameboy->IsCgb()) { diff --git a/Core/Gameboy/GbPpu.h b/Core/Gameboy/GbPpu.h index 84a0cf58..4b589bb5 100644 --- a/Core/Gameboy/GbPpu.h +++ b/Core/Gameboy/GbPpu.h @@ -110,6 +110,8 @@ public: uint8_t ReadCgbRegister(uint16_t addr); void WriteCgbRegister(uint16_t addr, uint8_t value); - + + void DebugSendFrame(); + void Serialize(Serializer& s) override; }; diff --git a/Core/NES/BaseNesPpu.cpp b/Core/NES/BaseNesPpu.cpp index 68b7a8dd..849d33b5 100644 --- a/Core/NES/BaseNesPpu.cpp +++ b/Core/NES/BaseNesPpu.cpp @@ -1,7 +1,10 @@ #include "stdafx.h" -#include "BaseNesPpu.h" +#include "NES/BaseNesPpu.h" #include "NES/NesTypes.h" +#include "NES/NesConstants.h" #include "NES/NesConsole.h" +#include "Shared/Emulator.h" +#include "Shared/Video/VideoDecoder.h" #include "Shared/SettingTypes.h" void BaseNesPpu::GetState(NesPpuState& state) @@ -98,6 +101,18 @@ void BaseNesPpu::WritePaletteRam(uint16_t addr, uint8_t value) } } +void BaseNesPpu::DebugSendFrame() +{ + int offset = std::max(0, (int)(_cycle + _scanline * NesConstants::ScreenWidth)); + int pixelsToClear = NesConstants::ScreenPixelCount - offset; + if(pixelsToClear > 0) { + memset(_currentOutputBuffer + offset, 0, pixelsToClear * sizeof(uint16_t)); + } + + RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount); + _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); +} + /* Applies the effect of grayscale/intensify bits to the output buffer (batched) */ void BaseNesPpu::UpdateGrayscaleAndIntensifyBits() { diff --git a/Core/NES/BaseNesPpu.h b/Core/NES/BaseNesPpu.h index 6381b9c7..ec93f959 100644 --- a/Core/NES/BaseNesPpu.h +++ b/Core/NES/BaseNesPpu.h @@ -140,6 +140,8 @@ public: uint8_t ReadPaletteRam(uint16_t addr); void WritePaletteRam(uint16_t addr, uint8_t value); + void DebugSendFrame(); + virtual PpuModel GetPpuModel() = 0; virtual uint32_t GetPixelBrightness(uint8_t x, uint8_t y) = 0; diff --git a/Core/NES/Debugger/NesDebugger.cpp b/Core/NES/Debugger/NesDebugger.cpp index 8545d2df..f4c45a6e 100644 --- a/Core/NES/Debugger/NesDebugger.cpp +++ b/Core/NES/Debugger/NesDebugger.cpp @@ -268,6 +268,11 @@ void NesDebugger::Step(int32_t stepCount, StepType type) _step.reset(new StepRequest(step)); } +void NesDebugger::DrawPartialFrame() +{ + _ppu->DebugSendFrame(); +} + void NesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) { AddressInfo src = _mapper->GetAbsoluteAddress(_prevProgramCounter); diff --git a/Core/NES/Debugger/NesDebugger.h b/Core/NES/Debugger/NesDebugger.h index 55dd2511..20f9548b 100644 --- a/Core/NES/Debugger/NesDebugger.h +++ b/Core/NES/Debugger/NesDebugger.h @@ -77,6 +77,8 @@ public: void Run() override; void Step(int32_t stepCount, StepType type) override; + void DrawPartialFrame() override; + DebuggerFeatures GetSupportedFeatures() override; void SetProgramCounter(uint32_t addr) override; uint32_t GetProgramCounter(bool getInstPc) override; diff --git a/Core/NES/NesPpu.cpp b/Core/NES/NesPpu.cpp index e8ccabce..c0b07de1 100644 --- a/Core/NES/NesPpu.cpp +++ b/Core/NES/NesPpu.cpp @@ -1035,12 +1035,6 @@ template void NesPpu::WriteSpriteRam(uint8_t addr, uint8_t value) } } -template void NesPpu::DebugSendFrame() -{ - RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount); - _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); -} - template uint16_t* NesPpu::GetScreenBuffer(bool previousBuffer) { return previousBuffer ? ((_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]) : _currentOutputBuffer; @@ -1066,9 +1060,6 @@ template void NesPpu::SendFrame() RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates()); frame.Data = frameData; //HD packs -#ifdef LIBRETRO - _console->GetVideoDecoder()->UpdateFrame(frame, true, false); -#else if(_console->GetVsMainConsole() || _console->GetVsSubConsole()) { SendFrameVsDualSystem(); if(_console->IsVsMainConsole()) { @@ -1081,7 +1072,6 @@ template void NesPpu::SendFrame() } _enableOamDecay = _settings->GetNesConfig().EnableOamDecay; -#endif } template void NesPpu::SendFrameVsDualSystem() diff --git a/Core/NES/NesPpu.h b/Core/NES/NesPpu.h index 7cb2d799..b616fbcc 100644 --- a/Core/NES/NesPpu.h +++ b/Core/NES/NesPpu.h @@ -105,7 +105,6 @@ public: void Reset() override; - void DebugSendFrame(); uint16_t* GetScreenBuffer(bool previousBuffer) override; void DebugCopyOutputBuffer(uint16_t* target); void DebugUpdateFrameBuffer(bool toGrayscale); diff --git a/Core/PCE/Debugger/PceDebugger.cpp b/Core/PCE/Debugger/PceDebugger.cpp index aed6277f..83646a03 100644 --- a/Core/PCE/Debugger/PceDebugger.cpp +++ b/Core/PCE/Debugger/PceDebugger.cpp @@ -14,6 +14,7 @@ #include "PCE/PceCpu.h" #include "PCE/PceVdc.h" #include "PCE/PceVce.h" +#include "PCE/PceVpc.h" #include "PCE/PceMemoryManager.h" #include "PCE/Debugger/PceDebugger.h" #include "PCE/Debugger/PceTraceLogger.h" @@ -40,6 +41,7 @@ PceDebugger::PceDebugger(Debugger* debugger) _cpu = console->GetCpu(); _vdc = console->GetVdc(); _vce = console->GetVce(); + _vpc = console->GetVpc(); _memoryManager = console->GetMemoryManager(); _traceLogger.reset(new PceTraceLogger(debugger, this, _vdc)); @@ -243,6 +245,11 @@ void PceDebugger::Step(int32_t stepCount, StepType type) _step.reset(new StepRequest(step)); } +void PceDebugger::DrawPartialFrame() +{ + _vpc->DebugSendFrame(); +} + void PceDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) { AddressInfo src = _memoryManager->GetAbsoluteAddress(_prevProgramCounter); diff --git a/Core/PCE/Debugger/PceDebugger.h b/Core/PCE/Debugger/PceDebugger.h index bcfb727f..80c5a3ee 100644 --- a/Core/PCE/Debugger/PceDebugger.h +++ b/Core/PCE/Debugger/PceDebugger.h @@ -21,6 +21,7 @@ class Emulator; class PceCpu; class PceVdc; class PceVce; +class PceVpc; class PceMemoryManager; class DummyPceCpu; @@ -38,6 +39,7 @@ class PceDebugger final : public IDebugger PceCpu* _cpu; PceVdc* _vdc; PceVce* _vce; + PceVpc* _vpc; PceMemoryManager* _memoryManager; unique_ptr _codeDataLogger; @@ -76,6 +78,8 @@ public: void Run() override; void Step(int32_t stepCount, StepType type) override; + void DrawPartialFrame() override; + DebuggerFeatures GetSupportedFeatures() override; void SetProgramCounter(uint32_t addr) override; uint32_t GetProgramCounter(bool getInstPc) override; diff --git a/Core/PCE/PceVpc.cpp b/Core/PCE/PceVpc.cpp index e969907f..1e500cfb 100644 --- a/Core/PCE/PceVpc.cpp +++ b/Core/PCE/PceVpc.cpp @@ -19,6 +19,7 @@ PceVpc::PceVpc(Emulator* emu, PceConsole* console, PceVce* vce) _outBuffer[0] = new uint16_t[PceConstants::MaxScreenWidth * PceConstants::ScreenHeight]; _outBuffer[1] = new uint16_t[PceConstants::MaxScreenWidth * PceConstants::ScreenHeight]; _currentOutBuffer = _outBuffer[0]; + _currentClockDividers = _rowVceClockDivider[0]; memset(_outBuffer[0], 0, PceConstants::MaxScreenWidth * PceConstants::ScreenHeight * sizeof(uint16_t)); memset(_outBuffer[1], 0, PceConstants::MaxScreenWidth * PceConstants::ScreenHeight * sizeof(uint16_t)); @@ -249,6 +250,26 @@ void PceVpc::SendFrame(PceVdc* vdc) _console->GetControlManager()->UpdateControlDevices(); } +void PceVpc::DebugSendFrame() +{ + uint16_t scanline = _vdc1->GetScanline(); + if(scanline >= 14 && scanline < 256) { + DrawScanline(); + ProcessScanlineEnd(_vdc1, scanline, _vdc1->GetRowBuffer()); + + uint32_t lastPixel = _vdc1->GetHClock() / _vce->GetClockDivider(); + int offset = std::max(0, (int)(lastPixel + (scanline - 14) * PceConstants::MaxScreenWidth)); + int pixelsToClear = PceConstants::MaxScreenWidth * PceConstants::ScreenHeight - offset; + if(pixelsToClear > 0) { + memset(_currentOutBuffer + offset, 0, pixelsToClear * sizeof(uint16_t)); + } + } + + RenderedFrame frame(_currentOutBuffer, 512, PceConstants::ScreenHeight * 2, 0.5, _vdc1->GetState().FrameCount); + frame.Data = _currentClockDividers; + _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); +} + void PceVpc::UpdateIrqState() { if(_hasIrqVdc1 || _hasIrqVdc2) { diff --git a/Core/PCE/PceVpc.h b/Core/PCE/PceVpc.h index a66eb49b..e01de1cd 100644 --- a/Core/PCE/PceVpc.h +++ b/Core/PCE/PceVpc.h @@ -50,6 +50,8 @@ public: void ProcessScanlineStart(PceVdc* vdc, uint16_t scanline); void ProcessScanlineEnd(PceVdc* vdc, uint16_t scanline, uint16_t* rowBuffer); void SendFrame(PceVdc* vdc); + + void DebugSendFrame(); void SetIrq(PceVdc* vdc); void ClearIrq(PceVdc* vdc); diff --git a/Core/SNES/Debugger/SnesDebugger.cpp b/Core/SNES/Debugger/SnesDebugger.cpp index ed5a34f1..1a405639 100644 --- a/Core/SNES/Debugger/SnesDebugger.cpp +++ b/Core/SNES/Debugger/SnesDebugger.cpp @@ -289,6 +289,11 @@ void SnesDebugger::Step(int32_t stepCount, StepType type) _step.reset(new StepRequest(step)); } +void SnesDebugger::DrawPartialFrame() +{ + _ppu->DebugSendFrame(); +} + void SnesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) { AddressInfo src = _memoryMappings->GetAbsoluteAddress(_prevProgramCounter); diff --git a/Core/SNES/Debugger/SnesDebugger.h b/Core/SNES/Debugger/SnesDebugger.h index 3d6b106e..94a4ecbd 100644 --- a/Core/SNES/Debugger/SnesDebugger.h +++ b/Core/SNES/Debugger/SnesDebugger.h @@ -94,6 +94,8 @@ public: void Run() override; void Step(int32_t stepCount, StepType type) override; + + void DrawPartialFrame() override; DebuggerFeatures GetSupportedFeatures() override; void SetProgramCounter(uint32_t addr) override; diff --git a/Core/SNES/SnesPpu.cpp b/Core/SNES/SnesPpu.cpp index 7d410d5b..7d6d9b9d 100644 --- a/Core/SNES/SnesPpu.cpp +++ b/Core/SNES/SnesPpu.cpp @@ -1480,21 +1480,41 @@ void SnesPpu::SendFrame() bool isRewinding = _emu->GetRewindManager()->IsRewinding(); RenderedFrame frame(_currentBuffer, width, height, _useHighResOutput ? 0.5 : 1.0, _frameCount, _console->GetControlManager()->GetPortStates()); -#ifdef LIBRETRO - _emu->GetVideoDecoder()->UpdateFrame(frame, true, isRewinding); -#else if(isRewinding || _interlacedFrame) { _emu->GetVideoDecoder()->UpdateFrame(frame, true, isRewinding); } else { _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); } -#endif if(!_skipRender) { _frameSkipTimer.Reset(); } } +void SnesPpu::DebugSendFrame() +{ + if(_interlacedFrame) { + //Not supported + return; + } + + RenderScanline(); + + uint16_t width = _useHighResOutput ? 512 : 256; + uint16_t height = _useHighResOutput ? 478 : 239; + + int lastDrawnPixel = _drawEndX * (_useHighResOutput ? 2 : 1); + int scanline = _overscanFrame ? ((int)_scanline - 1) : ((int)_scanline + 6); + + int offset = std::max(0, lastDrawnPixel + 1 + scanline * width); + int pixelsToClear = width * height - offset; + if(pixelsToClear > 0) { + memset(_currentBuffer + offset, 0, pixelsToClear * sizeof(uint16_t)); + } + + RenderedFrame frame(_currentBuffer, width, height, _useHighResOutput ? 0.5 : 1.0, _frameCount); + _emu->GetVideoDecoder()->UpdateFrame(frame, false, false); +} bool SnesPpu::IsHighResOutput() { diff --git a/Core/SNES/SnesPpu.h b/Core/SNES/SnesPpu.h index a9486a90..e911af16 100644 --- a/Core/SNES/SnesPpu.h +++ b/Core/SNES/SnesPpu.h @@ -237,6 +237,8 @@ public: uint8_t* GetCgRam(); uint8_t* GetSpriteRam(); + void DebugSendFrame(); + void SetLocationLatchRequest(uint16_t x, uint16_t y); void ProcessLocationLatchRequest(); void LatchLocationValues(); diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h index a5331163..9ba14355 100644 --- a/Core/Shared/SettingTypes.h +++ b/Core/Shared/SettingTypes.h @@ -770,6 +770,7 @@ enum class DebuggerFlags : uint64_t BreakOnUninitRead = (1 << 4), ShowJumpLabels = (1 << 5), + DrawPartialFrame = (1 << 6), ShowVerifiedData = (1 << 8), DisassembleVerifiedData = (1 << 9), diff --git a/NewUI/Config/Debugger/DebuggerConfig.cs b/NewUI/Config/Debugger/DebuggerConfig.cs index 6fcfde99..5baca22d 100644 --- a/NewUI/Config/Debugger/DebuggerConfig.cs +++ b/NewUI/Config/Debugger/DebuggerConfig.cs @@ -19,6 +19,7 @@ namespace Mesen.Config [Reactive] public bool UseLowerCaseDisassembly { get; set; } = false; [Reactive] public bool ShowJumpLabels { get; set; } = false; + [Reactive] public bool DrawPartialFrame { get; set; } = false; [Reactive] public SnesDebuggerConfig Snes { get; set; } = new(); [Reactive] public NesDebuggerConfig Nes { get; set; } = new(); @@ -89,6 +90,7 @@ namespace Mesen.Config ConfigApi.SetDebuggerFlag(DebuggerFlags.BreakOnUninitRead, BreakOnUninitRead); ConfigApi.SetDebuggerFlag(DebuggerFlags.ShowJumpLabels, ShowJumpLabels); + ConfigApi.SetDebuggerFlag(DebuggerFlags.DrawPartialFrame, DrawPartialFrame); ConfigApi.SetDebuggerFlag(DebuggerFlags.ShowUnidentifiedData, UnidentifiedBlockDisplay == CodeDisplayMode.Show); ConfigApi.SetDebuggerFlag(DebuggerFlags.DisassembleUnidentifiedData, UnidentifiedBlockDisplay == CodeDisplayMode.Disassemble); diff --git a/NewUI/Debugger/Views/DebuggerOptionsView.axaml b/NewUI/Debugger/Views/DebuggerOptionsView.axaml index c67d46c5..9b6c1f06 100644 --- a/NewUI/Debugger/Views/DebuggerOptionsView.axaml +++ b/NewUI/Debugger/Views/DebuggerOptionsView.axaml @@ -158,6 +158,12 @@ IsChecked="{CompiledBinding Config.BringToFrontOnBreak}" Content="{l:Translate chkBringToFrontOnBreak}" /> + + + Misc. settings Bring to front on break + Draw partial frame Use predictive breakpoints Break once per instruction