diff --git a/Core/CPU.h b/Core/CPU.h index 59aeb45e..e8d5af9b 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -152,7 +152,7 @@ private: uint8_t SP() { return _state.SP; } void SetSP(uint8_t value) { _state.SP = value; } uint8_t PS() { return _state.PS; } - void SetPS(uint8_t value) { _state.PS = (value & 0xDF) | PSFlags::Reserved; } + void SetPS(uint8_t value) { _state.PS = (value & 0xCF) | PSFlags::Reserved; } uint16_t PC() { return _state.PC; } void SetPC(uint16_t value) { _state.PC = value; } @@ -609,12 +609,20 @@ private: void BRK() { Push((uint16_t)(PC() + 1)); - - uint8_t flags = PS() | PSFlags::Break; - Push((uint8_t)flags); - SetFlags(PSFlags::Interrupt); - SetPC(MemoryReadWord(CPU::IRQVector)); + uint8_t flags = PS() | PSFlags::Break; + if(CPU::NMIFlag) { + Push((uint8_t)flags); + SetFlags(PSFlags::Interrupt); + + SetPC(MemoryReadWord(CPU::NMIVector)); + } else { + + Push((uint8_t)flags); + SetFlags(PSFlags::Interrupt); + + SetPC(MemoryReadWord(CPU::IRQVector)); + } } void NMI() { @@ -626,9 +634,17 @@ private: void IRQ() { Push((uint16_t)(PC())); - Push((uint8_t)PS()); - SetFlags(PSFlags::Interrupt); - SetPC(MemoryReadWord(CPU::IRQVector)); + + if(CPU::NMIFlag) { + Push((uint8_t)PS()); + SetFlags(PSFlags::Interrupt); + + SetPC(MemoryReadWord(CPU::NMIVector)); + } else { + Push((uint8_t)PS()); + SetFlags(PSFlags::Interrupt); + SetPC(MemoryReadWord(CPU::IRQVector)); + } } @@ -652,6 +668,7 @@ public: CPU::CycleCount += cycles; } static void SetNMIFlag() { CPU::NMIFlag = true; } + static void ClearNMIFlag() { CPU::NMIFlag = false; } static void SetIRQSource(IRQSource source) { CPU::IRQFlag |= (int)source; diff --git a/Core/MemoryManager.cpp b/Core/MemoryManager.cpp index a0a2c6a4..b33f56b9 100644 --- a/Core/MemoryManager.cpp +++ b/Core/MemoryManager.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "MemoryManager.h" +#include "PPU.h" MemoryManager::MemoryManager(shared_ptr mapper) { @@ -103,21 +104,25 @@ void MemoryManager::RegisterIODevice(IMemoryHandler *handler) uint8_t MemoryManager::Read(uint16_t addr) { + uint8_t value; + PPU::ExecStatic(3); if(addr <= 0x1FFF) { - return _internalRAM[addr & 0x07FF]; + value = _internalRAM[addr & 0x07FF]; } else if(addr <= 0x401F) { - return ReadRegister(addr); + value = ReadRegister(addr); } else if(addr <= 0x5FFF) { - return _expansionRAM[addr & 0x1FFF]; + value = _expansionRAM[addr & 0x1FFF]; } else if(addr <= 0x7FFF) { - return _SRAM[addr & 0x1FFF]; + value = _SRAM[addr & 0x1FFF]; } else { - return ReadRegister(addr); + value = ReadRegister(addr); } + return value; } void MemoryManager::Write(uint16_t addr, uint8_t value) { + PPU::ExecStatic(3); if(addr <= 0x1FFF) { _internalRAM[addr & 0x07FF] = value; } else if(addr <= 0x401F) { diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 50e90a1c..bdc30c53 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -211,10 +211,14 @@ void PPU::SetControlRegister(uint8_t value) _flags.BackgroundPatternAddr = ((_state.Control & 0x10) == 0x10) ? 0x1000 : 0x0000; _flags.LargeSprites = (_state.Control & 0x20) == 0x20; + //"By toggling NMI_output ($2000 bit 7) during vertical blank without reading $2002, a program can cause /NMI to be pulled low multiple times, causing multiple NMIs to be generated." bool originalVBlank = _flags.VBlank; _flags.VBlank = (_state.Control & 0x80) == 0x80; + if(!originalVBlank && _flags.VBlank && _statusFlags.VerticalBlank) { CPU::SetNMIFlag(); + } else if(_scanline == 241 && _cycle < 3 && !_flags.VBlank) { + CPU::ClearNMIFlag(); } } @@ -238,8 +242,14 @@ void PPU::UpdateStatusFlag() ((uint8_t)_statusFlags.VerticalBlank << 7); _statusFlags.VerticalBlank = false; - if(_scanline == 241 && _cycle == 0) { - _doNotSetVBFlag = true; + if(_scanline == 241) { + if(_cycle < 3) { + CPU::ClearNMIFlag(); + + if(_cycle == 0) { + _doNotSetVBFlag = true; + } + } } } @@ -473,7 +483,7 @@ void PPU::ProcessPrerenderScanline() { ProcessPreVBlankScanline(); - if(_cycle == 1) { + if(_cycle == 0) { _statusFlags.SpriteOverflow = false; _statusFlags.Sprite0Hit = false; _statusFlags.VerticalBlank = false; @@ -486,8 +496,8 @@ void PPU::ProcessPrerenderScanline() //copy vertical scrolling value from t _state.VideoRamAddr = (_state.VideoRamAddr & ~0x7BE0) | (_state.TmpVideoRamAddr & 0x7BE0); } - } else if(_cycle == 339 && _flags.BackgroundEnabled && (_frameCount % 2 == 1)) { - //Skip a cycle for odd frames, if background drawing is enabled + } else if(_cycle == 339 && IsRenderingEnabled() && (_frameCount % 2 == 1)) { + //"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340) _cycle = -1; _scanline = 0; } else if(_cycle == 321 || _cycle == 329) { @@ -616,7 +626,7 @@ void PPU::CopyOAMData() void PPU::BeginVBlank() { - if(_cycle == 1) { + if(_cycle == 0) { if(!_doNotSetVBFlag) { _statusFlags.VerticalBlank = true; if(_flags.VBlank) { @@ -634,10 +644,15 @@ void PPU::EndVBlank() } } -void PPU::Exec() +void PPU::Exec(uint32_t extraCycles) { uint64_t equivalentCycleCount = CPU::GetCycleCount() * 3; - uint32_t gap = (uint32_t)(equivalentCycleCount - _cycleCount); + int32_t gap = (int32_t)((int64_t)equivalentCycleCount - (int64_t)_cycleCount); + if(gap < 0) { + gap = 0; + } + gap += extraCycles; + _cycleCount += gap; while(gap > 0) { if(_scanline == -1) { diff --git a/Core/PPU.h b/Core/PPU.h index f4ae8c90..e0b5d50a 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -88,7 +88,7 @@ class PPU : public IMemoryHandler, public Snapshotable uint32_t _frameCount = 0; uint64_t _cycleCount = 0; uint8_t _memoryReadBuffer = 0; - + uint8_t _paletteRAM[0x100]; uint8_t _spriteRAM[0x100]; @@ -178,7 +178,7 @@ class PPU : public IMemoryHandler, public Snapshotable uint8_t ReadRAM(uint16_t addr); void WriteRAM(uint16_t addr, uint8_t value); - void Exec(); + void Exec(uint32_t extraCycles = 0); static void RegisterVideoDevice(IVideoDevice *videoDevice) { @@ -209,4 +209,9 @@ class PPU : public IMemoryHandler, public Snapshotable { return PPU::Instance->_scanline; } + + static void ExecStatic(uint32_t cycles) + { + PPU::Instance->Exec(cycles); + } };