Mesen2/Core/NES/Debugger/NesEventManager.cpp

335 lines
No EOL
10 KiB
C++

#include "pch.h"
#include "NES/Debugger/NesEventManager.h"
#include "NES/NesConsole.h"
#include "NES/BaseMapper.h"
#include "NES/NesCpu.h"
#include "NES/NesPpu.h"
#include "NES/NesDefaultVideoFilter.h"
#include "NES/NesTypes.h"
#include "NES/NesConstants.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugBreakHelper.h"
#include "Debugger/BaseEventManager.h"
NesEventManager::NesEventManager(Debugger *debugger, NesConsole* console)
{
_debugger = debugger;
_console = console;
_cpu = console->GetCpu();
_mapper = console->GetMapper();
_snapshotScanlineOffset = -1;
NesDefaultVideoFilter::GetFullPalette(_palette, console->GetNesConfig(), console->GetPpu()->GetPpuModel());
_ppuBuffer = new uint16_t[NesConstants::ScreenPixelCount];
memset(_ppuBuffer, 0, NesConstants::ScreenPixelCount * sizeof(uint16_t));
}
NesEventManager::~NesEventManager()
{
delete[] _ppuBuffer;
}
void NesEventManager::AddEvent(DebugEventType type, MemoryOperationInfo& operation, int32_t breakpointId)
{
BaseNesPpu* ppu = _console->GetPpu();
DebugEventInfo evt = {};
evt.Type = type;
evt.Operation = operation;
evt.Scanline = (int16_t)ppu->GetCurrentScanline();
evt.Cycle = (uint16_t)ppu->GetCurrentCycle();
evt.BreakpointId = breakpointId;
evt.ProgramCounter = _debugger->GetProgramCounter(CpuType::Nes, true);
evt.DmaChannel = -1;
evt.Flags = (uint32_t)EventFlags::ReadWriteOp;
uint32_t addr = operation.Address;
bool isWrite = operation.Type == MemoryOperationType::Write || operation.Type == MemoryOperationType::DmaWrite || operation.Type == MemoryOperationType::DummyWrite;
if(isWrite && (addr & 0xE000) == 0x2000) {
NesPpuState state;
ppu->GetState(state);
switch(addr & 0x07) {
case 4:
//OAM write
evt.TargetMemory.Type = MemoryOperationType::Write;
evt.TargetMemory.MemType = MemoryType::NesSpriteRam;
evt.TargetMemory.Address = state.SpriteRamAddr;
evt.TargetMemory.Value = operation.Value;
evt.Flags |= (uint32_t)EventFlags::WithTargetMemory;
break;
case 5:
case 6:
//2005/2006 PPU register writes, mark as 2nd write when needed
if(state.WriteToggle) {
evt.Flags |= (uint32_t)EventFlags::RegSecondWrite;
} else {
evt.Flags |= (uint32_t)EventFlags::RegFirstWrite;
}
break;
case 7:
//VRAM write
evt.TargetMemory.Type = MemoryOperationType::Write;
evt.TargetMemory.MemType = MemoryType::NesPpuMemory;
evt.TargetMemory.Address = state.BusAddress;
evt.TargetMemory.Value = operation.Value;
evt.Flags |= (uint32_t)EventFlags::WithTargetMemory;
break;
}
}
_debugEvents.push_back(evt);
}
void NesEventManager::AddEvent(DebugEventType type)
{
MemoryOperationInfo op = {};
if(type == DebugEventType::BgColorChange) {
op.Address = _console->GetPpu()->GetCurrentBgColor();
}
BaseNesPpu* ppu = _console->GetPpu();
DebugEventInfo evt = {};
evt.Type = type;
evt.Operation = op;
evt.Scanline = (int16_t)ppu->GetCurrentScanline();
evt.Cycle = (uint16_t)ppu->GetCurrentCycle();
evt.BreakpointId = -1;
evt.ProgramCounter = _cpu->GetState().PC;
evt.DmaChannel = -1;
_debugEvents.push_back(evt);
}
void NesEventManager::ClearFrameEvents()
{
BaseEventManager::ClearFrameEvents();
AddEvent(DebugEventType::BgColorChange);
}
DebugEventInfo NesEventManager::GetEvent(uint16_t y, uint16_t x)
{
auto lock = _lock.AcquireSafe();
int cycle = x / 2; //convert to cycle value
int scanline = ((int)y / 2) - 1; //convert to scanline value
//Search without including larger background color first
for(int i = (int)_sentEvents.size() - 1; i >= 0; i--) {
DebugEventInfo& evt = _sentEvents[i];
if(evt.Cycle == cycle && evt.Scanline == scanline) {
return evt;
}
}
//If no exact match, extend to the background color
for(int i = (int)_sentEvents.size() - 1; i >= 0; i--) {
DebugEventInfo& evt = _sentEvents[i];
if(std::abs((int)evt.Cycle - cycle) <= 1 && std::abs((int)evt.Scanline - scanline) <= 1) {
return evt;
}
}
DebugEventInfo empty = {};
empty.ProgramCounter = 0xFFFFFFFF;
return empty;
}
bool NesEventManager::ShowPreviousFrameEvents()
{
return _config.ShowPreviousFrameEvents;
}
void NesEventManager::SetConfiguration(BaseEventViewerConfig& config)
{
_config = (NesEventViewerConfig&)config;
}
EventViewerCategoryCfg NesEventManager::GetEventConfig(DebugEventInfo& evt)
{
switch(evt.Type) {
case DebugEventType::Irq: return _config.Irq;
case DebugEventType::Nmi: return _config.Nmi;
case DebugEventType::SpriteZeroHit: return _config.SpriteZeroHit;
case DebugEventType::DmcDmaRead: return _config.DmcDmaReads;
case DebugEventType::DmaRead: return _config.OtherDmaReads;
case DebugEventType::Breakpoint: return _config.MarkedBreakpoints;
case DebugEventType::Register:
uint16_t addr = (uint16_t)evt.Operation.Address;
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type == MemoryOperationType::DmaWrite || evt.Operation.Type == MemoryOperationType::DummyWrite;
if(isWrite) {
if(addr >= 0x2000 && addr <= 0x3FFF) {
switch(addr & 0x200F) {
case 0x2000: return _config.Ppu2000Write;
case 0x2001: return _config.Ppu2001Write;
case 0x2003: return _config.Ppu2003Write;
case 0x2004: return _config.Ppu2004Write;
case 0x2005: return _config.Ppu2005Write;
case 0x2006: return _config.Ppu2006Write;
case 0x2007: return _config.Ppu2007Write;
}
} else if(addr >= 0x4020 && _mapper->IsWriteRegister(addr)) {
return _config.MapperRegisterWrites;
} else if((addr >= 0x4000 && addr <= 0x4015) || addr == 0x4017 || addr == 0x401A) {
return _config.ApuRegisterWrites;
} else if(addr == 0x4016) {
return _config.ControlRegisterWrites;
}
} else {
if(addr >= 0x2000 && addr <= 0x3FFF) {
switch(addr & 0x200F) {
case 0x2002: return _config.Ppu2002Read;
case 0x2004: return _config.Ppu2004Read;
case 0x2007: return _config.Ppu2007Read;
}
} else if(addr >= 0x4020 && _mapper->IsReadRegister(addr)) {
return _config.MapperRegisterReads;
} else if((addr >= 0x4000 && addr <= 0x4015) || (addr >= 0x4018 && addr <= 0x401A)) {
return _config.ApuRegisterReads;
} else if(addr == 0x4016 || addr == 0x4017) {
return _config.ControlRegisterReads;
}
}
return {};
}
return {};
}
void NesEventManager::ConvertScanlineCycleToRowColumn(int32_t& x, int32_t& y)
{
y = (y + 1) * 2;
x = x * 2;
}
uint32_t NesEventManager::TakeEventSnapshot(bool forAutoRefresh)
{
BaseNesPpu* ppu = _console->GetPpu();
DebugBreakHelper breakHelper(_debugger);
auto lock = _lock.AcquireSafe();
uint16_t cycle = _console->GetPpu()->GetCurrentCycle();
uint16_t scanline = ppu->GetCurrentScanline() + 1;
if(scanline >= 240 || (scanline == 0 && cycle == 0)) {
memcpy(_ppuBuffer, ppu->GetScreenBuffer(false, true), NesConstants::ScreenPixelCount * sizeof(uint16_t));
} else {
uint32_t offset = (NesConstants::ScreenWidth * scanline);
memcpy(_ppuBuffer, ppu->GetScreenBuffer(false, true), offset * sizeof(uint16_t));
memcpy(_ppuBuffer + offset, ppu->GetScreenBuffer(true) + offset, (NesConstants::ScreenPixelCount - offset) * sizeof(uint16_t));
}
_snapshotCurrentFrame = _debugEvents;
_snapshotPrevFrame = _prevDebugEvents;
_snapshotScanline = scanline;
_snapshotCycle = cycle;
_forAutoRefresh = forAutoRefresh;
_scanlineCount = ppu->GetScanlineCount();
return _scanlineCount;
}
FrameInfo NesEventManager::GetDisplayBufferSize()
{
FrameInfo size;
size.Width = NesConstants::CyclesPerLine * 2;
size.Height = _scanlineCount * 2;
return size;
}
void NesEventManager::DrawScreen(uint32_t *buffer)
{
uint16_t *src = _ppuBuffer;
for(uint32_t y = 0, len = NesConstants::ScreenHeight*2; y < len; y++) {
int rowOffset = (y + 2) * NesConstants::CyclesPerLine * 2;
for(uint32_t x = 0; x < NesConstants::ScreenWidth *2; x++) {
int srcOffset = ((y >> 1) << 8) | (x >> 1);
buffer[rowOffset + x + 1 * 2] = _palette[src[srcOffset]];
}
}
if(_config.ShowNtscBorders) {
DrawNtscBorders(buffer);
}
}
void NesEventManager::DrawPixel(uint32_t *buffer, int32_t x, uint32_t y, uint32_t color)
{
if(x < 0) {
x += NesConstants::CyclesPerLine;
y--;
} else if(x >= NesConstants::CyclesPerLine) {
x -= NesConstants::CyclesPerLine;
y++;
}
buffer[y * NesConstants::CyclesPerLine * 4 + x * 2] = color;
buffer[y * NesConstants::CyclesPerLine * 4 + x * 2 + 1] = color;
buffer[y * NesConstants::CyclesPerLine * 4 + NesConstants::CyclesPerLine * 2 + x * 2] = color;
buffer[y * NesConstants::CyclesPerLine * 4 + NesConstants::CyclesPerLine * 2 + x * 2 + 1] = color;
}
void NesEventManager::ProcessNtscBorderColorEvents(vector<DebugEventInfo>& events, vector<uint16_t>& bgColor, uint32_t& currentPos, uint16_t& currentColor)
{
for(DebugEventInfo& evt : events) {
if(evt.Type == DebugEventType::BgColorChange) {
uint32_t pos = ((evt.Scanline + 1) * NesConstants::CyclesPerLine) + evt.Cycle;
if(evt.Scanline >= 242) {
break;
}
if(pos >= currentPos) {
std::fill(bgColor.begin() + currentPos, bgColor.begin() + pos, currentColor);
currentPos = pos;
currentColor = evt.Operation.Address;
}
}
}
}
void NesEventManager::DrawNtscBorders(uint32_t *buffer)
{
//Generate array of bg color for all pixels on the screen
uint32_t currentPos = 0;
uint16_t currentColor = 0;
vector<uint16_t> bgColor;
bgColor.resize(NesConstants::CyclesPerLine * 243);
ProcessNtscBorderColorEvents(_snapshotCurrentFrame, bgColor, currentPos, currentColor);
if(!_forAutoRefresh && _snapshotScanline < 242) {
uint32_t snapshotPos = (_snapshotScanline * NesConstants::CyclesPerLine) + _snapshotCycle;
std::fill(bgColor.begin() + currentPos, bgColor.begin() + snapshotPos, currentColor);
currentPos = snapshotPos;
ProcessNtscBorderColorEvents(_snapshotPrevFrame, bgColor, currentPos, currentColor);
}
std::fill(bgColor.begin() + currentPos, bgColor.end(), currentColor);
for(uint32_t y = 1; y < 241; y++) {
//Pulse
uint32_t basePos = y * NesConstants::CyclesPerLine;
DrawPixel(buffer, -15, y, _palette[bgColor[basePos - 16] & 0x1F0]);
//Left border
for(int32_t x = 0; x < 15; x++) {
DrawPixel(buffer, -x, y, _palette[bgColor[basePos - x]]);
}
//Right border
for(int32_t x = 0; x < 11; x++) {
DrawPixel(buffer, 257+x, y, _palette[bgColor[basePos + 257 + x]]);
}
}
for(uint32_t y = 240; y < 242; y++) {
//Bottom border
uint32_t basePos = y * NesConstants::CyclesPerLine;
DrawPixel(buffer, 326, y, _palette[bgColor[basePos + 326] & 0x1F0]);
for(int32_t x = 0; x < 282; x++) {
DrawPixel(buffer, 327 + x, y, _palette[bgColor[basePos + 327 + x]]);
}
}
}