Skip to content

Instantly share code, notes, and snippets.

@ITotalJustice
Created November 28, 2022 12:41
Show Gist options
  • Select an option

  • Save ITotalJustice/2834e3ea28c65bb4cfc4cc5a2e096e22 to your computer and use it in GitHub Desktop.

Select an option

Save ITotalJustice/2834e3ea28c65bb4cfc4cc5a2e096e22 to your computer and use it in GitHub Desktop.
nes square
#include "apu.h"
uint8_t envelope_get_volume(const struct Nes_ApuEnvelope* envelope)
{
return envelope->constant_volume_flag ? envelope->volume : envelope->counter;
}
void envelope_reload(struct Nes_ApuEnvelope* envelope)
{
envelope->start_flag = true;
}
void clock_envelope(struct Nes_ApuEnvelope* envelope)
{
if (envelope->start_flag)
{
envelope->counter = 15;
envelope->divider = envelope->volume;
envelope->start_flag = false;
}
else
{
if (envelope->divider == 0)
{
envelope->divider = envelope->volume;
if (envelope->counter != 0)
{
envelope->counter--;
}
else if (envelope->loop_flag)
{
envelope->counter = 15;
}
}
else
{
envelope->divider--;
}
}
}
#include "apu.h"
static const uint8_t LENGTH_COUNTER_TABLE[0x20] =
{
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E,
};
void length_reload(struct Nes_ApuLength* length, uint8_t index)
{
length->counter = LENGTH_COUNTER_TABLE[index];
}
void clock_length(struct Nes_ApuLength* length)
{
if (length->halt)
{
return;
}
if (length->counter > 0)
{
length->counter--;
}
}
#include "../nes.h"
#include "../internal.h"
#include "apu.h"
static const bool SQUARE_DUTY[4][8] =
{
[0] = { 0, 1, 0, 0, 0, 0, 0, 0 }, // (12.5%)
[1] = { 0, 1, 1, 0, 0, 0, 0, 0 }, // (25%)
[2] = { 0, 1, 1, 1, 1, 0, 0, 0 }, // (50%)
[3] = { 1, 0, 0, 1, 1, 1, 1, 1 }, // (25% negated)
};
bool is_square1_enabled(const struct NES_Core* nes)
{
return STATUS.square1_enable;
}
bool is_square2_enabled(const struct NES_Core* nes)
{
return STATUS.square1_enable;
}
static uint16_t get_square_freq(const struct NES_Square* square)
{
return square->timer_reload;
}
void clock_square_length(struct NES_Square* square)
{
clock_length(&square->length);
}
void clock_square_envelope(struct NES_Square* square)
{
clock_envelope(&square->envelope);
}
void clock_square_sweep(struct NES_Square* square, bool is_square2)
{
clock_sweep(&square->sweep, &square->timer_reload, is_square2);
}
static void clock_square_duty(struct NES_Square* square)
{
++square->duty_index;
square->duty_index &= 0x7;
}
uint8_t sample_square(const struct NES_Square* square)
{
if (square->length.counter == 0)
{
return 0;
}
if (square->sweep.muted)
{
return 0;
}
if (!SQUARE_DUTY[square->duty][square->duty_index])
{
return 0;
}
return envelope_get_volume(&square->envelope);
}
void tick_square_channel(struct NES_Square* square, const uint16_t cycles_elapsed)
{
if (square->timer > 0 || get_square_freq(square))
{
square->timer -= cycles_elapsed;
if (square->timer <= 0)
{
square->timer += get_square_freq(square) * 2 + 1;
clock_square_duty(square);
}
}
}
#include "apu.h"
void sweep_update_target_period(struct NES_ApuSweep* sweep, uint16_t timer, bool is_square2)
{
const uint16_t current_period = timer;
const uint16_t sum = current_period >> sweep->shift_count;
if (sweep->negate_flag)
{
sweep->target_period = current_period - sum;
if (!is_square2)
{
sweep->target_period--;
}
}
else
{
sweep->target_period = current_period + sum;
}
if (current_period < 8 || sweep->target_period > 0x7FF)
{
sweep->muted = true;
}
else
{
sweep->muted = false;
}
}
void clock_sweep(struct NES_ApuSweep* sweep, uint16_t* timer, bool is_square2)
{
if (sweep->divider == 0 && sweep->enabled_flag && !sweep->muted && sweep->shift_count)
{
*timer = sweep->target_period;
sweep_update_target_period(sweep, *timer, is_square2);
}
if (sweep->divider == 0 || sweep->reload_flag)
{
sweep->divider = sweep->period;
sweep->reload_flag = false;
}
else
{
sweep->divider--;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment