Code: Select all
template <typename CounterType>
struct Counter {
CounterType counter;
CounterType reload;
std::function<void()> function;
Counter(
const CounterType reload,
const std::function<void()>& function)
: reload{reload}, function{function}, counter{reload} {
}
void tick(CounterType ticks = 1) {
if((counter -= ticks) < 0) {
counter += reload + 1;
function();
}
}
}
struct Dmc {
Apu& apu;
u8_fast irqId;
u8_fast volume {0};
bool irqEnabled {true};
bool silence;
bool enabled {false};
bool finished;
bool loop;
u8_fast shiftRegister;
//-1 indicates an empty sample buffer:
s16_fast sampleBuffer {-1};
u16_fast startAddress;
u16_fast address;
Dmc(Apu& apu)
: apu{apu} {
irqId = apu.cpu.connectIrq();
}
s16_fast emptySampleBuffer() {
s16_fast tmp {sampleBuffer};
if (enabled) {
if (!finished) {
//TODO: stall cpu
sampleBuffer = apu.cpu.memory[address++];
address |= 0x8000;
}
bytesRemaining.tick();
}
else {
sampleBuffer = -1;
}
return tmp;
}
Counter<s16_fast> bytesRemaining{0, [&] () {
if (loop) {
address = startAddress;
}
else {
finished = true;
bytesRemaining.counter = 0;
if (irqEnabled) {
apu.cpu.pullIrq(irqId);
}
}
}};
Counter<s8_fast> bitsRemaining{7, [&] () {
s16_fast tmp {emptySampleBuffer()};
if (tmp == -1) {
silence = true;
}
else {
silence = false;
shiftRegister = tmp;
}
}};
Counter<s16_fast> timer{0, [&] () {
if (!silence) {
if (shiftRegister & 0x01 && volume <= 125) {
volume += 2;
}
else if (!(shiftRegister & 0x01) && volume >= 2) {
volume -= 2;
}
}
shiftRegister >>= 1;
bitsRemaining.tick();
}};
void tick() {
timer.tick();
if (!irqEnabled) {
apu.cpu.releaseIrq(irqId);
}
}
void toggle(const bool enable) {
enabled = enable;
bytesRemaining.counter = enabled
? (finished
? bytesRemaining.reload
: bytesRemaining.counter)
: 0;
finished = !enabled;
}
u8_fast output() const {
return volume;
}
};