Files
DbNesEmulator/nescorelib/emu/dma.cpp
2018-12-16 22:19:06 +01:00

200 lines
4.9 KiB
C++

#include "dma.h"
// Qt includes
#include <QDataStream>
// local includes
#include "nesemulator.h"
Dma::Dma(NesEmulator &emu) :
m_emu(emu)
{
}
void Dma::hardReset()
{
m_dmcDmaWaitCycles = 0;
m_oamDmaWaitCycles = 0;
m_isOamDma = false;
m_dmcOn = false;
m_oamOn = false;
m_dmcOccurring = false;
m_oamOccurring = false;
m_oamFinishCounter = 0;
m_oamAddress = 0;
m_oamCycle = 0;
m_latch = 0;
}
void Dma::softReset()
{
m_dmcDmaWaitCycles = 0;
m_oamDmaWaitCycles = 0;
m_isOamDma = false;
m_dmcOn = false;
m_oamOn = false;
m_dmcOccurring = false;
m_oamOccurring = false;
m_oamFinishCounter = 0;
m_oamAddress = 0;
m_oamCycle = 0;
m_latch = 0;
}
void Dma::assertDmcDma()
{
if(m_oamOccurring)
{
if(m_oamCycle < 508)
// OAM DMA is occurring here
m_dmcDmaWaitCycles = m_emu.memory().busRw() ? 1 : 0;
else
{
// Here the oam dma is about to finish
// Remaining cycles of oam dma determines the dmc dma waiting cycles.
m_dmcDmaWaitCycles = 4 - (512 - m_oamCycle);
}
}
else if(m_dmcOccurring)
{
// DMC occurring now !!? is that possible ?
// Anyway, let's ignore this call !
return;
}
else
{
// Nothing occurring, initialize brand new dma
// DMC DMA depends on r/w flag forthe wait cycles.
m_dmcDmaWaitCycles = m_emu.memory().busRw() ? 3 : 2;
// After 2 cycles of oam dma, add extra cycle forthe waiting.
if(m_oamFinishCounter == 3)
m_dmcDmaWaitCycles++;
}
m_isOamDma = false;
m_dmcOn = true;
}
void Dma::assertOamDma()
{
if(m_oamOccurring)
return;
// Setup
// OAM DMA depends on apu odd timer forodd cycles
m_oamDmaWaitCycles = m_emu.apu().oddCycle() ? 1 : 2;
m_isOamDma = true;
m_oamOn = true;
}
void Dma::clock()
{
if(m_oamFinishCounter > 0)
m_oamFinishCounter--;
if(!m_emu.memory().busRw()) // Clocks only on reads
{
if(m_dmcDmaWaitCycles > 0)
m_dmcDmaWaitCycles--;
if(m_oamDmaWaitCycles > 0)
m_oamDmaWaitCycles--;
return;
}
if(m_dmcOn)
{
m_dmcOccurring = true;
// This is it !
m_dmcOn = false;
// Do wait cycles (extra reads)
if(m_dmcDmaWaitCycles > 0)
{
if(m_emu.memory().busAddress() == 0x4016 || m_emu.memory().busAddress() == 0x4017)
{
m_emu.memory().read(m_emu.memory().busAddress());
m_dmcDmaWaitCycles--;
while(m_dmcDmaWaitCycles > 0)
{
m_emu.emuClockComponents();
m_dmcDmaWaitCycles--;
}
}
else
{
while(m_dmcDmaWaitCycles > 0)
{
m_emu.memory().read(m_emu.memory().busAddress());
m_dmcDmaWaitCycles--;
}
}
}
// Do DMC DMA
m_emu.apu().dmc().apuDmcDoDma();
m_dmcOccurring = false;
}
if(m_oamOn)
{
m_oamOccurring = true;
// This is it ! pause the cpu
m_oamOn = false;
// Do wait cycles (extra reads)
if(m_oamDmaWaitCycles > 0)
{
if(m_emu.memory().busAddress() == 0x4016 || m_emu.memory().busAddress() == 0x4017)
{
m_emu.memory().read(m_emu.memory().busAddress());
m_oamDmaWaitCycles--;
while(m_oamDmaWaitCycles > 0)
{
m_emu.emuClockComponents();
m_oamDmaWaitCycles--;
}
}
else
{
while(m_oamDmaWaitCycles > 0)
{
m_emu.memory().read(m_emu.memory().busAddress());
m_oamDmaWaitCycles--;
}
}
}
// Do OAM DMA
m_oamCycle = 0;
for(auto i = 0; i < 256; i++)
{
m_latch = m_emu.memory().read(m_oamAddress);
m_oamCycle++;
m_emu.memory().write(0x2004, m_latch);
m_oamCycle++;
m_oamAddress++;
m_oamAddress = m_oamAddress & 0xFFFF;
}
m_oamCycle = 0;
m_oamFinishCounter = 5;
m_oamOccurring = false;
}
}
void Dma::writeState(QDataStream &dataStream) const
{
dataStream << m_dmcDmaWaitCycles << m_oamDmaWaitCycles << m_isOamDma << m_dmcOn << m_oamOn << m_dmcOccurring
<< m_oamOccurring << m_oamFinishCounter << m_oamAddress << m_oamCycle << m_latch;
}
void Dma::readState(QDataStream &dataStream)
{
dataStream >> m_dmcDmaWaitCycles >> m_oamDmaWaitCycles >> m_isOamDma >> m_dmcOn >> m_oamOn >> m_dmcOccurring
>> m_oamOccurring >> m_oamFinishCounter >> m_oamAddress >> m_oamCycle >> m_latch;
}
void Dma::setOamAddress(quint16 oamAddress)
{
m_oamAddress = oamAddress;
}