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());
_cpu.reset(new Cpu(this));
_memoryManager->IncrementMasterClockValue<162>();
_memoryManager->IncrementMasterClockValue<170>();
//if(_debugger) {
//Reset debugger if it was running before

View file

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

View file

@ -191,23 +191,24 @@ void DmaController::Write(uint16_t addr, uint8_t value)
switch(addr) {
case 0x420B: {
//MDMAEN - DMA Enable
//"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);
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);
//"and an extra 8 master cycles overhead for the whole thing"
_memoryManager->IncrementMasterClockValue<8>();
for(int i = 0; i < 8; 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]);
//"and an extra 8 master cycles overhead for the whole thing"
_memoryManager->IncrementMasterClockValue<8>();
for(int i = 0; i < 8; 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;
}

View file

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

View file

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

View file

@ -112,11 +112,11 @@ void Ppu::Exec()
if(_regs->IsNmiEnabled()) {
_console->GetCpu()->SetNmiFlag();
}
} else if(_scanline == 240 && _frameCount & 0x01) {
//Skip 1 tick every other frame
//TODO : some modes don't skip this?
} else if(_scanline == 240 && _frameCount & 0x01 && !_screenInterlace) {
//"In non-interlace mode scanline 240 of every other frame (those with $213f.7=1) is only 1360 cycles."
_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);
_scanline = 0;
_rangeOver = false;
@ -157,7 +157,7 @@ void Ppu::Exec()
}
EvaluateNextLineSprites();
_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)
_console->GetMemoryManager()->IncrementMasterClockValue<40>();
}