Redo channel 4's timing accurately, emulate NR43 write quirks
This commit is contained in:
parent
1baa0446a9
commit
7de6194e28
125
Core/apu.c
125
Core/apu.c
@ -422,6 +422,28 @@ void GB_apu_div_event(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void step_lfsr(GB_gameboy_t *gb, unsigned cycles_offset)
|
||||||
|
{
|
||||||
|
unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
|
||||||
|
bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1;
|
||||||
|
gb->apu.noise_channel.lfsr >>= 1;
|
||||||
|
|
||||||
|
if (new_high_bit) {
|
||||||
|
gb->apu.noise_channel.lfsr |= high_bit_mask;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* This code is not redundent, it's relevant when switching LFSR widths */
|
||||||
|
gb->apu.noise_channel.lfsr &= ~high_bit_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
|
||||||
|
if (gb->apu.is_active[GB_NOISE]) {
|
||||||
|
update_sample(gb, GB_NOISE,
|
||||||
|
gb->apu.current_lfsr_sample ?
|
||||||
|
gb->apu.noise_channel.current_volume : 0,
|
||||||
|
cycles_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GB_apu_run(GB_gameboy_t *gb)
|
void GB_apu_run(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
@ -507,38 +529,37 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->apu.is_active[GB_NOISE]) {
|
// The noise channel can step even if inactive on the DMG
|
||||||
|
if (gb->apu.is_active[GB_NOISE] || !GB_is_cgb(gb)) {
|
||||||
uint8_t cycles_left = cycles;
|
uint8_t cycles_left = cycles;
|
||||||
while (unlikely(cycles_left > gb->apu.noise_channel.sample_countdown)) {
|
unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2;
|
||||||
cycles_left -= gb->apu.noise_channel.sample_countdown + 1;
|
if (!divisor) divisor = 2;
|
||||||
gb->apu.noise_channel.sample_countdown = gb->apu.noise_channel.sample_length * 4 + 3;
|
if (gb->apu.noise_channel.counter_countdown == 0) {
|
||||||
|
gb->apu.noise_channel.counter_countdown = divisor;
|
||||||
|
}
|
||||||
|
while (unlikely(cycles_left >= gb->apu.noise_channel.counter_countdown)) {
|
||||||
|
cycles_left -= gb->apu.noise_channel.counter_countdown;
|
||||||
|
gb->apu.noise_channel.counter_countdown = divisor + gb->apu.channel_4_delta;
|
||||||
|
gb->apu.channel_4_delta = 0;
|
||||||
|
bool old_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
|
||||||
|
gb->apu.noise_channel.counter++;
|
||||||
|
gb->apu.noise_channel.counter &= 0x3FFF;
|
||||||
|
bool new_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
|
||||||
|
|
||||||
/* Step LFSR */
|
/* Step LFSR */
|
||||||
unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
|
if (new_bit && !old_bit) {
|
||||||
bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1;
|
step_lfsr(gb, cycles - cycles_left);
|
||||||
gb->apu.noise_channel.lfsr >>= 1;
|
if (cycles_left == 0 && gb->apu.samples[GB_NOISE] == 0) {
|
||||||
|
gb->apu.pcm_mask[1] &= 0x0F;
|
||||||
if (new_high_bit) {
|
}
|
||||||
gb->apu.noise_channel.lfsr |= high_bit_mask;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
/* This code is not redundent, it's relevant when switching LFSR widths */
|
|
||||||
gb->apu.noise_channel.lfsr &= ~high_bit_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
|
|
||||||
|
|
||||||
if (cycles_left == 0 && gb->apu.samples[GB_NOISE] == 0) {
|
|
||||||
gb->apu.pcm_mask[1] &= 0x0F;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_sample(gb, GB_NOISE,
|
|
||||||
gb->apu.current_lfsr_sample ?
|
|
||||||
gb->apu.noise_channel.current_volume : 0,
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
if (cycles_left) {
|
if (cycles_left) {
|
||||||
gb->apu.noise_channel.sample_countdown -= cycles_left;
|
gb->apu.noise_channel.counter_countdown -= cycles_left;
|
||||||
|
gb->apu.channel_4_countdown_reloaded = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->apu.channel_4_countdown_reloaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -938,33 +959,43 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
|
|
||||||
case GB_IO_NR43: {
|
case GB_IO_NR43: {
|
||||||
gb->apu.noise_channel.narrow = value & 8;
|
gb->apu.noise_channel.narrow = value & 8;
|
||||||
unsigned divisor = (value & 0x07) << 1;
|
bool old_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
|
||||||
if (!divisor) divisor = 1;
|
gb->io_registers[GB_IO_NR43] = value;
|
||||||
gb->apu.noise_channel.sample_length = (divisor << (value >> 4)) - 1;
|
bool new_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
|
||||||
|
if (gb->apu.channel_4_countdown_reloaded) {
|
||||||
/* Todo: changing the frequency sometimes delays the next sample. This is probably
|
unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2;
|
||||||
due to how the frequency is actually calculated in the noise channel, which is probably
|
if (!divisor) divisor = 2;
|
||||||
not by calculating the effective sample length and counting simiarly to the other channels.
|
gb->apu.noise_channel.counter_countdown =
|
||||||
This is not emulated correctly. */
|
divisor + (divisor == 2? 0 : (uint8_t[]){2, 1, 0, 3}[(gb->apu.noise_channel.alignment) & 3]);
|
||||||
|
gb->apu.channel_4_delta = 0;
|
||||||
|
}
|
||||||
|
/* Step LFSR */
|
||||||
|
if (new_bit && !old_bit) {
|
||||||
|
step_lfsr(gb, 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GB_IO_NR44: {
|
case GB_IO_NR44: {
|
||||||
if (value & 0x80) {
|
if (value & 0x80) {
|
||||||
gb->apu.noise_channel.sample_countdown = (gb->apu.noise_channel.sample_length) * 2 + 6 - gb->apu.lf_div;
|
unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2;
|
||||||
|
if (!divisor) divisor = 2;
|
||||||
/* I'm COMPLETELY unsure about this logic, but it passes all relevant tests.
|
gb->apu.channel_4_delta = 0;
|
||||||
See comment in NR43. */
|
gb->apu.noise_channel.counter_countdown = divisor + 4;
|
||||||
if ((gb->io_registers[GB_IO_NR43] & 7) && (gb->apu.noise_channel.alignment & 2) == 0) {
|
if (divisor == 2) {
|
||||||
if ((gb->io_registers[GB_IO_NR43] & 7) == 1) {
|
gb->apu.noise_channel.counter_countdown += 1 - gb->apu.lf_div;
|
||||||
gb->apu.noise_channel.sample_countdown += 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gb->apu.noise_channel.sample_countdown -= 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (gb->apu.is_active[GB_NOISE]) {
|
else {
|
||||||
gb->apu.noise_channel.sample_countdown += 2;
|
gb->apu.noise_channel.counter_countdown += (uint8_t[]){2, 1, 0, 3}[gb->apu.noise_channel.alignment & 3];
|
||||||
|
if (((gb->apu.noise_channel.alignment + 1) & 3) < 2) {
|
||||||
|
if ((gb->io_registers[GB_IO_NR43] & 0x07) == 1) {
|
||||||
|
gb->apu.noise_channel.counter_countdown -= 2;
|
||||||
|
gb->apu.channel_4_delta = 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->apu.noise_channel.counter_countdown -= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4;
|
gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4;
|
||||||
|
@ -105,8 +105,9 @@ typedef struct
|
|||||||
uint16_t lfsr;
|
uint16_t lfsr;
|
||||||
bool narrow;
|
bool narrow;
|
||||||
|
|
||||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length)
|
uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz)
|
||||||
uint16_t sample_length; // From NR43, in APU ticks
|
uint8_t __padding;
|
||||||
|
uint16_t counter; // A bit from this 14-bit register ticks LFSR
|
||||||
bool length_enabled; // NR44
|
bool length_enabled; // NR44
|
||||||
|
|
||||||
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
|
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
|
||||||
@ -121,6 +122,9 @@ typedef struct
|
|||||||
bool current_lfsr_sample;
|
bool current_lfsr_sample;
|
||||||
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
|
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
|
||||||
uint8_t channel_1_restart_hold;
|
uint8_t channel_1_restart_hold;
|
||||||
|
int8_t channel_4_delta;
|
||||||
|
bool channel_4_countdown_reloaded;
|
||||||
|
|
||||||
} GB_apu_t;
|
} GB_apu_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -1821,10 +1821,10 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
|
|
||||||
|
|
||||||
GB_log(gb, "\nCH4:\n");
|
GB_log(gb, "\nCH4:\n");
|
||||||
GB_log(gb, " Current volume: %u, current sample length: %u APU ticks (next in %u ticks)\n",
|
GB_log(gb, " Current volume: %u, current internal counter: 0x%x (next increase in %u ticks)\n",
|
||||||
gb->apu.noise_channel.current_volume,
|
gb->apu.noise_channel.current_volume,
|
||||||
gb->apu.noise_channel.sample_length * 4 + 3,
|
gb->apu.noise_channel.counter,
|
||||||
gb->apu.noise_channel.sample_countdown);
|
gb->apu.noise_channel.counter_countdown);
|
||||||
|
|
||||||
GB_log(gb, " %u 256 Hz ticks till next volume %screase (out of %u)\n",
|
GB_log(gb, " %u 256 Hz ticks till next volume %screase (out of %u)\n",
|
||||||
gb->apu.noise_channel.volume_countdown,
|
gb->apu.noise_channel.volume_countdown,
|
||||||
|
@ -25,8 +25,11 @@ void GB_handle_rumble(GB_gameboy_t *gb)
|
|||||||
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
|
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
|
||||||
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
|
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
|
||||||
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
|
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
|
||||||
|
unsigned ch4_divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 1;
|
||||||
|
if (!ch4_divisor) ch4_divisor = 1;
|
||||||
|
unsigned ch4_sample_length = (ch4_divisor << (gb->io_registers[GB_IO_NR43] >> 4)) - 1;
|
||||||
|
|
||||||
double ch4_rumble = (MIN(gb->apu.noise_channel.sample_length * (gb->apu.noise_channel.narrow? 8 : 1) , 4096) * ((signed) gb->apu.noise_channel.current_volume * gb->apu.noise_channel.current_volume * ch4_volume / 32.0 - 50) - 2048) / 2048.0;
|
double ch4_rumble = (MIN(ch4_sample_length * (gb->apu.noise_channel.narrow? 8 : 1) , 4096) * ((signed) gb->apu.noise_channel.current_volume * gb->apu.noise_channel.current_volume * ch4_volume / 32.0 - 50) - 2048) / 2048.0;
|
||||||
|
|
||||||
ch4_rumble = MIN(ch4_rumble, 1.0);
|
ch4_rumble = MIN(ch4_rumble, 1.0);
|
||||||
ch4_rumble = MAX(ch4_rumble, 0.0);
|
ch4_rumble = MAX(ch4_rumble, 0.0);
|
||||||
|
Loading…
Reference in New Issue
Block a user