Debugger: Event Viewer - Added option to show NTSC borders

+ Ported most of Mesen-S' event viewer code to allow this to work
This commit is contained in:
Sour 2019-11-19 22:28:45 -05:00
parent 43811ae7ac
commit 8508b211cc
27 changed files with 1299 additions and 477 deletions

View file

@ -48,6 +48,7 @@
#include "NotificationManager.h"
#include "HistoryViewer.h"
#include "ConsolePauseHelper.h"
#include "EventManager.h"
#include "PgoUtilities.h"
Console::Console(shared_ptr<Console> 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

View file

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

View file

@ -537,6 +537,7 @@
<ClInclude Include="DummyCpu.h" />
<ClInclude Include="Eeprom24C01.h" />
<ClInclude Include="Eeprom24C02.h" />
<ClInclude Include="EventManager.h" />
<ClInclude Include="FaridSlrom.h" />
<ClInclude Include="FaridUnrom.h" />
<ClInclude Include="FdsSystemActionManager.h" />
@ -970,6 +971,7 @@
<ClCompile Include="BatteryManager.cpp" />
<ClCompile Include="DebugHud.cpp" />
<ClCompile Include="DrawRectangleCommand.h" />
<ClCompile Include="EventManager.cpp" />
<ClCompile Include="FceuxMovie.cpp" />
<ClCompile Include="FdsLoader.cpp" />
<ClCompile Include="HdAudioDevice.cpp" />

View file

@ -1487,6 +1487,9 @@
<ClInclude Include="BaseEeprom24C0X.h">
<Filter>Nes\Mappers\Bandai</Filter>
</ClInclude>
<ClInclude Include="EventManager.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -1786,5 +1789,8 @@
<ClCompile Include="PerformanceTracker.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="EventManager.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -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> console, shared_ptr<CPU> 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<PerformanceTracker> Debugger::GetPerformanceTracker()
return _performanceTracker;
}
shared_ptr<EventManager> 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<DebugEventInfo> &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);

View file

@ -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> _traceLogger;
shared_ptr<Profiler> _profiler;
shared_ptr<PerformanceTracker> _performanceTracker;
shared_ptr<EventManager> _eventManager;
unique_ptr<CodeRunner> _codeRunner;
shared_ptr<Console> _console;
@ -140,9 +142,6 @@ private:
uint32_t _inputOverride[4];
vector<DebugEventInfo> _prevDebugEvents;
vector<DebugEventInfo> _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<MemoryDumper> GetMemoryDumper();
shared_ptr<MemoryAccessCounter> GetMemoryAccessCounter();
shared_ptr<PerformanceTracker> GetPerformanceTracker();
shared_ptr<EventManager> 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);
};

View file

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

284
Core/EventManager.cpp Normal file
View file

@ -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<DebugEventInfo> &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<uint32_t>((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<uint8_t> 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]]);
}
}
}

80
Core/EventManager.h Normal file
View file

@ -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<DebugEventInfo> _debugEvents;
vector<DebugEventInfo> _prevDebugEvents;
vector<DebugEventInfo> _sentEvents;
vector<DebugEventInfo> _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;
};

View file

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

View file

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

View file

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

View file

@ -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")]

View file

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

View file

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

View file

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

View file

@ -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<int, List<DebugEventInfo>> _debugEventsByCycle = new Dictionary<int, List<DebugEventInfo>>();
private List<DebugEventInfo> _debugEvents = new List<DebugEventInfo>();
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<int, List<DebugEventInfo>>();
List<DebugEventInfo> eventList = new List<DebugEventInfo>(eventInfoArray.Length+prevEventInfoArray.Length);
Action<DebugEventInfo> addEvent = (DebugEventInfo eventInfo) => {
int frameCycle = (eventInfo.Scanline + 1) * 341 + eventInfo.Cycle;
List<DebugEventInfo> infoList;
if(!debugEvents.TryGetValue(frameCycle, out infoList)) {
infoList = new List<DebugEventInfo>();
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<List<Color>> colors = new List<List<Color>> {
null, //None
new List<Color> { d.EventViewerPpuRegisterWrite2000Color, d.EventViewerPpuRegisterWrite2001Color, Color.Black, d.EventViewerPpuRegisterWrite2003Color, d.EventViewerPpuRegisterWrite2004Color, d.EventViewerPpuRegisterWrite2005Color, d.EventViewerPpuRegisterWrite2006Color, d.EventViewerPpuRegisterWrite2007Color }, //PpuRegisterWrite
new List<Color> { Color.Black, Color.Black, d.EventViewerPpuRegisterRead2002Color, Color.Black, d.EventViewerPpuRegisterRead2004Color, Color.Black, Color.Black, d.EventViewerPpuRegisterRead2007Color }, //PpuRegisterRead
new List<Color> { d.EventViewerMapperRegisterWriteColor }, //MapperRegisterWrite
new List<Color> { d.EventViewerMapperRegisterReadColor }, //MapperRegisterRead
new List<Color> { d.EventViewerNmiColor }, //Nmi
new List<Color> { d.EventViewerIrqColor }, //Irq
new List<Color> { d.EventViewerSpriteZeroHitColor }, //SpriteZeroHit
new List<Color> { d.EventViewerBreakpointColor }, //Breakpoint
new List<Color> { 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<List<Color>> colors)
private EventViewerDisplayOptions GetInteropOptions()
{
var enumValues = Enum.GetValues(typeof(DebugEventType));
IGrouping<int, DebugEventInfo>[][] groupedEvents = new IGrouping<int, DebugEventInfo>[enumValues.Length][];
foreach(DebugEventType eventType in Enum.GetValues(typeof(DebugEventType))) {
List<Color> 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<List<Color>> colors, bool drawFg, IGrouping<int, DebugEventInfo>[][] 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<Rectangle> rects = new List<Rectangle>(eventGroup.Count());
foreach(DebugEventInfo evt in eventGroup) {
rects.Add(new Rectangle(evt.Cycle * 2 - offset, evt.Scanline * 2 - offset + 2, size, size));
}
List<Color> 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<DebugEventInfo> eventList;
if(_debugEventsByCycle.TryGetValue(key, out eventList)) {
foreach(DebugEventInfo debugEvent in eventList) {
if(ShowEvent(debugEvent)) {
if(key != _lastKey) {
ResetTooltip();
Dictionary<string, string> values = new Dictionary<string, string>() {
{ "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<Breakpoint> 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<string, string> values = new Dictionary<string, string>() {
{ "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<Breakpoint> 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();
}
}
}

View file

@ -0,0 +1,73 @@
namespace Mesen.GUI.Debugger
{
partial class ctrlImagePanel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

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

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

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

View file

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

View file

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

View file

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

View file

@ -297,6 +297,9 @@
<Compile Include="Controls\ctrlNsfPlayer.Designer.cs">
<DependentUpon>ctrlNsfPlayer.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ctrlPanel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\ctrlRecentGames.cs">
<SubType>UserControl</SubType>
</Compile>
@ -445,6 +448,15 @@
<Compile Include="Debugger\Controls\ctrlFlagStatus.Designer.cs">
<DependentUpon>ctrlFlagStatus.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlImagePanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Debugger\Controls\ctrlImagePanel.Designer.cs">
<DependentUpon>ctrlImagePanel.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlImageViewer.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Debugger\Controls\ctrlLabelList.cs">
<SubType>UserControl</SubType>
</Compile>
@ -1383,6 +1395,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlEventViewerPpuView.resx">
<DependentUpon>ctrlEventViewerPpuView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlImagePanel.resx">
<DependentUpon>ctrlImagePanel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlScanlineCycleSelect.resx">
<DependentUpon>ctrlScanlineCycleSelect.cs</DependentUpon>
</EmbeddedResource>

View file

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

View file

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