Improved timing, passes some more VBL/NMI tests

This commit is contained in:
Souryo 2014-06-27 12:18:07 -04:00
parent 616546ae5b
commit 6e1742393d
4 changed files with 66 additions and 24 deletions

View file

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

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "MemoryManager.h"
#include "PPU.h"
MemoryManager::MemoryManager(shared_ptr<BaseMapper> 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) {

View file

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

View file

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