mirror of https://github.com/bsnes-emu/bsnes.git
Accuracy improvements (Sweep)
This commit is contained in:
parent
5d84ee250e
commit
8d011ca4b9
59
Core/apu.c
59
Core/apu.c
|
@ -31,7 +31,7 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, uint8_t value, unsig
|
||||||
}
|
}
|
||||||
unsigned right_volume = 0;
|
unsigned right_volume = 0;
|
||||||
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
||||||
right_volume = (gb->io_registers[GB_IO_NR50] >> 4) & 7;;
|
right_volume = (gb->io_registers[GB_IO_NR50] >> 4) & 7;
|
||||||
}
|
}
|
||||||
GB_sample_t output = {(0xf - value) * left_volume, (0xf - value) * right_volume};
|
GB_sample_t output = {(0xf - value) * left_volume, (0xf - value) * right_volume};
|
||||||
if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) {
|
if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) {
|
||||||
|
@ -106,11 +106,8 @@ static void render(GB_gameboy_t *gb)
|
||||||
gb->apu_output.lock = false;
|
gb->apu_output.lock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t new_sweep_frequency(GB_gameboy_t *gb)
|
static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
if ((gb->io_registers[GB_IO_NR10] & 0x70) == 0) {
|
|
||||||
return gb->apu.square_channels[GB_SQUARE_1].sample_length;
|
|
||||||
}
|
|
||||||
uint16_t delta = gb->apu.square_channels[GB_SQUARE_1].sample_length >> (gb->io_registers[GB_IO_NR10] & 7);
|
uint16_t delta = gb->apu.square_channels[GB_SQUARE_1].sample_length >> (gb->io_registers[GB_IO_NR10] & 7);
|
||||||
if (gb->io_registers[GB_IO_NR10] & 8) {
|
if (gb->io_registers[GB_IO_NR10] & 8) {
|
||||||
return gb->apu.square_channels[GB_SQUARE_1].sample_length - delta;
|
return gb->apu.square_channels[GB_SQUARE_1].sample_length - delta;
|
||||||
|
@ -118,16 +115,6 @@ static uint16_t new_sweep_frequency(GB_gameboy_t *gb)
|
||||||
return gb->apu.square_channels[GB_SQUARE_1].sample_length + delta;
|
return gb->apu.square_channels[GB_SQUARE_1].sample_length + delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sweep_event(GB_gameboy_t *gb)
|
|
||||||
{
|
|
||||||
gb->apu.square_channels[GB_SQUARE_1].sample_length = new_sweep_frequency(gb);
|
|
||||||
/* Overflow checking only occurs after a delay */
|
|
||||||
gb->apu.square_sweep_stop_countdown = 0x13 - gb->apu.lf_div;
|
|
||||||
|
|
||||||
gb->apu.square_channels[GB_SQUARE_1].sample_length &= 0x7FF;
|
|
||||||
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GB_apu_div_event(GB_gameboy_t *gb)
|
void GB_apu_div_event(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
if (!gb->apu.global_enable) return;
|
if (!gb->apu.global_enable) return;
|
||||||
|
@ -214,9 +201,19 @@ 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) {
|
||||||
sweep_event(gb);
|
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.new_sweep_sample_legnth;
|
||||||
|
}
|
||||||
|
/* Recalculation and overflow check only occurs after a delay */
|
||||||
|
gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div;
|
||||||
|
|
||||||
|
gb->apu.square_channels[GB_SQUARE_1].sample_length &= 0x7FF;
|
||||||
|
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,17 +231,19 @@ void GB_apu_run(GB_gameboy_t *gb)
|
||||||
gb->apu.lf_div ^= cycles & 1;
|
gb->apu.lf_div ^= cycles & 1;
|
||||||
gb->apu.noise_channel.alignment += cycles;
|
gb->apu.noise_channel.alignment += cycles;
|
||||||
|
|
||||||
if (gb->apu.square_sweep_stop_countdown) {
|
if (gb->apu.square_sweep_calculate_countdown) {
|
||||||
if (gb->apu.square_sweep_stop_countdown > cycles) {
|
if (gb->apu.square_sweep_calculate_countdown > cycles) {
|
||||||
gb->apu.square_sweep_stop_countdown -= cycles;
|
gb->apu.square_sweep_calculate_countdown -= cycles;
|
||||||
}
|
}
|
||||||
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 */
|
||||||
if (new_sweep_frequency(gb) > 0x7ff) {
|
gb->apu.new_sweep_sample_legnth = new_sweep_sample_legnth(gb);
|
||||||
|
if (gb->apu.new_sweep_sample_legnth > 0x7ff) {
|
||||||
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_stop_countdown - cycles);
|
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles);
|
||||||
|
gb->apu.new_sweep_sample_legnth = gb->apu.square_channels[0].sample_length;
|
||||||
}
|
}
|
||||||
gb->apu.square_sweep_stop_countdown = 0;
|
gb->apu.square_sweep_calculate_countdown = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +344,7 @@ void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
|
||||||
|
|
||||||
if (count > gb->apu_output.buffer_position) {
|
if (count > gb->apu_output.buffer_position) {
|
||||||
// GB_log(gb, "Audio underflow: %d\n", count - gb->apu_output.buffer_position);
|
// GB_log(gb, "Audio underflow: %d\n", count - gb->apu_output.buffer_position);
|
||||||
GB_sample_t output = {0,0};
|
GB_sample_t output = {-gb->apu_output.highpass_diff.left, -gb->apu_output.highpass_diff.right};
|
||||||
for (unsigned i = GB_N_CHANNELS; i--;) {
|
for (unsigned i = GB_N_CHANNELS; i--;) {
|
||||||
output.left += gb->apu_output.current_sample[i].left * CH_STEP;
|
output.left += gb->apu_output.current_sample[i].left * CH_STEP;
|
||||||
output.right += gb->apu_output.current_sample[i].right * CH_STEP;
|
output.right += gb->apu_output.current_sample[i].right * CH_STEP;
|
||||||
|
@ -477,6 +476,7 @@ 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:
|
||||||
gb->apu.square_sweep_countdown = ((value >> 4) & 7);
|
gb->apu.square_sweep_countdown = ((value >> 4) & 7);
|
||||||
|
if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GB_IO_NR11:
|
case GB_IO_NR11:
|
||||||
|
@ -504,6 +504,9 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||||
unsigned index = reg == GB_IO_NR23? GB_SQUARE_2: GB_SQUARE_1;
|
unsigned index = reg == GB_IO_NR23? GB_SQUARE_2: GB_SQUARE_1;
|
||||||
gb->apu.square_channels[index].sample_length &= ~0xFF;
|
gb->apu.square_channels[index].sample_length &= ~0xFF;
|
||||||
gb->apu.square_channels[index].sample_length |= value & 0xFF;
|
gb->apu.square_channels[index].sample_length |= value & 0xFF;
|
||||||
|
if (index == GB_SQUARE_1) {
|
||||||
|
gb->apu.new_sweep_sample_legnth = gb->apu.square_channels[0].sample_length;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +515,9 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||||
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
||||||
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.new_sweep_sample_legnth = gb->apu.square_channels[0].sample_length;
|
||||||
|
}
|
||||||
if (value & 0x80) {
|
if (value & 0x80) {
|
||||||
gb->apu.square_channels[index].current_sample_index = 7;
|
gb->apu.square_channels[index].current_sample_index = 7;
|
||||||
|
|
||||||
|
@ -543,7 +549,12 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||||
|
|
||||||
if (index == GB_SQUARE_1 && gb->io_registers[GB_IO_NR10] & 7) {
|
if (index == GB_SQUARE_1 && 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_stop_countdown = 0x13 - gb->apu.lf_div;
|
/* Todo: check actual timing */
|
||||||
|
gb->apu.square_sweep_calculate_countdown = 0x3 - gb->apu.lf_div;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == GB_SQUARE_1) {
|
||||||
|
gb->apu.sweep_enabled = gb->io_registers[GB_IO_NR10] & 0x77;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note that we don't change the sample just yet! This was verified on hardware. */
|
/* Note that we don't change the sample just yet! This was verified on hardware. */
|
||||||
|
|
|
@ -48,7 +48,9 @@ typedef struct
|
||||||
// need to divide the signal.
|
// need to divide the signal.
|
||||||
|
|
||||||
uint8_t square_sweep_countdown; // In 128Hz
|
uint8_t square_sweep_countdown; // In 128Hz
|
||||||
uint8_t square_sweep_stop_countdown; // In 2 MHz
|
uint8_t square_sweep_calculate_countdown; // In 2 MHz
|
||||||
|
uint16_t new_sweep_sample_legnth;
|
||||||
|
bool sweep_enabled;
|
||||||
|
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue