From 0485124076aef3e66c9b1095ec0b30ee2329ab51 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 28 Nov 2020 19:31:25 +0200 Subject: [PATCH] Redo channel 1 sweep based on DMG schematics; emulates two newly discovered behaviors and also fixes #309 --- Core/apu.c | 51 ++++++++++++++++++++----------------------------- Core/apu.h | 4 ++-- Core/debugger.c | 2 +- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index 2b5dc3b9..9275f813 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -230,15 +230,6 @@ static void render(GB_gameboy_t *gb) gb->apu_output.sample_callback(gb, &filtered_output); } -static uint16_t new_sweep_sample_length(GB_gameboy_t *gb) -{ - uint16_t delta = gb->apu.shadow_sweep_sample_length >> (gb->io_registers[GB_IO_NR10] & 7); - if (gb->io_registers[GB_IO_NR10] & 8) { - return gb->apu.shadow_sweep_sample_length - delta; - } - return gb->apu.shadow_sweep_sample_length + delta; -} - static void update_square_sample(GB_gameboy_t *gb, unsigned index) { if (gb->apu.square_channels[index].current_sample_index & 0x80) return; @@ -405,21 +396,22 @@ void GB_apu_div_event(GB_gameboy_t *gb) } if ((gb->apu.div_divider & 3) == 3) { - if (!gb->apu.sweep_enabled) { - return; - } if (gb->apu.square_sweep_countdown) { - if (!--gb->apu.square_sweep_countdown) { + if (!--gb->apu.square_sweep_countdown && gb->apu.sweep_enabled) { if ((gb->io_registers[GB_IO_NR10] & 0x70) && (gb->io_registers[GB_IO_NR10] & 0x07)) { gb->apu.square_channels[GB_SQUARE_1].sample_length = - gb->apu.shadow_sweep_sample_length = - gb->apu.new_sweep_sample_length; + gb->apu.sweep_length_addend + gb->apu.shadow_sweep_sample_length + !!(gb->io_registers[GB_IO_NR10] & 0x8); + gb->apu.square_channels[GB_SQUARE_1].sample_length &= 0x7FF; } + gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length; + gb->apu.sweep_length_addend >>= (gb->io_registers[GB_IO_NR10] & 7); if (gb->io_registers[GB_IO_NR10] & 0x70) { /* Recalculation and overflow check only occurs after a delay */ - gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div; + gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 7 - gb->apu.lf_div; } + + gb->apu.sweep_enabled = gb->io_registers[GB_IO_NR10] & 0x70; gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7); if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8; @@ -447,13 +439,14 @@ void GB_apu_run(GB_gameboy_t *gb) } else { /* APU bug: sweep frequency is checked after adding the sweep delta twice */ - gb->apu.new_sweep_sample_length = new_sweep_sample_length(gb); - if (gb->apu.new_sweep_sample_length > 0x7ff) { + gb->apu.shadow_sweep_sample_length = gb->apu.square_channels[GB_SQUARE_1].sample_length; + if (gb->io_registers[GB_IO_NR10] & 8) { + gb->apu.sweep_length_addend ^= 0x7FF; + } + if (gb->apu.shadow_sweep_sample_length + gb->apu.sweep_length_addend > 0x7FF && !(gb->io_registers[GB_IO_NR10] & 8)) { gb->apu.is_active[GB_SQUARE_1] = false; update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles); - gb->apu.sweep_enabled = false; } - gb->apu.sweep_decreasing |= gb->io_registers[GB_IO_NR10] & 8; gb->apu.square_sweep_calculate_countdown = 0; } } @@ -672,10 +665,9 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) /* Square channels */ case GB_IO_NR10: - if (gb->apu.sweep_decreasing && !(value & 8)) { + if (gb->apu.shadow_sweep_sample_length + gb->apu.sweep_length_addend > 0x7FF && !(gb->io_registers[GB_IO_NR10] & 8)) { gb->apu.is_active[GB_SQUARE_1] = false; update_sample(gb, GB_SQUARE_1, 0, 0); - gb->apu.sweep_enabled = false; gb->apu.square_sweep_calculate_countdown = 0; } if ((value & 0x70) == 0) { @@ -744,12 +736,12 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) gb->apu.square_channels[index].sample_length &= 0xFF; gb->apu.square_channels[index].sample_length |= (value & 7) << 8; - if (index == GB_SQUARE_1) { - gb->apu.shadow_sweep_sample_length = - gb->apu.new_sweep_sample_length = - gb->apu.square_channels[0].sample_length; - } if (value & 0x80) { + if (index == GB_SQUARE_1) { + gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length; + gb->apu.shadow_sweep_sample_length = 0; + gb->apu.sweep_enabled = gb->io_registers[GB_IO_NR10] & 0x70; + } /* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by turning the APU off. */ if (!gb->apu.is_active[index]) { @@ -782,15 +774,14 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) } if (index == GB_SQUARE_1) { - gb->apu.sweep_decreasing = false; if (gb->io_registers[GB_IO_NR10] & 7) { /* APU bug: if shift is nonzero, overflow check also occurs on trigger */ - gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div; + gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 7 - gb->apu.lf_div; + gb->apu.sweep_length_addend >>= (gb->io_registers[GB_IO_NR10] & 7); } else { gb->apu.square_sweep_calculate_countdown = 0; } - gb->apu.sweep_enabled = gb->io_registers[GB_IO_NR10] & 0x77; gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7); if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8; } diff --git a/Core/apu.h b/Core/apu.h index a3a36a63..0512723f 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -64,10 +64,10 @@ typedef struct uint8_t square_sweep_countdown; // In 128Hz uint8_t square_sweep_calculate_countdown; // In 2 MHz - uint16_t new_sweep_sample_length; + uint16_t sweep_length_addend; uint16_t shadow_sweep_sample_length; bool sweep_enabled; - bool sweep_decreasing; + GB_PADDING(bool, sweep_decreasing); struct { uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks diff --git a/Core/debugger.c b/Core/debugger.c index 3e1b66c5..28db01cc 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -1780,7 +1780,7 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg if (channel == GB_SQUARE_1) { GB_log(gb, " Frequency sweep %s and %s (next in %u APU ticks)\n", gb->apu.sweep_enabled? "active" : "inactive", - gb->apu.sweep_decreasing? "decreasing" : "increasing", + (gb->io_registers[GB_IO_NR10] & 0x8) ? "decreasing" : "increasing", gb->apu.square_sweep_calculate_countdown); }