From 10cb978bc3250730a5d69f8f2869acfb68125b0e Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Sun, 5 Jan 2025 02:16:05 -0800 Subject: [PATCH] Complete APU triangle channel --- src/apu.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++------ src/apu.h | 10 +++++-- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/apu.c b/src/apu.c index d0691f3..338ba8e 100644 --- a/src/apu.c +++ b/src/apu.c @@ -63,10 +63,23 @@ static void nes_apu_write_square(nes_apu_Channel* channel, // TODO } +static inline void apu_channel_update_timer( + nes_apu_Channel *channel) { + channel->period = ( + channel->reg[2] | + (((uint16_t)channel->reg[3] & 0x7U) << 8) + ) * nes_clock_cpu_div; +} + static void nes_apu_write_triangle(nes_apu_Channel* channel, int reg, uint8_t val) { APU_LOG("APU: Triangle %d < %02x\n", reg, val); - // TODO + + if (2 <= reg) { + channel->reg[reg] = val; + apu_channel_update_timer(channel); + if (3 == reg) channel->reload = 1; + } } static void nes_apu_write_noise(nes_apu_Channel* channel, @@ -149,7 +162,7 @@ void nes_apu_reset(nes_apu* apu) { apu->channels[chan].length = 0; } - apu->channels[apu_Channel_Triangle].sample = 15; + apu->channels[apu_Channel_Triangle].step = 31; apu->channels[apu_Channel_DMC].reg[1] &= 1; apu->channels[apu_Channel_DMC].sample = 0; } @@ -190,6 +203,7 @@ void nes_apu_write(nes_apu* apu, uint16_t addr, uint8_t val) { if ( chan != apu_Channel_DMC && apu->status & (1 << chan)) { channel->length = apu_length_lut[val >> 3]; + // TODO: Handle Envelope vs. Triangle channel->envelope = 15; channel->env_period = (channel->reg[0] & apu_Envelope_Volume); channel->env_delay = channel->env_period; @@ -330,6 +344,49 @@ static void nes_apu_run_noise(nes_apu* apu, } } +static uint8_t triangle_steps[32] = { + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +}; + +static void nes_apu_run_triangle(nes_apu* apu, + nes_apu_Channel* channel, + int cycles) { + if ( channel->length <= 0 || + channel->period <= 0 || + channel->counter <= 0) { + return; + } + + int time = apu->time; + + while (cycles > 0) { + int run = cycles; + if (run > channel->delay) { + run = channel->delay; + } + + channel->delay -= run; + time += run; + + if (channel->delay <= 0) { + channel->delay = channel->period; + + channel->step = ((channel->step + 1) & 31U); + + channel->sample = triangle_steps[channel->step]; + + int delta = channel_update(channel); + if (delta) { + blip_add_delta(apu->blip, time, delta); + } + } + + cycles -= run; + } +} + + static inline void nes_apu_clock_length(nes_apu_Channel* channel, uint8_t halt_mask) { if ( channel->length > 0 && @@ -350,6 +407,17 @@ static inline void nes_apu_clock_envelope(nes_apu_Channel* channel) { } } +static inline void nes_apu_clock_linear(nes_apu_Channel* channel) { + if (channel->reload) { + channel->counter = (channel->reg[0] & apu_Triangle_Count); + } else if (channel->counter > 0) { + channel->counter--; + } + if (!(channel->reg[0] & apu_Triangle_Halt)) { + channel->reload = 0; + } +} + nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { nes_apu_run_dmc(apu, &apu->channels[apu_Channel_DMC], cycles); @@ -363,11 +431,13 @@ nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { apu->blip, run); nes_apu_run_square(&apu->channels[apu_Channel_Square_1], apu->blip, run); - nes_apu_run_triangle(&apu->channels[apu_Channel_Triangle], - apu->blip, run); */ - nes_apu_run_noise(apu, &apu->channels[apu_Channel_Noise], - run); + nes_apu_run_triangle( + apu, &apu->channels[apu_Channel_Triangle], run + ); + nes_apu_run_noise( + apu, &apu->channels[apu_Channel_Noise], run + ); apu->frame_delay -= run; @@ -400,8 +470,8 @@ nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { /* nes_apu_clock_length(&apu->channels[apu_Channel_Square_0], apu_Envelope_Halt); nes_apu_clock_length(&apu->channels[apu_Channel_Square_1], apu_Envelope_Halt); - nes_apu_clock_length(&apu->channels[apu_Channel_Triangle], apu_Triangle_Halt); */ + nes_apu_clock_length(&apu->channels[apu_Channel_Triangle], apu_Triangle_Halt); nes_apu_clock_length(&apu->channels[apu_Channel_Noise], apu_Envelope_Halt); /* nes_apu_clock_sweep(&apu->channels[apu_Channel_Square_0], -1); @@ -415,9 +485,7 @@ nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { nes_apu_clock_envelope(&apu->channels[apu_Channel_Square_1]); */ nes_apu_clock_envelope(&apu->channels[apu_Channel_Noise]); -/* nes_apu_clock_linear(&apu->channels[apu_Channel_Triangle]); -*/ } if (end) { diff --git a/src/apu.h b/src/apu.h index a970c8e..be85e18 100644 --- a/src/apu.h +++ b/src/apu.h @@ -89,10 +89,10 @@ typedef struct nes_apu_Channel_t { int sample; // Current sample int output; // Last output (sample * gain) - uint16_t period; // Noise, DMC + uint16_t period; union { - // Square/Triangle/Noise + // Square/Noise struct { int phase; int sweep; @@ -101,6 +101,12 @@ typedef struct nes_apu_Channel_t { uint8_t env_delay; uint16_t lfsr; }; + // Triangle + struct { + uint8_t step; + uint8_t reload; + uint8_t counter; + }; // DMC struct { uint8_t interrupt;