#include "channel1.h"
#include "../savestate.h"
#include <algorithm>
namespace gambatte {
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) :
disableMaster(disabler),
dutyUnit(dutyUnit),
shadow(0),
nr0(0),
negging(false)
{}
unsigned Channel1::SweepUnit::calcFreq() {
unsigned freq = shadow >> (nr0 & 0x07);
if (nr0 & 0x08) {
freq = shadow - freq;
negging = true;
} else
freq = shadow + freq;
if (freq & 2048)
disableMaster();
return freq;
}
void Channel1::SweepUnit::event() {
const unsigned long period = nr0 >> 4 & 0x07;
if (period) {
const unsigned freq = calcFreq();
if (!(freq & 2048) && (nr0 & 0x07)) {
shadow = freq;
dutyUnit.setFreq(freq, counter);
calcFreq();
}
counter += period << 14;
} else
counter += 8ul << 14;
}
void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
if (negging && !(newNr0 & 0x08))
disableMaster();
nr0 = newNr0;
}
void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
negging = false;
shadow = dutyUnit.getFreq();
const unsigned period = nr0 >> 4 & 0x07;
const unsigned shift = nr0 & 0x07;
if (period | shift)
counter = ((cc >> 14) + (period ? period : 8)) << 14;
else
counter = COUNTER_DISABLED;
if (shift)
calcFreq();
}
void Channel1::SweepUnit::reset() {
counter = COUNTER_DISABLED;
}
void Channel1::SweepUnit::loadState(const SaveState &state) {
counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
shadow = state.spu.ch1.sweep.shadow;
nr0 = state.spu.ch1.sweep.nr0;
negging = state.spu.ch1.sweep.negging;
}
template<bool isReader>
void Channel1::SweepUnit::SyncState(NewState *ns)
{
NSS(counter);
NSS(shadow);
NSS(nr0);
NSS(negging);
}
Channel1::Channel1() :
staticOutputTest(*this, dutyUnit),
disableMaster(master, dutyUnit),
lengthCounter(disableMaster, 0x3F),
envelopeUnit(staticOutputTest),
sweepUnit(disableMaster, dutyUnit),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
setEvent();
}
void Channel1::setEvent() {
nextEventUnit = &sweepUnit;
if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
}
void Channel1::setNr0(const unsigned data) {
sweepUnit.nr0Change(data);
setEvent();
}
void Channel1::setNr1(const unsigned data) {
lengthCounter.nr1Change(data, nr4, cycleCounter);
dutyUnit.nr1Change(data, cycleCounter);
setEvent();
}
void Channel1::setNr2(const unsigned data) {
if (envelopeUnit.nr2Change(data))
disableMaster();
else
staticOutputTest(cycleCounter);
setEvent();
}
void Channel1::setNr3(const unsigned data) {
dutyUnit.nr3Change(data, cycleCounter);
setEvent();
}
void Channel1::setNr4(const unsigned data) {
lengthCounter.nr4Change(nr4, data, cycleCounter);
nr4 = data;
dutyUnit.nr4Change(data, cycleCounter);
if (data & 0x80) {
nr4 &= 0x7F;
master = !envelopeUnit.nr4Init(cycleCounter);
sweepUnit.nr4Init(cycleCounter);
staticOutputTest(cycleCounter);
}
setEvent();
}
void Channel1::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
void Channel1::reset() {
cycleCounter = 0x1000 | (cycleCounter & 0xFFF);
dutyUnit.reset();
envelopeUnit.reset();
sweepUnit.reset();
setEvent();
}
void Channel1::init(const bool cgb) {
lengthCounter.init(cgb);
}
void Channel1::loadState(const SaveState &state) {
sweepUnit.loadState(state);
dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter);
envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch1.nr4;
master = state.spu.ch1.master;
}
void Channel1::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 = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf = out - prevOut;
prevOut = out;
buf += dutyUnit.getCounter() - cycleCounter;
cycleCounter = dutyUnit.getCounter();
dutyUnit.event();
out = dutyUnit.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) {
dutyUnit.resetCounters(cycleCounter);
lengthCounter.resetCounters(cycleCounter);
envelopeUnit.resetCounters(cycleCounter);
sweepUnit.resetCounters(cycleCounter);
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
SYNCFUNC(Channel1)
{
SSS(lengthCounter);
SSS(dutyUnit);
SSS(envelopeUnit);
SSS(sweepUnit);
EBS(nextEventUnit, 0);
EVS(nextEventUnit, &dutyUnit, 1);
EVS(nextEventUnit, &sweepUnit, 2);
EVS(nextEventUnit, &envelopeUnit, 3);
EVS(nextEventUnit, &lengthCounter, 4);
EES(nextEventUnit, NULL);
NSS(cycleCounter);
NSS(soMask);
NSS(prevOut);
NSS(nr4);
NSS(master);
}
}