CPU/PPU: Improved timings

This commit is contained in:
Sour 2019-03-08 10:26:54 -05:00
parent 46663e8e53
commit 7211eece7c
6 changed files with 49 additions and 43 deletions

View file

@ -142,7 +142,7 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile)
_memoryManager->Initialize(shared_from_this()); _memoryManager->Initialize(shared_from_this());
_cpu.reset(new Cpu(this)); _cpu.reset(new Cpu(this));
_memoryManager->IncrementMasterClockValue<162>(); _memoryManager->IncrementMasterClockValue<170>();
//if(_debugger) { //if(_debugger) {
//Reset debugger if it was running before //Reset debugger if it was running before

View file

@ -12,6 +12,7 @@ Cpu::Cpu(Console *console)
_state = {}; _state = {};
_state.PC = ReadDataWord(Cpu::ResetVector); _state.PC = ReadDataWord(Cpu::ResetVector);
_state.SP = 0x1FF; _state.SP = 0x1FF;
_state.SP = ProcFlags::IrqDisable;
_state.EmulationMode = true; _state.EmulationMode = true;
_nmiFlag = false; _nmiFlag = false;
_prevNmiFlag = false; _prevNmiFlag = false;
@ -346,6 +347,7 @@ uint8_t Cpu::GetOpCode()
void Cpu::Idle() void Cpu::Idle()
{ {
_state.CycleCount++;
#ifndef DUMMYCPU #ifndef DUMMYCPU
_prevNmiFlag = _nmiFlag; _prevNmiFlag = _nmiFlag;
_prevIrqSource = _irqSource; _prevIrqSource = _irqSource;

View file

@ -191,23 +191,24 @@ void DmaController::Write(uint16_t addr, uint8_t value)
switch(addr) { switch(addr) {
case 0x420B: { case 0x420B: {
//MDMAEN - DMA Enable //MDMAEN - DMA Enable
if(value) {
//"after the pause, wait 2-8 master cycles to reach a whole multiple of 8 master cycles since reset"
uint8_t clocksToWait = 8 - (_memoryManager->GetMasterClock() % 8);
_memoryManager->IncrementMasterClockValue(clocksToWait ? clocksToWait : 8);
//"after the pause, wait 2-8 master cycles to reach a whole multiple of 8 master cycles since reset" //"and an extra 8 master cycles overhead for the whole thing"
uint8_t clocksToWait = 8 - (_memoryManager->GetMasterClock() % 8); _memoryManager->IncrementMasterClockValue<8>();
_memoryManager->IncrementMasterClockValue(clocksToWait ? clocksToWait : 8); for(int i = 0; i < 8; i++) {
if(value & (1 << i)) {
//"and an extra 8 master cycles overhead for the whole thing" //"Then perform the DMA: 8 master cycles overhead and 8 master cycles per byte per channel"
_memoryManager->IncrementMasterClockValue<8>(); _memoryManager->IncrementMasterClockValue<8>();
for(int i = 0; i < 8; i++) { RunDma(_channel[i]);
if(value & (1 << i)) { }
//"Then perform the DMA: 8 master cycles overhead and 8 master cycles per byte per channel"
_memoryManager->IncrementMasterClockValue<8>();
RunDma(_channel[i]);
} }
//"Then wait 2-8 master cycles to reach a whole number of CPU Clock cycles since the pause"
clocksToWait = 8 - (_memoryManager->GetMasterClock() % 8);
_memoryManager->IncrementMasterClockValue(clocksToWait ? clocksToWait : 8);
} }
//"Then wait 2-8 master cycles to reach a whole number of CPU Clock cycles since the pause"
clocksToWait = 8 - (_memoryManager->GetMasterClock() % 8);
_memoryManager->IncrementMasterClockValue(clocksToWait ? clocksToWait : 8);
break; break;
} }

View file

@ -14,7 +14,6 @@
void MemoryManager::Initialize(shared_ptr<Console> console) void MemoryManager::Initialize(shared_ptr<Console> console)
{ {
_cyclesToRun = 0;
_masterClock = 0; _masterClock = 0;
_console = console; _console = console;
_regs = console->GetInternalRegisters().get(); _regs = console->GetInternalRegisters().get();
@ -88,17 +87,18 @@ void MemoryManager::RegisterHandler(uint32_t startAddr, uint32_t endAddr, IMemor
void MemoryManager::GenerateMasterClockTable() void MemoryManager::GenerateMasterClockTable()
{ {
//This is incredibly inaccurate
for(int j = 0; j < 2; j++) { for(int j = 0; j < 2; j++) {
for(int i = 0; i < 0x10000; i++) { for(int i = 0; i < 0x10000; i++) {
uint8_t bank = (i & 0xFF00) >> 8; uint8_t bank = (i & 0xFF00) >> 8;
if(bank >= 0x40 && bank <= 0x7F) { if(bank >= 0x40 && bank <= 0x7F) {
//Slow //Slow
_masterClockTable[j][i] = 8; _masterClockTable[j][i] = 8;
} else if(bank >= 0xCF) { } else if(bank >= 0xC0) {
//Banks $C0-$FF
//Slow or fast (depending on register) //Slow or fast (depending on register)
_masterClockTable[j][i] = j == 1 ? 6 : 8; _masterClockTable[j][i] = j == 1 ? 6 : 8;
} else { } else {
//Banks $00-$3F and $80-$BF
uint8_t page = (i & 0xFF); uint8_t page = (i & 0xFF);
if(page <= 0x1F) { if(page <= 0x1F) {
//Slow //Slow
@ -115,6 +115,9 @@ void MemoryManager::GenerateMasterClockTable()
} else if(page >= 0x60 && page <= 0x7F) { } else if(page >= 0x60 && page <= 0x7F) {
//Slow //Slow
_masterClockTable[j][i] = 8; _masterClockTable[j][i] = 8;
} else if(bank <= 0x3F) {
//Slow
_masterClockTable[j][i] = 8;
} else { } else {
//page >= $80 //page >= $80
//Slow or fast (depending on register) //Slow or fast (depending on register)
@ -130,22 +133,22 @@ void MemoryManager::IncrementMasterClock(uint32_t addr)
IncrementMasterClockValue(_masterClockTable[(uint8_t)_regs->IsFastRomEnabled()][addr >> 8]); IncrementMasterClockValue(_masterClockTable[(uint8_t)_regs->IsFastRomEnabled()][addr >> 8]);
} }
void MemoryManager::IncrementMasterClockValue(uint16_t value) void MemoryManager::IncrementMasterClockValue(uint16_t cyclesToRun)
{ {
_masterClock += value; switch(cyclesToRun) {
_cyclesToRun += value; case 12: cyclesToRun -= 2; Exec();
case 10: cyclesToRun -= 2; Exec();
case 8: cyclesToRun -= 2; Exec();
case 6: cyclesToRun -= 2; Exec();
case 4: cyclesToRun -= 2; Exec();
case 2: cyclesToRun -= 2; Exec();
}
}
if(_cyclesToRun >= 12) { void MemoryManager::Exec()
_cyclesToRun -= 12; {
_ppu->Exec(); _masterClock += 2;
_ppu->Exec(); if((_masterClock & 0x03) == 0) {
_ppu->Exec();
} else if(_cyclesToRun >= 8) {
_cyclesToRun -= 8;
_ppu->Exec();
_ppu->Exec();
} else if(_cyclesToRun >= 4) {
_cyclesToRun -= 4;
_ppu->Exec(); _ppu->Exec();
} }
} }

View file

@ -32,9 +32,10 @@ private:
uint8_t *_workRam; uint8_t *_workRam;
uint64_t _masterClock; uint64_t _masterClock;
uint64_t _cyclesToRun;
uint8_t _masterClockTable[2][0x10000]; uint8_t _masterClockTable[2][0x10000];
__forceinline void Exec();
public: public:
void Initialize(shared_ptr<Console> console); void Initialize(shared_ptr<Console> console);
~MemoryManager(); ~MemoryManager();
@ -66,10 +67,9 @@ public:
template<uint16_t value> template<uint16_t value>
void MemoryManager::IncrementMasterClockValue() void MemoryManager::IncrementMasterClockValue()
{ {
_masterClock += value; uint16_t cyclesToRun = value;
_cyclesToRun += value; while(cyclesToRun >= 2) {
while(_cyclesToRun >= 4) { cyclesToRun -= 2;
_cyclesToRun -= 4; Exec();
_ppu->Exec();
} }
} }

View file

@ -112,11 +112,11 @@ void Ppu::Exec()
if(_regs->IsNmiEnabled()) { if(_regs->IsNmiEnabled()) {
_console->GetCpu()->SetNmiFlag(); _console->GetCpu()->SetNmiFlag();
} }
} else if(_scanline == 240 && _frameCount & 0x01) { } else if(_scanline == 240 && _frameCount & 0x01 && !_screenInterlace) {
//Skip 1 tick every other frame //"In non-interlace mode scanline 240 of every other frame (those with $213f.7=1) is only 1360 cycles."
//TODO : some modes don't skip this?
_cycle++; _cycle++;
} else if(_scanline == 262) { } else if((_scanline == 262 && (!_screenInterlace || (_frameCount & 0x01))) || _scanline == 263) {
//"Frames are 262 scanlines in non-interlace mode, while in interlace mode frames with $213f.7=0 are 263 scanlines"
_regs->SetNmiFlag(false); _regs->SetNmiFlag(false);
_scanline = 0; _scanline = 0;
_rangeOver = false; _rangeOver = false;
@ -157,7 +157,7 @@ void Ppu::Exec()
} }
EvaluateNextLineSprites(); EvaluateNextLineSprites();
_console->GetDmaController()->ProcessHdmaChannels(); _console->GetDmaController()->ProcessHdmaChannels();
} else if(_cycle == 134) { } else if((_cycle == 134 || _cycle == 135) && (_console->GetMemoryManager()->GetMasterClock() & 0x07) == 0) {
//TODO Approximation (DRAM refresh timing is not exact) //TODO Approximation (DRAM refresh timing is not exact)
_console->GetMemoryManager()->IncrementMasterClockValue<40>(); _console->GetMemoryManager()->IncrementMasterClockValue<40>();
} }