220 lines
6.7 KiB
C++
220 lines
6.7 KiB
C++
#include "apusq1.h"
|
|
|
|
// Qt includes
|
|
#include <QDataStream>
|
|
|
|
// local includes
|
|
#include "emusettings.h"
|
|
#include "apu.h"
|
|
|
|
ApuSq1::ApuSq1(Apu &apu) :
|
|
m_apu(apu)
|
|
{
|
|
}
|
|
|
|
void ApuSq1::apuSq1HardReset()
|
|
{
|
|
m_apuSq1DutyCycle = 0;
|
|
m_apuSq1LengthHalt = false;
|
|
m_apuSq1ConstantVolumeEnvelope = false;
|
|
m_apuSq1VolumeDeviderPeriod = 0;
|
|
m_apuSq1SweepEnable = false;
|
|
m_apuSq1SweepDeviderPeriod = 0;
|
|
m_apuSq1SweepNegate = false;
|
|
m_apuSq1SweepShiftCount = 0;
|
|
m_apuSq1Timer = 0;
|
|
m_apuSq1PeriodDevider = 0;
|
|
m_apuSq1Seqencer = 0;
|
|
m_apuSq1LengthEnabled = false;
|
|
m_apuSq1LengthCounter = 0;
|
|
m_apuSq1EnvelopeStartFlag = false;
|
|
m_apuSq1EnvelopeDevider = 0;
|
|
m_apuSq1EnvelopeDecayLevelCounter = 0;
|
|
m_apuSq1Envelope = 0;
|
|
m_apuSq1SweepCounter = 0;
|
|
m_apuSq1SweepReload = false;
|
|
m_apuSq1SweepChange = 0;
|
|
m_apuSq1ValidFreq = false;
|
|
m_apuSq1Output = 0;
|
|
m_apuSq1IgnoreReload = false;
|
|
}
|
|
|
|
void ApuSq1::apuSq1SoftReset()
|
|
{
|
|
apuSq1HardReset();
|
|
}
|
|
|
|
void ApuSq1::apuSq1Clock()
|
|
{
|
|
m_apuSq1PeriodDevider--;
|
|
if (m_apuSq1PeriodDevider <= 0)
|
|
{
|
|
m_apuSq1PeriodDevider = m_apuSq1Timer + 1;
|
|
m_apuSq1Seqencer = (m_apuSq1Seqencer + 1) & 0x7;
|
|
if (m_apuSq1LengthCounter > 0 && m_apuSq1ValidFreq)
|
|
{
|
|
if (EmuSettings::Audio::ChannelEnabled::SQ1)
|
|
m_apuSq1Output = m_apu.m_sqDutyCycleSequences[m_apuSq1DutyCycle][m_apuSq1Seqencer] * m_apuSq1Envelope;
|
|
}
|
|
else
|
|
m_apuSq1Output = 0;
|
|
}
|
|
}
|
|
|
|
void ApuSq1::apuSq1ClockLength()
|
|
{
|
|
if (m_apuSq1LengthCounter > 0 && !m_apuSq1LengthHalt)
|
|
{
|
|
m_apuSq1LengthCounter--;
|
|
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() == 0x3 && m_apu.regAccessW())
|
|
{
|
|
m_apuSq1IgnoreReload = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sweep unit
|
|
m_apuSq1SweepCounter--;
|
|
if (m_apuSq1SweepCounter == 0)
|
|
{
|
|
m_apuSq1SweepCounter = m_apuSq1SweepDeviderPeriod + 1;
|
|
if (m_apuSq1SweepEnable && m_apuSq1SweepShiftCount > 0 && m_apuSq1ValidFreq)
|
|
{
|
|
m_apuSq1SweepChange = m_apuSq1Timer >> m_apuSq1SweepShiftCount;
|
|
m_apuSq1Timer += m_apuSq1SweepNegate ? ~m_apuSq1SweepChange : m_apuSq1SweepChange;
|
|
apuSq1CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
if (m_apuSq1SweepReload)
|
|
{
|
|
m_apuSq1SweepCounter = m_apuSq1SweepDeviderPeriod + 1;
|
|
m_apuSq1SweepReload = false;
|
|
}
|
|
}
|
|
|
|
void ApuSq1::apuSq1ClockEnvelope()
|
|
{
|
|
if (m_apuSq1EnvelopeStartFlag)
|
|
{
|
|
m_apuSq1EnvelopeStartFlag = false;
|
|
m_apuSq1EnvelopeDecayLevelCounter = 15;
|
|
m_apuSq1EnvelopeDevider = m_apuSq1VolumeDeviderPeriod + 1;
|
|
}
|
|
else
|
|
{
|
|
if (m_apuSq1EnvelopeDevider > 0)
|
|
m_apuSq1EnvelopeDevider--;
|
|
else
|
|
{
|
|
m_apuSq1EnvelopeDevider = m_apuSq1VolumeDeviderPeriod + 1;
|
|
if (m_apuSq1EnvelopeDecayLevelCounter > 0)
|
|
m_apuSq1EnvelopeDecayLevelCounter--;
|
|
else if (m_apuSq1LengthHalt)
|
|
m_apuSq1EnvelopeDecayLevelCounter = 0xF;
|
|
}
|
|
}
|
|
m_apuSq1Envelope = m_apuSq1ConstantVolumeEnvelope ? m_apuSq1VolumeDeviderPeriod : m_apuSq1EnvelopeDecayLevelCounter;
|
|
}
|
|
|
|
void ApuSq1::apuOnRegister4000()
|
|
{
|
|
// Only writes accepted
|
|
if (!m_apu.regAccessW())
|
|
return;
|
|
m_apuSq1DutyCycle = (m_apu.regIoDb() & 0xC0) >> 6;
|
|
m_apuSq1VolumeDeviderPeriod = m_apu.regIoDb() & 0xF;
|
|
m_apuSq1LengthHalt = m_apu.regIoDb() & 0x20;
|
|
m_apuSq1ConstantVolumeEnvelope = m_apu.regIoDb() & 0x10;
|
|
|
|
m_apuSq1Envelope = m_apuSq1ConstantVolumeEnvelope ? m_apuSq1VolumeDeviderPeriod : m_apuSq1EnvelopeDecayLevelCounter;
|
|
}
|
|
|
|
void ApuSq1::apuOnRegister4001()
|
|
{
|
|
// Only writes accepted
|
|
if (!m_apu.regAccessW())
|
|
return;
|
|
|
|
m_apuSq1SweepEnable = (m_apu.regIoDb() & 0x80) == 0x80;
|
|
m_apuSq1SweepDeviderPeriod = (m_apu.regIoDb() >> 4) & 7;
|
|
m_apuSq1SweepNegate = (m_apu.regIoDb() & 0x8) == 0x8;
|
|
m_apuSq1SweepShiftCount = m_apu.regIoDb() & 7;
|
|
m_apuSq1SweepReload = true;
|
|
apuSq1CalculateValidFreq();
|
|
}
|
|
|
|
void ApuSq1::apuOnRegister4002()
|
|
{
|
|
// Only writes accepted
|
|
if (!m_apu.regAccessW())
|
|
return;
|
|
|
|
m_apuSq1Timer = (m_apuSq1Timer & 0xFF00) | m_apu.regIoDb();
|
|
apuSq1CalculateValidFreq();
|
|
}
|
|
|
|
void ApuSq1::apuOnRegister4003()
|
|
{
|
|
// Only writes accepted
|
|
if (!m_apu.regAccessW())
|
|
return;
|
|
|
|
m_apuSq1Timer = (m_apuSq1Timer & 0x00FF) | ((m_apu.regIoDb() & 0x7) << 8);
|
|
|
|
if (m_apuSq1LengthEnabled && !m_apuSq1IgnoreReload)
|
|
{
|
|
m_apuSq1LengthCounter = m_apu.m_sqDurationTable[m_apu.regIoDb() >> 3];
|
|
}
|
|
|
|
m_apuSq1IgnoreReload = false;
|
|
|
|
m_apuSq1Seqencer = 0;
|
|
m_apuSq1EnvelopeStartFlag = true;
|
|
apuSq1CalculateValidFreq();
|
|
}
|
|
|
|
void ApuSq1::apuSq1On4015()
|
|
{
|
|
m_apuSq1LengthEnabled = m_apu.regIoDb() & 0x01;
|
|
if (!m_apuSq1LengthEnabled)
|
|
m_apuSq1LengthCounter = 0;
|
|
}
|
|
|
|
void ApuSq1::apuSq1Read4015()
|
|
{
|
|
if (m_apuSq1LengthCounter > 0)
|
|
m_apu.setRegIoDb((m_apu.regIoDb() & 0xFE) | 0x01);
|
|
}
|
|
|
|
void ApuSq1::apuSq1CalculateValidFreq()
|
|
{
|
|
m_apuSq1ValidFreq = (m_apuSq1Timer >= 0x8) && ((m_apuSq1SweepNegate) || (((m_apuSq1Timer + (m_apuSq1Timer >> m_apuSq1SweepShiftCount)) & 0x800) == 0));
|
|
}
|
|
|
|
void ApuSq1::apuSq1WriteState(QDataStream &dataStream) const
|
|
{
|
|
dataStream << m_apuSq1DutyCycle << m_apuSq1LengthHalt << m_apuSq1ConstantVolumeEnvelope << m_apuSq1VolumeDeviderPeriod
|
|
<< m_apuSq1SweepEnable << m_apuSq1SweepDeviderPeriod << m_apuSq1SweepNegate << m_apuSq1SweepShiftCount << m_apuSq1Timer
|
|
<< m_apuSq1PeriodDevider << m_apuSq1Seqencer << m_apuSq1LengthEnabled << m_apuSq1LengthCounter << m_apuSq1EnvelopeStartFlag
|
|
<< m_apuSq1EnvelopeDevider << m_apuSq1EnvelopeDecayLevelCounter << m_apuSq1Envelope << m_apuSq1SweepCounter
|
|
<< m_apuSq1SweepReload << m_apuSq1SweepChange << m_apuSq1ValidFreq << m_apuSq1Output << m_apuSq1IgnoreReload;
|
|
}
|
|
|
|
void ApuSq1::apuSq1ReadState(QDataStream &dataStream)
|
|
{
|
|
dataStream >> m_apuSq1DutyCycle >> m_apuSq1LengthHalt >> m_apuSq1ConstantVolumeEnvelope >> m_apuSq1VolumeDeviderPeriod
|
|
>> m_apuSq1SweepEnable >> m_apuSq1SweepDeviderPeriod >> m_apuSq1SweepNegate >> m_apuSq1SweepShiftCount >> m_apuSq1Timer
|
|
>> m_apuSq1PeriodDevider >> m_apuSq1Seqencer >> m_apuSq1LengthEnabled >> m_apuSq1LengthCounter >> m_apuSq1EnvelopeStartFlag
|
|
>> m_apuSq1EnvelopeDevider >> m_apuSq1EnvelopeDecayLevelCounter >> m_apuSq1Envelope >> m_apuSq1SweepCounter
|
|
>> m_apuSq1SweepReload >> m_apuSq1SweepChange >> m_apuSq1ValidFreq >> m_apuSq1Output >> m_apuSq1IgnoreReload;
|
|
}
|
|
|
|
qint32 ApuSq1::output() const
|
|
{
|
|
return m_apuSq1Output;
|
|
}
|