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

217 lines
6.7 KiB
C++

#include "apusq2.h"
// Qt includes
#include <QDataStream>
// local includes
#include "emusettings.h"
#include "apu.h"
ApuSq2::ApuSq2(Apu &apu) :
m_apu(apu)
{
}
void ApuSq2::apuSq2HardReset()
{
m_apuSq2DutyCycle = 0;
m_apuSq2LengthHalt = false;
m_apuSq2ConstantVolumeEnvelope = false;
m_apuSq2VolumeDeviderPeriod = 0;
m_apuSq2SweepEnable = false;
m_apuSq2SweepDeviderPeriod = 0;
m_apuSq2SweepNegate = false;
m_apuSq2SweepShiftCount = 0;
m_apuSq2Timer = 0;
m_apuSq2PeriodDevider = 0;
m_apuSq2Seqencer = 0;
m_apuSq2LengthEnabled = false;
m_apuSq2LengthCounter = 0;
m_apuSq2EnvelopeStartFlag = false;
m_apuSq2EnvelopeDevider = 0;
m_apuSq2EnvelopeDecayLevelCounter = 0;
m_apuSq2Envelope = 0;
m_apuSq2SweepCounter = 0;
m_apuSq2SweepReload = false;
m_apuSq2SweepChange = 0;
m_apuSq2ValidFreq = false;
m_apuSq2Output = 0;
m_apuSq2IgnoreReload = false;
}
void ApuSq2::apuSq2SoftReset()
{
apuSq2HardReset();
}
void ApuSq2::apuSq2Clock()
{
m_apuSq2PeriodDevider--;
if (m_apuSq2PeriodDevider <= 0)
{
m_apuSq2PeriodDevider = m_apuSq2Timer + 1;
m_apuSq2Seqencer = (m_apuSq2Seqencer + 1) & 0x7;
if (m_apuSq2LengthCounter > 0 && m_apuSq2ValidFreq)
{
if (EmuSettings::Audio::ChannelEnabled::SQ2)
m_apuSq2Output = m_apu.m_sqDutyCycleSequences[m_apuSq2DutyCycle][m_apuSq2Seqencer] * m_apuSq2Envelope;
}
else
m_apuSq2Output = 0;
}
}
void ApuSq2::apuSq2ClockLength()
{
if (m_apuSq2LengthCounter > 0 && !m_apuSq2LengthHalt)
{
m_apuSq2LengthCounter--;
if (m_apu.regAccessHappened())
{
// This is not a hack, there is some hidden mechanism in the nes, that do reload and clock stuff
if (m_apu.regIoAddr() == 0x7 && m_apu.regAccessW())
m_apuSq2IgnoreReload = true;
}
}
// Sweep unit
m_apuSq2SweepCounter--;
if (m_apuSq2SweepCounter == 0)
{
m_apuSq2SweepCounter = m_apuSq2SweepDeviderPeriod + 1;
if (m_apuSq2SweepEnable && m_apuSq2SweepShiftCount > 0 && m_apuSq2ValidFreq)
{
m_apuSq2SweepChange = m_apuSq2Timer >> m_apuSq2SweepShiftCount;
m_apuSq2Timer += m_apuSq2SweepNegate ? -m_apuSq2SweepChange : m_apuSq2SweepChange;
apuSq2CalculateValidFreq();
}
}
else if (m_apuSq2SweepReload)
{
m_apuSq2SweepCounter = m_apuSq2SweepDeviderPeriod + 1;
m_apuSq2SweepReload = false;
}
}
void ApuSq2::apuSq2ClockEnvelope()
{
if (m_apuSq2EnvelopeStartFlag)
{
m_apuSq2EnvelopeStartFlag = false;
m_apuSq2EnvelopeDecayLevelCounter = 15;
m_apuSq2EnvelopeDevider = m_apuSq2VolumeDeviderPeriod + 1;
}
else
{
if (m_apuSq2EnvelopeDevider > 0)
m_apuSq2EnvelopeDevider--;
else
{
m_apuSq2EnvelopeDevider = m_apuSq2VolumeDeviderPeriod + 1;
if (m_apuSq2EnvelopeDecayLevelCounter > 0)
m_apuSq2EnvelopeDecayLevelCounter--;
else if (m_apuSq2LengthHalt)
m_apuSq2EnvelopeDecayLevelCounter = 0xF;
}
}
m_apuSq2Envelope = m_apuSq2ConstantVolumeEnvelope ? m_apuSq2VolumeDeviderPeriod : m_apuSq2EnvelopeDecayLevelCounter;
}
void ApuSq2::apuOnRegister4004()
{
// Only writes accepted
if (!m_apu.regAccessW())
return;
m_apuSq2DutyCycle = (m_apu.regIoDb() & 0xC0) >> 6;
m_apuSq2VolumeDeviderPeriod = m_apu.regIoDb() & 0xF;
m_apuSq2LengthHalt = m_apu.regIoDb() & 0x20;
m_apuSq2ConstantVolumeEnvelope = m_apu.regIoDb() & 0x10;
m_apuSq2Envelope = m_apuSq2ConstantVolumeEnvelope ? m_apuSq2VolumeDeviderPeriod : m_apuSq2EnvelopeDecayLevelCounter;
}
void ApuSq2::apuOnRegister4005()
{
// Only writes accepted
if (!m_apu.regAccessW())
return;
m_apuSq2SweepEnable = (m_apu.regIoDb() & 0x80) == 0x80;
m_apuSq2SweepDeviderPeriod = (m_apu.regIoDb() >> 4) & 7;
m_apuSq2SweepNegate = (m_apu.regIoDb() & 0x8) == 0x8;
m_apuSq2SweepShiftCount = m_apu.regIoDb() & 7;
m_apuSq2SweepReload = true;
apuSq2CalculateValidFreq();
}
void ApuSq2::apuOnRegister4006()
{
// Only writes accepted
if (!m_apu.regAccessW())
return;
m_apuSq2Timer = (m_apuSq2Timer & 0xFF00) | m_apu.regIoDb();
apuSq2CalculateValidFreq();
}
void ApuSq2::apuOnRegister4007()
{
// Only writes accepted
if (!m_apu.regAccessW())
return;
m_apuSq2Timer = (m_apuSq2Timer & 0x00FF) | ((m_apu.regIoDb() & 0x7) << 8);
if (m_apuSq2LengthEnabled && !m_apuSq2IgnoreReload)
m_apuSq2LengthCounter = m_apu.m_sqDurationTable[m_apu.regIoDb() >> 3];
if (m_apuSq2IgnoreReload)
m_apuSq2IgnoreReload = false;
m_apuSq2Seqencer = 0;
m_apuSq2EnvelopeStartFlag = true;
apuSq2CalculateValidFreq();
}
void ApuSq2::apuSq2On4015()
{
m_apuSq2LengthEnabled = (m_apu.regIoDb() & 0x02) != 0;
if (!m_apuSq2LengthEnabled)
m_apuSq2LengthCounter = 0;
}
void ApuSq2::apuSq2Read4015()
{
if (m_apuSq2LengthCounter > 0)
m_apu.setRegIoDb((m_apu.regIoDb() & 0xFD) | 0x02);
}
void ApuSq2::apuSq2CalculateValidFreq()
{
m_apuSq2ValidFreq = (m_apuSq2Timer >= 0x8) && ((m_apuSq2SweepNegate) || (((m_apuSq2Timer + (m_apuSq2Timer >> m_apuSq2SweepShiftCount)) & 0x800) == 0));
}
void ApuSq2::apuSq2WriteState(QDataStream &dataStream) const
{
dataStream
<< m_apuSq2DutyCycle << m_apuSq2LengthHalt << m_apuSq2ConstantVolumeEnvelope << m_apuSq2VolumeDeviderPeriod << m_apuSq2SweepEnable
<< m_apuSq2SweepDeviderPeriod << m_apuSq2SweepNegate << m_apuSq2SweepShiftCount << m_apuSq2Timer << m_apuSq2PeriodDevider << m_apuSq2Seqencer
<< m_apuSq2LengthEnabled << m_apuSq2LengthCounter << m_apuSq2EnvelopeStartFlag << m_apuSq2EnvelopeDevider << m_apuSq2EnvelopeDecayLevelCounter
<< m_apuSq2Envelope << m_apuSq2SweepCounter << m_apuSq2SweepReload << m_apuSq2SweepChange << m_apuSq2ValidFreq << m_apuSq2Output << m_apuSq2IgnoreReload;
}
void ApuSq2::apuSq2ReadState(QDataStream &dataStream)
{
dataStream
>> m_apuSq2DutyCycle >> m_apuSq2LengthHalt >> m_apuSq2ConstantVolumeEnvelope >> m_apuSq2VolumeDeviderPeriod >> m_apuSq2SweepEnable
>> m_apuSq2SweepDeviderPeriod >> m_apuSq2SweepNegate >> m_apuSq2SweepShiftCount >> m_apuSq2Timer >> m_apuSq2PeriodDevider >> m_apuSq2Seqencer
>> m_apuSq2LengthEnabled >> m_apuSq2LengthCounter >> m_apuSq2EnvelopeStartFlag >> m_apuSq2EnvelopeDevider >> m_apuSq2EnvelopeDecayLevelCounter
>> m_apuSq2Envelope >> m_apuSq2SweepCounter >> m_apuSq2SweepReload >> m_apuSq2SweepChange >> m_apuSq2ValidFreq >> m_apuSq2Output >> m_apuSq2IgnoreReload;
}
qint32 ApuSq2::output() const
{
return m_apuSq2Output;
}