From c0582fd994be894adfe5049d4cf180d8cef8f9c8 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 8 Jan 2021 16:43:00 +0200 Subject: [PATCH] More accurate emulation of NR10 writes --- Core/apu.c | 45 +++++++++++++++++++++++++-------------------- Core/apu.h | 3 ++- Core/gb.c | 1 + Core/mbc.c | 2 +- Core/sm83_cpu.c | 25 +++++++++++++++++++++++-- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index 6c397a6..2596e0f 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -394,6 +394,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) * 2 + 5 - gb->apu.lf_div; + gb->apu.enable_zombie_calculate_stepping = false; 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; } @@ -501,31 +502,31 @@ void GB_apu_run(GB_gameboy_t *gb) uint8_t cycles = gb->apu.apu_cycles >> 2; gb->apu.apu_cycles = 0; if (!cycles) return; + bool start_ch4 = false; - if (gb->apu.channel_4_dmg_delayed_start) { - if (gb->apu.channel_4_dmg_delayed_start == cycles) { - gb->apu.channel_4_dmg_delayed_start = 0; - start_ch4 = true; - } - else if (gb->apu.channel_4_dmg_delayed_start > cycles) { - gb->apu.channel_4_dmg_delayed_start -= cycles; - } - else { - /* Split it into two */ - cycles -= gb->apu.channel_4_dmg_delayed_start; - gb->apu.apu_cycles = gb->apu.channel_4_dmg_delayed_start * 2; - GB_apu_run(gb); - } - } - if (likely(!gb->stopped || GB_is_cgb(gb))) { + if (gb->apu.channel_4_dmg_delayed_start) { + if (gb->apu.channel_4_dmg_delayed_start == cycles) { + gb->apu.channel_4_dmg_delayed_start = 0; + start_ch4 = true; + } + else if (gb->apu.channel_4_dmg_delayed_start > cycles) { + gb->apu.channel_4_dmg_delayed_start -= cycles; + } + else { + /* Split it into two */ + cycles -= gb->apu.channel_4_dmg_delayed_start; + gb->apu.apu_cycles = gb->apu.channel_4_dmg_delayed_start * 2; + GB_apu_run(gb); + } + } /* To align the square signal to 1MHz */ gb->apu.lf_div ^= cycles & 1; gb->apu.noise_channel.alignment += cycles; if (gb->apu.square_sweep_calculate_countdown && (((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep) || - gb->apu.square_sweep_calculate_countdown <= (gb->model > GB_MODEL_CGB_C? 3 : 1))) { // Calculation is paused if the lower bits + gb->apu.square_sweep_calculate_countdown <= 3)) { // Calculation is paused if the lower bits are 0 if (gb->apu.square_sweep_calculate_countdown > cycles) { gb->apu.square_sweep_calculate_countdown -= cycles; } @@ -541,6 +542,8 @@ void GB_apu_run(GB_gameboy_t *gb) gb->apu.is_active[GB_SQUARE_1] = false; update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles); } + gb->apu.channel1_completed_addend = gb->apu.sweep_length_addend; + gb->apu.square_sweep_calculate_countdown = 0; } } @@ -643,6 +646,7 @@ void GB_apu_run(GB_gameboy_t *gb) GB_apu_write(gb, GB_IO_NR44, gb->io_registers[GB_IO_NR44] | 0x80); } } + void GB_apu_init(GB_gameboy_t *gb) { memset(&gb->apu, 0, sizeof(gb->apu)); @@ -769,8 +773,7 @@ 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; gb->io_registers[GB_IO_NR10] = value; - if (gb->apu.square_sweep_calculate_countdown == 0 && - gb->apu.shadow_sweep_sample_length + gb->apu.sweep_length_addend + old_negate > 0x7FF && + if (gb->apu.shadow_sweep_sample_length + gb->apu.channel1_completed_addend + old_negate > 0x7FF && !(value & 8)) { gb->apu.is_active[GB_SQUARE_1] = false; update_sample(gb, GB_SQUARE_1, 0, 0); @@ -886,11 +889,13 @@ 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; 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) * 2 + 5 - gb->apu.lf_div; + gb->apu.enable_zombie_calculate_stepping = false; gb->apu.unshifted_sweep = false; - if (gb->model > GB_MODEL_CGB_C && !was_active) { + if (!was_active) { gb->apu.square_sweep_calculate_countdown += 2; } gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length; diff --git a/Core/apu.h b/Core/apu.h index eadae1b..6394a55 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -67,7 +67,7 @@ typedef struct uint16_t sweep_length_addend; uint16_t shadow_sweep_sample_length; bool unshifted_sweep; - GB_PADDING(bool, sweep_decreasing); + bool enable_zombie_calculate_stepping; struct { uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks @@ -125,6 +125,7 @@ typedef struct int8_t channel_4_delta; bool channel_4_countdown_reloaded; uint8_t channel_4_dmg_delayed_start; + uint16_t channel1_completed_addend; } GB_apu_t; typedef enum { diff --git a/Core/gb.c b/Core/gb.c index 77ea144..985ed4d 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1111,6 +1111,7 @@ bool GB_serial_get_data_bit(GB_gameboy_t *gb) } return gb->io_registers[GB_IO_SB] & 0x80; } + void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data) { if (gb->io_registers[GB_IO_SC] & 1) { diff --git a/Core/mbc.c b/Core/mbc.c index 2259681..1e91ce2 100644 --- a/Core/mbc.c +++ b/Core/mbc.c @@ -139,7 +139,7 @@ void GB_configure_cart(GB_gameboy_t *gb) gb->mbc_ram = malloc(gb->mbc_ram_size); } - /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */ + /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridge types? */ memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); } diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index cf73b31..042514c 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -22,6 +22,7 @@ typedef enum { GB_CONFLICT_SGB_LCDC, GB_CONFLICT_WX, GB_CONFLICT_CGB_LCDC, + GB_CONFLICT_NR10, } GB_conflict_t; /* Todo: How does double speed mode affect these? */ @@ -33,6 +34,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = { [GB_IO_BGP] = GB_CONFLICT_PALETTE_CGB, [GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB, [GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB, + [GB_IO_NR10] = GB_CONFLICT_NR10, /* Todo: most values not verified, and probably differ between revisions */ }; @@ -50,7 +52,8 @@ static const GB_conflict_t dmg_conflict_map[0x80] = { [GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG, [GB_IO_WY] = GB_CONFLICT_READ_OLD, [GB_IO_WX] = GB_CONFLICT_WX, - + [GB_IO_NR10] = GB_CONFLICT_NR10, + /* Todo: these were not verified at all */ [GB_IO_SCX] = GB_CONFLICT_READ_NEW, }; @@ -68,7 +71,8 @@ static const GB_conflict_t sgb_conflict_map[0x80] = { [GB_IO_OBP1] = GB_CONFLICT_READ_NEW, [GB_IO_WY] = GB_CONFLICT_READ_OLD, [GB_IO_WX] = GB_CONFLICT_WX, - + [GB_IO_NR10] = GB_CONFLICT_NR10, + /* Todo: these were not verified at all */ [GB_IO_SCX] = GB_CONFLICT_READ_NEW, }; @@ -272,6 +276,23 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->pending_cycles = 4; } return; + + case GB_CONFLICT_NR10: + /* Hack: Due to the coupling between DIV and the APU, GB_apu_run only runs at M-cycle + resolutions, but this quirk requires 2MHz even in single speed mode. To work + around this, we specifically just step the calculate countdown if needed. */ + GB_advance_cycles(gb, gb->pending_cycles); + if (gb->model <= GB_MODEL_CGB_C) { + // TODO: Double speed mode? This logic is also a bit weird, it needs more tests + if (gb->apu.square_sweep_calculate_countdown > 3 && gb->apu.enable_zombie_calculate_stepping) { + gb->apu.square_sweep_calculate_countdown -= 2; + } + gb->apu.enable_zombie_calculate_stepping = true; + GB_write_memory(gb, addr, 0xFF); + } + GB_write_memory(gb, addr, value); + gb->pending_cycles = 4; + return; } }