Debugger: Added "draw partial frame" option

This commit is contained in:
Sour 2022-06-26 21:57:03 -04:00
parent b438d9dde0
commit 60889176b7
25 changed files with 140 additions and 22 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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()) {

View file

@ -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;
};

View file

@ -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()
{

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -1035,12 +1035,6 @@ template<class T> void NesPpu<T>::WriteSpriteRam(uint8_t addr, uint8_t value)
}
}
template<class T> void NesPpu<T>::DebugSendFrame()
{
RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount);
_emu->GetVideoDecoder()->UpdateFrame(frame, false, false);
}
template<class T> uint16_t* NesPpu<T>::GetScreenBuffer(bool previousBuffer)
{
return previousBuffer ? ((_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]) : _currentOutputBuffer;
@ -1066,9 +1060,6 @@ template<class T> void NesPpu<T>::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<class T> void NesPpu<T>::SendFrame()
}
_enableOamDecay = _settings->GetNesConfig().EnableOamDecay;
#endif
}
template<class T> void NesPpu<T>::SendFrameVsDualSystem()

View file

@ -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);

View file

@ -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);

View file

@ -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> _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;

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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()
{

View file

@ -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();

View file

@ -770,6 +770,7 @@ enum class DebuggerFlags : uint64_t
BreakOnUninitRead = (1 << 4),
ShowJumpLabels = (1 << 5),
DrawPartialFrame = (1 << 6),
ShowVerifiedData = (1 << 8),
DisassembleVerifiedData = (1 << 9),

View file

@ -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);

View file

@ -158,6 +158,12 @@
IsChecked="{CompiledBinding Config.BringToFrontOnBreak}"
Content="{l:Translate chkBringToFrontOnBreak}"
/>
<CheckBox
IsChecked="{CompiledBinding Config.DrawPartialFrame}"
Content="{l:Translate chkDrawPartialFrame}"
/>
<CheckBox
IsChecked="{CompiledBinding Config.UsePredictiveBreakpoints}"
Content="{l:Translate chkUsePredictiveBreakpoints}"

View file

@ -58,6 +58,7 @@ namespace Mesen.Interop
BreakOnUninitRead = (1 << 4),
ShowJumpLabels = (1 << 5),
DrawPartialFrame = (1 << 6),
ShowVerifiedData = (1 << 8),
DisassembleVerifiedData = (1 << 9),

View file

@ -988,6 +988,7 @@
<Control ID="lblMiscSettings">Misc. settings</Control>
<Control ID="chkBringToFrontOnBreak">Bring to front on break</Control>
<Control ID="chkDrawPartialFrame">Draw partial frame</Control>
<Control ID="chkUsePredictiveBreakpoints">Use predictive breakpoints</Control>
<Control ID="chkSingleBreakpointPerInstruction">Break once per instruction</Control>
</Form>