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

221 lines
5.9 KiB
C++

#include "apunos.h"
// Qt includes
#include <QDataStream>
// local includes
#include "emusettings.h"
#include "apu.h"
ApuNos::ApuNos(Apu &apu) :
m_apu(apu)
{
}
void ApuNos::apuNosHardReset()
{
m_apuNosLengthHalt = false;
m_apuNosConstantVolumeEnvelope = false;
m_apuNosVolumeDeviderPeriod = 0;
m_apuNosShiftReg = 1;
m_apuNosTimer = 0;
m_apuNosMode = false;
m_apuNosPeriodDevider = 0;
m_apuNosLengthEnabled = false;
m_apuNosLengthCounter = 0;
m_apuNosEnvelopeStartFlag = false;
m_apuNosEnvelopeDevider = 0;
m_apuNosEnvelopeDecayLevelCounter = 0;
m_apuNosEnvelope = 0;
m_apuNosOutput = 0;
m_apuNosFeedback = 0;
m_apuNosIgnoreReload = false;
}
void ApuNos::apuNosSoftReset()
{
apuNosHardReset();
}
void ApuNos::apuNosClock()
{
m_apuNosPeriodDevider--;
if (m_apuNosPeriodDevider > 0)
return;
m_apuNosPeriodDevider = m_apuNosTimer;
if (m_apuNosMode)
m_apuNosFeedback = ((m_apuNosShiftReg >> 6) & 0x1) ^ (m_apuNosShiftReg & 0x1);
else
m_apuNosFeedback = ((m_apuNosShiftReg >> 1) & 0x1) ^ (m_apuNosShiftReg & 0x1);
m_apuNosShiftReg >>= 1;
m_apuNosShiftReg = (m_apuNosShiftReg & 0x3FFF) | ((m_apuNosFeedback & 1) << 14);
if (m_apuNosLengthCounter > 0 && ((m_apuNosShiftReg & 1) == 0))
{
if (EmuSettings::Audio::ChannelEnabled::NOZ)
m_apuNosOutput = m_apuNosEnvelope;
}
else
m_apuNosOutput = 0;
}
void ApuNos::apuNosClockLength()
{
if (m_apuNosLengthCounter > 0 && !m_apuNosLengthHalt)
{
m_apuNosLengthCounter--;
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() == 0xF && m_apu.regAccessW())
{
m_apuNosIgnoreReload = true;
}
}
}
}
void ApuNos::apuNosClockEnvelope()
{
if (m_apuNosEnvelopeStartFlag)
{
m_apuNosEnvelopeStartFlag = false;
m_apuNosEnvelopeDecayLevelCounter = 15;
m_apuNosEnvelopeDevider = m_apuNosVolumeDeviderPeriod + 1;
}
else
{
if (m_apuNosEnvelopeDevider > 0)
m_apuNosEnvelopeDevider--;
else
{
m_apuNosEnvelopeDevider = m_apuNosVolumeDeviderPeriod + 1;
if (m_apuNosEnvelopeDecayLevelCounter > 0)
m_apuNosEnvelopeDecayLevelCounter--;
else if (m_apuNosLengthHalt)
m_apuNosEnvelopeDecayLevelCounter = 0xF;
}
}
m_apuNosEnvelope = m_apuNosConstantVolumeEnvelope ? m_apuNosVolumeDeviderPeriod : m_apuNosEnvelopeDecayLevelCounter;
}
void ApuNos::apuOnRegister400C()
{
// Only writes accepted
if (!m_apu.regAccessW())
return;
m_apuNosVolumeDeviderPeriod = m_apu.regIoDb() & 0xF;
m_apuNosLengthHalt = (m_apu.regIoDb() & 0x20) != 0;
m_apuNosConstantVolumeEnvelope = m_apu.regIoDb() & 0x10;
m_apuNosEnvelope = m_apuNosConstantVolumeEnvelope ? m_apuNosVolumeDeviderPeriod : m_apuNosEnvelopeDecayLevelCounter;
}
void ApuNos::apuOnRegister400D()
{
}
void ApuNos::apuOnRegister400E()
{
static constexpr std::array<qint32, 16> freqTable = [](){
switch(EmuSettings::region)
{
case EmuRegion::NTSC:
return std::array<qint32, 16> {
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
};
case EmuRegion::PALB:
return std::array<qint32, 16> {
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778
};
case EmuRegion::DENDY:
return std::array<qint32, 16> {
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
};
}
}();
// Only writes accepted
if (!m_apu.regAccessW())
return;
m_apuNosTimer = freqTable[m_apu.regIoDb() & 0x0F] / 2;
m_apuNosMode = (m_apu.regIoDb() & 0x80) == 0x80;
}
void ApuNos::apuOnRegister400F()
{
// Only writes accepted
if (!m_apu.regAccessW())
return;
if (m_apuNosLengthEnabled && !m_apuNosIgnoreReload)
m_apuNosLengthCounter = m_apu.m_sqDurationTable[m_apu.regIoDb() >> 3];
if (m_apuNosIgnoreReload)
m_apuNosIgnoreReload = false;
m_apuNosEnvelopeStartFlag = true;
}
void ApuNos::apuNosOn4015()
{
m_apuNosLengthEnabled = (m_apu.regIoDb() & 0x08) != 0;
if (!m_apuNosLengthEnabled)
m_apuNosLengthCounter = 0;
}
void ApuNos::apuNosRead4015()
{
if (m_apuNosLengthCounter > 0)
m_apu.setRegIoDb((m_apu.regIoDb() & 0xF7) | 0x08);
}
void ApuNos::apuNosWriteState(QDataStream &dataStream) const
{
dataStream
<< m_apuNosLengthHalt
<< m_apuNosConstantVolumeEnvelope
<< m_apuNosVolumeDeviderPeriod
<< m_apuNosTimer
<< m_apuNosMode
<< m_apuNosPeriodDevider
<< m_apuNosLengthEnabled
<< m_apuNosLengthCounter
<< m_apuNosEnvelopeStartFlag
<< m_apuNosEnvelopeDevider
<< m_apuNosEnvelopeDecayLevelCounter
<< m_apuNosEnvelope
<< m_apuNosOutput
<< m_apuNosShiftReg
<< m_apuNosFeedback
<< m_apuNosIgnoreReload;
}
void ApuNos::apuNosReadState(QDataStream &dataStream)
{
dataStream
>> m_apuNosLengthHalt
>> m_apuNosConstantVolumeEnvelope
>> m_apuNosVolumeDeviderPeriod
>> m_apuNosTimer
>> m_apuNosMode
>> m_apuNosPeriodDevider
>> m_apuNosLengthEnabled
>> m_apuNosLengthCounter
>> m_apuNosEnvelopeStartFlag
>> m_apuNosEnvelopeDevider
>> m_apuNosEnvelopeDecayLevelCounter
>> m_apuNosEnvelope
>> m_apuNosOutput
>> m_apuNosShiftReg
>> m_apuNosFeedback
>> m_apuNosIgnoreReload;
}
qint32 ApuNos::output() const
{
return m_apuNosOutput;
}