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,42 +73,54 @@ static void render(GB_gameboy_t *gb)
void GB_apu_div_event(GB_gameboy_t *gb)
{
if (!gb->apu.global_enable) return;
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
if (gb->apu.square_channels[i].length_enabled) {
if (gb->apu.square_channels[i].pulse_length) {
if (!--gb->apu.square_channels[i].pulse_length) {
gb->apu.is_active[i] = false;
update_sample(gb, i, 0, 0);
gb->apu.div_divider++;
if ((gb->apu.div_divider & 1) == 1) {
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
if (gb->apu.square_channels[i].length_enabled) {
if (gb->apu.square_channels[i].pulse_length) {
if (!--gb->apu.square_channels[i].pulse_length) {
gb->apu.is_active[i] = false;
update_sample(gb, i, 0, 0);
}
}
}
uint8_t nrx2 = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
if (gb->apu.square_channels[i].volume_countdown) {
if (!--gb->apu.square_channels[i].volume_countdown) {
if ((nrx2 & 8) && gb->apu.square_channels[i].current_volume < 0xF) {
gb->apu.square_channels[i].current_volume++;
}
else if (!(nrx2 & 8) && gb->apu.square_channels[i].current_volume > 0) {
gb->apu.square_channels[i].current_volume--;
}
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;
update_sample(gb, i,
duties[gb->apu.square_channels[i].current_sample_index + duty * 8]?
gb->apu.square_channels[i].current_volume : 0,
0);
}
}
}
uint8_t nrx2 = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
if (gb->apu.square_channels[i].volume_countdown) {
if (!--gb->apu.square_channels[i].volume_countdown) {
if ((nrx2 & 8) && gb->apu.square_channels[i].current_volume < 0xF) {
gb->apu.square_channels[i].current_volume++;
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);
}
else if (!(nrx2 & 8) && gb->apu.square_channels[i].current_volume > 0) {
gb->apu.square_channels[i].current_volume--;
}
gb->apu.square_channels[i].volume_countdown = (nrx2 & 7) * 8;
uint8_t duty = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6;
update_sample(gb, i,
duties[gb->apu.square_channels[i].current_sample_index + duty * 8]?
gb->apu.square_channels[i].current_volume : 0,
0);
}
}
}
gb->apu.square_sweep_div++;
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) {
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));
// gb->apu.wave_channels[0].duty = gb->apu.wave_channels[1].duty = 4;
// gb->apu.lfsr = 0x7FFF;
gb->io_registers[GB_IO_NR50] = 0x77;
for (int i = 0; i < 4; i++) {
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.square_carry = 1;
}
@ -314,7 +317,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
}
gb->io_registers[reg] = value;
switch (reg) {
/* Globals */
case GB_IO_NR50:
@ -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_NR21: {
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;
}
@ -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_NR24: {
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 |= ((~value) & 7) << 8;
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].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) {
gb->apu.is_active[index] = true;
}
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. */
}
/* 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;
}
@ -421,7 +442,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
}
break;
case GB_IO_NR31:
gb->apu.wave_channel.pulse_length = (0x100 - value) * 2 - 1;
gb->apu.wave_channel.pulse_length = (0x100 - value);
break;
case GB_IO_NR32:
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;
break;
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 |= ((~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
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;
/* 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.current_sample_index = 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. */
}
/* 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;
default:

View File

@ -11,8 +11,7 @@
#define CH_STEP (MAX_CH_AMP/0xF/7)
#endif
/* Lengths are in either DIV ticks (512Hz, triggered by the DIV register) or
APU ticks (2MHz, triggered by an internal APU clock) */
/* APU ticks are 2MHz, triggered by an internal APU clock. */
typedef struct
{
@ -38,29 +37,31 @@ typedef struct
bool right_enabled[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,
// 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_stop_countdown; // In 2 MHz
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 volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index;
bool sample_emitted;
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
} square_channels[2];
struct {
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
uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34