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

218 lines
5.4 KiB
C++

#include "apudmc.h"
// Qt includes
#include <QDataStream>
// local includes
#include "emusettings.h"
#include "apu.h"
#include "nesemulator.h"
ApuDmc::ApuDmc(Apu &apu) :
m_apu(apu)
{
}
void ApuDmc::apuDmcHardReset()
{
m_apuDmcOutputA = 0;
m_apuDmcOutput = 0;
m_apuDmcPeriodDevider = 0;
m_apuDmcLoopFlag = false;
m_apuDmcRateIndex = 0;
m_apuDmcIrqEnabled = false;
m_apuDmcDmaAddr = 0xC000;
m_apuDmcAddrRefresh = 0xC000;
m_apuDmcSizeRefresh = 0;
m_apuDmcDmaBits = 1;
m_apuDmcDmaByte = 1;
m_apuDmcPeriodDevider = 0;
m_apuDmcDmaEnabled = false;
m_apuDmcBufferFull = false;
m_apuDmcDmaSize = 0;
}
void ApuDmc::apuDmcSoftReset()
{
apuDmcHardReset();
}
void ApuDmc::apuDmcClock()
{
static constexpr std::array<qint32, 32> freqTable = [](){
switch(EmuSettings::region)
{
case EmuRegion::NTSC:
return std::array<qint32, 32> {
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54
};
case EmuRegion::PALB:
return std::array<qint32, 32> {
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50
};
case EmuRegion::DENDY:
return std::array<qint32, 32> {
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54
};
}
}();
if (--m_apuDmcPeriodDevider <= 0)
{
m_apuDmcPeriodDevider = freqTable[m_apuDmcRateIndex];
if (m_apuDmcDmaEnabled)
{
if ((m_apuDmcDmaByte & 0x01) != 0)
{
if (m_apuDmcOutputA <= 0x7D)
m_apuDmcOutputA += 2;
}
else
{
if (m_apuDmcOutputA >= 0x02)
m_apuDmcOutputA -= 2;
}
m_apuDmcDmaByte >>= 1;
}
if (--m_apuDmcDmaBits == 0)
{
m_apuDmcDmaBits = 8;
if (m_apuDmcBufferFull)
{
m_apuDmcBufferFull = false;
m_apuDmcDmaEnabled = true;
m_apuDmcDmaByte = m_apuDmcDmaBuffer;
// RDY ?
if (m_apuDmcDmaSize > 0)
m_apu.emu().dma().assertDmcDma();
}
else
m_apuDmcDmaEnabled = false;
}
if (EmuSettings::Audio::ChannelEnabled::DMC)
m_apuDmcOutput = m_apuDmcOutputA;
}
}
void ApuDmc::apuDmcDoDma()
{
m_apuDmcBufferFull = true;
m_apuDmcDmaBuffer = m_apu.emu().memory().read(m_apuDmcDmaAddr);
if (m_apuDmcDmaAddr == 0xFFFF)
m_apuDmcDmaAddr = 0x8000;
else
m_apuDmcDmaAddr++;
if (m_apuDmcDmaSize > 0)
m_apuDmcDmaSize--;
if (m_apuDmcDmaSize == 0)
{
if (m_apuDmcLoopFlag)
{
m_apuDmcDmaSize = m_apuDmcSizeRefresh;
m_apuDmcDmaAddr = m_apuDmcAddrRefresh;
}
else if (m_apuDmcIrqEnabled)
{
m_apu.emu().interrupts().addFlag(Interrupts::IRQ_DMC);
m_apu.setIrqDeltaOccur(true);
}
}
}
void ApuDmc::apuOnRegister4010()
{
if (!m_apu.regAccessW())
return;
m_apuDmcIrqEnabled = m_apu.regIoDb() & 0x80;
m_apuDmcLoopFlag = m_apu.regIoDb() & 0x40;
if (!m_apuDmcIrqEnabled)
{
m_apu.setIrqDeltaOccur(false);
m_apu.emu().interrupts().removeFlag(Interrupts::IRQ_DMC);
}
m_apuDmcRateIndex = m_apu.regIoDb() & 0x0F;
}
void ApuDmc::apuOnRegister4011()
{
if (!m_apu.regAccessW())
return;
m_apuDmcOutputA = m_apu.regIoDb() & 0x7F;
}
void ApuDmc::apuOnRegister4012()
{
if (!m_apu.regAccessW())
return;
m_apuDmcAddrRefresh = (m_apu.regIoDb() << 6) | 0xC000;
}
void ApuDmc::apuOnRegister4013()
{
if (!m_apu.regAccessW())
return;
m_apuDmcSizeRefresh = (m_apu.regIoDb() << 4) | 0x0001;
}
void ApuDmc::apuDmcOn4015()
{
// DMC
if (m_apu.regIoDb() & 0x10)
{
if (m_apuDmcDmaSize == 0)
{
m_apuDmcDmaSize = m_apuDmcSizeRefresh;
m_apuDmcDmaAddr = m_apuDmcAddrRefresh;
}
}
else
m_apuDmcDmaSize = 0;
// Disable DMC IRQ
m_apu.setIrqDeltaOccur(false);
m_apu.emu().interrupts().removeFlag(Interrupts::IRQ_DMC);
// RDY ?
if (!m_apuDmcBufferFull && m_apuDmcDmaSize > 0)
m_apu.emu().dma().assertDmcDma();
}
void ApuDmc::apuDmcRead4015()
{
if (m_apuDmcDmaSize > 0)
m_apu.setRegIoDb((m_apu.regIoDb() & 0xEF) | 0x10);
}
void ApuDmc::apuDmcWriteState(QDataStream &dataStream) const
{
dataStream << m_apuDmcOutputA << m_apuDmcOutput << m_apuDmcPeriodDevider << m_apuDmcIrqEnabled << m_apuDmcLoopFlag
<< m_apuDmcRateIndex << m_apuDmcAddrRefresh << m_apuDmcSizeRefresh << m_apuDmcDmaEnabled << m_apuDmcDmaByte
<< m_apuDmcDmaBits << m_apuDmcBufferFull << m_apuDmcDmaBuffer << m_apuDmcDmaSize << m_apuDmcDmaAddr;
}
void ApuDmc::apuDmcReadState(QDataStream &dataStream)
{
dataStream >> m_apuDmcOutputA >> m_apuDmcOutput >> m_apuDmcPeriodDevider >> m_apuDmcIrqEnabled >> m_apuDmcLoopFlag
>> m_apuDmcRateIndex >> m_apuDmcAddrRefresh >> m_apuDmcSizeRefresh >> m_apuDmcDmaEnabled >> m_apuDmcDmaByte
>> m_apuDmcDmaBits >> m_apuDmcBufferFull >> m_apuDmcDmaBuffer >> m_apuDmcDmaSize >> m_apuDmcDmaAddr;
}
qint32 ApuDmc::output() const
{
return m_apuDmcOutput;
}