diff --git a/Core/Console.cpp b/Core/Console.cpp index 94c2e053..e96367a8 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -48,6 +48,7 @@ #include "NotificationManager.h" #include "HistoryViewer.h" #include "ConsolePauseHelper.h" +#include "EventManager.h" #include "PgoUtilities.h" Console::Console(shared_ptr master, EmulationSettings* initialSettings) @@ -1482,6 +1483,15 @@ void Console::DebugSetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool up #endif } +void Console::DebugAddDebugEvent(DebugEventType type) +{ +#ifndef LIBRETRO + if(_debugger) { + _debugger->GetEventManager()->AddSpecialEvent(type); + } +#endif +} + bool Console::DebugProcessRamOperation(MemoryOperationType type, uint16_t & addr, uint8_t & value) { #ifndef LIBRETRO diff --git a/Core/Console.h b/Core/Console.h index 6196fc29..e886b345 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -39,6 +39,7 @@ enum class ScaleFilterType; enum class ConsoleFeatures; enum class DebugMemoryType; enum class EventType; +enum class DebugEventType : uint8_t; enum class RamPowerOnState; class Console : public std::enable_shared_from_this @@ -233,6 +234,7 @@ public: void DebugProcessEvent(EventType type); void DebugProcessInterrupt(uint16_t cpuAddr, uint16_t destCpuAddr, bool forNmi); void DebugSetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly); + void DebugAddDebugEvent(DebugEventType type); bool DebugProcessRamOperation(MemoryOperationType type, uint16_t &addr, uint8_t &value); void DebugProcessVramReadOperation(MemoryOperationType type, uint16_t addr, uint8_t &value); void DebugProcessVramWriteOperation(uint16_t addr, uint8_t &value); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 23dc5ba5..ee755795 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -537,6 +537,7 @@ + @@ -970,6 +971,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index c684f61a..dd103cf6 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1487,6 +1487,9 @@ Nes\Mappers\Bandai + + Debugger + @@ -1786,5 +1789,8 @@ Debugger + + Debugger + \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 9cafc872..b5a1ee0e 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -31,6 +31,7 @@ #include "DebugHud.h" #include "DummyCpu.h" #include "PerformanceTracker.h" +#include "EventManager.h" const int Debugger::BreakpointTypeCount; string Debugger::_disassemblerOutput = ""; @@ -57,6 +58,7 @@ Debugger::Debugger(shared_ptr console, shared_ptr cpu, shared_ptr< _memoryAccessCounter.reset(new MemoryAccessCounter(this)); _profiler.reset(new Profiler(this)); _performanceTracker.reset(new PerformanceTracker(console)); + _eventManager.reset(new EventManager(this, cpu.get(), ppu.get(), _console->GetSettings())); _traceLogger.reset(new TraceLogger(this, memoryManager, _labelManager)); _bpExpEval.reset(new ExpressionEvaluator(this)); @@ -413,7 +415,7 @@ bool Debugger::ProcessBreakpoints(BreakpointType type, OperationInfo &operationI } if(needMark && allowMark) { - AddDebugEvent(DebugEventType::Breakpoint, operationInfo.Address, (uint8_t)operationInfo.Value, markBreakpointId); + _eventManager->AddDebugEvent(DebugEventType::Breakpoint, operationInfo.Address, (uint8_t)operationInfo.Value, markBreakpointId); } if(needBreak && allowBreak) { @@ -700,7 +702,7 @@ bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uin //Used to flag the data in the CDL file isDmcRead = true; type = MemoryOperationType::Read; - AddDebugEvent(DebugEventType::DmcDmaRead, addr, value); + _eventManager->AddDebugEvent(DebugEventType::DmcDmaRead, addr, value); } ProcessCpuOperation(addr, value, type); @@ -912,12 +914,12 @@ bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uin if(addr >= 0x2000 && addr <= 0x3FFF) { if((addr & 0x07) == 5 || (addr & 0x07) == 6) { GetState(&_debugState, false); - AddDebugEvent(DebugEventType::PpuRegisterWrite, addr, value, -1, _debugState.PPU.State.WriteToggle ? 1 : 0); + _eventManager->AddDebugEvent(DebugEventType::PpuRegisterWrite, addr, value, -1, _debugState.PPU.State.WriteToggle ? 1 : 0); } else { - AddDebugEvent(DebugEventType::PpuRegisterWrite, addr, value); + _eventManager->AddDebugEvent(DebugEventType::PpuRegisterWrite, addr, value); } } else if(addr >= 0x4018 && _mapper->IsWriteRegister(addr)) { - AddDebugEvent(DebugEventType::MapperRegisterWrite, addr, value); + _eventManager->AddDebugEvent(DebugEventType::MapperRegisterWrite, addr, value); } if(_frozenAddresses[addr]) { @@ -925,9 +927,9 @@ bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uin } } else if(type == MemoryOperationType::Read) { if(addr >= 0x2000 && addr <= 0x3FFF) { - AddDebugEvent(DebugEventType::PpuRegisterRead, addr, value); + _eventManager->AddDebugEvent(DebugEventType::PpuRegisterRead, addr, value); } else if(addr >= 0x4018 && _mapper->IsReadRegister(addr)) { - AddDebugEvent(DebugEventType::MapperRegisterRead, addr, value); + _eventManager->AddDebugEvent(DebugEventType::MapperRegisterRead, addr, value); } } else if(type == MemoryOperationType::ExecOpCode) { if(!_needRewind) { @@ -1316,6 +1318,11 @@ shared_ptr Debugger::GetPerformanceTracker() return _performanceTracker; } +shared_ptr Debugger::GetEventManager() +{ + return _eventManager; +} + bool Debugger::IsExecutionStopped() { return _executionStopped || _console->IsExecutionStopped(); @@ -1616,59 +1623,19 @@ void Debugger::ProcessEvent(EventType type) if(CheckFlag(DebuggerFlags::PpuPartialDraw)) { _ppu->DebugUpdateFrameBuffer(CheckFlag(DebuggerFlags::PpuShowPreviousFrame)); } - _prevDebugEvents = _debugEvents; - _debugEvents.clear(); + + _eventManager->ClearFrameEvents(); } else if(type == EventType::Nmi) { - AddDebugEvent(DebugEventType::Nmi); + _eventManager->AddDebugEvent(DebugEventType::Nmi); } else if(type == EventType::Irq) { - AddDebugEvent(DebugEventType::Irq); + _eventManager->AddDebugEvent(DebugEventType::Irq); } else if(type == EventType::SpriteZeroHit) { - AddDebugEvent(DebugEventType::SpriteZeroHit); + _eventManager->AddDebugEvent(DebugEventType::SpriteZeroHit); } else if(type == EventType::Reset) { _enableBreakOnUninitRead = true; } } -void Debugger::AddDebugEvent(DebugEventType type, uint16_t address, uint8_t value, int16_t breakpointId, int8_t ppuLatch) -{ - _debugEvents.push_back({ - (uint16_t)_ppu->GetCurrentCycle(), - (int16_t)_ppu->GetCurrentScanline(), - _cpu->GetDebugPC(), - address, - breakpointId, - type, - value, - ppuLatch, - }); -} - -void Debugger::GetDebugEvents(uint32_t* pictureBuffer, DebugEventInfo *infoArray, uint32_t &maxEventCount, bool returnPreviousFrameData) -{ - DebugBreakHelper helper(this); - - uint16_t *ppuBuffer = new uint16_t[PPU::PixelCount]; - uint32_t *palette = _console->GetSettings()->GetRgbPalette(); - _ppu->DebugCopyOutputBuffer(ppuBuffer); - - for(int i = 0; i < PPU::PixelCount; i++) { - pictureBuffer[i] = palette[ppuBuffer[i] & 0x3F]; - } - - delete[] ppuBuffer; - - vector &events = returnPreviousFrameData ? _prevDebugEvents : _debugEvents; - uint32_t eventCount = std::min(maxEventCount, (uint32_t)events.size()); - memcpy(infoArray, events.data(), eventCount * sizeof(DebugEventInfo)); - maxEventCount = eventCount; -} - -uint32_t Debugger::GetDebugEventCount(bool returnPreviousFrameData) -{ - DebugBreakHelper helper(this); - return (uint32_t)(returnPreviousFrameData ? _prevDebugEvents.size() : _debugEvents.size()); -} - uint32_t Debugger::GetScreenPixel(uint8_t x, uint8_t y) { return _ppu->GetPixel(x, y); diff --git a/Core/Debugger.h b/Core/Debugger.h index dcc889eb..1a92d0b5 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -31,6 +31,7 @@ class Breakpoint; class CodeDataLogger; class ExpressionEvaluator; class DummyCpu; +class EventManager; struct ExpressionData; enum EvalResultType : int32_t; @@ -53,6 +54,7 @@ private: shared_ptr _traceLogger; shared_ptr _profiler; shared_ptr _performanceTracker; + shared_ptr _eventManager; unique_ptr _codeRunner; shared_ptr _console; @@ -140,9 +142,6 @@ private: uint32_t _inputOverride[4]; - vector _prevDebugEvents; - vector _debugEvents; - private: bool ProcessBreakpoints(BreakpointType type, OperationInfo &operationInfo, bool allowBreak = true, bool allowMark = true); void ProcessAllBreakpoints(OperationInfo &operationInfo); @@ -153,8 +152,6 @@ private: void ProcessStepConditions(uint16_t addr); bool SleepUntilResume(BreakSource source, uint32_t breakpointId = 0, BreakpointType bpType = BreakpointType::Global, uint16_t bpAddress = 0, uint8_t bpValue = 0, MemoryOperationType bpMemOpType = MemoryOperationType::Read); - void AddDebugEvent(DebugEventType type, uint16_t address = -1, uint8_t value = 0, int16_t breakpointId = -1, int8_t ppuLatch = -1); - void UpdatePpuCyclesToProcess(); void ResetStepState(); @@ -236,6 +233,7 @@ public: shared_ptr GetMemoryDumper(); shared_ptr GetMemoryAccessCounter(); shared_ptr GetPerformanceTracker(); + shared_ptr GetEventManager(); int32_t EvaluateExpression(string expression, EvalResultType &resultType, bool useCache); @@ -280,8 +278,5 @@ public: void ProcessPpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type); void ProcessEvent(EventType type); - void GetDebugEvents(uint32_t* pictureBuffer, DebugEventInfo *infoArray, uint32_t &maxEventCount, bool returnPreviousFrameData); - uint32_t GetDebugEventCount(bool returnPreviousFrameData); - uint32_t GetScreenPixel(uint8_t x, uint8_t y); }; \ No newline at end of file diff --git a/Core/DebuggerTypes.h b/Core/DebuggerTypes.h index dec4ff4a..803d563a 100644 --- a/Core/DebuggerTypes.h +++ b/Core/DebuggerTypes.h @@ -166,13 +166,14 @@ enum class DebugEventType : uint8_t SpriteZeroHit, Breakpoint, DmcDmaRead, + BgColorChange, }; struct DebugEventInfo { uint16_t Cycle; int16_t Scanline; - uint16_t ProgramCounter; + uint32_t ProgramCounter; uint16_t Address; int16_t BreakpointId; DebugEventType Type; diff --git a/Core/EventManager.cpp b/Core/EventManager.cpp new file mode 100644 index 00000000..0f88f814 --- /dev/null +++ b/Core/EventManager.cpp @@ -0,0 +1,284 @@ +#include "stdafx.h" +#include "EventManager.h" +#include "DebuggerTypes.h" +#include "CPU.h" +#include "PPU.h" +#include "Debugger.h" +#include "DebugBreakHelper.h" +#include "DefaultVideoFilter.h" + +EventManager::EventManager(Debugger *debugger, CPU *cpu, PPU *ppu, EmulationSettings *settings) +{ + _debugger = debugger; + _cpu = cpu; + _ppu = ppu; + _settings = settings; + + _ppuBuffer = new uint16_t[256*240]; + memset(_ppuBuffer, 0, 256*240 * sizeof(uint16_t)); +} + +EventManager::~EventManager() +{ + delete[] _ppuBuffer; +} + +void EventManager::AddSpecialEvent(DebugEventType type) +{ + if(type == DebugEventType::BgColorChange) { + AddDebugEvent(DebugEventType::BgColorChange, 0, _ppu->GetCurrentBgColor()); + } +} + +void EventManager::AddDebugEvent(DebugEventType type, uint16_t address, uint8_t value, int16_t breakpointId, int8_t ppuLatch) +{ + _debugEvents.push_back({ + (uint16_t)_ppu->GetCurrentCycle(), + (int16_t)_ppu->GetCurrentScanline(), + _cpu->GetDebugPC(), + address, + breakpointId, + type, + value, + ppuLatch, + }); +} + +void EventManager::GetEvents(DebugEventInfo *eventArray, uint32_t &maxEventCount, bool getPreviousFrameData) +{ + DebugBreakHelper breakHelper(_debugger); + + vector &events = getPreviousFrameData ? _prevDebugEvents : _debugEvents; + uint32_t eventCount = std::min(maxEventCount, (uint32_t)events.size()); + memcpy(eventArray, events.data(), eventCount * sizeof(DebugEventInfo)); + maxEventCount = eventCount; +} + +DebugEventInfo EventManager::GetEvent(int16_t scanline, uint16_t cycle, EventViewerDisplayOptions &options) +{ + auto lock = _lock.AcquireSafe(); + + for(DebugEventInfo &evt : _sentEvents) { + if(evt.Cycle == cycle && evt.Scanline == scanline) { + return evt; + } + } + + DebugEventInfo empty = {}; + empty.ProgramCounter = 0xFFFFFFFF; + return empty; +} + +uint32_t EventManager::GetEventCount(bool getPreviousFrameData) +{ + DebugBreakHelper breakHelper(_debugger); + return (uint32_t)(getPreviousFrameData ? _prevDebugEvents.size() : _debugEvents.size()); +} + +void EventManager::ClearFrameEvents() +{ + _prevDebugEvents = _debugEvents; + _debugEvents.clear(); + AddDebugEvent(DebugEventType::BgColorChange, 0, _ppu->ReadPaletteRAM(0)); +} + +void EventManager::DrawEvent(DebugEventInfo &evt, bool drawBackground, uint32_t *buffer, EventViewerDisplayOptions &options) +{ + bool showEvent = false; + uint32_t color = 0; + switch(evt.Type) { + case DebugEventType::Breakpoint: showEvent = options.ShowMarkedBreakpoints; color = options.BreakpointColor; break; + case DebugEventType::Irq: showEvent = options.ShowIrq; color = options.IrqColor; break; + case DebugEventType::Nmi: showEvent = options.ShowNmi; color = options.NmiColor; break; + case DebugEventType::DmcDmaRead: showEvent = options.ShowDmcDmaReads; color = options.DmcDmaReadColor; break; + case DebugEventType::SpriteZeroHit: showEvent = options.ShowSpriteZeroHit; color = options.SpriteZeroHitColor; break; + + case DebugEventType::MapperRegisterWrite: + showEvent = options.ShowMapperRegisterWrites; + color = options.MapperRegisterWriteColor; + break; + + case DebugEventType::MapperRegisterRead: + showEvent = options.ShowMapperRegisterReads; + color = options.MapperRegisterReadColor; + break; + + case DebugEventType::PpuRegisterWrite: + showEvent = options.ShowPpuRegisterWrites[evt.Address & 0x07]; + color = options.PpuRegisterWriteColors[evt.Address & 0x07]; + break; + + case DebugEventType::PpuRegisterRead: + showEvent = options.ShowPpuRegisterReads[evt.Address & 0x07]; + color = options.PpuRegisterReadColors[evt.Address & 0x07]; + break; + } + + if(!showEvent) { + return; + } + + if(drawBackground) { + color = 0xFF000000 | ((color >> 1) & 0x7F7F7F); + } else { + _sentEvents.push_back(evt); + color |= 0xFF000000; + } + + int iMin = drawBackground ? -2 : 0; + int iMax = drawBackground ? 3 : 1; + int jMin = drawBackground ? -2 : 0; + int jMax = drawBackground ? 3 : 1; + uint32_t y = std::min((evt.Scanline + 1) * 2, _scanlineCount * 2); + uint32_t x = evt.Cycle * 2; + + for(int i = iMin; i <= iMax; i++) { + for(int j = jMin; j <= jMax; j++) { + int32_t pos = (y + i) * 341 * 2 + x + j; + if(pos < 0 || pos >= 341 * 2 * (int)_scanlineCount * 2) { + continue; + } + buffer[pos] = color; + } + } +} + +uint32_t EventManager::TakeEventSnapshot(EventViewerDisplayOptions options) +{ + DebugBreakHelper breakHelper(_debugger); + auto lock = _lock.AcquireSafe(); + _snapshot.clear(); + + uint16_t cycle = _ppu->GetCurrentCycle(); + uint16_t scanline = _ppu->GetCurrentScanline() + 1; + uint32_t key = (scanline << 9) + cycle; + constexpr uint32_t size = 256 * 240; + + if(scanline >= 240 || (scanline == 0 && cycle == 0)) { + memcpy(_ppuBuffer, _ppu->GetScreenBuffer(false), size * sizeof(uint16_t)); + } else { + uint32_t offset = (256 * scanline); + memcpy(_ppuBuffer, _ppu->GetScreenBuffer(false), offset * sizeof(uint16_t)); + memcpy(_ppuBuffer + offset, _ppu->GetScreenBuffer(true) + offset, (size - offset) * sizeof(uint16_t)); + } + + _snapshot = _debugEvents; + _snapshotScanline = scanline; + if(options.ShowPreviousFrameEvents && scanline != 0) { + for(DebugEventInfo &evt : _prevDebugEvents) { + uint32_t evtKey = (evt.Scanline << 9) + evt.Cycle; + if(evtKey > key) { + _snapshot.push_back(evt); + } + } + } + + PPUDebugState state; + _ppu->GetState(state); + _scanlineCount = state.ScanlineCount; + return _scanlineCount; +} + +void EventManager::GetDisplayBuffer(uint32_t *buffer, EventViewerDisplayOptions options) +{ + auto lock = _lock.AcquireSafe(); + _sentEvents.clear(); + + for(int i = 0; i < 341 * 2 * (int)_scanlineCount * 2; i++) { + buffer[i] = 0xFF555555; + } + + uint16_t *src = _ppuBuffer; + uint32_t* pal = _settings->GetRgbPalette(); + for(uint32_t y = 0, len = 240*2; y < len; y++) { + int rowOffset = (y + 2) * 341 * 2; + + for(uint32_t x = 0; x < 256*2; x++) { + int srcOffset = ((y >> 1) << 8) | (x >> 1); + buffer[rowOffset + x + 1 * 2] = pal[src[srcOffset]]; + } + } + + if(options.ShowNtscBorders) { + DrawNtscBorders(buffer); + } + + constexpr uint32_t currentScanlineColor = 0xFFFFFF55; + uint32_t scanlineOffset = _snapshotScanline * 2 * 341 * 2; + for(int i = 0; i < 341 * 2; i++) { + if(_snapshotScanline != 0) { + buffer[scanlineOffset + i] = currentScanlineColor; + buffer[scanlineOffset + 341 * 2 + i] = currentScanlineColor; + } + } + + for(DebugEventInfo &evt : _snapshot) { + DrawEvent(evt, true, buffer, options); + } + for(DebugEventInfo &evt : _snapshot) { + DrawEvent(evt, false, buffer, options); + } +} + +void EventManager::DrawPixel(uint32_t *buffer, int32_t x, uint32_t y, uint32_t color) +{ + if(x < 0) { + x += 341; + y--; + } else if(x >= 341) { + x -= 341; + y++; + } + + buffer[y * 341 * 4 + x * 2] = color; + buffer[y * 341 * 4 + x * 2 + 1] = color; + buffer[y * 341 * 4 + 341*2 + x * 2] = color; + buffer[y * 341 * 4 + 341*2 + x * 2 + 1] = color; +} + +void EventManager::DrawNtscBorders(uint32_t *buffer) +{ + //Generate array of bg color for all pixels on the screen + uint32_t currentPos = 0; + uint8_t currentColor = 0; + vector bgColor; + bgColor.resize(341 * 243); + uint32_t* pal = _settings->GetRgbPalette(); + + for(DebugEventInfo &evt : _snapshot) { + if(evt.Type == DebugEventType::BgColorChange) { + uint32_t pos = ((evt.Scanline + 1) * 341) + evt.Cycle; + if(pos >= currentPos && evt.Scanline < 242) { + memset(bgColor.data() + currentPos, currentColor, pos - currentPos); + currentColor = evt.Value; + currentPos = pos; + } + } + } + memset(bgColor.data() + currentPos, currentColor, 341 * 243 - currentPos); + + for(uint32_t y = 1; y < 241; y++) { + //Pulse + uint32_t basePos = y * 341; + DrawPixel(buffer, -15, y, pal[bgColor[basePos - 16] & 0x30]); + + //Left border + for(int32_t x = 0; x < 15; x++) { + DrawPixel(buffer, -x, y, pal[bgColor[basePos - x]]); + } + + //Right border + for(int32_t x = 0; x < 11; x++) { + DrawPixel(buffer, 257+x, y, pal[bgColor[basePos + 257 + x]]); + } + } + + for(uint32_t y = 240; y < 242; y++) { + //Bottom border + uint32_t basePos = y * 341; + DrawPixel(buffer, 326, y, pal[bgColor[basePos + 326] & 0x30]); + for(int32_t x = 0; x < 282; x++) { + DrawPixel(buffer, 327 + x, y, pal[bgColor[basePos + 327 + x]]); + } + } +} \ No newline at end of file diff --git a/Core/EventManager.h b/Core/EventManager.h new file mode 100644 index 00000000..d7248108 --- /dev/null +++ b/Core/EventManager.h @@ -0,0 +1,80 @@ +#pragma once +#include "stdafx.h" +#include "Types.h" +#include "../Utilities/SimpleLock.h" + +enum class DebugEventType : uint8_t; +struct DebugEventInfo; +struct EventViewerDisplayOptions; +class CPU; +class PPU; +class EmulationSettings; +class Debugger; + +class EventManager +{ +private: + CPU *_cpu; + PPU *_ppu; + EmulationSettings *_settings; + Debugger *_debugger; + vector _debugEvents; + vector _prevDebugEvents; + vector _sentEvents; + + vector _snapshot; + uint16_t _snapshotScanline; + SimpleLock _lock; + + uint32_t _scanlineCount = 262; + uint16_t *_ppuBuffer; + + void DrawEvent(DebugEventInfo &evt, bool drawBackground, uint32_t *buffer, EventViewerDisplayOptions &options); + void DrawNtscBorders(uint32_t *buffer); + void DrawPixel(uint32_t *buffer, int32_t x, uint32_t y, uint32_t color); + +public: + EventManager(Debugger *debugger, CPU *cpu, PPU *ppu, EmulationSettings *settings); + ~EventManager(); + + void AddSpecialEvent(DebugEventType type); + void AddDebugEvent(DebugEventType type, uint16_t address = -1, uint8_t value = 0, int16_t breakpointId = -1, int8_t ppuLatch = -1); + + void GetEvents(DebugEventInfo *eventArray, uint32_t &maxEventCount, bool getPreviousFrameData); + uint32_t GetEventCount(bool getPreviousFrameData); + void ClearFrameEvents(); + + uint32_t TakeEventSnapshot(EventViewerDisplayOptions options); + void GetDisplayBuffer(uint32_t *buffer, EventViewerDisplayOptions options); + + DebugEventInfo GetEvent(int16_t scanline, uint16_t cycle, EventViewerDisplayOptions &options); +}; + +struct EventViewerDisplayOptions +{ + uint32_t IrqColor; + uint32_t NmiColor; + uint32_t DmcDmaReadColor; + uint32_t SpriteZeroHitColor; + uint32_t BreakpointColor; + uint32_t MapperRegisterReadColor; + uint32_t MapperRegisterWriteColor; + + uint32_t PpuRegisterReadColors[8]; + uint32_t PpuRegisterWriteColors[8]; + + bool ShowMapperRegisterWrites; + bool ShowMapperRegisterReads; + + bool ShowPpuRegisterWrites[8]; + bool ShowPpuRegisterReads[8]; + + bool ShowNmi; + bool ShowIrq; + bool ShowDmcDmaReads; + bool ShowSpriteZeroHit; + + bool ShowMarkedBreakpoints; + bool ShowPreviousFrameEvents; + bool ShowNtscBorders; +}; \ No newline at end of file diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 1019cf99..0254fbc7 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -197,6 +197,10 @@ void PPU::UpdateVideoRamAddr() if(_scanline >= 240 || !IsRenderingEnabled()) { _state.VideoRamAddr = (_state.VideoRamAddr + (_flags.VerticalWrite ? 32 : 1)) & 0x7FFF; + if(!_renderingEnabled) { + _console->DebugAddDebugEvent(DebugEventType::BgColorChange); + } + //Trigger memory read when setting the vram address - needed by MMC3 IRQ counter //"Should be clocked when A12 changes to 1 via $2007 read/write" SetBusAddress(_state.VideoRamAddr & 0x3FFF); @@ -872,6 +876,15 @@ void PPU::DrawPixel() } } +uint8_t PPU::GetCurrentBgColor() +{ + if(IsRenderingEnabled() || (_state.VideoRamAddr & 0x3F00) != 0x3F00) { + return _paletteRAM[0]; + } else { + return _paletteRAM[_state.VideoRamAddr & 0x1F]; + } +} + void PPU::UpdateGrayscaleAndIntensifyBits() { if(_scanline < 0 || _scanline > _nmiScanline) { @@ -1121,7 +1134,7 @@ uint8_t PPU::ReadSpriteRam(uint8_t addr) debugger->BreakImmediately(BreakSource::BreakOnDecayedOamRead); } } - //If this 8-byte row hasn't been read/written to in over 3000 cpu cycles (~1.7ms), return 0xFF to simulate decay + //If this 8-byte row hasn't been read/written to in over 3000 cpu cycles (~1.7ms), return 0x10 to simulate decay return 0x10; } } @@ -1140,6 +1153,11 @@ void PPU::DebugSendFrame() _console->GetVideoDecoder()->UpdateFrame(_currentOutputBuffer); } +uint16_t* PPU::GetScreenBuffer(bool previousBuffer) +{ + return previousBuffer ? ((_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]) : _currentOutputBuffer; +} + void PPU::DebugCopyOutputBuffer(uint16_t *target) { memcpy(target, _currentOutputBuffer, PPU::PixelCount * sizeof(uint16_t)); @@ -1237,7 +1255,6 @@ void PPU::Exec() SetBusAddress(_state.VideoRamAddr); SendFrame(); _frameCount++; - } else if(_scanline == _nmiScanline) { } } else { //Cycle > 0 @@ -1276,7 +1293,10 @@ void PPU::UpdateState() //Rendering enabled flag is apparently set with a 1 cycle delay (i.e setting it at cycle 5 will render cycle 6 like cycle 5 and then take the new settings for cycle 7) _prevRenderingEnabled = _renderingEnabled; - _renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled; + if(_renderingEnabled != (_flags.BackgroundEnabled | _flags.SpritesEnabled)) { + _renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled; + _console->DebugAddDebugEvent(DebugEventType::BgColorChange); + } if(_prevRenderingEnabled != _renderingEnabled) { _needStateUpdate = true; } @@ -1319,6 +1339,10 @@ void PPU::UpdateState() _state.VideoRamAddr = _updateVramAddr; } + if(!_renderingEnabled) { + _console->DebugAddDebugEvent(DebugEventType::BgColorChange); + } + //The glitches updates corrupt both V and T, so set the new value of V back into T _state.TmpVideoRamAddr = _state.VideoRamAddr; diff --git a/Core/PPU.h b/Core/PPU.h index ab4f024e..6773f436 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -179,6 +179,7 @@ class PPU : public IMemoryHandler, public Snapshotable void Reset(); void DebugSendFrame(); + uint16_t* GetScreenBuffer(bool previousBuffer); void DebugCopyOutputBuffer(uint16_t *target); void DebugUpdateFrameBuffer(bool toGrayscale); void GetState(PPUDebugState &state); @@ -237,6 +238,7 @@ class PPU : public IMemoryHandler, public Snapshotable } uint32_t GetPixelBrightness(uint8_t x, uint8_t y); + uint8_t GetCurrentBgColor(); uint16_t GetPixel(uint8_t x, uint8_t y) { diff --git a/GUI.NET/Config/DebugInfo.cs b/GUI.NET/Config/DebugInfo.cs index 9ed19c8d..e9f8c95e 100644 --- a/GUI.NET/Config/DebugInfo.cs +++ b/GUI.NET/Config/DebugInfo.cs @@ -153,6 +153,7 @@ namespace Mesen.GUI.Config public bool ShowBreakpointLabels = true; public Point EventViewerLocation; + public Size EventViewerSize; public bool EventViewerRefreshOnBreak = true; public bool EventViewerShowPpuWrite2000 = true; public bool EventViewerShowPpuWrite2001 = true; @@ -172,6 +173,7 @@ namespace Mesen.GUI.Config public bool EventViewerShowMarkedBreakpoints = true; public bool EventViewerShowDmcDmaReads = true; public bool EventViewerShowPreviousFrameEvents = true; + public bool EventViewerShowNtscBorders = true; public XmlColor EventViewerMapperRegisterWriteColor = ColorTranslator.FromHtml("#007597"); public XmlColor EventViewerMapperRegisterReadColor = ColorTranslator.FromHtml("#C92929"); diff --git a/GUI.NET/Config/DebuggerShortcutsConfig.cs b/GUI.NET/Config/DebuggerShortcutsConfig.cs index ee0ff0e1..d87db6bd 100644 --- a/GUI.NET/Config/DebuggerShortcutsConfig.cs +++ b/GUI.NET/Config/DebuggerShortcutsConfig.cs @@ -54,6 +54,11 @@ namespace Mesen.GUI.Config [ShortcutName("Go to All")] public XmlKeys GoToAll = Keys.Control | Keys.Oemcomma; + [ShortcutName("Zoom In")] + public XmlKeys ZoomIn = Keys.Control | Keys.Oemplus; + [ShortcutName("Zoom Out")] + public XmlKeys ZoomOut = Keys.Control | Keys.OemMinus; + [ShortcutName("PPU Viewer: Toggle View")] public XmlKeys PpuViewer_ToggleView = Keys.Control | Keys.Q; [ShortcutName("PPU Viewer: Toggle Zoom")] diff --git a/GUI.NET/Controls/ctrlPanel.cs b/GUI.NET/Controls/ctrlPanel.cs new file mode 100644 index 00000000..dbc7116d --- /dev/null +++ b/GUI.NET/Controls/ctrlPanel.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + public class ctrlPanel : Panel + { + public delegate void ZoomEventHandler(int scaleDelta); + public event ZoomEventHandler OnZoom; + + public ctrlPanel() + { + this.DoubleBuffered = true; + } + + protected override Point ScrollToControl(Control activeControl) + { + // Returning the current location prevents the panel from + // scrolling to the active control when the panel loses and regains focus + return this.DisplayRectangle.Location; + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + if(Control.ModifierKeys != Keys.Control) { + int hori = this.HorizontalScroll.Value; + int vert = this.VerticalScroll.Value; + + if(Control.ModifierKeys == Keys.Shift) { + hori = Math.Max(0, Math.Min(hori - e.Delta, this.HorizontalScroll.Maximum)); + } else { + vert = Math.Max(0, Math.Min(vert - e.Delta, this.VerticalScroll.Maximum)); + } + + this.HorizontalScroll.Value = hori; + this.HorizontalScroll.Value = hori; + this.VerticalScroll.Value = vert; + this.VerticalScroll.Value = vert; + } else { + if(Program.IsMono) { + //Patch for Mono to prevent a scrolling bug when zooming out. + //Breaks the zoom in/out logic to keep the current scroll position when zooming, but that's better than the original bug. + this.HorizontalScroll.Value = 0; + this.VerticalScroll.Value = 0; + } + this.OnZoom?.Invoke(e.Delta > 0 ? 1 : -1); + } + } + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlEventViewerListView.cs b/GUI.NET/Debugger/Controls/ctrlEventViewerListView.cs index 4494d54d..1c7937bb 100644 --- a/GUI.NET/Debugger/Controls/ctrlEventViewerListView.cs +++ b/GUI.NET/Debugger/Controls/ctrlEventViewerListView.cs @@ -37,10 +37,8 @@ namespace Mesen.GUI.Debugger.Controls public void GetData() { - DebugEventInfo[] eventInfoArray; - byte[] pictureData; _breakpoints = BreakpointManager.Breakpoints; - InteropEmu.DebugGetDebugEvents(false, out pictureData, out eventInfoArray); + DebugEventInfo[] eventInfoArray = InteropEmu.GetDebugEvents(false); this.BeginInvoke((Action)(() => { lstEvents.BeginUpdate(); diff --git a/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.Designer.cs b/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.Designer.cs index d51c57ad..184152e5 100644 --- a/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.Designer.cs @@ -30,25 +30,30 @@ namespace Mesen.GUI.Debugger.Controls private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - this.picPicture = new Mesen.GUI.Controls.ctrlMesenPictureBox(); + this.picViewer = new Mesen.GUI.Debugger.ctrlImagePanel(); this.tmrOverlay = new System.Windows.Forms.Timer(this.components); - ((System.ComponentModel.ISupportInitialize)(this.picPicture)).BeginInit(); this.SuspendLayout(); // // picPicture // - this.picPicture.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.picPicture.Cursor = System.Windows.Forms.Cursors.Default; - this.picPicture.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; - this.picPicture.Location = new System.Drawing.Point(1, 1); - this.picPicture.Margin = new System.Windows.Forms.Padding(0); - this.picPicture.Name = "picPicture"; - this.picPicture.Size = new System.Drawing.Size(684, 526); - this.picPicture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; - this.picPicture.TabIndex = 0; - this.picPicture.TabStop = false; - this.picPicture.MouseLeave += new System.EventHandler(this.picPicture_MouseLeave); - this.picPicture.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picPicture_MouseMove); + this.picViewer.Cursor = System.Windows.Forms.Cursors.Default; + this.picViewer.Dock = System.Windows.Forms.DockStyle.Fill; + this.picViewer.GridSizeX = 0; + this.picViewer.GridSizeY = 0; + this.picViewer.Image = null; + this.picViewer.ImageScale = 1; + this.picViewer.ImageSize = new System.Drawing.Size(0, 0); + this.picViewer.Location = new System.Drawing.Point(0, 0); + this.picViewer.Margin = new System.Windows.Forms.Padding(0); + this.picViewer.Name = "picViewer"; + this.picViewer.Overlay = new System.Drawing.Rectangle(0, 0, 0, 0); + this.picViewer.Selection = new System.Drawing.Rectangle(0, 0, 0, 0); + this.picViewer.SelectionWrapPosition = 0; + this.picViewer.Size = new System.Drawing.Size(481, 405); + this.picViewer.TabIndex = 0; + this.picViewer.TabStop = false; + this.picViewer.MouseLeave += new System.EventHandler(this.picPicture_MouseLeave); + this.picViewer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picViewer_MouseMove); // // tmrOverlay // @@ -59,17 +64,16 @@ namespace Mesen.GUI.Debugger.Controls // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.picPicture); + this.Controls.Add(this.picViewer); this.Name = "ctrlEventViewerPpuView"; - this.Size = new System.Drawing.Size(686, 530); - ((System.ComponentModel.ISupportInitialize)(this.picPicture)).EndInit(); + this.Size = new System.Drawing.Size(481, 405); this.ResumeLayout(false); } #endregion - private ctrlMesenPictureBox picPicture; + private ctrlImagePanel picViewer; private System.Windows.Forms.Timer tmrOverlay; } } diff --git a/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.cs b/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.cs index 91bca42b..1c2684f3 100644 --- a/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.cs +++ b/GUI.NET/Debugger/Controls/ctrlEventViewerPpuView.cs @@ -16,22 +16,18 @@ using System.Drawing.Imaging; namespace Mesen.GUI.Debugger.Controls { - public partial class ctrlEventViewerPpuView : BaseControl, ICompactControl + public partial class ctrlEventViewerPpuView : BaseControl { - public event EventHandler OnPictureResized; + private int _baseWidth = 341 * 2; + private UInt32 _scanlineCount = 262; - private DebugState _state = new DebugState(); private Point _lastPos = new Point(-1, -1); private bool _needUpdate = false; private Bitmap _screenBitmap = null; - private Bitmap _eventBitmap = null; private Bitmap _overlayBitmap = null; private Bitmap _displayBitmap = null; private byte[] _pictureData = null; - private Dictionary> _debugEventsByCycle = new Dictionary>(); - private List _debugEvents = new List(); private Font _overlayFont; - private double _scale = 1; public ctrlEventViewerPpuView() { @@ -50,49 +46,8 @@ namespace Mesen.GUI.Debugger.Controls public void GetData() { - DebugState state = new DebugState(); - InteropEmu.DebugGetState(ref state); - - DebugEventInfo[] eventInfoArray; - DebugEventInfo[] prevEventInfoArray = new DebugEventInfo[0]; - - InteropEmu.DebugGetDebugEvents(false, out _pictureData, out eventInfoArray); - if(ConfigManager.Config.DebugInfo.EventViewerShowPreviousFrameEvents && (state.PPU.Scanline != -1 || state.PPU.Cycle != 0)) { - //Get the previous frame's data, too - InteropEmu.DebugGetDebugEvents(true, out _pictureData, out prevEventInfoArray); - } - - int currentCycle = (int)((state.PPU.Scanline + 1) * 341 + state.PPU.Cycle); - var debugEvents = new Dictionary>(); - - List eventList = new List(eventInfoArray.Length+prevEventInfoArray.Length); - Action addEvent = (DebugEventInfo eventInfo) => { - int frameCycle = (eventInfo.Scanline + 1) * 341 + eventInfo.Cycle; - - List infoList; - if(!debugEvents.TryGetValue(frameCycle, out infoList)) { - infoList = new List(); - debugEvents[frameCycle] = infoList; - } - infoList.Add(eventInfo); - eventList.Add(eventInfo); - }; - - for(int i = 0; i < eventInfoArray.Length; i++) { - addEvent(eventInfoArray[i]); - } - - //Show events from the previous frame, too - for(int i = 0; i < prevEventInfoArray.Length; i++) { - int frameCycle = (prevEventInfoArray[i].Scanline + 1) * 341 + prevEventInfoArray[i].Cycle; - if(frameCycle > currentCycle) { - addEvent(prevEventInfoArray[i]); - } - } - - _debugEvents = eventList; - _debugEventsByCycle = debugEvents; - _state = state; + EventViewerDisplayOptions options = GetInteropOptions(); + _scanlineCount = InteropEmu.TakeEventSnapshot(options); } public static bool ShowEvent(DebugEventInfo evt) @@ -136,69 +91,30 @@ namespace Mesen.GUI.Debugger.Controls public void RefreshViewer() { + EventViewerDisplayOptions options = GetInteropOptions(); + _pictureData = InteropEmu.GetEventViewerOutput(_scanlineCount, options); + + int picHeight = (int)_scanlineCount * 2; + if(_screenBitmap == null || _screenBitmap.Height != picHeight) { + _screenBitmap = new Bitmap(_baseWidth, picHeight, PixelFormat.Format32bppPArgb); + _overlayBitmap = new Bitmap(_baseWidth, picHeight, PixelFormat.Format32bppPArgb); + _displayBitmap = new Bitmap(_baseWidth, picHeight, PixelFormat.Format32bppPArgb); + } + GCHandle handle = GCHandle.Alloc(this._pictureData, GCHandleType.Pinned); try { - Bitmap source = new Bitmap(256, 240, 256*4, PixelFormat.Format32bppPArgb, handle.AddrOfPinnedObject()); - int picHeight = (int)_state.PPU.ScanlineCount * 2; - if(_eventBitmap == null || _eventBitmap.Height != picHeight) { - _screenBitmap = new Bitmap(682, picHeight, PixelFormat.Format32bppPArgb); - _eventBitmap = new Bitmap(682, picHeight, PixelFormat.Format32bppPArgb); - _overlayBitmap = new Bitmap(682, picHeight, PixelFormat.Format32bppPArgb); - _displayBitmap = new Bitmap(682, picHeight, PixelFormat.Format32bppPArgb); - } - - Size picSize = new Size((int)((_eventBitmap.Width * _scale) + 2), (int)((_eventBitmap.Height * _scale) + 2)); - if(picSize != this.picPicture.Size) { - this.picPicture.Size = picSize; - this.OnPictureResized?.Invoke(this, EventArgs.Empty); - } - - var d = ConfigManager.Config.DebugInfo; - - List> colors = new List> { - null, //None - new List { d.EventViewerPpuRegisterWrite2000Color, d.EventViewerPpuRegisterWrite2001Color, Color.Black, d.EventViewerPpuRegisterWrite2003Color, d.EventViewerPpuRegisterWrite2004Color, d.EventViewerPpuRegisterWrite2005Color, d.EventViewerPpuRegisterWrite2006Color, d.EventViewerPpuRegisterWrite2007Color }, //PpuRegisterWrite - new List { Color.Black, Color.Black, d.EventViewerPpuRegisterRead2002Color, Color.Black, d.EventViewerPpuRegisterRead2004Color, Color.Black, Color.Black, d.EventViewerPpuRegisterRead2007Color }, //PpuRegisterRead - new List { d.EventViewerMapperRegisterWriteColor }, //MapperRegisterWrite - new List { d.EventViewerMapperRegisterReadColor }, //MapperRegisterRead - new List { d.EventViewerNmiColor }, //Nmi - new List { d.EventViewerIrqColor }, //Irq - new List { d.EventViewerSpriteZeroHitColor }, //SpriteZeroHit - new List { d.EventViewerBreakpointColor }, //Breakpoint - new List { d.EventViewerDmcDmaReadColor }, //DMC DMA - }; - + Bitmap source = new Bitmap(_baseWidth, (int)_scanlineCount * 2, _baseWidth * 4, PixelFormat.Format32bppPArgb, handle.AddrOfPinnedObject()); using(Graphics g = Graphics.FromImage(_screenBitmap)) { - g.Clear(Color.Gray); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; - g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half; - g.ScaleTransform(2, 2); - g.DrawImageUnscaled(source, 1, 1); - - g.ResetTransform(); - - int nmiStart = (int)(_state.PPU.NmiScanline + 1) * 2 + 1; - int nmiEnd = (int)(_state.PPU.SafeOamScanline + 1) * 2 + 2; - g.FillRectangle(Brushes.DimGray, 0, nmiStart, 682, nmiEnd - nmiStart); - - g.DrawLine(Pens.Blue, 0, nmiStart, 682, nmiStart); - g.DrawLine(Pens.Red, 0, nmiEnd, 682, nmiEnd); - - if(_state.PPU.Scanline > 0) { - int currentScanline = (_state.PPU.Scanline + 1) * 2 + 1; - g.FillRectangle(Brushes.Yellow, 0, currentScanline, 682, 2); - } + g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None; + g.DrawImageUnscaled(source, 0, 0); } - - using(Graphics g = Graphics.FromImage(_eventBitmap)) { - g.Clear(Color.Transparent); - DrawEvents(g, colors); - } - UpdateDisplay(true); } finally { handle.Free(); } + + UpdateDisplay(true); } private void UpdateDisplay(bool forceUpdate) @@ -210,53 +126,56 @@ namespace Mesen.GUI.Debugger.Controls using(Graphics g = Graphics.FromImage(_displayBitmap)) { g.DrawImage(_screenBitmap, 0, 0); g.DrawImage(_overlayBitmap, 0, 0); - g.DrawImage(_eventBitmap, 0, 0); if(_lastPos.X >= 0) { - string location = _lastPos.X / 2 + ", " + ((_lastPos.Y / 2) - 1); + string location = _lastPos.X + ", " + (_lastPos.Y - 1); SizeF size = g.MeasureString(location, _overlayFont); - int x = _lastPos.X + 15; - int y = _lastPos.Y - (int)size.Height - 5; - if(x + size.Width > _displayBitmap.Width - 5) { - x -= (int)size.Width + 20; + int x = _lastPos.X + 5; + int y = _lastPos.Y - (int)size.Height / 2 - 5; + + if(x * 2 - picViewer.ScrollOffsets.X / picViewer.ImageScale + size.Width > (picViewer.Width / picViewer.ImageScale) - 5) { + x -= (int)size.Width / 2 + 10; } - if(y < size.Height + 5) { + if(y * 2 - picViewer.ScrollOffsets.Y / picViewer.ImageScale < size.Height + 5) { y = _lastPos.Y + 5; } - g.DrawOutlinedString(location, _overlayFont, Brushes.White, Brushes.Black, x, y); + g.DrawOutlinedString(location, _overlayFont, Brushes.Black, Brushes.White, x * 2, y * 2); } } - picPicture.Image = _displayBitmap; + picViewer.ImageSize = new Size(_baseWidth, (int)_scanlineCount * 2); + picViewer.Image = _displayBitmap; _needUpdate = false; } + + private Point GetCycleScanline(Point location) + { + return new Point( + ((location.X & ~0x01) / picViewer.ImageScale) / 2, + ((location.Y & ~0x01) / picViewer.ImageScale) / 2 + ); + } private void UpdateOverlay(Point p) { - int x = (int)(p.X / 2 * 2 / _scale); - int y = (int)(p.Y / 2 * 2 / _scale); + Point pos = GetCycleScanline(p); - if(_lastPos.X == x && _lastPos.Y == y) { + if(_lastPos == pos) { //Same x,y location, no need to update return; } using(Graphics g = Graphics.FromImage(_overlayBitmap)) { g.Clear(Color.Transparent); - - using(Pen bg = new Pen(Color.FromArgb(128, Color.Black))) { - g.DrawRectangle(bg, x - 1, 0, 3, _overlayBitmap.Height); - g.DrawRectangle(bg, 0, y - 1, _overlayBitmap.Width, 3); - } - using(Pen fg = new Pen(Color.FromArgb(230, Color.Orange))) { - g.DrawRectangle(fg, x, 0, 1, _overlayBitmap.Height); - g.DrawRectangle(fg, 0, y, _overlayBitmap.Width, 1); + using(Pen bg = new Pen(Color.FromArgb(128, Color.LightGray))) { + g.DrawRectangle(bg, pos.X * 2 - 1, 0, 3, _overlayBitmap.Height); + g.DrawRectangle(bg, 0, pos.Y * 2 - 1, _overlayBitmap.Width, 3); } } _needUpdate = true; - _lastPos = new Point(x, y); + _lastPos = pos; } private void ClearOverlay() @@ -268,122 +187,133 @@ namespace Mesen.GUI.Debugger.Controls _lastPos = new Point(-1, -1); } - private void DrawEvents(Graphics g, List> colors) + private EventViewerDisplayOptions GetInteropOptions() { - var enumValues = Enum.GetValues(typeof(DebugEventType)); - IGrouping[][] groupedEvents = new IGrouping[enumValues.Length][]; - foreach(DebugEventType eventType in Enum.GetValues(typeof(DebugEventType))) { - List colorList = colors[(int)eventType]; - groupedEvents[(int)eventType] = _debugEvents.Where((v) => v.Type == eventType && ShowEvent(v)).GroupBy((v) => v.Address % colorList.Count).ToArray(); - } - - DrawEvents(g, colors, false, groupedEvents); - DrawEvents(g, colors, true, groupedEvents); + DebugInfo cfg = ConfigManager.Config.DebugInfo; + return new EventViewerDisplayOptions() { + ShowPpuRegisterWrites = new byte[8] { + (byte)(cfg.EventViewerShowPpuWrite2000 ? 1 : 0), + (byte)(cfg.EventViewerShowPpuWrite2001 ? 1 : 0), + 0, + (byte)(cfg.EventViewerShowPpuWrite2003 ? 1 : 0), + (byte)(cfg.EventViewerShowPpuWrite2004 ? 1 : 0), + (byte)(cfg.EventViewerShowPpuWrite2005 ? 1 : 0), + (byte)(cfg.EventViewerShowPpuWrite2006 ? 1 : 0), + (byte)(cfg.EventViewerShowPpuWrite2007 ? 1 : 0), + }, + ShowPpuRegisterReads = new byte[8] { + 0, + 0, + (byte)(cfg.EventViewerShowPpuRead2002 ? 1 : 0), + 0, + (byte)(cfg.EventViewerShowPpuRead2004 ? 1 : 0), + 0, + (byte)(cfg.EventViewerShowPpuRead2007 ? 1 : 0), + 0 + }, + ShowMapperRegisterWrites = cfg.EventViewerShowMapperRegisterWrites, + ShowMapperRegisterReads = cfg.EventViewerShowMapperRegisterReads, + MapperRegisterWriteColor = (uint)cfg.EventViewerMapperRegisterWriteColor.Color.ToArgb(), + MapperRegisterReadColor = (uint)cfg.EventViewerMapperRegisterReadColor.Color.ToArgb(), + ShowNmi = cfg.EventViewerShowNmi, + ShowIrq = cfg.EventViewerShowIrq, + ShowDmcDmaReads = cfg.EventViewerShowDmcDmaReads, + ShowSpriteZeroHit = cfg.EventViewerShowSpriteZeroHit, + ShowMarkedBreakpoints = cfg.EventViewerShowMarkedBreakpoints, + ShowPreviousFrameEvents = cfg.EventViewerShowPreviousFrameEvents, + ShowNtscBorders = cfg.EventViewerShowNtscBorders, + IrqColor = (uint)cfg.EventViewerIrqColor.Color.ToArgb(), + NmiColor = (uint)cfg.EventViewerNmiColor.Color.ToArgb(), + DmcDmaReadColor = (uint)cfg.EventViewerDmcDmaReadColor.Color.ToArgb(), + SpriteZeroHitColor = (uint)cfg.EventViewerSpriteZeroHitColor.Color.ToArgb(), + BreakpointColor = (uint)cfg.EventViewerBreakpointColor.Color.ToArgb(), + PpuRegisterWriteColor = new uint[8] { + (uint)cfg.EventViewerPpuRegisterWrite2000Color.Color.ToArgb(), + (uint)cfg.EventViewerPpuRegisterWrite2001Color.Color.ToArgb(), + 0, + (uint)cfg.EventViewerPpuRegisterWrite2003Color.Color.ToArgb(), + (uint)cfg.EventViewerPpuRegisterWrite2004Color.Color.ToArgb(), + (uint)cfg.EventViewerPpuRegisterWrite2005Color.Color.ToArgb(), + (uint)cfg.EventViewerPpuRegisterWrite2006Color.Color.ToArgb(), + (uint)cfg.EventViewerPpuRegisterWrite2007Color.Color.ToArgb() + }, + PpuRegisterReadColors = new uint[8] { + 0, + 0, + (uint)cfg.EventViewerPpuRegisterRead2002Color.Color.ToArgb(), + 0, + (uint)cfg.EventViewerPpuRegisterRead2004Color.Color.ToArgb(), + 0, + (uint)cfg.EventViewerPpuRegisterRead2007Color.Color.ToArgb(), + 0 + }, + }; } - private static void DrawEvents(Graphics g, List> colors, bool drawFg, IGrouping[][] groupedEvents) - { - int size = drawFg ? 2 : 6; - int offset = drawFg ? 0 : 2; - foreach(DebugEventType eventType in Enum.GetValues(typeof(DebugEventType))) { - if(groupedEvents[(int)eventType] != null) { - foreach(var eventGroup in groupedEvents[(int)eventType]) { - List rects = new List(eventGroup.Count()); - foreach(DebugEventInfo evt in eventGroup) { - rects.Add(new Rectangle(evt.Cycle * 2 - offset, evt.Scanline * 2 - offset + 2, size, size)); - } - - List colorList = colors[(int)eventType]; - Color color = colorList[eventGroup.Key]; - using(Brush b = new SolidBrush(drawFg ? color : ControlPaint.Dark(color))) { - g.FillRectangles(b, rects.ToArray()); - } - } - } - } - } - - private int _lastKey = -1; private frmCodeTooltip _tooltip = null; - private void picPicture_MouseMove(object sender, MouseEventArgs e) + private void picViewer_MouseMove(object sender, MouseEventArgs e) { - int cycle = e.X * 341 / (picPicture.Width - 2); - int scanline = e.Y * (int)_state.PPU.ScanlineCount / (picPicture.Height - 2) - 1; - - int[] offsets = new int[3] { 0, -1, 1 }; - - for(int y = 0; y < 3; y++) { - for(int x = 0; x < 3; x++) { - int key = (scanline + offsets[y] + 1) * 341 + cycle + offsets[x]; - - List eventList; - if(_debugEventsByCycle.TryGetValue(key, out eventList)) { - foreach(DebugEventInfo debugEvent in eventList) { - if(ShowEvent(debugEvent)) { - if(key != _lastKey) { - ResetTooltip(); - - Dictionary values = new Dictionary() { - { "Type", ResourceHelper.GetEnumText(debugEvent.Type) }, - { "Scanline", debugEvent.Scanline.ToString() }, - { "Cycle", debugEvent.Cycle.ToString() }, - { "PC", "$" + debugEvent.ProgramCounter.ToString("X4") }, - }; - - switch(debugEvent.Type) { - case DebugEventType.MapperRegisterRead: - case DebugEventType.MapperRegisterWrite: - case DebugEventType.PpuRegisterRead: - case DebugEventType.PpuRegisterWrite: - values["Register"] = "$" + debugEvent.Address.ToString("X4"); - values["Value"] = "$" + debugEvent.Value.ToString("X2"); - - if(debugEvent.PpuLatch >= 0) { - values["2nd Write"] = debugEvent.PpuLatch == 0 ? "false" : "true"; - } - break; - - case DebugEventType.DmcDmaRead: - values["Address"] = "$" + debugEvent.Address.ToString("X4"); - values["Value"] = "$" + debugEvent.Value.ToString("X2"); - break; - - case DebugEventType.Breakpoint: - ReadOnlyCollection breakpoints = BreakpointManager.Breakpoints; - if(debugEvent.BreakpointId >= 0 && debugEvent.BreakpointId < breakpoints.Count) { - Breakpoint bp = breakpoints[debugEvent.BreakpointId]; - values["BP Type"] = bp.ToReadableType(); - values["BP Addresses"] = bp.GetAddressString(true); - if(bp.Condition.Length > 0) { - values["BP Condition"] = bp.Condition; - } - } - break; - } - - UpdateOverlay(new Point((int)(debugEvent.Cycle * 2 * _scale), (int)((debugEvent.Scanline + 1) * 2 * _scale))); - - Form parentForm = this.FindForm(); - _tooltip = new frmCodeTooltip(parentForm, values, null, null, null, 10); - _tooltip.FormClosed += (s, evt) => { _tooltip = null; }; - Point location = PointToScreen(e.Location); - location.Offset(10, 10); - _tooltip.SetFormLocation(location, this); - _lastKey = key; - } - - //Found a matching write to display, stop processing - return; - } - } - } - } + Point pos = GetCycleScanline(e.Location); + if(_lastPos == pos) { + return; } - UpdateOverlay(e.Location); + EventViewerDisplayOptions options = GetInteropOptions(); + DebugEventInfo debugEvent = new DebugEventInfo(); + InteropEmu.GetEventViewerEvent(ref debugEvent, (Int16)(pos.Y - 1), (UInt16)pos.X, options); + if(debugEvent.ProgramCounter == 0xFFFFFFFF) { + ResetTooltip(); + UpdateOverlay(e.Location); + return; + } + + Dictionary values = new Dictionary() { + { "Type", ResourceHelper.GetEnumText(debugEvent.Type) }, + { "Scanline", debugEvent.Scanline.ToString() }, + { "Cycle", debugEvent.Cycle.ToString() }, + { "PC", "$" + debugEvent.ProgramCounter.ToString("X4") }, + }; + + switch(debugEvent.Type) { + case DebugEventType.MapperRegisterRead: + case DebugEventType.MapperRegisterWrite: + case DebugEventType.PpuRegisterRead: + case DebugEventType.PpuRegisterWrite: + values["Register"] = "$" + debugEvent.Address.ToString("X4"); + values["Value"] = "$" + debugEvent.Value.ToString("X2"); + + if(debugEvent.PpuLatch >= 0) { + values["2nd Write"] = debugEvent.PpuLatch == 0 ? "false" : "true"; + } + break; + + case DebugEventType.DmcDmaRead: + values["Address"] = "$" + debugEvent.Address.ToString("X4"); + values["Value"] = "$" + debugEvent.Value.ToString("X2"); + break; + + case DebugEventType.Breakpoint: + ReadOnlyCollection breakpoints = BreakpointManager.Breakpoints; + if(debugEvent.BreakpointId >= 0 && debugEvent.BreakpointId < breakpoints.Count) { + Breakpoint bp = breakpoints[debugEvent.BreakpointId]; + values["BP Type"] = bp.ToReadableType(); + values["BP Addresses"] = bp.GetAddressString(true); + if(bp.Condition.Length > 0) { + values["BP Condition"] = bp.Condition; + } + } + break; + } - //No match found, make sure any existing tooltip is closed ResetTooltip(); + UpdateOverlay(new Point((int)(debugEvent.Cycle * 2 * picViewer.ImageScale), (int)((debugEvent.Scanline + 1) * 2 * picViewer.ImageScale))); + + Form parentForm = this.FindForm(); + _tooltip = new frmCodeTooltip(parentForm, values, null, null, null, 10); + _tooltip.FormClosed += (s, evt) => { _tooltip = null; }; + Point location = this.PointToScreen(new Point(e.Location.X - picViewer.ScrollOffsets.X, e.Location.Y - picViewer.ScrollOffsets.Y)); + location.Offset(10, 10); + _tooltip.SetFormLocation(location, this); } private void ResetTooltip() @@ -392,7 +322,6 @@ namespace Mesen.GUI.Debugger.Controls _tooltip.Close(); _tooltip = null; } - _lastKey = -1; } private void picPicture_MouseLeave(object sender, EventArgs e) @@ -406,15 +335,14 @@ namespace Mesen.GUI.Debugger.Controls UpdateDisplay(false); } - public Size GetCompactSize(bool includeMargins) + public void ZoomIn() { - return picPicture.Size + picPicture.Margin.Size; + picViewer.ZoomIn(); } - public void ScaleImage(double scale) + public void ZoomOut() { - _scale *= scale; - picPicture.Size = new Size((int)(picPicture.Width * scale), (int)(picPicture.Height * scale)); + picViewer.ZoomOut(); } } } diff --git a/GUI.NET/Debugger/Controls/ctrlImagePanel.Designer.cs b/GUI.NET/Debugger/Controls/ctrlImagePanel.Designer.cs new file mode 100644 index 00000000..d01c1df9 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlImagePanel.Designer.cs @@ -0,0 +1,73 @@ +namespace Mesen.GUI.Debugger +{ + partial class ctrlImagePanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.ctrlPanel = new Mesen.GUI.Controls.ctrlPanel(); + this.ctrlImageViewer = new Mesen.GUI.Debugger.ctrlImageViewer(); + this.ctrlPanel.SuspendLayout(); + this.SuspendLayout(); + // + // ctrlPanel + // + this.ctrlPanel.AutoScroll = true; + this.ctrlPanel.Controls.Add(this.ctrlImageViewer); + this.ctrlPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlPanel.Location = new System.Drawing.Point(0, 0); + this.ctrlPanel.Name = "ctrlPanel"; + this.ctrlPanel.Size = new System.Drawing.Size(327, 326); + this.ctrlPanel.TabIndex = 0; + // + // ctrlImageViewer + // + this.ctrlImageViewer.Image = null; + this.ctrlImageViewer.ImageScale = 1; + this.ctrlImageViewer.Location = new System.Drawing.Point(0, 0); + this.ctrlImageViewer.Name = "ctrlImageViewer"; + this.ctrlImageViewer.Selection = new System.Drawing.Rectangle(0, 0, 0, 0); + this.ctrlImageViewer.Size = new System.Drawing.Size(327, 326); + this.ctrlImageViewer.TabIndex = 0; + this.ctrlImageViewer.Text = "ctrlImageViewer"; + // + // ctrlImagePanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.ctrlPanel); + this.Name = "ctrlImagePanel"; + this.Size = new System.Drawing.Size(327, 326); + this.ctrlPanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private Mesen.GUI.Controls.ctrlPanel ctrlPanel; + private ctrlImageViewer ctrlImageViewer; + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlImagePanel.cs b/GUI.NET/Debugger/Controls/ctrlImagePanel.cs new file mode 100644 index 00000000..9fe155b7 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlImagePanel.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Controls; +using Mesen.GUI.Forms; +using System.Drawing.Imaging; + +namespace Mesen.GUI.Debugger +{ + public partial class ctrlImagePanel : BaseControl + { + private int _scale = 1; + private Size _imageSize; + + private bool _mouseDown = false; + private Point _lastLocation = Point.Empty; + + public Rectangle Selection { get { return ctrlImageViewer.Selection; } set { ctrlImageViewer.Selection = value; } } + public Rectangle Overlay { get { return ctrlImageViewer.Overlay; } set { ctrlImageViewer.Overlay = value; } } + public int SelectionWrapPosition { get { return ctrlImageViewer.SelectionWrapPosition; } set { ctrlImageViewer.SelectionWrapPosition = value; } } + + public Size ImageSize { get { return _imageSize; } set { _imageSize = value; UpdateMapSize(); } } + public Image Image { get { return ctrlImageViewer.Image; } set { ctrlImageViewer.Image = value; } } + public int ImageScale { get { return _scale; } set { _scale = value; UpdateMapSize(); } } + public int GridSizeX { get { return ctrlImageViewer.GridSizeX; } set { ctrlImageViewer.GridSizeX = value; } } + public int GridSizeY { get { return ctrlImageViewer.GridSizeY; } set { ctrlImageViewer.GridSizeY = value; } } + public Point ScrollOffsets { get { return new Point(ctrlPanel.HorizontalScroll.Value, ctrlPanel.VerticalScroll.Value); } } + + public new event EventHandler MouseLeave { add { ctrlImageViewer.MouseLeave += value; } remove { ctrlImageViewer.MouseLeave -= value; } } + public new event MouseEventHandler MouseMove { add { ctrlImageViewer.MouseMove += value; } remove { ctrlImageViewer.MouseMove -= value; } } + public new event MouseEventHandler MouseClick; + + public ctrlImagePanel() + { + InitializeComponent(); + + if(DesignMode) { + return; + } + + ctrlPanel.OnZoom += (scaleDelta) => { + double hori = (double)ctrlPanel.HorizontalScroll.Value / _scale + (double)ctrlPanel.Width / 2 / _scale; + double vert = (double)ctrlPanel.VerticalScroll.Value / _scale + (double)ctrlPanel.Height / 2 / _scale; + + _scale = Math.Min(16, Math.Max(1, _scale + scaleDelta)); + UpdateMapSize(); + + int horizontalScroll = Math.Max(0, Math.Min((int)(hori * _scale) - ctrlPanel.Width / 2, ctrlPanel.HorizontalScroll.Maximum)); + int verticalScroll = Math.Max(0, Math.Min((int)(vert * _scale) - ctrlPanel.Height / 2, ctrlPanel.VerticalScroll.Maximum)); + + //Set the values twice to avoid a scroll position bug + ctrlPanel.HorizontalScroll.Value = horizontalScroll; + ctrlPanel.HorizontalScroll.Value = horizontalScroll; + ctrlPanel.VerticalScroll.Value = verticalScroll; + ctrlPanel.VerticalScroll.Value = verticalScroll; + }; + + ctrlImageViewer.MouseDown += (s, e) => { + if(e.Button == MouseButtons.Left) { + _mouseDown = true; + _lastLocation = e.Location; + } + }; + + ctrlImageViewer.MouseUp += (s, e) => { + _mouseDown = false; + }; + + ctrlImageViewer.MouseClick += (s, e) => { + this.MouseClick?.Invoke(s, e); + }; + + ctrlImageViewer.MouseMove += ctrlImageViewer_MouseMove; + } + + private void ctrlImageViewer_MouseMove(object sender, MouseEventArgs e) + { + if(_mouseDown) { + ctrlImageViewer.MouseMove -= ctrlImageViewer_MouseMove; + int hScroll = Math.Min(ctrlPanel.HorizontalScroll.Maximum, Math.Max(0, ctrlPanel.HorizontalScroll.Value - (e.Location.X - _lastLocation.X))); + int vScroll = Math.Min(ctrlPanel.VerticalScroll.Maximum, Math.Max(0, ctrlPanel.VerticalScroll.Value - (e.Location.Y - _lastLocation.Y))); + + ctrlPanel.HorizontalScroll.Value = hScroll; + ctrlPanel.HorizontalScroll.Value = hScroll; + ctrlPanel.VerticalScroll.Value = vScroll; + ctrlPanel.VerticalScroll.Value = vScroll; + ctrlImageViewer.MouseMove += ctrlImageViewer_MouseMove; + } + } + + private void UpdateMapSize() + { + ctrlImageViewer.Width = ImageSize.Width * _scale; + ctrlImageViewer.Height = ImageSize.Height * _scale; + ctrlImageViewer.ImageScale = _scale; + ctrlImageViewer.Invalidate(); + } + + protected override void OnInvalidated(InvalidateEventArgs e) + { + base.OnInvalidated(e); + ctrlImageViewer.Invalidate(); + } + + public void ZoomIn() + { + _scale = Math.Min(16, _scale + 1); + UpdateMapSize(); + } + + public void ZoomOut() + { + _scale = Math.Max(1, _scale - 1); + UpdateMapSize(); + } + + public void ScrollTo(int scrollPos) + { + ctrlPanel.VerticalScroll.Value = scrollPos; + ctrlPanel.VerticalScroll.Value = scrollPos; + + ctrlPanel.HorizontalScroll.Value = 0; + ctrlPanel.HorizontalScroll.Value = 0; + } + + public void CopyToClipboard() + { + Clipboard.SetImage(this.Image); + } + + public void SaveAsPng() + { + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.SetFilter("PNG files|*.png"); + if(sfd.ShowDialog() == DialogResult.OK) { + this.Image.Save(sfd.FileName, ImageFormat.Png); + } + } + } + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlImagePanel.resx b/GUI.NET/Debugger/Controls/ctrlImagePanel.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlImagePanel.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GUI.NET/Debugger/Controls/ctrlImageViewer.cs b/GUI.NET/Debugger/Controls/ctrlImageViewer.cs new file mode 100644 index 00000000..d1316fd7 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlImageViewer.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger +{ + public class ctrlImageViewer : Control + { + private Image _image = null; + private Rectangle _selection = Rectangle.Empty; + private Rectangle _overlay = Rectangle.Empty; + private int _selectionWrapPosition = 0; + private int _gridSizeX = 0; + private int _gridSizeY = 0; + + public ctrlImageViewer() + { + this.DoubleBuffered = true; + this.ResizeRedraw = true; + } + + public Image Image + { + get { return _image; } + set { _image = value; this.Invalidate(); } + } + + public Rectangle Selection + { + get { return _selection; } + set { _selection = value; this.Invalidate(); } + } + + public Rectangle Overlay + { + get { return _overlay; } + set { _overlay = value; this.Invalidate(); } + } + + public int GridSizeX + { + get { return _gridSizeX; } + set { _gridSizeX = value; this.Invalidate(); } + } + + public int GridSizeY + { + get { return _gridSizeY; } + set { _gridSizeY = value; this.Invalidate(); } + } + + public int SelectionWrapPosition + { + get { return _selectionWrapPosition; } + set { _selectionWrapPosition = value; this.Invalidate(); } + } + + public int ImageScale { get; set; } = 1; + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; + e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half; + e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; + e.Graphics.ScaleTransform(this.ImageScale, this.ImageScale); + + if(this.Image != null) { + e.Graphics.DrawImage(this.Image, 0, 0); + } + e.Graphics.ResetTransform(); + + using(Pen gridPen = new Pen(Color.FromArgb(180, Color.LightBlue))) { + if(_gridSizeX > 1) { + for(int i = this.ImageScale * _gridSizeX; i < this.Width; i += this.ImageScale * _gridSizeX) { + e.Graphics.DrawLine(gridPen, i, 0, i, this.Height); + } + } + if(_gridSizeY > 1) { + for(int i = this.ImageScale * _gridSizeY; i < this.Height; i += this.ImageScale * _gridSizeY) { + e.Graphics.DrawLine(gridPen, 0, i, this.Width, i); + } + } + } + + if(_overlay != Rectangle.Empty) { + using(SolidBrush brush = new SolidBrush(Color.FromArgb(100, 240, 240, 240))) { + int scale = this.ImageScale; + Rectangle rect = new Rectangle(_overlay.Left * scale % this.Width, _overlay.Top * scale % this.Height, _overlay.Width * scale, _overlay.Height * scale); + + e.Graphics.FillRectangle(brush, rect.Left, rect.Top, rect.Width, rect.Height); + e.Graphics.DrawRectangle(Pens.Gray, rect.Left, rect.Top, rect.Width, rect.Height); + + if((rect.Top + rect.Height) > this.Height) { + e.Graphics.FillRectangle(brush, rect.Left, rect.Top - this.Height, rect.Width, rect.Height); + e.Graphics.DrawRectangle(Pens.Gray, rect.Left, rect.Top - this.Height, rect.Width, rect.Height); + } + + if((rect.Left + rect.Width) > this.Width) { + e.Graphics.FillRectangle(brush, rect.Left - this.Width, rect.Top, rect.Width, rect.Height); + e.Graphics.DrawRectangle(Pens.Gray, rect.Left - this.Width, rect.Top, rect.Width, rect.Height); + + if((rect.Top + rect.Height) > this.Height) { + e.Graphics.FillRectangle(brush, rect.Left - this.Width, rect.Top - this.Height, rect.Width, rect.Height); + e.Graphics.DrawRectangle(Pens.Gray, rect.Left - this.Width, rect.Top - this.Height, rect.Width, rect.Height); + } + } + } + } + + if(_selection != Rectangle.Empty) { + int scale = this.ImageScale; + e.Graphics.DrawRectangle(Pens.White, _selection.Left * scale, _selection.Top * scale, _selection.Width * scale + 0.5f, _selection.Height * scale + 0.5f); + e.Graphics.DrawRectangle(Pens.Gray, _selection.Left * scale - 1, _selection.Top * scale - 1, _selection.Width * scale + 2.5f, _selection.Height * scale + 2.5f); + + if(_selectionWrapPosition > 0 && _selection.Top + _selection.Height > _selectionWrapPosition) { + e.Graphics.DrawRectangle(Pens.White, _selection.Left * scale, _selection.Top * scale - _selectionWrapPosition * scale, _selection.Width * scale + 0.5f, _selection.Height * scale + 0.5f); + e.Graphics.DrawRectangle(Pens.Gray, _selection.Left * scale - 1, _selection.Top * scale - 1 - _selectionWrapPosition * scale, _selection.Width * scale + 2.5f, _selection.Height * scale + 2.5f); + } + } + } + } +} diff --git a/GUI.NET/Debugger/frmDbgPreferences.cs b/GUI.NET/Debugger/frmDbgPreferences.cs index c1c6a3e7..374668c7 100644 --- a/GUI.NET/Debugger/frmDbgPreferences.cs +++ b/GUI.NET/Debugger/frmDbgPreferences.cs @@ -23,6 +23,8 @@ namespace Mesen.GUI.Debugger GetMember(nameof(DebuggerShortcutsConfig.IncreaseFontSize)), GetMember(nameof(DebuggerShortcutsConfig.DecreaseFontSize)), GetMember(nameof(DebuggerShortcutsConfig.ResetFontSize)), + GetMember(nameof(DebuggerShortcutsConfig.ZoomIn)), + GetMember(nameof(DebuggerShortcutsConfig.ZoomOut)), GetMember(nameof(DebuggerShortcutsConfig.GoTo)), GetMember(nameof(DebuggerShortcutsConfig.Find)), GetMember(nameof(DebuggerShortcutsConfig.FindNext)), diff --git a/GUI.NET/Debugger/frmEventViewer.Designer.cs b/GUI.NET/Debugger/frmEventViewer.Designer.cs index 380938c7..a76e4625 100644 --- a/GUI.NET/Debugger/frmEventViewer.Designer.cs +++ b/GUI.NET/Debugger/frmEventViewer.Designer.cs @@ -33,8 +33,10 @@ { this.tabMain = new System.Windows.Forms.TabControl(); this.tpgPpuView = new System.Windows.Forms.TabPage(); + this.ctrlEventViewerPpuView = new Mesen.GUI.Debugger.Controls.ctrlEventViewerPpuView(); this.grpShow = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.chkShowNtscBorders = new System.Windows.Forms.CheckBox(); this.lblPpuWrites = new System.Windows.Forms.Label(); this.chkShowMapperRegisterWrites = new System.Windows.Forms.CheckBox(); this.chkShowPreviousFrameEvents = new System.Windows.Forms.CheckBox(); @@ -73,7 +75,6 @@ this.picSpriteZeroHit = new Mesen.GUI.Debugger.ctrlColorPicker(); this.picBreakpoint = new Mesen.GUI.Debugger.ctrlColorPicker(); this.picDmcDmaRead = new Mesen.GUI.Debugger.ctrlColorPicker(); - this.ctrlEventViewerPpuView = new Mesen.GUI.Debugger.Controls.ctrlEventViewerPpuView(); this.tpgListView = new System.Windows.Forms.TabPage(); this.ctrlEventViewerListView = new Mesen.GUI.Debugger.Controls.ctrlEventViewerListView(); this.menuStrip1 = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); @@ -83,8 +84,9 @@ this.mnuRefreshOnBreak = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.mnuResetColors = new System.Windows.Forms.ToolStripMenuItem(); - this.chkToggleZoom = new System.Windows.Forms.CheckBox(); - this.btnToggleView = new System.Windows.Forms.Button(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuZoomIn = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuZoomOut = new System.Windows.Forms.ToolStripMenuItem(); this.tabMain.SuspendLayout(); this.tpgPpuView.SuspendLayout(); this.grpShow.SuspendLayout(); @@ -124,8 +126,8 @@ // // tpgPpuView // - this.tpgPpuView.Controls.Add(this.grpShow); this.tpgPpuView.Controls.Add(this.ctrlEventViewerPpuView); + this.tpgPpuView.Controls.Add(this.grpShow); this.tpgPpuView.Location = new System.Drawing.Point(4, 22); this.tpgPpuView.Name = "tpgPpuView"; this.tpgPpuView.Padding = new System.Windows.Forms.Padding(3); @@ -134,6 +136,14 @@ this.tpgPpuView.Text = "PPU View"; this.tpgPpuView.UseVisualStyleBackColor = true; // + // ctrlEventViewerPpuView + // + this.ctrlEventViewerPpuView.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlEventViewerPpuView.Location = new System.Drawing.Point(3, 3); + this.ctrlEventViewerPpuView.Name = "ctrlEventViewerPpuView"; + this.ctrlEventViewerPpuView.Size = new System.Drawing.Size(686, 532); + this.ctrlEventViewerPpuView.TabIndex = 0; + // // grpShow // this.grpShow.Controls.Add(this.tableLayoutPanel2); @@ -152,6 +162,7 @@ this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 31F)); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 25F)); + this.tableLayoutPanel2.Controls.Add(this.chkShowNtscBorders, 0, 20); this.tableLayoutPanel2.Controls.Add(this.lblPpuWrites, 0, 0); this.tableLayoutPanel2.Controls.Add(this.chkShowMapperRegisterWrites, 0, 9); this.tableLayoutPanel2.Controls.Add(this.chkShowPreviousFrameEvents, 0, 19); @@ -194,7 +205,7 @@ this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; - this.tableLayoutPanel2.RowCount = 20; + this.tableLayoutPanel2.RowCount = 21; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); @@ -214,10 +225,23 @@ this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel2.Size = new System.Drawing.Size(180, 513); this.tableLayoutPanel2.TabIndex = 2; // + // chkShowNtscBorders + // + this.chkShowNtscBorders.AutoSize = true; + this.tableLayoutPanel2.SetColumnSpan(this.chkShowNtscBorders, 4); + this.chkShowNtscBorders.Location = new System.Drawing.Point(3, 405); + this.chkShowNtscBorders.Name = "chkShowNtscBorders"; + this.chkShowNtscBorders.Size = new System.Drawing.Size(123, 17); + this.chkShowNtscBorders.TabIndex = 67; + this.chkShowNtscBorders.Text = "Show NTSC borders"; + this.chkShowNtscBorders.UseVisualStyleBackColor = true; + this.chkShowNtscBorders.Click += new System.EventHandler(this.chkShowHide_Click); + // // lblPpuWrites // this.lblPpuWrites.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); @@ -657,15 +681,6 @@ this.picDmcDmaRead.TabStop = false; this.picDmcDmaRead.BackColorChanged += new System.EventHandler(this.picColor_BackColorChanged); // - // ctrlEventViewerPpuView - // - this.ctrlEventViewerPpuView.Location = new System.Drawing.Point(0, 0); - this.ctrlEventViewerPpuView.Margin = new System.Windows.Forms.Padding(0); - this.ctrlEventViewerPpuView.Name = "ctrlEventViewerPpuView"; - this.ctrlEventViewerPpuView.Size = new System.Drawing.Size(685, 532); - this.ctrlEventViewerPpuView.TabIndex = 0; - this.ctrlEventViewerPpuView.OnPictureResized += new System.EventHandler(this.ctrlEventViewerPpuView_OnPictureResized); - // // tpgListView // this.tpgListView.Controls.Add(this.ctrlEventViewerListView); @@ -717,7 +732,10 @@ this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuRefreshOnBreak, this.toolStripMenuItem1, - this.mnuResetColors}); + this.mnuResetColors, + this.toolStripMenuItem2, + this.mnuZoomIn, + this.mnuZoomOut}); this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); this.viewToolStripMenuItem.Text = "View"; @@ -742,41 +760,30 @@ this.mnuResetColors.Text = "Reset colors to default"; this.mnuResetColors.Click += new System.EventHandler(this.mnuResetColors_Click); // - // chkToggleZoom + // toolStripMenuItem2 // - this.chkToggleZoom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.chkToggleZoom.Appearance = System.Windows.Forms.Appearance.Button; - this.chkToggleZoom.AutoCheck = false; - this.chkToggleZoom.Image = global::Mesen.GUI.Properties.Resources.Zoom2x; - this.chkToggleZoom.Location = new System.Drawing.Point(824, 1); - this.chkToggleZoom.Name = "chkToggleZoom"; - this.chkToggleZoom.Size = new System.Drawing.Size(27, 22); - this.chkToggleZoom.TabIndex = 8; - this.chkToggleZoom.UseVisualStyleBackColor = true; - this.chkToggleZoom.Click += new System.EventHandler(this.chkToggleZoom_Click); + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(195, 6); // - // btnToggleView + // mnuZoomIn // - this.btnToggleView.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnToggleView.Image = global::Mesen.GUI.Properties.Resources.Collapse; - this.btnToggleView.Location = new System.Drawing.Point(857, 1); - this.btnToggleView.Name = "btnToggleView"; - this.btnToggleView.Size = new System.Drawing.Size(27, 22); - this.btnToggleView.TabIndex = 7; - this.btnToggleView.UseVisualStyleBackColor = true; - this.btnToggleView.Click += new System.EventHandler(this.btnToggleView_Click); + this.mnuZoomIn.Name = "mnuZoomIn"; + this.mnuZoomIn.Size = new System.Drawing.Size(198, 22); + this.mnuZoomIn.Text = "Zoom In"; + // + // mnuZoomOut + // + this.mnuZoomOut.Name = "mnuZoomOut"; + this.mnuZoomOut.Size = new System.Drawing.Size(198, 22); + this.mnuZoomOut.Text = "Zoom Out"; // // frmEventViewer // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(886, 588); - this.Controls.Add(this.chkToggleZoom); - this.Controls.Add(this.btnToggleView); this.Controls.Add(this.tabMain); this.Controls.Add(this.menuStrip1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MaximizeBox = false; this.Name = "frmEventViewer"; this.Text = "Event Viewer"; this.tabMain.ResumeLayout(false); @@ -830,8 +837,6 @@ private System.Windows.Forms.TabPage tpgListView; private Controls.ctrlEventViewerListView ctrlEventViewerListView; private System.Windows.Forms.CheckBox chkShowPreviousFrameEvents; - private System.Windows.Forms.CheckBox chkToggleZoom; - private System.Windows.Forms.Button btnToggleView; private System.Windows.Forms.Label lblPpuWrites; private System.Windows.Forms.CheckBox chkWrite2000; private ctrlColorPicker picWrite2000; @@ -865,5 +870,9 @@ private System.Windows.Forms.ToolStripMenuItem mnuResetColors; private System.Windows.Forms.CheckBox chkShowDmcDmaRead; private ctrlColorPicker picDmcDmaRead; + private System.Windows.Forms.CheckBox chkShowNtscBorders; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem mnuZoomIn; + private System.Windows.Forms.ToolStripMenuItem mnuZoomOut; } } \ No newline at end of file diff --git a/GUI.NET/Debugger/frmEventViewer.cs b/GUI.NET/Debugger/frmEventViewer.cs index 475f98b1..73cd2f37 100644 --- a/GUI.NET/Debugger/frmEventViewer.cs +++ b/GUI.NET/Debugger/frmEventViewer.cs @@ -19,10 +19,6 @@ namespace Mesen.GUI.Debugger private EntityBinder _binder = new EntityBinder(); private bool _inListViewTab = false; private bool _refreshing = false; - private bool _isZoomed = false; - private bool _isCompact = false; - private Size _originalSize; - private Size _previousPictureSize; public frmEventViewer() { @@ -79,20 +75,7 @@ namespace Mesen.GUI.Debugger _binder.AddBinding(nameof(DebugInfo.EventViewerDmcDmaReadColor), picDmcDmaRead); _binder.AddBinding(nameof(DebugInfo.EventViewerShowPreviousFrameEvents), chkShowPreviousFrameEvents); - - string toggleViewTooltip = "Toggle Compact/Normal View"; - if(ConfigManager.Config.DebugInfo.Shortcuts.PpuViewer_ToggleView != Keys.None) { - toggleViewTooltip += " (" + DebuggerShortcutsConfig.GetShortcutDisplay(ConfigManager.Config.DebugInfo.Shortcuts.PpuViewer_ToggleView) + ")"; - } - this.toolTip.SetToolTip(this.btnToggleView, toggleViewTooltip); - - string toggleZoomTooltip = "Toggle 2x Zoom"; - if(ConfigManager.Config.DebugInfo.Shortcuts.PpuViewer_ToggleZoom != Keys.None) { - toggleZoomTooltip += " (" + DebuggerShortcutsConfig.GetShortcutDisplay(ConfigManager.Config.DebugInfo.Shortcuts.PpuViewer_ToggleZoom) + ")"; - } - this.toolTip.SetToolTip(this.chkToggleZoom, toggleZoomTooltip); - - _previousPictureSize = ctrlEventViewerPpuView.Size; + _binder.AddBinding(nameof(DebugInfo.EventViewerShowNtscBorders), chkShowNtscBorders); this.GetData(); @@ -102,13 +85,24 @@ namespace Mesen.GUI.Debugger DebugWorkspaceManager.GetWorkspace(); - RestoreLocation(ConfigManager.Config.DebugInfo.EventViewerLocation); + RestoreLocation(ConfigManager.Config.DebugInfo.EventViewerLocation, ConfigManager.Config.DebugInfo.EventViewerSize); this._notifListener = new InteropEmu.NotificationListener(ConfigManager.Config.DebugInfo.DebugConsoleId); this._notifListener.OnNotification += this._notifListener_OnNotification; + + InitShortcuts(); } } + private void InitShortcuts() + { + mnuZoomIn.InitShortcut(this, nameof(DebuggerShortcutsConfig.ZoomIn)); + mnuZoomOut.InitShortcut(this, nameof(DebuggerShortcutsConfig.ZoomOut)); + + mnuZoomIn.Click += (s, evt) => ctrlEventViewerPpuView.ZoomIn(); + mnuZoomOut.Click += (s, evt) => ctrlEventViewerPpuView.ZoomOut(); + } + protected override void OnFormClosing(FormClosingEventArgs e) { base.OnFormClosing(e); @@ -117,21 +111,10 @@ namespace Mesen.GUI.Debugger _binder.UpdateObject(); ConfigManager.Config.DebugInfo.EventViewerLocation = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Location : this.Location; + ConfigManager.Config.DebugInfo.EventViewerSize = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Size : this.Size; ConfigManager.ApplyChanges(); } - - protected override bool ProcessCmdKey(ref Message msg, Keys keyData) - { - if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.PpuViewer_ToggleZoom) { - ToggleZoom(); - return true; - } else if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.PpuViewer_ToggleView) { - ToggleView(); - return true; - } - return base.ProcessCmdKey(ref msg, keyData); - } - + private void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e) { switch(e.NotificationType) { @@ -202,69 +185,6 @@ namespace Mesen.GUI.Debugger } } - private void ctrlEventViewerPpuView_OnPictureResized(object sender, EventArgs e) - { - Size picSize = ctrlEventViewerPpuView.GetCompactSize(false); - this.Size += (picSize - _previousPictureSize); - _originalSize += (picSize - _previousPictureSize); - ctrlEventViewerPpuView.Size += (picSize - _previousPictureSize); - _previousPictureSize = picSize; - } - - private void ToggleView() - { - if(!_isCompact) { - _isCompact = true; - _originalSize = this.Size; - - this.ClientSize = ctrlEventViewerPpuView.GetCompactSize(false) + new Size(3, menuStrip1.Height + 3); - - this.Controls.Add(ctrlEventViewerPpuView); - ctrlEventViewerPpuView.BringToFront(); - ctrlEventViewerPpuView.Dock = DockStyle.Fill; - - tabMain.Visible = false; - } else { - _isCompact = false; - this.Size = _originalSize; - ctrlEventViewerPpuView.Dock = DockStyle.None; - ctrlEventViewerPpuView.Size = ctrlEventViewerPpuView.GetCompactSize(false); - tabMain.Visible = true; - tpgPpuView.Controls.Add(ctrlEventViewerPpuView); - } - - btnToggleView.Image = _isCompact ? Properties.Resources.Expand : Properties.Resources.Collapse; - RefreshViewer(); - } - - private void ToggleZoom() - { - ICompactControl ctrl = ctrlEventViewerPpuView; - - if(!_isZoomed) { - Size pictureSize = ctrl.GetCompactSize(false); - ctrl.ScaleImage(2); - _isZoomed = true; - } else { - Size pictureSize = ctrl.GetCompactSize(false); - Size halfSize = new Size(pictureSize.Width / 2, pictureSize.Height / 2); - ctrl.ScaleImage(0.5); - _isZoomed = false; - } - chkToggleZoom.Checked = _isZoomed; - RefreshViewer(); - } - - private void btnToggleView_Click(object sender, EventArgs e) - { - ToggleView(); - } - - private void chkToggleZoom_Click(object sender, EventArgs e) - { - ToggleZoom(); - } - private void picColor_BackColorChanged(object sender, EventArgs e) { RefreshViewer(); diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 234f6f7e..9a676dec 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -297,6 +297,9 @@ ctrlNsfPlayer.cs + + Component + UserControl @@ -445,6 +448,15 @@ ctrlFlagStatus.cs + + UserControl + + + ctrlImagePanel.cs + + + Component + UserControl @@ -1383,6 +1395,9 @@ ctrlEventViewerPpuView.cs + + ctrlImagePanel.cs + ctrlScanlineCycleSelect.cs diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index eb6e0353..f8f58bf5 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -490,27 +490,31 @@ namespace Mesen.GUI return frameData; } - [DllImport(DLLPath)] private static extern UInt32 DebugGetDebugEventCount([MarshalAs(UnmanagedType.I1)]bool returnPreviousFrameData); - [DllImport(DLLPath, EntryPoint = "DebugGetDebugEvents")] private static extern void DebugGetDebugEventsWrapper(IntPtr frameBuffer, IntPtr infoArray, ref UInt32 maxEventCount, [MarshalAs(UnmanagedType.I1)]bool returnPreviousFrameData); - public static void DebugGetDebugEvents(bool returnPreviousFrameData, out byte[] pictureData, out DebugEventInfo[] debugEvents) + [DllImport(DLLPath)] private static extern UInt32 GetDebugEventCount([MarshalAs(UnmanagedType.I1)]bool getPreviousFrameData); + [DllImport(DLLPath, EntryPoint = "GetDebugEvents")] private static extern void GetDebugEventsWrapper([In, Out]DebugEventInfo[] eventArray, ref UInt32 maxEventCount, [MarshalAs(UnmanagedType.I1)]bool getPreviousFrameData); + public static DebugEventInfo[] GetDebugEvents(bool getPreviousFrameData) { - pictureData = new byte[256 * 240 * 4]; - UInt32 maxEventCount = DebugGetDebugEventCount(returnPreviousFrameData); - debugEvents = new DebugEventInfo[maxEventCount]; - - GCHandle hPictureData = GCHandle.Alloc(pictureData, GCHandleType.Pinned); - GCHandle hDebugEvents = GCHandle.Alloc(debugEvents, GCHandleType.Pinned); - try { - InteropEmu.DebugGetDebugEventsWrapper(hPictureData.AddrOfPinnedObject(), hDebugEvents.AddrOfPinnedObject(), ref maxEventCount, returnPreviousFrameData); - } finally { - hPictureData.Free(); - hDebugEvents.Free(); - } + UInt32 maxEventCount = GetDebugEventCount(getPreviousFrameData); + DebugEventInfo[] debugEvents = new DebugEventInfo[maxEventCount]; + InteropEmu.GetDebugEventsWrapper(debugEvents, ref maxEventCount, getPreviousFrameData); if(maxEventCount < debugEvents.Length) { //Remove the excess from the array if needed Array.Resize(ref debugEvents, (int)maxEventCount); } + + return debugEvents; + } + + [DllImport(DLLPath)] public static extern void GetEventViewerEvent(ref DebugEventInfo evtInfo, Int16 scanline, UInt16 cycle, EventViewerDisplayOptions options); + [DllImport(DLLPath)] public static extern UInt32 TakeEventSnapshot(EventViewerDisplayOptions options); + + [DllImport(DLLPath, EntryPoint = "GetEventViewerOutput")] private static extern void GetEventViewerOutputWrapper([In, Out]byte[] buffer, EventViewerDisplayOptions options); + public static byte[] GetEventViewerOutput(UInt32 scanlineCount, EventViewerDisplayOptions options) + { + byte[] buffer = new byte[341 * 2 * scanlineCount * 2 * 4]; + InteropEmu.GetEventViewerOutputWrapper(buffer, options); + return buffer; } [DllImport(DLLPath, EntryPoint = "DebugGetProfilerData")] private static extern void DebugGetProfilerDataWrapper(IntPtr profilerData, ProfilerDataType dataType); @@ -1243,7 +1247,7 @@ namespace Mesen.GUI { public UInt16 Cycle; public Int16 Scanline; - public UInt16 ProgramCounter; + public UInt32 ProgramCounter; public UInt16 Address; public Int16 BreakpointId; public DebugEventType Type; @@ -2156,6 +2160,41 @@ namespace Mesen.GUI public RecordMovieFrom RecordFrom; } + public struct EventViewerDisplayOptions + { + public UInt32 IrqColor; + public UInt32 NmiColor; + public UInt32 DmcDmaReadColor; + public UInt32 SpriteZeroHitColor; + public UInt32 BreakpointColor; + public UInt32 MapperRegisterReadColor; + public UInt32 MapperRegisterWriteColor; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public UInt32[] PpuRegisterReadColors; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public UInt32[] PpuRegisterWriteColor; + + [MarshalAs(UnmanagedType.I1)] public bool ShowMapperRegisterWrites; + [MarshalAs(UnmanagedType.I1)] public bool ShowMapperRegisterReads; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] ShowPpuRegisterWrites; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] ShowPpuRegisterReads; + + [MarshalAs(UnmanagedType.I1)] public bool ShowNmi; + [MarshalAs(UnmanagedType.I1)] public bool ShowIrq; + [MarshalAs(UnmanagedType.I1)] public bool ShowDmcDmaReads; + [MarshalAs(UnmanagedType.I1)] public bool ShowSpriteZeroHit; + + [MarshalAs(UnmanagedType.I1)] public bool ShowMarkedBreakpoints; + [MarshalAs(UnmanagedType.I1)] public bool ShowPreviousFrameEvents; + [MarshalAs(UnmanagedType.I1)] public bool ShowNtscBorders; + } + public enum BreakpointType { Global = 0, diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index c794afb9..e14be2c5 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -9,6 +9,7 @@ #include "../Core/Assembler.h" #include "../Core/TraceLogger.h" #include "../Core/PerformanceTracker.h" +#include "../Core/EventManager.h" #include "../Core/LuaScriptingContext.h" enum class ConsoleId; @@ -149,6 +150,9 @@ extern "C" DllExport const char* __stdcall DebugGetScriptLog(int32_t scriptId) { return GetDebugger()->GetScriptLog(scriptId); } DllExport void __stdcall DebugSetScriptTimeout(uint32_t timeout) { LuaScriptingContext::SetScriptTimeout(timeout); } - DllExport void __stdcall DebugGetDebugEvents(uint32_t* pictureBuffer, DebugEventInfo *infoArray, uint32_t &maxEventCount, bool returnPreviousFrameData) { GetDebugger()->GetDebugEvents(pictureBuffer, infoArray, maxEventCount, returnPreviousFrameData); } - DllExport uint32_t __stdcall DebugGetDebugEventCount(bool returnPreviousFrameData) { return GetDebugger()->GetDebugEventCount(returnPreviousFrameData); } + DllExport void __stdcall GetDebugEvents(DebugEventInfo *infoArray, uint32_t &maxEventCount, bool getPreviousFrameData) { GetDebugger()->GetEventManager()->GetEvents(infoArray, maxEventCount, getPreviousFrameData); } + DllExport uint32_t __stdcall GetDebugEventCount(bool getPreviousFrameData) { return GetDebugger()->GetEventManager()->GetEventCount(getPreviousFrameData); } + DllExport void __stdcall GetEventViewerOutput(uint32_t *buffer, EventViewerDisplayOptions options) { GetDebugger()->GetEventManager()->GetDisplayBuffer(buffer, options); } + DllExport void __stdcall GetEventViewerEvent(DebugEventInfo *evtInfo, int16_t scanline, uint16_t cycle, EventViewerDisplayOptions options) { *evtInfo = GetDebugger()->GetEventManager()->GetEvent(scanline, cycle, options); } + DllExport uint32_t __stdcall TakeEventSnapshot(EventViewerDisplayOptions options) { return GetDebugger()->GetEventManager()->TakeEventSnapshot(options); } };