Implemented ignored reads on PPU $2007 when read 2 cpu cycles in a row (i.e with a dummy read)

Changed DMC stall-time read behavior to read only every other cycle
Fixes tests: "dma_2007_read", "dma_4016_read", "double_2007_read"
This commit is contained in:
Souryo 2016-01-03 17:20:18 -05:00
parent a6188aef7f
commit c79a0326ed
3 changed files with 25 additions and 10 deletions

View File

@ -164,8 +164,9 @@ private:
uint8_t MemoryRead(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read) { uint8_t MemoryRead(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read) {
while(_dmcDmaRunning) { while(_dmcDmaRunning) {
//Stall CPU until we can process a DMC read //Stall CPU until we can process a DMC read
if((addr != 0x4016 && addr != 0x4017) || _dmcCounter == 1) { if((addr != 0x4016 && addr != 0x4017 && (_cycleCount & 0x01)) || _dmcCounter == 1) {
//While the CPU is stalled, reads are performed on the current address //While the CPU is stalled, reads are performed on the current address
//Reads are only performed every other cycle? This fixes "dma_2007_read" test
//This behavior causes the $4016/7 data corruption when a DMC is running. //This behavior causes the $4016/7 data corruption when a DMC is running.
//When reading $4016/7, only the last read counts (because this only occurs to low-to-high transitions, i.e once in this case) //When reading $4016/7, only the last read counts (because this only occurs to low-to-high transitions, i.e once in this case)
_memoryManager->Read(addr); _memoryManager->Read(addr);

View File

@ -31,6 +31,7 @@ PPU::~PPU()
void PPU::Reset() void PPU::Reset()
{ {
_ignoreVramRead = 0;
_openBus = 0; _openBus = 0;
memset(_openBusDecayStamp, 0, sizeof(_openBusDecayStamp)); memset(_openBusDecayStamp, 0, sizeof(_openBusDecayStamp));
@ -146,17 +147,24 @@ uint8_t PPU::ReadRAM(uint16_t addr)
break; break;
case PPURegisters::VideoMemoryData: case PPURegisters::VideoMemoryData:
returnValue = _memoryReadBuffer; if(_ignoreVramRead) {
_memoryReadBuffer = _memoryManager->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read); //2 reads to $2007 in quick succession (2 consecutive CPU cycles) causes the 2nd read to be ignored (normally depends on PPU/CPU timing, but this is the simplest solution)
//Return open bus in this case? (which will match the last value read)
if(_state.VideoRamAddr >= 0x3F00) { openBusMask = 0xFF;
returnValue = ReadPaletteRAM(_state.VideoRamAddr) | (_openBus & 0xC0);
openBusMask = 0xC0;
} else { } else {
openBusMask = 0x00; returnValue = _memoryReadBuffer;
} _memoryReadBuffer = _memoryManager->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read);
UpdateVideoRamAddr(); if(_state.VideoRamAddr >= 0x3F00) {
returnValue = ReadPaletteRAM(_state.VideoRamAddr) | (_openBus & 0xC0);
openBusMask = 0xC0;
} else {
openBusMask = 0x00;
}
UpdateVideoRamAddr();
_ignoreVramRead = 2;
}
break; break;
default: default:
@ -782,6 +790,9 @@ void PPU::ExecStatic()
PPU::Instance->Exec(); PPU::Instance->Exec();
PPU::Instance->Exec(); PPU::Instance->Exec();
PPU::Instance->Exec(); PPU::Instance->Exec();
if(PPU::Instance->_ignoreVramRead) {
PPU::Instance->_ignoreVramRead--;
}
if(PPU::Instance->_nesModel == NesModel::PAL && CPU::GetCycleCount() % 5 == 0) { if(PPU::Instance->_nesModel == NesModel::PAL && CPU::GetCycleCount() % 5 == 0) {
//PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks //PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks
PPU::Instance->Exec(); PPU::Instance->Exec();
@ -873,6 +884,8 @@ void PPU::StreamState(bool saving)
Stream<uint8_t>(_openBus); Stream<uint8_t>(_openBus);
StreamArray<int32_t>(_openBusDecayStamp, 8); StreamArray<int32_t>(_openBusDecayStamp, 8);
Stream<uint32_t>(_ignoreVramRead);
if(!saving) { if(!saving) {
SetNesModel(_nesModel); SetNesModel(_nesModel);
} }

View File

@ -133,6 +133,7 @@ class PPU : public IMemoryHandler, public Snapshotable
uint8_t _openBus = 0; uint8_t _openBus = 0;
int32_t _openBusDecayStamp[8]; int32_t _openBusDecayStamp[8];
uint32_t _ignoreVramRead = 0;
uint16_t _spriteDmaCounter = 0; uint16_t _spriteDmaCounter = 0;
uint16_t _spriteDmaAddr = 0; uint16_t _spriteDmaAddr = 0;