From 0e22ad8eb1526f7a83720f47168a112429cdd20c Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 11 Aug 2017 17:57:08 +0300 Subject: [PATCH] Noise channel support --- Core/apu.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++--- Core/apu.h | 18 ++++++- 2 files changed, 156 insertions(+), 10 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index bf49be9..ce24054 100755 --- a/Core/apu.c +++ b/Core/apu.c @@ -86,7 +86,7 @@ static void sweep_event(GB_gameboy_t *gb) { gb->apu.square_channels[GB_SQUARE_1].sample_length = new_sweep_frequency(gb); /* Overflow checking only occurs after a delay */ - gb->apu.square_sweep_stop_countdown = 0x13 - gb->apu.square_carry; + gb->apu.square_sweep_stop_countdown = 0x13 - gb->apu.lf_div; gb->apu.square_channels[GB_SQUARE_1].sample_length &= 0x7FF; gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7); @@ -140,6 +140,36 @@ void GB_apu_div_event(GB_gameboy_t *gb) } } } + + if (gb->apu.noise_channel.length_enabled) { + if (gb->apu.noise_channel.pulse_length) { + if (!--gb->apu.noise_channel.pulse_length) { + gb->apu.is_active[GB_NOISE] = false; + update_sample(gb, GB_NOISE, 0, 0); + } + } + } + + uint8_t nr42 = gb->io_registers[GB_IO_NR42]; + + if (gb->apu.noise_channel.volume_countdown) { + if (!--gb->apu.noise_channel.volume_countdown) { + if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) { + gb->apu.noise_channel.current_volume++; + } + + else if (!(nr42 & 8) && gb->apu.noise_channel.current_volume > 0) { + gb->apu.noise_channel.current_volume--; + } + + gb->apu.noise_channel.volume_countdown = (nr42 & 7) * 4; + + update_sample(gb, GB_NOISE, + (gb->apu.noise_channel.lfsr & 1) ? + gb->apu.noise_channel.current_volume : 0, + 0); + } + } } if ((gb->apu.div_divider & 3) == 3) { @@ -158,8 +188,9 @@ void GB_apu_run(GB_gameboy_t *gb) uint8_t cycles = gb->apu.apu_cycles >> 1; gb->apu.apu_cycles = 0; if (!cycles) return; + /* To align the square signal to 1MHz */ - gb->apu.square_carry ^= cycles & 1; + gb->apu.lf_div ^= cycles & 1; if (gb->apu.square_sweep_stop_countdown) { if (gb->apu.square_sweep_stop_countdown > cycles) { @@ -219,6 +250,36 @@ void GB_apu_run(GB_gameboy_t *gb) } } + if (gb->apu.is_active[GB_NOISE]) { + uint8_t cycles_left = cycles; + while (unlikely(cycles_left > gb->apu.noise_channel.sample_countdown)) { + cycles_left -= gb->apu.noise_channel.sample_countdown + 1; + gb->apu.noise_channel.sample_countdown = gb->apu.noise_channel.sample_length * 2 + 1; + + /* Step LFSR */ + unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000; + // This formula is different on a GBA! + bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1; + gb->apu.noise_channel.lfsr >>= 1; + + if (new_high_bit) { + gb->apu.noise_channel.lfsr |= high_bit_mask; + } + else { + /* This code is not redundent, it's relevant when switching LFSR widths */ + gb->apu.noise_channel.lfsr &= ~high_bit_mask; + } + + update_sample(gb, GB_NOISE, + (gb->apu.noise_channel.lfsr & 1) ? + gb->apu.noise_channel.current_volume : 0, + 0); + } + if (cycles_left) { + gb->apu.noise_channel.sample_countdown -= cycles_left; + } + } + if (gb->apu_output.sample_rate) { gb->apu_output.cycles_since_render += cycles; double cycles_per_sample = CPU_FREQUENCY / (double)gb->apu_output.sample_rate; // TODO: this should be cached! @@ -267,7 +328,8 @@ void GB_apu_init(GB_gameboy_t *gb) for (int i = 0; i < 4; i++) { gb->apu.left_enabled[i] = gb->apu.right_enabled[i] = true; } - gb->apu.square_carry = 1; + gb->apu.lf_div = 1; + gb->apu.noise_channel.sample_length = 1; } uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg) @@ -350,6 +412,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) } break; + /* Square channels */ case GB_IO_NR10: gb->apu.square_sweep_countdown = ((value >> 4) & 7); break; @@ -382,7 +445,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) break; } - /* Square channels */ case GB_IO_NR14: case GB_IO_NR24: { unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1; @@ -392,17 +454,17 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) gb->apu.square_channels[index].current_sample_index = 7; if (!gb->apu.is_active[index]) { - gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 6 - gb->apu.square_carry; + gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 6 - gb->apu.lf_div; } else { /* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier. if both active AND already emitted a sample, sound starts the next 1MHz tick, and one sample is skipped */ if (!gb->apu.square_channels[index].sample_emitted) { - gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.square_carry; + gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.lf_div; } else { - gb->apu.square_channels[index].sample_countdown = gb->apu.square_carry; + gb->apu.square_channels[index].sample_countdown = gb->apu.lf_div; gb->apu.square_channels[index].current_sample_index = 0; } } @@ -419,7 +481,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) if (index == GB_SQUARE_1 && gb->io_registers[GB_IO_NR10] & 7) { /* APU bug: if shift is nonzero, overflow check also occurs on trigger */ - gb->apu.square_sweep_stop_countdown = 0x13 - gb->apu.square_carry; + gb->apu.square_sweep_stop_countdown = 0x13 - gb->apu.lf_div; } /* Note that we don't change the sample just yet! This was verified on hardware. */ @@ -524,6 +586,76 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) break; + /* Noise Channel */ + + case GB_IO_NR41: { + gb->apu.noise_channel.pulse_length = (0x40 - (value & 0x3f)); + break; + } + + case GB_IO_NR42: { + /* TODO: What happens when changing bits 0-2 after triggering? */ + if ((value & 0xF8) == 0) { + /* According to Blargg's test ROM this should disable the channel instantly + TODO: verify how "instant" the change is using PCM34 */ + update_sample(gb, GB_NOISE, 0, 0); + gb->apu.is_active[GB_NOISE] = false; + } + break; + } + + case GB_IO_NR43: { + gb->apu.noise_channel.narrow = value & 8; + unsigned divisor = (value & 0x07) << 2; + if (!divisor) divisor = 2; + gb->apu.noise_channel.sample_length = (divisor << (value >> 4)) - 1; + break; + } + + case GB_IO_NR44: { + if (value & 0x80) { + gb->apu.noise_channel.lfsr = 0; + + gb->apu.noise_channel.sample_countdown = (gb->apu.noise_channel.sample_length) * 2 + 4 - gb->apu.lf_div; + if (gb->apu.is_active[GB_NOISE]) { + gb->apu.noise_channel.sample_countdown += 2; + } + + gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4; + gb->apu.noise_channel.volume_countdown = (gb->io_registers[GB_IO_NR42] & 7) * 4; + + if ((gb->io_registers[GB_IO_NR42] & 0xF8) != 0) { + gb->apu.is_active[GB_NOISE] = true; + } + + if (gb->apu.noise_channel.pulse_length == 0) { + gb->apu.noise_channel.pulse_length = 0x40; + gb->apu.noise_channel.length_enabled = false; + } + + /* Note that we don't change the sample just yet! This was verified on hardware. */ + } + + /* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */ + if ((value & 0x40) && + !gb->apu.noise_channel.length_enabled && + (gb->apu.div_divider & 1) && + gb->apu.noise_channel.pulse_length) { + gb->apu.noise_channel.pulse_length--; + if (gb->apu.noise_channel.pulse_length == 0) { + if (value & 0x80) { + gb->apu.noise_channel.pulse_length = 0x3F; + } + else { + update_sample(gb, GB_NOISE, 0, 0); + gb->apu.is_active[GB_NOISE] = false; + } + } + } + gb->apu.noise_channel.length_enabled = value & 0x40; + break; + } + default: if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) { gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4; diff --git a/Core/apu.h b/Core/apu.h index d67ecd6..8b96562 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -40,8 +40,8 @@ typedef struct uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided // once more to generate 128Hz and 64Hz clocks - uint8_t square_carry; // The square channels tick at 1MHz instead of 2, - // so we need a carry to divide the signal + uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide + // need to divide the signal. uint8_t square_sweep_countdown; // In 128Hz uint8_t square_sweep_stop_countdown; // In 2 MHz @@ -73,6 +73,20 @@ typedef struct int8_t wave_form[32]; bool wave_form_just_read; } wave_channel; + + struct { + uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks + uint8_t current_volume; // Reloaded from NR42 + uint8_t volume_countdown; // Reloaded from NR42 + uint16_t lfsr; + bool narrow; + + uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length) + uint16_t sample_length; // From NR43, in APU ticks + bool length_enabled; // NR44 + + } noise_channel; + } GB_apu_t; typedef struct {