Accuracy improvements, especially to the length control

This commit is contained in:
Lior Halphon 2017-08-02 21:14:23 +03:00
parent d65c2247e5
commit ab5611119a
2 changed files with 99 additions and 54 deletions

View File

@ -73,6 +73,9 @@ static void render(GB_gameboy_t *gb)
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;
gb->apu.div_divider++;
if ((gb->apu.div_divider & 1) == 1) {
for (unsigned i = GB_SQUARE_2 + 1; i--;) { for (unsigned i = GB_SQUARE_2 + 1; i--;) {
if (gb->apu.square_channels[i].length_enabled) { if (gb->apu.square_channels[i].length_enabled) {
if (gb->apu.square_channels[i].pulse_length) { if (gb->apu.square_channels[i].pulse_length) {
@ -95,7 +98,7 @@ void GB_apu_div_event(GB_gameboy_t *gb)
gb->apu.square_channels[i].current_volume--; gb->apu.square_channels[i].current_volume--;
} }
gb->apu.square_channels[i].volume_countdown = (nrx2 & 7) * 8; gb->apu.square_channels[i].volume_countdown = (nrx2 & 7) * 4;
uint8_t duty = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; uint8_t duty = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6;
update_sample(gb, i, update_sample(gb, i,
@ -106,9 +109,18 @@ void GB_apu_div_event(GB_gameboy_t *gb)
} }
} }
gb->apu.square_sweep_div++; if (gb->apu.wave_channel.length_enabled) {
if (gb->apu.wave_channel.pulse_length) {
if (!--gb->apu.wave_channel.pulse_length) {
gb->apu.is_active[GB_WAVE] = false;
gb->apu.wave_channel.current_sample = 0;
update_sample(gb, GB_WAVE, 0, 0);
}
}
}
}
if ((gb->apu.square_sweep_div & 3) == 3) { if ((gb->apu.div_divider & 3) == 3) {
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.square_channels[GB_SQUARE_1].sample_length ^= 0x7FF; gb->apu.square_channels[GB_SQUARE_1].sample_length ^= 0x7FF;
@ -130,16 +142,6 @@ void GB_apu_div_event(GB_gameboy_t *gb)
} }
} }
} }
if (gb->apu.wave_channel.length_enabled) {
if (gb->apu.wave_channel.pulse_length) {
if (!--gb->apu.wave_channel.pulse_length) {
gb->apu.is_active[GB_WAVE] = false;
gb->apu.wave_channel.current_sample = 0;
update_sample(gb, GB_WAVE, 0, 0);
}
}
}
} }
@ -252,10 +254,11 @@ void GB_apu_init(GB_gameboy_t *gb)
memset(&gb->apu, 0, sizeof(gb->apu)); memset(&gb->apu, 0, sizeof(gb->apu));
// gb->apu.wave_channels[0].duty = gb->apu.wave_channels[1].duty = 4; // gb->apu.wave_channels[0].duty = gb->apu.wave_channels[1].duty = 4;
// gb->apu.lfsr = 0x7FFF; // gb->apu.lfsr = 0x7FFF;
gb->io_registers[GB_IO_NR50] = 0x77;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
gb->apu.left_enabled[i] = gb->apu.right_enabled[i] = true; gb->apu.left_enabled[i] = gb->apu.right_enabled[i] = true;
} }
gb->apu.square_channels[GB_SQUARE_1].sample_length = 0x7FF;
gb->apu.square_channels[GB_SQUARE_2].sample_length = 0x7FF;
gb->apu.wave_channel.sample_length = 0x7FF; gb->apu.wave_channel.sample_length = 0x7FF;
gb->apu.square_carry = 1; gb->apu.square_carry = 1;
} }
@ -347,7 +350,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR11: case GB_IO_NR11:
case GB_IO_NR21: { case GB_IO_NR21: {
unsigned index = reg == GB_IO_NR21? GB_SQUARE_2: GB_SQUARE_1; unsigned index = reg == GB_IO_NR21? GB_SQUARE_2: GB_SQUARE_1;
gb->apu.square_channels[index].pulse_length = (0x40 - (value & 0x3f)) * 2 - 1; gb->apu.square_channels[index].pulse_length = (0x40 - (value & 0x3f));
break; break;
} }
@ -376,7 +379,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR14: case GB_IO_NR14:
case GB_IO_NR24: { case GB_IO_NR24: {
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].length_enabled = value & 0x40;
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 (value & 0x80) { if (value & 0x80) {
@ -398,16 +400,35 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
} }
} }
gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4; gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4;
gb->apu.square_channels[index].volume_countdown = (gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7) * 8; gb->apu.square_channels[index].volume_countdown = (gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7) * 4;
if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0) { if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0) {
gb->apu.is_active[index] = true; gb->apu.is_active[index] = true;
} }
if (gb->apu.square_channels[index].pulse_length == 0) { if (gb->apu.square_channels[index].pulse_length == 0) {
gb->apu.square_channels[index].pulse_length = 0x7F; gb->apu.square_channels[index].pulse_length = 0x40;
gb->apu.square_channels[index].length_enabled = false;
} }
/* 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. */
} }
/* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */
if ((value & 0x40) &&
!gb->apu.square_channels[index].length_enabled &&
(gb->apu.div_divider & 1) &&
gb->apu.square_channels[index].pulse_length) {
gb->apu.square_channels[index].pulse_length--;
if (gb->apu.square_channels[index].pulse_length == 0) {
if (value & 0x80) {
gb->apu.square_channels[index].pulse_length = 0x3F;
}
else {
update_sample(gb, index, 0, 0);
gb->apu.is_active[index] = false;
}
}
}
gb->apu.square_channels[index].length_enabled = value & 0x40;
break; break;
} }
@ -421,7 +442,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
} }
break; break;
case GB_IO_NR31: case GB_IO_NR31:
gb->apu.wave_channel.pulse_length = (0x100 - value) * 2 - 1; gb->apu.wave_channel.pulse_length = (0x100 - value);
break; break;
case GB_IO_NR32: case GB_IO_NR32:
gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3]; gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3];
@ -432,13 +453,15 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
gb->apu.wave_channel.sample_length |= (~value) & 0xFF; gb->apu.wave_channel.sample_length |= (~value) & 0xFF;
break; break;
case GB_IO_NR34: case GB_IO_NR34:
gb->apu.wave_channel.length_enabled = value & 0x40;
gb->apu.wave_channel.sample_length &= 0xFF; gb->apu.wave_channel.sample_length &= 0xFF;
gb->apu.wave_channel.sample_length |= ((~value) & 7) << 8; gb->apu.wave_channel.sample_length |= ((~value) & 7) << 8;
if ((value & 0x80) && gb->apu.wave_channel.enable) { if ((value & 0x80)) {
/* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU /* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU
reads from it. */ reads from it. */
if (!gb->is_cgb && gb->apu.is_active[GB_WAVE] && gb->apu.wave_channel.sample_countdown == 0) { if (!gb->is_cgb &&
gb->apu.is_active[GB_WAVE] &&
gb->apu.wave_channel.sample_countdown == 0 &&
gb->apu.wave_channel.enable) {
unsigned offset = ((gb->apu.wave_channel.current_sample_index + 1) >> 1) & 0xF; unsigned offset = ((gb->apu.wave_channel.current_sample_index + 1) >> 1) & 0xF;
/* On SGB2 (and probably SGB1 and MGB as well) this behavior is not accurate, /* On SGB2 (and probably SGB1 and MGB as well) this behavior is not accurate,
@ -461,10 +484,31 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length + 3; gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length + 3;
gb->apu.wave_channel.current_sample_index = 0; gb->apu.wave_channel.current_sample_index = 0;
if (gb->apu.wave_channel.pulse_length == 0) { if (gb->apu.wave_channel.pulse_length == 0) {
gb->apu.wave_channel.pulse_length = 0x1FF; gb->apu.wave_channel.pulse_length = 0x100;
gb->apu.wave_channel.length_enabled = false;
} }
/* 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. */
} }
/* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */
if ((value & 0x40) &&
!gb->apu.wave_channel.length_enabled &&
(gb->apu.div_divider & 1) &&
gb->apu.wave_channel.pulse_length) {
gb->apu.wave_channel.pulse_length--;
if (gb->apu.wave_channel.pulse_length == 0) {
if (value & 0x80) {
gb->apu.wave_channel.pulse_length = 0xFF;
}
else {
update_sample(gb, GB_WAVE, 0, 0);
gb->apu.is_active[GB_WAVE] = false;
}
}
}
gb->apu.wave_channel.length_enabled = value & 0x40;
gb->apu.is_active[GB_WAVE] &= gb->apu.wave_channel.enable;
break; break;
default: default:

View File

@ -11,8 +11,7 @@
#define CH_STEP (MAX_CH_AMP/0xF/7) #define CH_STEP (MAX_CH_AMP/0xF/7)
#endif #endif
/* Lengths are in either DIV ticks (512Hz, triggered by the DIV register) or /* APU ticks are 2MHz, triggered by an internal APU clock. */
APU ticks (2MHz, triggered by an internal APU clock) */
typedef struct typedef struct
{ {
@ -38,29 +37,31 @@ typedef struct
bool right_enabled[GB_N_CHANNELS]; bool right_enabled[GB_N_CHANNELS];
bool is_active[GB_N_CHANNELS]; bool is_active[GB_N_CHANNELS];
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
// once more to generate 128Hz and 64Hz clocks
uint8_t square_carry; // The square channels tick at 1MHz instead of 2, uint8_t square_carry; // The square channels tick at 1MHz instead of 2,
// so we need a carry to divide the signal // so we need a carry to divide the signal
uint8_t square_sweep_div; // The DIV-APU ticks are divided by 4 to handle tone sweeping
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_stop_countdown; // In 2 MHz
struct { struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in DIV ticks uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NRX2 uint8_t current_volume; // Reloaded from NRX2
uint8_t volume_countdown; // Reloaded from NRX2 uint8_t volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index; uint8_t current_sample_index;
bool sample_emitted; bool sample_emitted;
uint16_t sample_countdown; // in APU ticks uint16_t sample_countdown; // in APU ticks
uint16_t sample_length; // Reloaded from NRX3, NRX4, in APU ticks uint16_t sample_length; // From NRX3, NRX4, in APU ticks
bool length_enabled; // NRX4 bool length_enabled; // NRX4
} square_channels[2]; } square_channels[2];
struct { struct {
bool enable; // NR30 bool enable; // NR30
uint16_t pulse_length; // Reloaded from NR31 (xorred), in DIV ticks uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
uint8_t shift; // NR32 uint8_t shift; // NR32
uint16_t sample_length; // NR33, NR34, in APU ticks uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34 bool length_enabled; // NR34