Redo channel 1 sweep based on DMG schematics; emulates two newly discovered behaviors and also fixes #309

This commit is contained in:
Lior Halphon 2020-11-28 19:31:25 +02:00
parent 67c0e03f3b
commit 0485124076
3 changed files with 24 additions and 33 deletions

View File

@ -230,15 +230,6 @@ static void render(GB_gameboy_t *gb)
gb->apu_output.sample_callback(gb, &filtered_output); 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) static void update_square_sample(GB_gameboy_t *gb, unsigned index)
{ {
if (gb->apu.square_channels[index].current_sample_index & 0x80) return; 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.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) { 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)) { 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.square_channels[GB_SQUARE_1].sample_length =
gb->apu.shadow_sweep_sample_length = gb->apu.sweep_length_addend + gb->apu.shadow_sweep_sample_length + !!(gb->io_registers[GB_IO_NR10] & 0x8);
gb->apu.new_sweep_sample_length; 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) { if (gb->io_registers[GB_IO_NR10] & 0x70) {
/* Recalculation and overflow check only occurs after a delay */ /* 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); 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; 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 { else {
/* APU bug: sweep frequency is checked after adding the sweep delta twice */ /* APU bug: sweep frequency is checked after adding the sweep delta twice */
gb->apu.new_sweep_sample_length = new_sweep_sample_length(gb); gb->apu.shadow_sweep_sample_length = gb->apu.square_channels[GB_SQUARE_1].sample_length;
if (gb->apu.new_sweep_sample_length > 0x7ff) { 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; gb->apu.is_active[GB_SQUARE_1] = false;
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles); 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; 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 */ /* Square channels */
case GB_IO_NR10: 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; gb->apu.is_active[GB_SQUARE_1] = false;
update_sample(gb, GB_SQUARE_1, 0, 0); update_sample(gb, GB_SQUARE_1, 0, 0);
gb->apu.sweep_enabled = false;
gb->apu.square_sweep_calculate_countdown = 0; gb->apu.square_sweep_calculate_countdown = 0;
} }
if ((value & 0x70) == 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 &= 0xFF;
gb->apu.square_channels[index].sample_length |= (value & 7) << 8; 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 (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 /* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by
turning the APU off. */ turning the APU off. */
if (!gb->apu.is_active[index]) { 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) { if (index == GB_SQUARE_1) {
gb->apu.sweep_decreasing = false;
if (gb->io_registers[GB_IO_NR10] & 7) { if (gb->io_registers[GB_IO_NR10] & 7) {
/* APU bug: if shift is nonzero, overflow check also occurs on trigger */ /* 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 { else {
gb->apu.square_sweep_calculate_countdown = 0; 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); 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; if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8;
} }

View File

@ -64,10 +64,10 @@ typedef struct
uint8_t square_sweep_countdown; // In 128Hz uint8_t square_sweep_countdown; // In 128Hz
uint8_t square_sweep_calculate_countdown; // In 2 MHz 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; uint16_t shadow_sweep_sample_length;
bool sweep_enabled; bool sweep_enabled;
bool sweep_decreasing; GB_PADDING(bool, sweep_decreasing);
struct { struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks

View File

@ -1780,7 +1780,7 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
if (channel == GB_SQUARE_1) { if (channel == GB_SQUARE_1) {
GB_log(gb, " Frequency sweep %s and %s (next in %u APU ticks)\n", GB_log(gb, " Frequency sweep %s and %s (next in %u APU ticks)\n",
gb->apu.sweep_enabled? "active" : "inactive", 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); gb->apu.square_sweep_calculate_countdown);
} }