#include "channel4.h"
#include "../savestate.h"
#include <algorithm>
static unsigned long toPeriod(const unsigned nr3) {
unsigned s = (nr3 >> 4) + 3;
unsigned r = nr3 & 7;
if (!r) {
r = 1;
--s;
}
return r << s;
}
namespace gambatte {
Channel4::Lfsr::Lfsr() :
backupCounter(COUNTER_DISABLED),
reg(0x7FFF),
nr3(0),
master(false)
{}
void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
if (backupCounter <= cc) {
const unsigned long period = toPeriod(nr3);
unsigned long periods = (cc - backupCounter) / period + 1;
backupCounter += periods * period;
if (master && nr3 < 0xE0) {
if (nr3 & 8) {
while (periods > 6) {
const unsigned xored = (reg << 1 ^ reg) & 0x7E;
reg = (reg >> 6 & ~0x7E) | xored | xored << 8;
periods -= 6;
}
const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F;
reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8;
} else {
while (periods > 15) {
reg = reg ^ reg >> 1;
periods -= 15;
}
reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF);
}
}
}
}
void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
updateBackupCounter(cc);
counter = backupCounter;
}
inline void Channel4::Lfsr::event() {
if (nr3 < 0xE0) {
const unsigned shifted = reg >> 1;
const unsigned xored = (reg ^ shifted) & 1;
reg = shifted | xored << 14;
if (nr3 & 8)
reg = (reg & ~0x40) | xored << 6;
}
counter += toPeriod(nr3);
backupCounter = counter;
}
void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
updateBackupCounter(cc);
nr3 = newNr3;
}
void Channel4::Lfsr::nr4Init(unsigned long cc) {
disableMaster();
updateBackupCounter(cc);
master = true;
backupCounter += 4;
counter = backupCounter;
}
void Channel4::Lfsr::reset(const unsigned long cc) {
nr3 = 0;
disableMaster();
backupCounter = cc + toPeriod(nr3);
}
void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
updateBackupCounter(oldCc);
backupCounter -= COUNTER_MAX;
SoundUnit::resetCounters(oldCc);
}
void Channel4::Lfsr::loadState(const SaveState &state) {
counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter);
reg = state.spu.ch4.lfsr.reg;
master = state.spu.ch4.master;
nr3 = state.mem.ioamhram.get()[0x122];
}
template<bool isReader>
void Channel4::Lfsr::SyncState(NewState *ns)
{
NSS(counter);
NSS(backupCounter);
NSS(reg);
NSS(nr3);
NSS(master);
}
Channel4::Channel4() :
staticOutputTest(*this, lfsr),
disableMaster(master, lfsr),
lengthCounter(disableMaster, 0x3F),
envelopeUnit(staticOutputTest),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
setEvent();
}
void Channel4::setEvent() {
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
}
void Channel4::setNr1(const unsigned data) {
lengthCounter.nr1Change(data, nr4, cycleCounter);
setEvent();
}
void Channel4::setNr2(const unsigned data) {
if (envelopeUnit.nr2Change(data))
disableMaster();
else
staticOutputTest(cycleCounter);
setEvent();
}
void Channel4::setNr4(const unsigned data) {
lengthCounter.nr4Change(nr4, data, cycleCounter);
nr4 = data;
if (data & 0x80) {
nr4 &= 0x7F;
master = !envelopeUnit.nr4Init(cycleCounter);
if (master)
lfsr.nr4Init(cycleCounter);
staticOutputTest(cycleCounter);
}
setEvent();
}
void Channel4::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
void Channel4::reset() {
cycleCounter = 0x1000 | (cycleCounter & 0xFFF);
lfsr.reset(cycleCounter);
envelopeUnit.reset();
setEvent();
}
void Channel4::init(const bool cgb) {
lengthCounter.init(cgb);
}
void Channel4::loadState(const SaveState &state) {
lfsr.loadState(state);
envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch4.nr4;
master = state.spu.ch4.master;
}
void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
for (;;) {
const unsigned long outHigh = outBase * (envelopeUnit.getVolume() * 2 - 15ul);
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = lfsr.isHighState() ? outHigh : outLow;
while (lfsr.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += lfsr.getCounter() - cycleCounter;
cycleCounter = lfsr.getCounter();
lfsr.event();
out = lfsr.isHighState() ? outHigh : outLow;
}
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {
lengthCounter.resetCounters(cycleCounter);
lfsr.resetCounters(cycleCounter);
envelopeUnit.resetCounters(cycleCounter);
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
SYNCFUNC(Channel4)
{
SSS(lengthCounter);
SSS(envelopeUnit);
SSS(lfsr);
EBS(nextEventUnit, 0);
EVS(nextEventUnit, &lfsr, 1);
EVS(nextEventUnit, &envelopeUnit, 2);
EVS(nextEventUnit, &lengthCounter, 3);
EES(nextEventUnit, NULL);
NSS(cycleCounter);
NSS(soMask);
NSS(prevOut);
NSS(nr4);
NSS(master);
}
}