Skip to content

Commit

Permalink
Emulate several more pre-CGB-D channel 1 glitches
Browse files Browse the repository at this point in the history
  • Loading branch information
LIJI32 committed Nov 25, 2023
1 parent 35a9176 commit ecbdb9e
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 90 deletions.
3 changes: 2 additions & 1 deletion AppleCommon/GBViewBase.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#import <TargetConditionals.h>
#import <Core/gb.h>

#if TARGET_OS_IPHONE
#define NSView UIView
Expand All @@ -8,6 +7,8 @@
#import <Cocoa/Cocoa.h>
#endif

#import <Core/gb.h>

typedef enum {
GB_FRAME_BLENDING_MODE_DISABLED,
GB_FRAME_BLENDING_MODE_SIMPLE,
Expand Down
200 changes: 112 additions & 88 deletions Core/apu.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,22 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
}
}

static void sweep_calculation_done(GB_gameboy_t *gb, unsigned cycles)
{
/* APU bug: sweep frequency is checked after adding the sweep delta twice */
if (gb->apu.channel_1_restart_hold == 0) {
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 * 2 - cycles);
}
gb->apu.channel1_completed_addend = gb->apu.sweep_length_addend;
}

static void trigger_sweep_calculation(GB_gameboy_t *gb)
{
if ((gb->io_registers[GB_IO_NR10] & 0x70) && gb->apu.square_sweep_countdown == 7) {
Expand All @@ -477,9 +493,16 @@ 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;
// TODO: this is a hack because DIV write timing is inaccurate. Will probably break on odd mode.
gb->apu.square_sweep_calculate_countdown_reload_timer = 1 + gb->apu.lf_div;
if (!gb->cgb_double_speed && gb->during_div_write) {
gb->apu.square_sweep_calculate_countdown_reload_timer = 1;
}
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;
if (gb->apu.square_sweep_calculate_countdown == 0) {
gb->apu.square_sweep_instant_calculation_done = true;
}
}
}

Expand Down Expand Up @@ -615,22 +638,6 @@ static void step_lfsr(GB_gameboy_t *gb, unsigned cycles_offset)
}
}

static void sweep_calculation_done(GB_gameboy_t *gb, unsigned cycles)
{
/* APU bug: sweep frequency is checked after adding the sweep delta twice */
if (gb->apu.channel_1_restart_hold == 0) {
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 * 2 - cycles);
}
gb->apu.channel1_completed_addend = gb->apu.sweep_length_addend;
}

void GB_apu_run(GB_gameboy_t *gb, bool force)
{
uint32_t clock_rate = GB_get_clock_rate(gb) * 2;
Expand Down Expand Up @@ -689,34 +696,28 @@ 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 && gb->model > GB_MODEL_CGB_C) {
if (gb->apu.square_sweep_calculate_countdown_reload_timer && !gb->apu.square_sweep_calculate_countdown && gb->apu.square_sweep_instant_calculation_done) {
sweep_calculation_done(gb, cycles);
}
gb->apu.square_sweep_countdown_just_reloaded = gb->apu.square_sweep_calculate_countdown_reload_timer == sweep_cycles && sweep_cycles;
gb->apu.square_sweep_instant_calculation_done = false;
sweep_cycles -= gb->apu.square_sweep_calculate_countdown_reload_timer;
gb->apu.square_sweep_calculate_countdown_reload_timer = 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
(((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep))) { // Calculation is paused if the lower bits are 0
if (gb->apu.square_sweep_calculate_countdown > sweep_cycles) {
gb->apu.square_sweep_calculate_countdown -= sweep_cycles;
}
else {
sweep_calculation_done(gb, cycles);
gb->apu.square_sweep_calculate_countdown = 0;
sweep_calculation_done(gb, cycles);
}
}

Expand Down Expand Up @@ -1035,6 +1036,76 @@ static inline uint16_t effective_channel4_counter(GB_gameboy_t *gb)
return effective_counter;
}

static noinline void nr10_write_glitch(GB_gameboy_t *gb, uint8_t value)
{
// TODO: Check all of these in APU odd mode
if (gb->model <= GB_MODEL_CGB_C) {
if (gb->apu.square_sweep_calculate_countdown_reload_timer == 1 && !gb->apu.lf_div) {
if (gb->cgb_double_speed) {
/* 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, Coffee Bat's 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

// static const uint8_t corruption[8] = {7, 5, 1, 3, 3, 1, 5, 7}; // PinoBatch's CGB-B
// static const uint8_t corruption[8] = {7, 5, 1, 3, 3, *, 5, 7}; // GenericHeroGuy CGB-C


// TODO: How does this affect actual frequency calculation?

gb->apu.square_sweep_calculate_countdown = corruption[gb->apu.square_sweep_calculate_countdown & 7];
/* TODO: the value of 1 needs special handling, but it doesn't occur with the instance I'm emulating here */
}
}
else if (gb->apu.square_sweep_calculate_countdown_reload_timer > 1) {
if (gb->cgb_double_speed) {
// TODO: How does this affect actual frequency calculation?
gb->apu.square_sweep_calculate_countdown = value & 7;
}
}
else if (gb->apu.square_sweep_calculate_countdown) {
// No clue why 1 is a special case here
bool should_zombie_step = false;
if (!(gb->io_registers[GB_IO_NR10] & 7)) {
should_zombie_step = gb->apu.lf_div ^ gb->cgb_double_speed;
}
else if (gb->cgb_double_speed && gb->apu.square_sweep_calculate_countdown == 1) {
should_zombie_step = true;
}

if (should_zombie_step) {
gb->apu.square_sweep_calculate_countdown--;
if (gb->apu.square_sweep_calculate_countdown <= 1) {
gb->apu.square_sweep_calculate_countdown = 0;
sweep_calculation_done(gb, 0);
}
}
}
}
else {
if (gb->apu.square_sweep_calculate_countdown_reload_timer == 2) {
// Countdown just reloaded, re-reload it
gb->apu.square_sweep_calculate_countdown = value & 0x7;
if (!gb->apu.square_sweep_calculate_countdown) {
gb->apu.square_sweep_calculate_countdown_reload_timer = 0;
}
else {
// TODO: How does this affect actual frequency calculation?
}
}
if ((value & 7) && !(gb->io_registers[GB_IO_NR10] & 7) && !gb->apu.lf_div && gb->apu.square_sweep_calculate_countdown > 1) {
// TODO: Another odd glitch? Ditto
gb->apu.square_sweep_calculate_countdown--;
if (!gb->apu.square_sweep_calculate_countdown) {
sweep_calculation_done(gb, 0);
}
}
}

}

void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
{
GB_apu_run(gb, true);
Expand Down Expand Up @@ -1102,62 +1173,14 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)

/* Square channels */
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 (unlikely(gb->apu.square_sweep_calculate_countdown || gb->apu.square_sweep_calculate_countdown_reload_timer)) {
nr10_write_glitch(gb, value);
}
bool old_negate = gb->io_registers[GB_IO_NR10] & 8;
gb->io_registers[GB_IO_NR10] = value;
if (gb->model <= GB_MODEL_CGB_C) {
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;
}
}
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);
}
}
}
old_negate = true;
}
else {
if (gb->apu.square_sweep_calculate_countdown_reload_timer == 2) {
// Countdown just reloaded, re-reload it
gb->apu.square_sweep_calculate_countdown = value & 0x7;
if (!gb->apu.square_sweep_calculate_countdown) {
gb->apu.square_sweep_calculate_countdown_reload_timer = 0;
}
else {
// TODO: How does this affect actual frequency calculation?
}
}
if ((value & 7) && !(gb->io_registers[GB_IO_NR10] & 7) && !gb->apu.lf_div && gb->apu.square_sweep_calculate_countdown > 1) {
// TODO: Another odd glitch? Ditto
gb->apu.square_sweep_calculate_countdown--;
if (!gb->apu.square_sweep_calculate_countdown) {
sweep_calculation_done(gb, 0);
}
}
}
gb->io_registers[GB_IO_NR10] = value;
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;
Expand Down Expand Up @@ -1285,12 +1308,18 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
}

if (index == GB_SQUARE_1) {
gb->apu.square_sweep_instant_calculation_done = false;
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;
gb->apu.square_sweep_calculate_countdown_reload_timer = 2;
if ((gb->apu.lf_div ^ !gb->cgb_double_speed) && gb->model <= GB_MODEL_CGB_C) {
gb->apu.square_sweep_calculate_countdown_reload_timer = 3;
}
else {
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++;
Expand All @@ -1302,11 +1331,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
gb->apu.sweep_length_addend = 0;
}
gb->apu.channel_1_restart_hold = 2 - gb->apu.lf_div + (GB_is_cgb(gb) && gb->model != GB_MODEL_CGB_D) * 2;
/*
if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) {
// TODO: This if makes channel_1_sweep_restart_2 fail on CGB-C mode
gb->apu.channel_1_restart_hold += 2;
}*/
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7;
}
}
Expand Down
2 changes: 1 addition & 1 deletion Core/apu.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ typedef struct
uint16_t sweep_length_addend;
uint16_t shadow_sweep_sample_length;
bool unshifted_sweep;
bool square_sweep_countdown_just_reloaded;
bool square_sweep_instant_calculation_done;

uint8_t channel_1_restart_hold;
uint16_t channel1_completed_addend;
Expand Down
1 change: 1 addition & 0 deletions Core/gb.h
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ struct GB_gameboy_internal_s {
bool hdma_in_progress;
bool returned_open_bus;
uint16_t addr_for_hdma_conflict;
bool during_div_write;

/* Thread safety (debug only) */
void *running_thread_id;
Expand Down
2 changes: 2 additions & 0 deletions Core/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -1529,7 +1529,9 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return;

case GB_IO_DIV:
gb->during_div_write = true;
GB_set_internal_div_counter(gb, 0);
gb->during_div_write = false;
/* Reset the div state machine */
gb->div_state = 0;
gb->div_cycles = 0;
Expand Down

0 comments on commit ecbdb9e

Please sign in to comment.