DMA: Implement NMI/IRQ handler delay after DMA/HDMA

This commit is contained in:
Sour 2019-04-04 17:49:47 -04:00
parent 8377e62a9f
commit 260e0f089d
4 changed files with 34 additions and 10 deletions

View file

@ -3,6 +3,7 @@
#include "Cpu.h"
#include "Console.h"
#include "MemoryManager.h"
#include "DmaController.h"
#include "EventType.h"
#include "../Utilities/Serializer.h"
@ -10,6 +11,7 @@ Cpu::Cpu(Console *console)
{
_console = console;
_memoryManager = console->GetMemoryManager().get();
_dmaController = console->GetDmaController().get();
}
Cpu::~Cpu()
@ -72,15 +74,17 @@ void Cpu::Exec()
}
//Use the state of the IRQ/NMI flags on the previous cycle to determine if an IRQ is processed or not
if(_state.PrevNmiFlag) {
uint32_t originalPc = GetProgramAddress(_state.PC);
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyNmiVector : Cpu::NmiVector);
_console->ProcessInterrupt(originalPc, GetProgramAddress(_state.PC), true);
_state.NmiFlag = false;
} else if(_state.PrevIrqSource && !CheckFlag(ProcFlags::IrqDisable)) {
uint32_t originalPc = GetProgramAddress(_state.PC);
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::IrqVector);
_console->ProcessInterrupt(originalPc, GetProgramAddress(_state.PC), false);
if(!_dmaController->HasNmiIrqDelay()) {
if(_state.PrevNmiFlag) {
uint32_t originalPc = GetProgramAddress(_state.PC);
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyNmiVector : Cpu::NmiVector);
_console->ProcessInterrupt(originalPc, GetProgramAddress(_state.PC), true);
_state.NmiFlag = false;
} else if(_state.PrevIrqSource && !CheckFlag(ProcFlags::IrqDisable)) {
uint32_t originalPc = GetProgramAddress(_state.PC);
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::IrqVector);
_console->ProcessInterrupt(originalPc, GetProgramAddress(_state.PC), false);
}
}
}

View file

@ -10,6 +10,7 @@
#include "../Utilities/ISerializable.h"
class MemoryManager;
class DmaController;
class Console;
class Cpu : public ISerializable
@ -29,6 +30,7 @@ private:
typedef void(Cpu::*Func)();
MemoryManager *_memoryManager;
DmaController *_dmaController;
Console *_console;
bool _immediateMode = false;

View file

@ -28,6 +28,15 @@ void DmaController::Reset()
_hdmaChannels = 0;
}
bool DmaController::HasNmiIrqDelay()
{
if(_nmiIrqDelayCounter > 0) {
_nmiIrqDelayCounter--;
return true;
}
return false;
}
void DmaController::CopyDmaByte(uint32_t addressBusA, uint16_t addressBusB, bool fromBtoA)
{
if(fromBtoA) {
@ -234,6 +243,9 @@ void DmaController::ProcessHdmaChannels()
ch.DoTransfer = true;
}
}
//When DMA runs, the next instruction will not check the NMI/IRQ flags, which allows 2 instructions to run after DMA
_nmiIrqDelayCounter = 2;
}
}
@ -263,6 +275,9 @@ void DmaController::Write(uint16_t addr, uint8_t value)
//"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);
//When DMA runs, the next instruction will not check the NMI/IRQ flags, which allows 2 instructions to run after DMA
_nmiIrqDelayCounter = 2;
}
break;
}
@ -453,7 +468,7 @@ uint8_t DmaController::Read(uint16_t addr)
void DmaController::Serialize(Serializer &s)
{
s.Stream(_hdmaPending, _hdmaChannels);
s.Stream(_hdmaPending, _hdmaChannels, _nmiIrqDelayCounter);
for(int i = 0; i < 8; i++) {
s.Stream(
_channel[i].Decrement, _channel[i].DestAddress, _channel[i].DoTransfer, _channel[i].FixedTransfer,

View file

@ -34,6 +34,7 @@ class DmaController final : public ISerializable
private:
bool _hdmaPending = false;
uint8_t _hdmaChannels = 0;
uint8_t _nmiIrqDelayCounter = 0;
DmaChannelConfig _channel[8] = {};
MemoryManager *_memoryManager;
@ -48,6 +49,8 @@ public:
DmaController(MemoryManager *memoryManager);
void Reset();
bool HasNmiIrqDelay();
void InitHdmaChannels();
void ProcessHdmaChannels();