#include "stdafx.h" #include "GbEventManager.h" #include "DebugTypes.h" #include "GbCpu.h" #include "GbPpu.h" #include "Debugger.h" #include "DebugBreakHelper.h" #include "DefaultVideoFilter.h" #include "Gameboy.h" #include "Console.h" #include "BaseCartridge.h" #include "BaseEventManager.h" GbEventManager::GbEventManager(Debugger* debugger, GbCpu* cpu, GbPpu* ppu) { _debugger = debugger; _cpu = cpu; _ppu = ppu; _ppuBuffer = new uint16_t[456*GbEventManager::ScreenHeight]; memset(_ppuBuffer, 0, 456*GbEventManager::ScreenHeight * sizeof(uint16_t)); } GbEventManager::~GbEventManager() { delete[] _ppuBuffer; } void GbEventManager::AddEvent(DebugEventType type, MemoryOperationInfo& operation, int32_t breakpointId) { DebugEventInfo evt = {}; evt.Type = type; evt.Operation = operation; evt.Scanline = _ppu->GetState().Scanline; evt.Cycle = _ppu->GetState().Cycle; evt.BreakpointId = breakpointId; evt.DmaChannel = -1; evt.ProgramCounter = _cpu->GetState().PC; _debugEvents.push_back(evt); } void GbEventManager::AddEvent(DebugEventType type) { DebugEventInfo evt = {}; evt.Type = type; evt.Scanline = _ppu->GetState().Scanline; evt.Cycle = _ppu->GetState().Cycle; evt.BreakpointId = -1; evt.DmaChannel = -1; evt.ProgramCounter = _cpu->GetState().PC; _debugEvents.push_back(evt); } void GbEventManager::GetEvents(DebugEventInfo* eventArray, uint32_t& maxEventCount) { auto lock = _lock.AcquireSafe(); uint32_t eventCount = std::min(maxEventCount, (uint32_t)_sentEvents.size()); memcpy(eventArray, _sentEvents.data(), eventCount * sizeof(DebugEventInfo)); maxEventCount = eventCount; } DebugEventInfo GbEventManager::GetEvent(uint16_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 GbEventManager::GetEventCount(EventViewerDisplayOptions options) { auto lock = _lock.AcquireSafe(); FilterEvents(options); return (uint32_t)_sentEvents.size(); } void GbEventManager::ClearFrameEvents() { _prevDebugEvents = _debugEvents; _debugEvents.clear(); } void GbEventManager::FilterEvents(EventViewerDisplayOptions& options) { auto lock = _lock.AcquireSafe(); _sentEvents.clear(); vector events = _snapshot; if(options.ShowPreviousFrameEvents && _snapshotScanline != 0) { uint32_t key = (_snapshotScanline << 16) + _snapshotCycle; for(DebugEventInfo& evt : _prevDebugEvents) { uint32_t evtKey = (evt.Scanline << 16) + evt.Cycle; if(evtKey > key) { events.push_back(evt); } } } for(DebugEventInfo& evt : events) { bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type == MemoryOperationType::DmaWrite; bool showEvent = false; switch(evt.Type) { default: break; case DebugEventType::Breakpoint: showEvent = options.ShowMarkedBreakpoints; break; case DebugEventType::Irq: showEvent = options.ShowIrq; break; case DebugEventType::Register: uint16_t reg = evt.Operation.Address & 0xFFFF; if(reg >= 0xFE00 && reg <= 0xFE9F) { showEvent = isWrite ? options.ShowPpuRegisterOamWrites : options.ShowPpuRegisterReads; } else if(reg >= 0xFF42 && reg <= 0xFF43) { showEvent = isWrite ? options.ShowPpuRegisterBgScrollWrites : options.ShowPpuRegisterReads; } else if(reg >= 0x8000 && reg <= 0x9FFF) { showEvent = isWrite ? options.ShowPpuRegisterVramWrites : options.ShowPpuRegisterReads; } else if((reg >= 0xFF47 && reg <= 0xFF49) || (reg >= 0xFF68 && reg <= 0xFF6B)) { showEvent = isWrite ? options.ShowPpuRegisterCgramWrites : options.ShowPpuRegisterReads; } else if(reg >= 0xFF4A && reg <= 0xFF4B) { showEvent = isWrite ? options.ShowPpuRegisterWindowWrites : options.ShowPpuRegisterReads; } else if(reg >= 0xFF40 && reg <= 0xFF70) { showEvent = isWrite ? options.ShowPpuRegisterOtherWrites : options.ShowPpuRegisterReads; } else if(reg >= 0xFF10 && reg <= 0xFF3F) { showEvent = isWrite ? options.ShowApuRegisterWrites : options.ShowApuRegisterReads; } else { showEvent = isWrite ? options.ShowCpuRegisterWrites : options.ShowCpuRegisterReads; } break; } if(showEvent) { _sentEvents.push_back(evt); } } } void GbEventManager::DrawEvent(DebugEventInfo& evt, bool drawBackground, uint32_t* buffer, EventViewerDisplayOptions& options) { bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type == MemoryOperationType::DmaWrite; uint32_t color = 0; uint32_t ppuReadColor = options.PpuRegisterReadColor; switch(evt.Type) { default: break; case DebugEventType::Breakpoint: color = options.BreakpointColor; break; case DebugEventType::Irq: color = options.IrqColor; break; case DebugEventType::Register: uint16_t reg = evt.Operation.Address & 0xFFFF; if(reg >= 0xFE00 && reg <= 0xFE9F) { color = isWrite ? options.PpuRegisterWriteOamColor : ppuReadColor; } else if(reg >= 0xFF42 && reg <= 0xFF43) { color = isWrite ? options.PpuRegisterWriteBgScrollColor : ppuReadColor; } else if(reg >= 0x8000 && reg <= 0x9FFF) { color = isWrite ? options.PpuRegisterWriteVramColor : ppuReadColor; } else if((reg >= 0xFF47 && reg <= 0xFF49) || (reg >= 0xFF68 && reg <= 0xFF6B)) { color = isWrite ? options.PpuRegisterWriteCgramColor : ppuReadColor; } else if(reg >= 0xFF4A && reg <= 0xFF4B) { color = isWrite ? options.PpuRegisterWriteWindowColor : ppuReadColor; } else if(reg >= 0xFF40 && reg <= 0xFF70) { color = isWrite ? options.PpuRegisterWriteOtherColor : ppuReadColor; } else if(reg >= 0xFF10 && reg <= 0xFF3F) { color = isWrite ? options.ApuRegisterWriteColor : options.ApuRegisterReadColor; } else { color = isWrite ? options.CpuRegisterWriteColor : options.CpuRegisterReadColor; } break; } if(drawBackground) { color = 0xFF000000 | ((color >> 1) & 0x7F7F7F); } else { 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 * 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) * GbEventManager::ScanlineWidth + x + j; if(pos < 0 || pos >= GbEventManager::ScanlineWidth * (int)_scanlineCount * 2) { continue; } buffer[pos] = color; } } } uint32_t GbEventManager::TakeEventSnapshot(EventViewerDisplayOptions options) { DebugBreakHelper breakHelper(_debugger); auto lock = _lock.AcquireSafe(); _snapshot.clear(); uint16_t cycle = _ppu->GetState().Cycle; uint16_t scanline = _ppu->GetState().Scanline; if(scanline >= GbEventManager::VBlankScanline || scanline == 0) { memcpy(_ppuBuffer, _ppu->GetEventViewerBuffer(), 456 * GbEventManager::ScreenHeight * sizeof(uint16_t)); } else { uint32_t size = 456 * GbEventManager::ScreenHeight; uint32_t offset = 456 * scanline; memcpy(_ppuBuffer, _ppu->GetEventViewerBuffer(), offset * sizeof(uint16_t)); memcpy(_ppuBuffer + offset, _ppu->GetPreviousEventViewerBuffer() + offset, (size - offset) * sizeof(uint16_t)); } _snapshot = _debugEvents; _snapshotScanline = scanline; _snapshotCycle = cycle; _scanlineCount = GbEventManager::ScreenHeight; return _scanlineCount; } void GbEventManager::GetDisplayBuffer(uint32_t* buffer, uint32_t bufferSize, EventViewerDisplayOptions options) { auto lock = _lock.AcquireSafe(); if(_snapshotScanline < 0 || bufferSize < _scanlineCount * 2 * GbEventManager::ScanlineWidth * 4) { return; } uint16_t *src = _ppuBuffer; for(uint32_t y = 0, len = GbEventManager::ScreenHeight*2; y < len; y++) { for(uint32_t x = 0; x < GbEventManager::ScanlineWidth; x++) { int srcOffset = (y >> 1) * 456 + (x >> 1); buffer[y*GbEventManager::ScanlineWidth + x] = DefaultVideoFilter::ToArgb(src[srcOffset]); } } constexpr uint32_t currentScanlineColor = 0xFFFFFF55; uint32_t scanlineOffset = _snapshotScanline * 2 * GbEventManager::ScanlineWidth; if(_snapshotScanline != 0) { for(int i = 0; i < GbEventManager::ScanlineWidth; i++) { buffer[scanlineOffset + i] = currentScanlineColor; buffer[scanlineOffset + GbEventManager::ScanlineWidth + i] = currentScanlineColor; } } FilterEvents(options); for(DebugEventInfo &evt : _sentEvents) { DrawEvent(evt, true, buffer, options); } for(DebugEventInfo &evt : _sentEvents) { DrawEvent(evt, false, buffer, options); } }