CPU/PPU: Improved timings
This commit is contained in:
parent
46663e8e53
commit
7211eece7c
6 changed files with 49 additions and 43 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
10
Core/Ppu.cpp
10
Core/Ppu.cpp
|
@ -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>();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue