Emulate CGB-C PCM read glitch, fix a potential noise volume envelope bug

This commit is contained in:
Lior Halphon 2020-05-10 00:37:52 +03:00
parent 620ee3cf51
commit 3cba3e8e27
4 changed files with 28 additions and 7 deletions

View File

@ -283,6 +283,15 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) { if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) {
if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) { if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) {
if (gb->cgb_double_speed) {
if (index == GB_SQUARE_1) {
gb->apu.pcm_mask[0] &= gb->apu.square_channels[GB_SQUARE_1].current_volume | 0xF1;
}
else {
gb->apu.pcm_mask[0] &= (gb->apu.square_channels[GB_SQUARE_2].current_volume << 2) | 0x1F;
}
}
if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) { if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) {
gb->apu.square_channels[index].current_volume++; gb->apu.square_channels[index].current_volume++;
} }
@ -305,7 +314,10 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
uint8_t nr42 = gb->io_registers[GB_IO_NR42]; uint8_t nr42 = gb->io_registers[GB_IO_NR42];
if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) { if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) {
if (!--gb->apu.noise_channel.volume_countdown) { if (!gb->apu.noise_channel.volume_countdown || !--gb->apu.noise_channel.volume_countdown) {
if (gb->cgb_double_speed) {
gb->apu.pcm_mask[0] &= (gb->apu.noise_channel.current_volume << 2) | 0x1F;
}
if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) { if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) {
gb->apu.noise_channel.current_volume++; gb->apu.noise_channel.current_volume++;
} }
@ -455,6 +467,9 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 1; gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 1;
gb->apu.square_channels[i].current_sample_index++; gb->apu.square_channels[i].current_sample_index++;
gb->apu.square_channels[i].current_sample_index &= 0x7; gb->apu.square_channels[i].current_sample_index &= 0x7;
if (cycles_left == 0 && gb->apu.samples[i] == 0) {
gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F;
}
update_square_sample(gb, i); update_square_sample(gb, i);
} }
@ -506,6 +521,10 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1; 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, update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ? gb->apu.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0, gb->apu.noise_channel.current_volume : 0,

View File

@ -119,6 +119,7 @@ typedef struct
#define GB_SKIP_DIV_EVENT_SKIP 2 #define GB_SKIP_DIV_EVENT_SKIP 2
uint8_t skip_div_event; uint8_t skip_div_event;
bool current_lfsr_sample; bool current_lfsr_sample;
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
} GB_apu_t; } GB_apu_t;
typedef enum { typedef enum {

View File

@ -303,12 +303,12 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_IO_PCM_12: case GB_IO_PCM_12:
if (!GB_is_cgb(gb)) return 0xFF; if (!GB_is_cgb(gb)) return 0xFF;
return (gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) | return ((gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) |
(gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0); (gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0)) & (gb->model <= GB_MODEL_CGB_C? gb->apu.pcm_mask[0] : 0xFF);
case GB_IO_PCM_34: case GB_IO_PCM_34:
if (!GB_is_cgb(gb)) return 0xFF; if (!GB_is_cgb(gb)) return 0xFF;
return (gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) | return ((gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) |
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0); (gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0)) & (gb->model <= GB_MODEL_CGB_C? gb->apu.pcm_mask[1] : 0xFF);
case GB_IO_JOYP: case GB_IO_JOYP:
GB_timing_sync(gb); GB_timing_sync(gb);
case GB_IO_TMA: case GB_IO_TMA:

View File

@ -215,6 +215,7 @@ static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{ {
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF; // Sort of hacky, but too many cross-component interactions to do it right
// Affected by speed boost // Affected by speed boost
gb->dma_cycles += cycles; gb->dma_cycles += cycles;