From e59bc436bdcc955eabb5f6fd2dfb7cd8a1c14d6c Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Mon, 25 Sep 2023 23:35:26 +0300 Subject: [PATCH] CGB-C NR10 conflict improvements --- Core/apu.c | 94 +++++++++++++++++++------------------------------ Core/apu.h | 6 ++-- Core/sm83_cpu.c | 12 +++++++ 3 files changed, 53 insertions(+), 59 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index 3fc8fb266..e88c775e2 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -477,8 +477,7 @@ static void trigger_sweep_calculation(GB_gameboy_t *gb) /* Recalculation and overflow check only occurs after a delay */ gb->apu.square_sweep_calculate_countdown = gb->io_registers[GB_IO_NR10] & 0x7; - gb->apu.square_sweep_calculate_countdown_reload_timer = 2 + (gb->model <= GB_MODEL_CGB_C); - gb->apu.square_sweep_stop_calc_if_no_zombie_write = false; + gb->apu.square_sweep_calculate_countdown_reload_timer = 2; gb->apu.unshifted_sweep = !(gb->io_registers[GB_IO_NR10] & 0x7); gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7; } @@ -690,23 +689,26 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) if ((cycles & 1) && !gb->apu.lf_div) { sweep_cycles++; } + + if (gb->apu.square_sweep_calculate_countdown_reload_timer == 0 && !gb->cgb_double_speed) { + gb->apu.square_sweep_calculate_countdown = 0; + } + gb->apu.square_sweep_countdown_just_reloaded = false; if (gb->apu.square_sweep_calculate_countdown_reload_timer > sweep_cycles) { gb->apu.square_sweep_calculate_countdown_reload_timer -= sweep_cycles; sweep_cycles = 0; } else { - if (gb->apu.square_sweep_calculate_countdown_reload_timer && !gb->apu.square_sweep_calculate_countdown) { + if (gb->apu.square_sweep_calculate_countdown_reload_timer && !gb->apu.square_sweep_calculate_countdown && gb->model > GB_MODEL_CGB_C) { sweep_calculation_done(gb, cycles); } + gb->apu.square_sweep_countdown_just_reloaded = gb->apu.square_sweep_calculate_countdown_reload_timer == sweep_cycles && sweep_cycles; sweep_cycles -= gb->apu.square_sweep_calculate_countdown_reload_timer; gb->apu.square_sweep_calculate_countdown_reload_timer = 0; - if (gb->apu.square_sweep_stop_calc_if_no_zombie_write) { - gb->apu.square_sweep_stop_calc_if_no_zombie_write = 0; - gb->apu.square_sweep_calculate_countdown = 0; - } } if (gb->apu.square_sweep_calculate_countdown && + !gb->apu_output.square_sweep_disable_stepping && (((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep) || gb->apu.square_sweep_calculate_countdown <= 1)) { // Calculation is paused if the lower bits are 0 if (gb->apu.square_sweep_calculate_countdown > sweep_cycles) { @@ -714,7 +716,6 @@ void GB_apu_run(GB_gameboy_t *gb, bool force) } else { sweep_calculation_done(gb, cycles); - gb->apu.square_sweep_calculate_countdown = 0; } } @@ -1103,50 +1104,38 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) case GB_IO_NR10: { bool old_negate = gb->io_registers[GB_IO_NR10] & 8; // TODO: Check all of these in APU odd mode + if (gb->apu.square_sweep_calculate_countdown_reload_timer || gb->apu.square_sweep_countdown_just_reloaded) { + GB_log(gb, "%04x: Glitch NR10 write at %d.%d (%d, %d)\n", gb->de, gb->apu.square_sweep_calculate_countdown, gb->apu.lf_div, gb->apu.square_sweep_calculate_countdown_reload_timer, gb->apu.square_sweep_countdown_just_reloaded); + } if (gb->model <= GB_MODEL_CGB_C) { - bool zombie_tick = !(gb->io_registers[GB_IO_NR10] & 7) && - (gb->apu.lf_div ^ gb->cgb_double_speed) && - gb->apu.square_sweep_calculate_countdown && - !gb->apu.square_sweep_calculate_countdown_reload_timer; - gb->apu.square_sweep_stop_calc_if_no_zombie_write = false; - if (zombie_tick) { - gb->apu.square_sweep_calculate_countdown--; - if (!gb->apu.square_sweep_calculate_countdown) { - sweep_calculation_done(gb, 0); + if (gb->apu.square_sweep_calculate_countdown_reload_timer) { + if (gb->cgb_double_speed) { + // TODO: How does this affect actual frequency calculation? + gb->apu.square_sweep_calculate_countdown = value & 7; } } - switch (gb->apu.square_sweep_calculate_countdown_reload_timer) { - case 1: { - if (!gb->apu.lf_div) { - /* This is some instance-specific data corruption. It might also be affect by revision. - At least for my CGB-0 (haven't tested any other CGB-0s), the '3' case is non-deterministic. */ - static const uint8_t corruption[8] = {0, 7, 5, 7, 3, 3, 5, 7}; // Two of my CGB-Cs, CGB-A - // static const uint8_t corruption[8] = {0, 7, 1, 3, 3, 3, 5, 7}; // My other CGB-C - // static const uint8_t corruption[8] = {0, 1, 1, 3, 3, 5, 5, 7}; // My CGB-B - // static const uint8_t corruption[8] = {0, 7, 1, *, 3, 3, 5, 7}; // My CGB-0 - - // TODO: How does this affect actual frequency calculation? - - gb->apu.square_sweep_calculate_countdown = corruption[gb->apu.square_sweep_calculate_countdown & 7]; - gb->apu.square_sweep_calculate_countdown_reload_timer = 0; + else if (gb->apu.square_sweep_countdown_just_reloaded) { + GB_log(gb, "%04x: Glitch NR10 write at %d.%d (%d, %d)\n", gb->de, gb->apu.square_sweep_calculate_countdown, gb->apu.lf_div, gb->apu.square_sweep_calculate_countdown_reload_timer, gb->apu.square_sweep_countdown_just_reloaded); + /* This is some instance-specific data corruption. It might also be affect by revision. + At least for my CGB-0 (haven't tested any other CGB-0s), the '3' case is non-deterministic. */ + static const uint8_t corruption[8] = {7, 7, 5, 7, 3, 3, 5, 7}; // Two of my CGB-Cs, CGB-A + // static const uint8_t corruption[8] = {7, 7, 1, 3, 3, 3, 5, 7}; // My other CGB-C + // static const uint8_t corruption[8] = {7, 1, 1, 3, 3, 5, 5, 7}; // My CGB-B + // static const uint8_t corruption[8] = {7, 7, 1, *, 3, 3, 5, 7}; // My CGB-0 + + // TODO: How does this affect actual frequency calculation? + + gb->apu.square_sweep_calculate_countdown = corruption[gb->apu.square_sweep_calculate_countdown & 7]; + } + else { + if (!(gb->io_registers[GB_IO_NR10] & 7) && + (gb->apu.lf_div ^ gb->cgb_double_speed) && + gb->apu.square_sweep_calculate_countdown) { + gb->apu.square_sweep_calculate_countdown--; + if (!gb->apu.square_sweep_calculate_countdown) { + sweep_calculation_done(gb, 0); } - break; } - case 2: - if (gb->apu.lf_div) { - // TODO: How does this affect actual frequency calculation? - gb->apu.square_sweep_calculate_countdown = value & 7; // TODO: Confirm for non-zero? - gb->apu.square_sweep_calculate_countdown_reload_timer = 0; - } - else { - case 3: - // Countdown just reloaded, re-reload it with glitch value (FF & 7) - gb->apu.square_sweep_calculate_countdown = 7; - gb->apu.square_sweep_stop_calc_if_no_zombie_write = true; - - } - break; - default:; } } else { @@ -1255,10 +1244,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) } gb->apu.square_channels[index].delay = 6 - gb->apu.lf_div; gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + gb->apu.square_channels[index].delay; - if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { - gb->apu.square_channels[index].sample_countdown += 2; - gb->apu.square_channels[index].delay += 2; - } } else { unsigned extra_delay = 0; @@ -1278,10 +1263,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) /* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/ gb->apu.square_channels[index].delay = 4 - gb->apu.lf_div + extra_delay; gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + gb->apu.square_channels[index].delay; - if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { - gb->apu.square_channels[index].sample_countdown += 2; - gb->apu.square_channels[index].delay += 2; - } } gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4; /* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously @@ -1306,11 +1287,10 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) if (index == GB_SQUARE_1) { gb->apu.shadow_sweep_sample_length = 0; gb->apu.channel1_completed_addend = 0; - gb->apu.square_sweep_stop_calc_if_no_zombie_write = 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 = gb->io_registers[GB_IO_NR10] & 0x7; - gb->apu.square_sweep_calculate_countdown_reload_timer = 2 + (gb->model <= GB_MODEL_CGB_C); + gb->apu.square_sweep_calculate_countdown_reload_timer = 2; gb->apu.unshifted_sweep = false; if (!was_active) { gb->apu.square_sweep_calculate_countdown_reload_timer++; diff --git a/Core/apu.h b/Core/apu.h index 8d6beecdc..24b9878de 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -75,8 +75,8 @@ typedef struct uint16_t sweep_length_addend; uint16_t shadow_sweep_sample_length; bool unshifted_sweep; - bool square_sweep_stop_calc_if_no_zombie_write; - + bool square_sweep_countdown_just_reloaded; + uint8_t channel_1_restart_hold; uint16_t channel1_completed_addend; struct { @@ -177,6 +177,8 @@ typedef struct { GB_audio_format_t output_format; int output_error; + /* Not output related, but it's temp state so I'll put it here */ + bool square_sweep_disable_stepping; } GB_apu_output_t; void GB_set_channel_muted(GB_gameboy_t *gb, GB_channel_t channel, bool muted); diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index 4e4f440e3..292aeb82f 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -25,6 +25,7 @@ typedef enum { GB_CONFLICT_SCX_CGB, GB_CONFLICT_LCDC_CGB_DOUBLE, GB_CONFLICT_STAT_CGB_DOUBLE, + GB_CONFLICT_NR10_CGB_DOUBLE, } conflict_t; static const conflict_t cgb_conflict_map[0x80] = { @@ -43,6 +44,7 @@ static const conflict_t cgb_double_conflict_map[0x80] = { [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, [GB_IO_LYC] = GB_CONFLICT_READ_OLD, [GB_IO_STAT] = GB_CONFLICT_STAT_CGB_DOUBLE, + [GB_IO_NR10] = GB_CONFLICT_NR10_CGB_DOUBLE, // Unconfirmed yet [GB_IO_SCX] = GB_CONFLICT_SCX_CGB, }; @@ -327,6 +329,16 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->pending_cycles = 4; } break; + + case GB_CONFLICT_NR10_CGB_DOUBLE: { + GB_advance_cycles(gb, gb->pending_cycles - 1); + gb->apu_output.square_sweep_disable_stepping = gb->model <= GB_MODEL_CGB_C && (value & 7) == 0; + GB_advance_cycles(gb, 1); + gb->apu_output.square_sweep_disable_stepping = false; + GB_write_memory(gb, addr, value); + gb->pending_cycles = 4; + break; + } } gb->address_bus = addr; }