From 02425d7453495c3063716847dbfd256de8a7b901 Mon Sep 17 00:00:00 2001 From: Sour Date: Thu, 21 Feb 2019 23:35:51 -0500 Subject: [PATCH] DMA: Added delay values for DMA/HDMA --- Core/Cpu.cpp | 2 +- Core/DmaController.cpp | 66 ++++++++++++++++++++++++++++++++---------- Core/MemoryManager.h | 38 ++++++++++++++++++++---- Core/Ppu.cpp | 2 +- 4 files changed, 85 insertions(+), 23 deletions(-) diff --git a/Core/Cpu.cpp b/Core/Cpu.cpp index 8b43f5d..56cc35e 100644 --- a/Core/Cpu.cpp +++ b/Core/Cpu.cpp @@ -338,7 +338,7 @@ uint8_t Cpu::GetOpCode() void Cpu::Idle() { - _memoryManager->ProcessCpuInternalOperation(); + _memoryManager->IncrementMasterClockValue<6>(); } uint8_t Cpu::ReadOperandByte() diff --git a/Core/DmaController.cpp b/Core/DmaController.cpp index 495df0b..7ef148b 100644 --- a/Core/DmaController.cpp +++ b/Core/DmaController.cpp @@ -16,11 +16,11 @@ void DmaController::RunSingleTransfer(DmaChannelConfig &channel) uint8_t i = 0; do { if(channel.InvertDirection) { - uint8_t valToWrite = _memoryManager->Read(0x2100 | channel.DestAddress + transferOffsets[i], MemoryOperationType::DmaRead); - _memoryManager->Write((channel.SrcBank << 16) | channel.SrcAddress, valToWrite, MemoryOperationType::DmaWrite); + uint8_t valToWrite = _memoryManager->ReadDma(0x2100 | channel.DestAddress + transferOffsets[i]); + _memoryManager->WriteDma((channel.SrcBank << 16) | channel.SrcAddress, valToWrite); } else { - uint8_t valToWrite = _memoryManager->Read((channel.SrcBank << 16) | channel.SrcAddress, MemoryOperationType::DmaRead); - _memoryManager->Write(0x2100 | channel.DestAddress + transferOffsets[i], valToWrite, MemoryOperationType::DmaWrite); + uint8_t valToWrite = _memoryManager->ReadDma((channel.SrcBank << 16) | channel.SrcAddress); + _memoryManager->WriteDma(0x2100 | channel.DestAddress + transferOffsets[i], valToWrite); } if(!channel.FixedTransfer) { @@ -45,6 +45,8 @@ void DmaController::RunDma(DmaChannelConfig &channel) void DmaController::InitHdmaChannels() { + //"The overhead is ~18 master cycles" + _memoryManager->IncrementMasterClockValue<18>(); for(int i = 0; i < 8; i++) { DmaChannelConfig &ch = _channel[i]; ch.HdmaFinished = false; @@ -53,7 +55,7 @@ void DmaController::InitHdmaChannels() ch.HdmaTableAddress = ch.SrcAddress; //"2. Load $43xA (Line Counter and Repeat) from the table. I believe $00 will terminate this channel immediately." - ch.HdmaLineCounterAndRepeat = _memoryManager->Read((ch.SrcBank << 16) | ch.HdmaTableAddress, MemoryOperationType::DmaRead); + ch.HdmaLineCounterAndRepeat = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress); ch.HdmaTableAddress++; if(ch.HdmaLineCounterAndRepeat == 0) { ch.HdmaFinished = true; @@ -61,9 +63,15 @@ void DmaController::InitHdmaChannels() //3. Load Indirect Address, if necessary. if(ch.HdmaIndirectAddressing) { - uint8_t lsb = _memoryManager->Read((ch.SrcBank << 16) | ch.HdmaTableAddress++, MemoryOperationType::DmaRead); - uint8_t msb = _memoryManager->Read((ch.SrcBank << 16) | ch.HdmaTableAddress++, MemoryOperationType::DmaRead); + uint8_t lsb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++); + uint8_t msb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++); ch.TransferSize = (msb << 8) | lsb; + + //"and 24 master cycles for each channel set for indirect HDMA" + _memoryManager->IncrementMasterClockValue<24>(); + } else { + //"plus 8 master cycles for each channel set for direct HDMA" + _memoryManager->IncrementMasterClockValue<8>(); } //4. Set DoTransfer to true. @@ -87,11 +95,11 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel) uint8_t i = 0; do { if(channel.InvertDirection) { - uint8_t valToWrite = _memoryManager->Read(0x2100 | channel.DestAddress + transferOffsets[i], MemoryOperationType::DmaRead); - _memoryManager->Write(srcAddress, valToWrite, MemoryOperationType::DmaWrite); + uint8_t valToWrite = _memoryManager->ReadDma(0x2100 | channel.DestAddress + transferOffsets[i]); + _memoryManager->WriteDma(srcAddress, valToWrite); } else { - uint8_t valToWrite = _memoryManager->Read(srcAddress, MemoryOperationType::DmaRead); - _memoryManager->Write(0x2100 | channel.DestAddress + transferOffsets[i], valToWrite, MemoryOperationType::DmaWrite); + uint8_t valToWrite = _memoryManager->ReadDma(srcAddress); + _memoryManager->WriteDma(0x2100 | channel.DestAddress + transferOffsets[i], valToWrite); } if(!channel.FixedTransfer) { @@ -111,6 +119,7 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel) void DmaController::ProcessHdmaChannels() { + bool needOverhead = true; if(_hdmaChannels) { _hdmaPending = true; @@ -120,6 +129,15 @@ void DmaController::ProcessHdmaChannels() return; } + if(needOverhead) { + //"For each scanline during which HDMA is active (i.e.at least one channel has not yet terminated for the frame), there are ~18 master cycles overhead." + _memoryManager->IncrementMasterClockValue<8>(); + needOverhead = false; + } + + //"Each active channel incurs another 8 master cycles overhead for every scanline" + _memoryManager->IncrementMasterClockValue<8>(); + //1. If DoTransfer is false, skip to step 3. if(ch.DoTransfer) { //2. For the number of bytes (1, 2, or 4) required for this Transfer Mode... @@ -135,20 +153,23 @@ void DmaController::ProcessHdmaChannels() //5. If Line Counter is zero... if((ch.HdmaLineCounterAndRepeat & 0x7F) == 0) { //"a. Read the next byte from Address into $43xA (thus, into both Line Counter and Repeat)." - ch.HdmaLineCounterAndRepeat = _memoryManager->Read(ch.HdmaTableAddress++, MemoryOperationType::DmaRead); + ch.HdmaLineCounterAndRepeat = _memoryManager->ReadDma(ch.HdmaTableAddress++); //"b. If Addressing Mode is Indirect, read two bytes from Address into Indirect Address(and increment Address by two bytes)." if(ch.HdmaIndirectAddressing) { if(ch.HdmaLineCounterAndRepeat == 0) { //"One oddity: if $43xA is 0 and this is the last active HDMA channel for this scanline, only load one byte for Address, //and use the $00 for the low byte.So Address ends up incremented one less than otherwise expected, and one less CPU Cycle is used." - uint8_t msb = _memoryManager->Read(ch.HdmaTableAddress++, MemoryOperationType::DmaRead); + uint8_t msb = _memoryManager->ReadDma(ch.HdmaTableAddress++); ch.TransferSize = (msb << 8); } else { - uint8_t lsb = _memoryManager->Read(ch.HdmaTableAddress++, MemoryOperationType::DmaRead); - uint8_t msb = _memoryManager->Read(ch.HdmaTableAddress++, MemoryOperationType::DmaRead); + uint8_t lsb = _memoryManager->ReadDma(ch.HdmaTableAddress++); + uint8_t msb = _memoryManager->ReadDma(ch.HdmaTableAddress++); ch.TransferSize = (msb << 8) | lsb; } + + //"If a new indirect address is required, 16 master cycles are taken to load it." + _memoryManager->IncrementMasterClockValue<8>(); //minus 8 before the ReadDmas call will increment it by 4 twice } //"c. If $43xA is zero, terminate this HDMA channel for this frame. The bit in $420c is not cleared, though, so it may be automatically restarted next frame." @@ -166,14 +187,27 @@ void DmaController::ProcessHdmaChannels() void DmaController::Write(uint16_t addr, uint8_t value) { switch(addr) { - case 0x420B: + 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); + + //"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); break; + } case 0x420C: //HDMAEN - HDMA Enable diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index 2af0ba6..165e95c 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -226,18 +226,19 @@ public: } } - void ProcessCpuInternalOperation() + template + void IncrementMasterClockValue() { - _masterClock += 6; + _masterClock += value; while(_lastMasterClock < _masterClock - 3) { _ppu->Exec(); _lastMasterClock += 4; } } - void ProcessDramRefresh() + void IncrementMasterClockValue(uint16_t value) { - _masterClock += 40; + _masterClock += value; while(_lastMasterClock < _masterClock - 3) { _ppu->Exec(); _lastMasterClock += 4; @@ -248,7 +249,7 @@ public: { IncrementMasterClock(addr); - uint8_t value = 0; + uint8_t value; if(_handlers[addr >> 12]) { value = _handlers[addr >> 12]->Read(addr); } else { @@ -261,6 +262,21 @@ public: return value; } + uint8_t ReadDma(uint32_t addr) + { + IncrementMasterClockValue<4>(); + uint8_t value; + if(_handlers[addr >> 12]) { + value = _handlers[addr >> 12]->Read(addr); + } else { + //open bus + value = (addr >> 12); + MessageManager::DisplayMessage("Debug", "Read - missing handler: $" + HexUtilities::ToHex(addr)); + } + _console->ProcessCpuRead(addr, value, MemoryOperationType::DmaRead); + return value; + } + uint8_t Peek(uint32_t addr) { //Read, without triggering side-effects @@ -283,6 +299,18 @@ public: } } + void WriteDma(uint32_t addr, uint8_t value) + { + IncrementMasterClockValue<4>(); + + _console->ProcessCpuWrite(addr, value, MemoryOperationType::DmaWrite); + if(_handlers[addr >> 12]) { + return _handlers[addr >> 12]->Write(addr, value); + } else { + MessageManager::DisplayMessage("Debug", "Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value)); + } + } + uint64_t GetMasterClock() { return _masterClock; } uint8_t* DebugGetWorkRam() { return _workRam; } }; \ No newline at end of file diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index fe93e63..5277afc 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -119,7 +119,7 @@ void Ppu::Exec() _console->GetDmaController()->ProcessHdmaChannels(); } else if(_cycle == 134) { //TODO Approximation - _console->GetMemoryManager()->ProcessDramRefresh(); + _console->GetMemoryManager()->IncrementMasterClockValue<40>(); } }