From 3c20bffd466f2ffc9cfe1328096e198f5466e32c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 22 May 2021 21:10:25 -0500 Subject: [PATCH] prepare for game boy platform using SameBoy code (MIT License) --- CMakeLists.txt | 1 + src/engine/platform/sound/gb/LICENSE | 21 + src/engine/platform/sound/gb/apu.c | 1462 ++++++++++++++++++ src/engine/platform/sound/gb/apu.h | 186 +++ src/engine/platform/sound/gb/gb.h | 677 ++++++++ src/engine/platform/sound/gb/gb_struct_def.h | 5 + 6 files changed, 2352 insertions(+) create mode 100644 src/engine/platform/sound/gb/LICENSE create mode 100644 src/engine/platform/sound/gb/apu.c create mode 100644 src/engine/platform/sound/gb/apu.h create mode 100644 src/engine/platform/sound/gb/gb.h create mode 100644 src/engine/platform/sound/gb/gb_struct_def.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d7b81e88..5d23f54d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ src/log.cpp extern/Nuked-OPN2/ym3438.c src/engine/platform/sound/sn76496.cpp +src/engine/platform/sound/gb/apu.c src/engine/blip_buf.c src/engine/safeReader.cpp diff --git a/src/engine/platform/sound/gb/LICENSE b/src/engine/platform/sound/gb/LICENSE new file mode 100644 index 00000000..3303e0d7 --- /dev/null +++ b/src/engine/platform/sound/gb/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2015-2021 Lior Halphon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/engine/platform/sound/gb/apu.c b/src/engine/platform/sound/gb/apu.c new file mode 100644 index 00000000..badf3fbc --- /dev/null +++ b/src/engine/platform/sound/gb/apu.c @@ -0,0 +1,1462 @@ +#include +#include +#include +#include +#include "gb.h" + +#define CGB 0 +#define GB_CLOCK_RATE 0x400000 + +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +static const uint8_t duties[] = { + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 0, +}; + +static void refresh_channel(GB_gameboy_t *gb, unsigned index, unsigned cycles_offset) +{ + unsigned multiplier = gb->apu_output.cycles_since_render + cycles_offset - gb->apu_output.last_update[index]; + gb->apu_output.summed_samples[index].left += gb->apu_output.current_sample[index].left * multiplier; + gb->apu_output.summed_samples[index].right += gb->apu_output.current_sample[index].right * multiplier; + gb->apu_output.last_update[index] = gb->apu_output.cycles_since_render + cycles_offset; +} + +bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index) +{ + if (gb->model >= GB_MODEL_AGB) { + /* On the AGB, mixing is done digitally, so there are no per-channel + DACs. Instead, all channels are summed digital regardless of + whatever the DAC state would be on a CGB or earlier model. */ + return true; + } + + switch (index) { + case GB_SQUARE_1: + return gb->io_registers[GB_IO_NR12] & 0xF8; + + case GB_SQUARE_2: + return gb->io_registers[GB_IO_NR22] & 0xF8; + + case GB_WAVE: + return gb->apu.wave_channel.enable; + + case GB_NOISE: + return gb->io_registers[GB_IO_NR42] & 0xF8; + } + + return false; +} + +static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index) +{ + if (!gb->apu.is_active[index]) return 0; + + switch (index) { + case GB_SQUARE_1: + return gb->apu.square_channels[GB_SQUARE_1].current_volume; + case GB_SQUARE_2: + return gb->apu.square_channels[GB_SQUARE_2].current_volume; + case GB_WAVE: + return 0; + case GB_NOISE: + return gb->apu.noise_channel.current_volume; + } + return 0; +} + +static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset) +{ + if (gb->model >= GB_MODEL_AGB) { + /* On the AGB, because no analog mixing is done, the behavior of NR51 is a bit different. + A channel that is not connected to a terminal is idenitcal to a connected channel + playing PCM sample 0. */ + gb->apu.samples[index] = value; + + if (gb->apu_output.sample_rate) { + unsigned right_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1; + unsigned left_volume = ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1; + + if (index == GB_WAVE) { + /* For some reason, channel 3 is inverted on the AGB */ + value ^= 0xF; + } + + GB_sample_t output; + uint8_t bias = agb_bias_for_channel(gb, index); + + if (gb->io_registers[GB_IO_NR51] & (1 << index)) { + output.right = (0xf - value * 2 + bias) * right_volume; + } + else { + output.right = 0xf * right_volume; + } + + if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) { + output.left = (0xf - value * 2 + bias) * left_volume; + } + else { + output.left = 0xf * left_volume; + } + + if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) { + refresh_channel(gb, index, cycles_offset); + gb->apu_output.current_sample[index] = output; + } + } + + return; + } + + if (!GB_apu_is_DAC_enabled(gb, index)) { + value = gb->apu.samples[index]; + } + else { + gb->apu.samples[index] = value; + } + + if (gb->apu_output.sample_rate) { + unsigned right_volume = 0; + if (gb->io_registers[GB_IO_NR51] & (1 << index)) { + right_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1; + } + unsigned left_volume = 0; + if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) { + left_volume = ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1; + } + GB_sample_t output = {(0xf - value * 2) * left_volume, (0xf - value * 2) * right_volume}; + if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) { + refresh_channel(gb, index, cycles_offset); + gb->apu_output.current_sample[index] = output; + } + } +} + +static double smooth(double x) +{ + return 3*x*x - 2*x*x*x; +} + +static signed interference(GB_gameboy_t *gb) +{ + /* These aren't scientifically measured, but based on ear based on several recordings */ + signed ret = 0; + if (gb->halted) { + if (gb->model != GB_MODEL_AGB) { + ret -= MAX_CH_AMP / 5; + } + else { + ret -= MAX_CH_AMP / 12; + } + } + if (gb->io_registers[GB_IO_LCDC] & 0x80) { + ret += MAX_CH_AMP / 7; + if ((gb->io_registers[GB_IO_STAT] & 3) == 3 && gb->model != GB_MODEL_AGB) { + ret += MAX_CH_AMP / 14; + } + else if ((gb->io_registers[GB_IO_STAT] & 3) == 1) { + ret -= MAX_CH_AMP / 7; + } + } + + if (gb->apu.global_enable) { + ret += MAX_CH_AMP / 10; + } + + if (CGB && gb->model < GB_MODEL_AGB && (gb->io_registers[GB_IO_RP] & 1)) { + ret += MAX_CH_AMP / 10; + } + + if (!CGB) { + ret /= 4; + } + + ret += rand() % (MAX_CH_AMP / 12); + + return ret; +} + +static void render(GB_gameboy_t *gb) +{ + GB_sample_t output = {0, 0}; + + unrolled for (unsigned i = 0; i < GB_N_CHANNELS; i++) { + double multiplier = CH_STEP; + + if (gb->model < GB_MODEL_AGB) { + if (!GB_apu_is_DAC_enabled(gb, i)) { + gb->apu_output.dac_discharge[i] -= ((double) DAC_DECAY_SPEED) / gb->apu_output.sample_rate; + if (gb->apu_output.dac_discharge[i] < 0) { + multiplier = 0; + gb->apu_output.dac_discharge[i] = 0; + } + else { + multiplier *= smooth(gb->apu_output.dac_discharge[i]); + } + } + else { + gb->apu_output.dac_discharge[i] += ((double) DAC_ATTACK_SPEED) / gb->apu_output.sample_rate; + if (gb->apu_output.dac_discharge[i] > 1) { + gb->apu_output.dac_discharge[i] = 1; + } + else { + multiplier *= smooth(gb->apu_output.dac_discharge[i]); + } + } + } + + if (likely(gb->apu_output.last_update[i] == 0)) { + output.left += gb->apu_output.current_sample[i].left * multiplier; + output.right += gb->apu_output.current_sample[i].right * multiplier; + } + else { + refresh_channel(gb, i, 0); + output.left += (signed long) gb->apu_output.summed_samples[i].left * multiplier + / gb->apu_output.cycles_since_render; + output.right += (signed long) gb->apu_output.summed_samples[i].right * multiplier + / gb->apu_output.cycles_since_render; + gb->apu_output.summed_samples[i] = (GB_sample_t){0, 0}; + } + gb->apu_output.last_update[i] = 0; + } + gb->apu_output.cycles_since_render = 0; + + GB_sample_t filtered_output = gb->apu_output.highpass_mode? + (GB_sample_t) {output.left - gb->apu_output.highpass_diff.left, + output.right - gb->apu_output.highpass_diff.right} : + output; + + switch (gb->apu_output.highpass_mode) { + case GB_HIGHPASS_OFF: + gb->apu_output.highpass_diff = (GB_double_sample_t) {0, 0}; + break; + case GB_HIGHPASS_ACCURATE: + gb->apu_output.highpass_diff = (GB_double_sample_t) + {output.left - filtered_output.left * gb->apu_output.highpass_rate, + output.right - filtered_output.right * gb->apu_output.highpass_rate}; + break; + case GB_HIGHPASS_REMOVE_DC_OFFSET: { + unsigned mask = gb->io_registers[GB_IO_NR51]; + unsigned left_volume = 0; + unsigned right_volume = 0; + unrolled for (unsigned i = GB_N_CHANNELS; i--;) { + if (gb->apu.is_active[i]) { + if (mask & 1) { + left_volume += (gb->io_registers[GB_IO_NR50] & 7) * CH_STEP * 0xF; + } + if (mask & 0x10) { + right_volume += ((gb->io_registers[GB_IO_NR50] >> 4) & 7) * CH_STEP * 0xF; + } + } + else { + left_volume += gb->apu_output.current_sample[i].left * CH_STEP; + right_volume += gb->apu_output.current_sample[i].right * CH_STEP; + } + mask >>= 1; + } + gb->apu_output.highpass_diff = (GB_double_sample_t) + {left_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.left * gb->apu_output.highpass_rate, + right_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.right * gb->apu_output.highpass_rate}; + + case GB_HIGHPASS_MAX:; + } + + } + + + if (gb->apu_output.interference_volume) { + signed interference_bias = interference(gb); + int16_t interference_sample = (interference_bias - gb->apu_output.interference_highpass); + gb->apu_output.interference_highpass = gb->apu_output.interference_highpass * gb->apu_output.highpass_rate + + (1 - gb->apu_output.highpass_rate) * interference_sample; + interference_bias *= gb->apu_output.interference_volume; + + filtered_output.left = MAX(MIN(filtered_output.left + interference_bias, 0x7FFF), -0x8000); + filtered_output.right = MAX(MIN(filtered_output.right + interference_bias, 0x7FFF), -0x8000); + } + assert(gb->apu_output.sample_callback); + gb->apu_output.sample_callback(gb, &filtered_output); +} + +static void update_square_sample(GB_gameboy_t *gb, unsigned index) +{ + if (gb->apu.square_channels[index].current_sample_index & 0x80) return; + + uint8_t duty = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; + update_sample(gb, index, + duties[gb->apu.square_channels[index].current_sample_index + duty * 8]? + gb->apu.square_channels[index].current_volume : 0, + 0); +} + + +/* the effects of NRX2 writes on current volume are not well documented and differ + between models and variants. The exact behavior can only be verified on CGB as it + requires the PCM12 register. The behavior implemented here was verified on *my* + CGB, which might behave differently from other CGB revisions, as well as from the + DMG, MGB or SGB/2 */ +static void _nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value, uint8_t *countdown, GB_envelope_clock_t *lock) +{ + if (lock->clock) { + *countdown = value & 7; + } + bool should_tick = (value & 7) && !(old_value & 7) && !lock->locked; + bool should_invert = (value & 8) ^ (old_value & 8); + + if ((value & 0xF) == 8 && (old_value & 0xF) == 8 && !lock->locked) { + should_tick = true; + } + + if (should_invert) { + // The weird way and over-the-top way clocks for this counter are connected cause + // some weird ways for it to invert + if (value & 8) { + if (!(old_value & 7) && !lock->locked) { + *volume ^= 0xF; + } + else { + *volume = 0xE - *volume; + *volume &= 0xF; + } + should_tick = false; // Somehow prevents ticking? + } + else { + *volume = 0x10 - *volume; + *volume &= 0xF; + } + } + if (should_tick) { + if (value & 8) { + (*volume)++; + } + else { + (*volume)--; + } + *volume &= 0xF; + } + else if (!(value & 7) && lock->clock) { + // *lock->locked = false; // Excepted from the schematics, but doesn't actually happen on any model? + if (!should_invert) { + if (*volume == 0xF && (value & 8)) { + lock->locked = true; + } + else if (*volume == 0 && !(value & 8)) { + lock->locked = true; + } + } + else if (*volume == 1 && !(value & 8)) { + lock->locked = true; + } + else if (*volume == 0xE && (value & 8)) { + lock->locked = true; + } + lock->clock = false; + } +} + +static void nrx2_glitch(GB_gameboy_t *gb, uint8_t *volume, uint8_t value, uint8_t old_value, uint8_t *countdown, GB_envelope_clock_t *lock) +{ + if (gb->model <= GB_MODEL_CGB_C) { + _nrx2_glitch(volume, 0xFF, old_value, countdown, lock); + _nrx2_glitch(volume, value, 0xFF, countdown, lock); + } + else { + _nrx2_glitch(volume, value, old_value, countdown, lock); + } +} + +static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index) +{ + uint8_t nrx2 = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22]; + + if (gb->apu.square_envelope_clock[index].locked) return; + if (!(nrx2 & 7)) return; + 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) { + if (gb->apu.square_channels[index].current_volume < 0xF) { + gb->apu.square_channels[index].current_volume++; + } + else { + gb->apu.square_envelope_clock[index].locked = true; + } + } + else { + if (gb->apu.square_channels[index].current_volume > 0) { + gb->apu.square_channels[index].current_volume--; + } + else { + gb->apu.square_envelope_clock[index].locked = true; + } + } + + if (gb->apu.is_active[index]) { + update_square_sample(gb, index); + } +} + +static void tick_noise_envelope(GB_gameboy_t *gb) +{ + uint8_t nr42 = gb->io_registers[GB_IO_NR42]; + + if (gb->apu.noise_envelope_clock.locked) return; + if (!(nr42 & 7)) return; + + if (gb->cgb_double_speed) { + gb->apu.pcm_mask[0] &= (gb->apu.noise_channel.current_volume << 2) | 0x1F; + } + + if (nr42 & 8) { + if (gb->apu.noise_channel.current_volume < 0xF) { + gb->apu.noise_channel.current_volume++; + } + else { + gb->apu.noise_envelope_clock.locked = true; + } + } + else { + if (gb->apu.noise_channel.current_volume > 0) { + gb->apu.noise_channel.current_volume--; + } + else { + gb->apu.noise_envelope_clock.locked = true; + } + } + + if (gb->apu.is_active[GB_NOISE]) { + update_sample(gb, GB_NOISE, + (gb->apu.noise_channel.lfsr & 1) ? + gb->apu.noise_channel.current_volume : 0, + 0); + } +} + +static void trigger_sweep_calculation(GB_gameboy_t *gb) +{ + if ((gb->io_registers[GB_IO_NR10] & 0x70) && gb->apu.square_sweep_countdown == 7) { + if (gb->io_registers[GB_IO_NR10] & 0x07) { + gb->apu.square_channels[GB_SQUARE_1].sample_length = + gb->apu.sweep_length_addend + gb->apu.shadow_sweep_sample_length + !!(gb->io_registers[GB_IO_NR10] & 0x8); + gb->apu.square_channels[GB_SQUARE_1].sample_length &= 0x7FF; + } + if (gb->apu.channel_1_restart_hold == 0) { + gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length; + gb->apu.sweep_length_addend >>= (gb->io_registers[GB_IO_NR10] & 7); + } + + /* Recalculation and overflow check only occurs after a delay */ + gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 5 - gb->apu.lf_div; + if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { + // gb->apu.square_sweep_calculate_countdown += 2; + } + gb->apu.enable_zombie_calculate_stepping = false; + gb->apu.unshifted_sweep = !(gb->io_registers[GB_IO_NR10] & 0x7); + gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7; + } +} + +void GB_apu_div_event(GB_gameboy_t *gb) +{ + if (!gb->apu.global_enable) return; + if (gb->apu.skip_div_event == GB_SKIP_DIV_EVENT_SKIP) { + gb->apu.skip_div_event = GB_SKIP_DIV_EVENT_SKIPPED; + return; + } + if (gb->apu.skip_div_event == GB_SKIP_DIV_EVENT_SKIPPED) { + gb->apu.skip_div_event = GB_SKIP_DIV_EVENT_INACTIVE; + } + else { + gb->apu.div_divider++; + } + + if ((gb->apu.div_divider & 7) == 7) { + unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) { + if (!gb->apu.square_envelope_clock[i].clock) { + gb->apu.square_channels[i].volume_countdown--; + gb->apu.square_channels[i].volume_countdown &= 7; + } + } + if (!gb->apu.noise_envelope_clock.clock) { + gb->apu.noise_channel.volume_countdown--; + gb->apu.noise_channel.volume_countdown &= 7; + } + } + + unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) { + if (gb->apu.square_envelope_clock[i].clock) { + tick_square_envelope(gb, i); + gb->apu.square_envelope_clock[i].clock = false; + } + } + + if (gb->apu.noise_envelope_clock.clock) { + tick_noise_envelope(gb); + gb->apu.noise_envelope_clock.clock = false; + } + + if ((gb->apu.div_divider & 1) == 1) { + unrolled 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); + } + } + } + } + + 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; + update_sample(gb, GB_WAVE, 0, 0); + } + } + } + + if (gb->apu.noise_channel.length_enabled) { + if (gb->apu.noise_channel.pulse_length) { + if (!--gb->apu.noise_channel.pulse_length) { + gb->apu.is_active[GB_NOISE] = false; + update_sample(gb, GB_NOISE, 0, 0); + } + } + } + } + + if ((gb->apu.div_divider & 3) == 3) { + gb->apu.square_sweep_countdown++; + gb->apu.square_sweep_countdown &= 7; + trigger_sweep_calculation(gb); + } +} + +void GB_apu_div_secondary_event(GB_gameboy_t *gb) +{ + unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) { + uint8_t nrx2 = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22]; + if (gb->apu.is_active[i] && gb->apu.square_channels[i].volume_countdown == 0) { + gb->apu.square_envelope_clock[i].clock = (gb->apu.square_channels[i].volume_countdown = nrx2 & 7); + } + } + + if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0) { + gb->apu.noise_envelope_clock.clock = (gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7); + } +} + +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) +{ + /* Convert 4MHZ to 2MHz. apu_cycles is always divisable by 4. */ + uint8_t cycles = gb->apu.apu_cycles >> 2; + gb->apu.apu_cycles = 0; + if (!cycles) return; + + bool start_ch4 = false; + if (likely(!gb->stopped || CGB)) { + if (gb->apu.channel_4_dmg_delayed_start) { + if (gb->apu.channel_4_dmg_delayed_start == cycles) { + gb->apu.channel_4_dmg_delayed_start = 0; + start_ch4 = true; + } + else if (gb->apu.channel_4_dmg_delayed_start > cycles) { + gb->apu.channel_4_dmg_delayed_start -= cycles; + } + else { + /* Split it into two */ + cycles -= gb->apu.channel_4_dmg_delayed_start; + gb->apu.apu_cycles = gb->apu.channel_4_dmg_delayed_start * 4; + GB_apu_run(gb); + } + } + /* To align the square signal to 1MHz */ + gb->apu.lf_div ^= cycles & 1; + gb->apu.noise_channel.alignment += cycles; + + if (gb->apu.square_sweep_calculate_countdown && + (((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep) || + gb->apu.square_sweep_calculate_countdown <= 3)) { // Calculation is paused if the lower bits are 0 + if (gb->apu.square_sweep_calculate_countdown > cycles) { + gb->apu.square_sweep_calculate_countdown -= cycles; + } + else { + /* APU bug: sweep frequency is checked after adding the sweep delta twice */ + if (gb->apu.channel_1_restart_hold == 0) { + gb->apu.shadow_sweep_sample_length = gb->apu.square_channels[GB_SQUARE_1].sample_length; + } + if (gb->io_registers[GB_IO_NR10] & 8) { + gb->apu.sweep_length_addend ^= 0x7FF; + } + if (gb->apu.shadow_sweep_sample_length + gb->apu.sweep_length_addend > 0x7FF && !(gb->io_registers[GB_IO_NR10] & 8)) { + gb->apu.is_active[GB_SQUARE_1] = false; + update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles); + } + gb->apu.channel1_completed_addend = gb->apu.sweep_length_addend; + + gb->apu.square_sweep_calculate_countdown = 0; + } + } + + if (gb->apu.channel_1_restart_hold) { + if (gb->apu.channel_1_restart_hold > cycles) { + gb->apu.channel_1_restart_hold -= cycles; + } + else { + gb->apu.channel_1_restart_hold = 0; + } + } + + unrolled for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; i++) { + if (gb->apu.is_active[i]) { + uint8_t cycles_left = cycles; + while (unlikely(cycles_left > gb->apu.square_channels[i].sample_countdown)) { + cycles_left -= gb->apu.square_channels[i].sample_countdown + 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 &= 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); + } + if (cycles_left) { + gb->apu.square_channels[i].sample_countdown -= cycles_left; + } + } + } + + gb->apu.wave_channel.wave_form_just_read = false; + if (gb->apu.is_active[GB_WAVE]) { + uint8_t cycles_left = cycles; + while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) { + cycles_left -= gb->apu.wave_channel.sample_countdown + 1; + gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF; + gb->apu.wave_channel.current_sample_index++; + gb->apu.wave_channel.current_sample_index &= 0x1F; + gb->apu.wave_channel.current_sample = + gb->apu.wave_channel.wave_form[gb->apu.wave_channel.current_sample_index]; + update_sample(gb, GB_WAVE, + gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, + cycles - cycles_left); + gb->apu.wave_channel.wave_form_just_read = true; + } + if (cycles_left) { + gb->apu.wave_channel.sample_countdown -= cycles_left; + gb->apu.wave_channel.wave_form_just_read = false; + } + } + + // The noise channel can step even if inactive on the DMG + if (gb->apu.is_active[GB_NOISE] || !CGB) { + uint8_t cycles_left = cycles; + unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2; + if (!divisor) divisor = 2; + 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 */ + if (new_bit && !old_bit) { + if (cycles_left == 0 && gb->apu.samples[GB_NOISE] == 0) { + gb->apu.pcm_mask[1] &= 0x0F; + } + step_lfsr(gb, cycles - cycles_left); + } + } + if (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; + } + } + } + + if (gb->apu_output.sample_rate) { + gb->apu_output.cycles_since_render += cycles; + + if (gb->apu_output.sample_cycles >= gb->apu_output.cycles_per_sample) { + gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample; + render(gb); + } + } + if (start_ch4) { + GB_apu_write(gb, GB_IO_NR44, gb->io_registers[GB_IO_NR44] | 0x80); + } +} + +void GB_apu_init(GB_gameboy_t *gb) +{ + memset(&gb->apu, 0, sizeof(gb->apu)); + /* Restore the wave form */ + for (unsigned reg = GB_IO_WAV_START; reg <= GB_IO_WAV_END; reg++) { + gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = gb->io_registers[reg] >> 4; + gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = gb->io_registers[reg] & 0xF; + } + gb->apu.lf_div = 1; + /* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on, + the first DIV/APU event is skipped. */ + if (gb->div_counter & (gb->cgb_double_speed? 0x2000 : 0x1000)) { + gb->apu.skip_div_event = GB_SKIP_DIV_EVENT_SKIP; + gb->apu.div_divider = 1; + } +} + +uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg) +{ + if (reg == GB_IO_NR52) { + uint8_t value = 0; + for (unsigned i = 0; i < GB_N_CHANNELS; i++) { + value >>= 1; + if (gb->apu.is_active[i]) { + value |= 0x8; + } + } + if (gb->apu.global_enable) { + value |= 0x80; + } + value |= 0x70; + return value; + } + + static const char read_mask[GB_IO_WAV_END - GB_IO_NR10 + 1] = { + /* NRX0 NRX1 NRX2 NRX3 NRX4 */ + 0x80, 0x3F, 0x00, 0xFF, 0xBF, // NR1X + 0xFF, 0x3F, 0x00, 0xFF, 0xBF, // NR2X + 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, // NR3X + 0xFF, 0xFF, 0x00, 0x00, 0xBF, // NR4X + 0x00, 0x00, 0x70, 0xFF, 0xFF, // NR5X + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Unused + // Wave RAM + 0, /* ... */ + }; + + if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) { + if (!CGB && !gb->apu.wave_channel.wave_form_just_read) { + return 0xFF; + } + if (gb->model == GB_MODEL_AGB) { + return 0xFF; + } + reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2; + } + + return gb->io_registers[reg] | read_mask[reg - GB_IO_NR10]; +} + +static inline uint16_t effective_channel4_counter(GB_gameboy_t *gb) +{ + /* + TODO: On revisions older than the CGB-D, this behaves differently when + the counter advanced this exact T-cycle. Also, in these revisions, + it seems that "passive" changes (due to the temporary FF value NR43 + has during writes) behave slightly different from non-passive ones. + */ + uint16_t effective_counter = gb->apu.noise_channel.counter; + /* Ladies and gentlemen, I present you the holy grail glitch of revision detection! */ + switch (gb->model) { + /* Pre CGB revisions are assumed to be like CGB-C, A and 0 for the lack of a better guess. + TODO: It could be verified with audio based test ROMs. */ +#if 0 + case GB_MODEL_CGB_B: + if (effective_counter & 8) { + effective_counter |= 0xE; // Seems to me F under some circumstances? + } + if (effective_counter & 0x80) { + effective_counter |= 0xFF; + } + if (effective_counter & 0x100) { + effective_counter |= 0x1; + } + if (effective_counter & 0x200) { + effective_counter |= 0x2; + } + if (effective_counter & 0x400) { + effective_counter |= 0x4; + } + if (effective_counter & 0x800) { + effective_counter |= 0x408; // TODO: Only my CGB-B does that! Others behave like C! + } + if (effective_counter & 0x1000) { + effective_counter |= 0x10; + } + if (effective_counter & 0x2000) { + effective_counter |= 0x20; + } + break; +#endif + case GB_MODEL_DMG_B: + case GB_MODEL_SGB_NTSC: + case GB_MODEL_SGB_PAL: + case GB_MODEL_SGB_NTSC_NO_SFC: + case GB_MODEL_SGB_PAL_NO_SFC: + case GB_MODEL_SGB2: + case GB_MODEL_SGB2_NO_SFC: + // case GB_MODEL_CGB_0: + // case GB_MODEL_CGB_A: + case GB_MODEL_CGB_C: + if (effective_counter & 8) { + effective_counter |= 0xE; // Sometimes F on some instances + } + if (effective_counter & 0x80) { + effective_counter |= 0xFF; + } + if (effective_counter & 0x100) { + effective_counter |= 0x1; + } + if (effective_counter & 0x200) { + effective_counter |= 0x2; + } + if (effective_counter & 0x400) { + effective_counter |= 0x4; + } + if (effective_counter & 0x800) { + if ((gb->io_registers[GB_IO_NR43] & 8)) { + effective_counter |= 0x400; + } + effective_counter |= 0x8; + } + if (effective_counter & 0x1000) { + effective_counter |= 0x10; + } + if (effective_counter & 0x2000) { + effective_counter |= 0x20; + } + break; +#if 0 + case GB_MODEL_CGB_D: + if (effective_counter & ((gb->io_registers[GB_IO_NR43] & 8)? 0x40 : 0x80)) { // This is so weird + effective_counter |= 0xFF; + } + if (effective_counter & 0x100) { + effective_counter |= 0x1; + } + if (effective_counter & 0x200) { + effective_counter |= 0x2; + } + if (effective_counter & 0x400) { + effective_counter |= 0x4; + } + if (effective_counter & 0x800) { + effective_counter |= 0x8; + } + if (effective_counter & 0x1000) { + effective_counter |= 0x10; + } + break; +#endif + case GB_MODEL_CGB_E: + if (effective_counter & ((gb->io_registers[GB_IO_NR43] & 8)? 0x40 : 0x80)) { // This is so weird + effective_counter |= 0xFF; + } + if (effective_counter & 0x1000) { + effective_counter |= 0x10; + } + break; + case GB_MODEL_AGB: + /* TODO: AGBs are not affected, but AGSes are. They don't seem to follow a simple + pattern like the other revisions. */ + /* For the most part, AGS seems to do: + 0x20 -> 0xA0 + 0x200 -> 0xA00 + 0x1000 -> 0x1010, but only if wide + */ + break; + } + return effective_counter; +} + +void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) +{ + if (!gb->apu.global_enable && reg != GB_IO_NR52 && reg < GB_IO_WAV_START && (CGB || + ( + reg != GB_IO_NR11 && + reg != GB_IO_NR21 && + reg != GB_IO_NR31 && + reg != GB_IO_NR41 + ) + )) { + return; + } + + if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) { + if (!CGB && !gb->apu.wave_channel.wave_form_just_read) { + return; + } + reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2; + } + + /* Todo: this can and should be rewritten with a function table. */ + switch (reg) { + /* Globals */ + case GB_IO_NR50: + case GB_IO_NR51: + gb->io_registers[reg] = value; + /* These registers affect the output of all 4 channels (but not the output of the PCM registers).*/ + /* We call update_samples with the current value so the APU output is updated with the new outputs */ + for (unsigned i = GB_N_CHANNELS; i--;) { + update_sample(gb, i, gb->apu.samples[i], 0); + } + break; + case GB_IO_NR52: { + + uint8_t old_pulse_lengths[] = { + gb->apu.square_channels[0].pulse_length, + gb->apu.square_channels[1].pulse_length, + gb->apu.wave_channel.pulse_length, + gb->apu.noise_channel.pulse_length + }; + if ((value & 0x80) && !gb->apu.global_enable) { + GB_apu_init(gb); + gb->apu.global_enable = true; + } + else if (!(value & 0x80) && gb->apu.global_enable) { + for (unsigned i = GB_N_CHANNELS; i--;) { + update_sample(gb, i, 0, 0); + } + memset(&gb->apu, 0, sizeof(gb->apu)); + memset(gb->io_registers + GB_IO_NR10, 0, GB_IO_WAV_START - GB_IO_NR10); + gb->apu.global_enable = false; + } + + if (!CGB && (value & 0x80)) { + gb->apu.square_channels[0].pulse_length = old_pulse_lengths[0]; + gb->apu.square_channels[1].pulse_length = old_pulse_lengths[1]; + gb->apu.wave_channel.pulse_length = old_pulse_lengths[2]; + gb->apu.noise_channel.pulse_length = old_pulse_lengths[3]; + } + } + break; + + /* Square channels */ + case GB_IO_NR10:{ + bool old_negate = gb->io_registers[GB_IO_NR10] & 8; + gb->io_registers[GB_IO_NR10] = value; + if (gb->apu.shadow_sweep_sample_length + gb->apu.channel1_completed_addend + old_negate > 0x7FF && + !(value & 8)) { + gb->apu.is_active[GB_SQUARE_1] = false; + update_sample(gb, GB_SQUARE_1, 0, 0); + } + trigger_sweep_calculation(gb); + break; + } + + 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)); + if (!gb->apu.global_enable) { + value &= 0x3f; + } + break; + } + + case GB_IO_NR12: + case GB_IO_NR22: { + unsigned index = reg == GB_IO_NR22? GB_SQUARE_2: GB_SQUARE_1; + if ((value & 0xF8) == 0) { + /* This disables the DAC */ + gb->io_registers[reg] = value; + gb->apu.is_active[index] = false; + update_sample(gb, index, 0, 0); + } + else if (gb->apu.is_active[index]) { + nrx2_glitch(gb, &gb->apu.square_channels[index].current_volume, + value, gb->io_registers[reg], &gb->apu.square_channels[index].volume_countdown, + &gb->apu.square_envelope_clock[index]); + update_square_sample(gb, index); + } + + break; + } + + case GB_IO_NR13: + case GB_IO_NR23: { + 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 |= value & 0xFF; + break; + } + + case GB_IO_NR14: + case GB_IO_NR24: { + /* TODO: GB_MODEL_CGB_D fails channel_1_sweep_restart_2, don't forget when adding support for this revision! */ + unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1; + bool was_active = gb->apu.is_active[index]; + /* TODO: When the sample length changes right before being updated, the countdown should change to the + old length, but the current sample should not change. Because our write timing isn't accurate to + the T-cycle, we hack around it by stepping the sample index backwards. */ + if ((value & 0x80) == 0 && gb->apu.is_active[index]) { + /* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on + double speed. */ + if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) { + if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) { + gb->apu.square_channels[index].current_sample_index--; + gb->apu.square_channels[index].current_sample_index &= 7; + } + } + } + + uint16_t old_sample_length = gb->apu.square_channels[index].sample_length; + gb->apu.square_channels[index].sample_length &= 0xFF; + gb->apu.square_channels[index].sample_length |= (value & 7) << 8; + if (value & 0x80) { + /* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by + turning the APU off. */ + gb->apu.square_envelope_clock[index].locked = false; + gb->apu.square_envelope_clock[index].clock = false; + if (!gb->apu.is_active[index]) { + gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 6 - gb->apu.lf_div; + if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { + gb->apu.square_channels[index].sample_countdown += 2; + } + } + else { + unsigned extra_delay = 0; + if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */) { + if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1) / 2) & 0x400)) { + gb->apu.square_channels[index].current_sample_index++; + gb->apu.square_channels[index].current_sample_index &= 0x7; + gb->apu.is_active[index] = true; + } + /* Todo: verify with the schematics what's going on in here */ + else if (gb->apu.square_channels[index].sample_length == 0x7FF && + old_sample_length != 0x7FF && + (gb->apu.square_channels[index].current_sample_index & 0x80)) { + extra_delay += 2; + } + } + /* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/ + gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.lf_div + extra_delay; + if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { + gb->apu.square_channels[index].sample_countdown += 2; + } + } + gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4; + /* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously + started sound). The playback itself is not instant which is why we don't update the sample for other + cases. */ + if (gb->apu.is_active[index]) { + update_square_sample(gb, index); + } + + gb->apu.square_channels[index].volume_countdown = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7; + + if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0 && !gb->apu.is_active[index]) { + gb->apu.is_active[index] = true; + update_sample(gb, index, 0, 0); + /* We use the highest bit in current_sample_index to mark this sample is not actually playing yet, */ + gb->apu.square_channels[index].current_sample_index |= 0x80; + } + if (gb->apu.square_channels[index].pulse_length == 0) { + gb->apu.square_channels[index].pulse_length = 0x40; + gb->apu.square_channels[index].length_enabled = false; + } + + if (index == GB_SQUARE_1) { + gb->apu.shadow_sweep_sample_length = 0; + gb->apu.channel1_completed_addend = 0; + if (gb->io_registers[GB_IO_NR10] & 7) { + /* APU bug: if shift is nonzero, overflow check also occurs on trigger */ + gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 5 - gb->apu.lf_div; + if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { + /* TODO: I used to think this is correct, but it caused several regressions. + More research is needed to figure how calculation time is different + in models prior to CGB-D */ + // gb->apu.square_sweep_calculate_countdown += 2; + } + gb->apu.enable_zombie_calculate_stepping = false; + gb->apu.unshifted_sweep = false; + if (!was_active) { + gb->apu.square_sweep_calculate_countdown += 2; + } + gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length; + gb->apu.sweep_length_addend >>= (gb->io_registers[GB_IO_NR10] & 7); + } + else { + gb->apu.sweep_length_addend = 0; + } + gb->apu.channel_1_restart_hold = 2 - gb->apu.lf_div + CGB * 2; + if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { + gb->apu.channel_1_restart_hold += 2; + } + gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7; + } + } + + /* 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 { + gb->apu.is_active[index] = false; + update_sample(gb, index, 0, 0); + } + } + } + gb->apu.square_channels[index].length_enabled = value & 0x40; + break; + } + + /* Wave channel */ + case GB_IO_NR30: + gb->apu.wave_channel.enable = value & 0x80; + if (!gb->apu.wave_channel.enable) { + gb->apu.is_active[GB_WAVE] = false; + update_sample(gb, GB_WAVE, 0, 0); + } + break; + case GB_IO_NR31: + 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]; + if (gb->apu.is_active[GB_WAVE]) { + update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, 0); + } + break; + case GB_IO_NR33: + gb->apu.wave_channel.sample_length &= ~0xFF; + gb->apu.wave_channel.sample_length |= value & 0xFF; + break; + case GB_IO_NR34: + gb->apu.wave_channel.sample_length &= 0xFF; + gb->apu.wave_channel.sample_length |= (value & 7) << 8; + if ((value & 0x80)) { + /* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU + reads from it. */ + if (!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; + + /* This glitch varies between models and even specific instances: + DMG-B: Most of them behave as emulated. A few behave differently. + SGB: As far as I know, all tested instances behave as emulated. + MGB, SGB2: Most instances behave non-deterministically, a few behave as emulated. + + Additionally, I believe DMGs, including those we behave differently than emulated, + are all deterministic. */ + if (offset < 4) { + gb->io_registers[GB_IO_WAV_START] = gb->io_registers[GB_IO_WAV_START + offset]; + gb->apu.wave_channel.wave_form[0] = gb->apu.wave_channel.wave_form[offset / 2]; + gb->apu.wave_channel.wave_form[1] = gb->apu.wave_channel.wave_form[offset / 2 + 1]; + } + else { + memcpy(gb->io_registers + GB_IO_WAV_START, + gb->io_registers + GB_IO_WAV_START + (offset & ~3), + 4); + memcpy(gb->apu.wave_channel.wave_form, + gb->apu.wave_channel.wave_form + (offset & ~3) * 2, + 8); + } + } + if (!gb->apu.is_active[GB_WAVE]) { + gb->apu.is_active[GB_WAVE] = true; + update_sample(gb, GB_WAVE, + gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, + 0); + } + gb->apu.wave_channel.sample_countdown = (gb->apu.wave_channel.sample_length ^ 0x7FF) + 3; + gb->apu.wave_channel.current_sample_index = 0; + if (gb->apu.wave_channel.pulse_length == 0) { + 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 { + gb->apu.is_active[GB_WAVE] = false; + update_sample(gb, GB_WAVE, 0, 0); + } + } + } + gb->apu.wave_channel.length_enabled = value & 0x40; + if (gb->apu.is_active[GB_WAVE] && !gb->apu.wave_channel.enable) { + gb->apu.is_active[GB_WAVE] = false; + update_sample(gb, GB_WAVE, 0, 0); + } + + break; + + /* Noise Channel */ + + case GB_IO_NR41: { + gb->apu.noise_channel.pulse_length = (0x40 - (value & 0x3f)); + break; + } + + case GB_IO_NR42: { + if ((value & 0xF8) == 0) { + /* This disables the DAC */ + gb->io_registers[reg] = value; + gb->apu.is_active[GB_NOISE] = false; + update_sample(gb, GB_NOISE, 0, 0); + } + else if (gb->apu.is_active[GB_NOISE]) { + nrx2_glitch(gb, &gb->apu.noise_channel.current_volume, + value, gb->io_registers[reg], &gb->apu.noise_channel.volume_countdown, + &gb->apu.noise_envelope_clock); + update_sample(gb, GB_NOISE, + gb->apu.current_lfsr_sample ? + gb->apu.noise_channel.current_volume : 0, + 0); + } + break; + } + + case GB_IO_NR43: { + gb->apu.noise_channel.narrow = value & 8; + uint16_t effective_counter = effective_channel4_counter(gb); + bool old_bit = (effective_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1; + gb->io_registers[GB_IO_NR43] = value; + bool new_bit = (effective_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1; + if (gb->apu.channel_4_countdown_reloaded) { + unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2; + if (!divisor) divisor = 2; + if (gb->model > GB_MODEL_CGB_C) { + gb->apu.noise_channel.counter_countdown = + divisor + (divisor == 2? 0 : (uint8_t[]){2, 1, 0, 3}[(gb->apu.noise_channel.alignment) & 3]); + } + else { + gb->apu.noise_channel.counter_countdown = + divisor + (divisor == 2? 0 : (uint8_t[]){2, 1, 4, 3}[(gb->apu.noise_channel.alignment) & 3]); + } + gb->apu.channel_4_delta = 0; + } + /* Step LFSR */ + if (new_bit && (!old_bit || gb->model <= GB_MODEL_CGB_C)) { + if (gb->model <= GB_MODEL_CGB_C) { + bool previous_narrow = gb->apu.noise_channel.narrow; + gb->apu.noise_channel.narrow = true; + step_lfsr(gb, 0); + gb->apu.noise_channel.narrow = previous_narrow; + } + else { + step_lfsr(gb, 0); + } + } + break; + } + + case GB_IO_NR44: { + if (value & 0x80) { + gb->apu.noise_envelope_clock.locked = false; + gb->apu.noise_envelope_clock.clock = false; + if (!CGB && (gb->apu.noise_channel.alignment & 3) != 0) { + gb->apu.channel_4_dmg_delayed_start = 6; + } + else { + unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2; + if (!divisor) divisor = 2; + gb->apu.channel_4_delta = 0; + gb->apu.noise_channel.counter_countdown = divisor + 4; + if (divisor == 2) { + if (gb->model <= GB_MODEL_CGB_C) { + gb->apu.noise_channel.counter_countdown += gb->apu.lf_div; + if (!gb->cgb_double_speed) { + gb->apu.noise_channel.counter_countdown -= 1; + } + } + else { + gb->apu.noise_channel.counter_countdown += 1 - gb->apu.lf_div; + } + } + else { + if (gb->model <= GB_MODEL_CGB_C) { + gb->apu.noise_channel.counter_countdown += (uint8_t[]){2, 1, 4, 3}[gb->apu.noise_channel.alignment & 3]; + } + else { + 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; + } + } + } + + /* TODO: These are quite weird. Verify further */ + if (gb->model <= GB_MODEL_CGB_C) { + if (gb->cgb_double_speed) { + if (!(gb->io_registers[GB_IO_NR43] & 0xF0) && (gb->io_registers[GB_IO_NR43] & 0x07)) { + gb->apu.noise_channel.counter_countdown -= 1; + } + else if ((gb->io_registers[GB_IO_NR43] & 0xF0) && !(gb->io_registers[GB_IO_NR43] & 0x07)) { + gb->apu.noise_channel.counter_countdown += 1; + } + } + else { + gb->apu.noise_channel.counter_countdown -= 2; + } + } + + gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4; + + /* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously + started sound). The playback itself is not instant which is why we don't update the sample for other + cases. */ + if (gb->apu.is_active[GB_NOISE]) { + update_sample(gb, GB_NOISE, + gb->apu.current_lfsr_sample ? + gb->apu.noise_channel.current_volume : 0, + 0); + } + gb->apu.noise_channel.lfsr = 0; + gb->apu.current_lfsr_sample = false; + gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7; + + if (!gb->apu.is_active[GB_NOISE] && (gb->io_registers[GB_IO_NR42] & 0xF8) != 0) { + gb->apu.is_active[GB_NOISE] = true; + update_sample(gb, GB_NOISE, 0, 0); + } + + if (gb->apu.noise_channel.pulse_length == 0) { + gb->apu.noise_channel.pulse_length = 0x40; + gb->apu.noise_channel.length_enabled = false; + } + } + } + + /* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */ + if ((value & 0x40) && + !gb->apu.noise_channel.length_enabled && + (gb->apu.div_divider & 1) && + gb->apu.noise_channel.pulse_length) { + gb->apu.noise_channel.pulse_length--; + if (gb->apu.noise_channel.pulse_length == 0) { + if (value & 0x80) { + gb->apu.noise_channel.pulse_length = 0x3F; + } + else { + gb->apu.is_active[GB_NOISE] = false; + update_sample(gb, GB_NOISE, 0, 0); + } + } + } + gb->apu.noise_channel.length_enabled = value & 0x40; + break; + } + + default: + if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) { + gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4; + gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF; + } + } + gb->io_registers[reg] = value; +} + +void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate) +{ + + gb->apu_output.sample_rate = sample_rate; + if (sample_rate) { + gb->apu_output.highpass_rate = pow(0.999958, GB_CLOCK_RATE / (double)sample_rate); + } + gb->apu_output.rate_set_in_clocks = false; + GB_apu_update_cycles_per_sample(gb); +} + +void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample) +{ + + if (cycles_per_sample == 0) { + GB_set_sample_rate(gb, 0); + return; + } + gb->apu_output.cycles_per_sample = cycles_per_sample; + gb->apu_output.sample_rate = GB_CLOCK_RATE / cycles_per_sample * 2; + gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample); + gb->apu_output.rate_set_in_clocks = true; +} + +void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback) +{ + gb->apu_output.sample_callback = callback; +} + +void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode) +{ + gb->apu_output.highpass_mode = mode; +} + +void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb) +{ + if (gb->apu_output.rate_set_in_clocks) return; + if (gb->apu_output.sample_rate) { + gb->apu_output.cycles_per_sample = 2 * GB_CLOCK_RATE / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */ + } +} + +void GB_set_interference_volume(GB_gameboy_t *gb, double volume) +{ + gb->apu_output.interference_volume = volume; +} diff --git a/src/engine/platform/sound/gb/apu.h b/src/engine/platform/sound/gb/apu.h new file mode 100644 index 00000000..06162034 --- /dev/null +++ b/src/engine/platform/sound/gb/apu.h @@ -0,0 +1,186 @@ +#ifndef apu_h +#define apu_h +#include +#include +#include +#include "gb_struct_def.h" + + +/* Speed = 1 / Length (in seconds) */ +#define DAC_DECAY_SPEED 20000 +#define DAC_ATTACK_SPEED 20000 + + +/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */ +#ifdef WIIU +/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/ +#define MAX_CH_AMP (0xFF0 / 2) +#else +#define MAX_CH_AMP 0xFF0 +#endif +#define CH_STEP (MAX_CH_AMP/0xF/8) + + + +/* APU ticks are 2MHz, triggered by an internal APU clock. */ + +typedef struct +{ + int16_t left; + int16_t right; +} GB_sample_t; + +typedef struct +{ + double left; + double right; +} GB_double_sample_t; + +enum GB_CHANNELS { + GB_SQUARE_1, + GB_SQUARE_2, + GB_WAVE, + GB_NOISE, + GB_N_CHANNELS +}; + +typedef struct +{ + bool locked:1; + bool clock:1; // Represents FOSY on channel 4 + unsigned padding:6; +} GB_envelope_clock_t; + +typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample); + +typedef struct +{ + bool global_enable; + uint8_t apu_cycles; + + uint8_t samples[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 lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide + // need to divide the signal. + + uint8_t square_sweep_countdown; // In 128Hz + uint8_t square_sweep_calculate_countdown; // In 2 MHz + uint16_t sweep_length_addend; + uint16_t shadow_sweep_sample_length; + bool unshifted_sweep; + bool enable_zombie_calculate_stepping; + + struct { + 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; /* For save state compatibility, + highest bit is reused (See NR14/NR24's + write code)*/ + + uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF) + 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 256Hz DIV ticks + uint8_t shift; // NR32 + uint16_t sample_length; // NR33, NR34, in APU ticks + bool length_enabled; // NR34 + + uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF) + uint8_t current_sample_index; + uint8_t current_sample; // Current sample before shifting. + + int8_t wave_form[32]; + bool wave_form_just_read; + } wave_channel; + + struct { + uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks + uint8_t current_volume; // Reloaded from NR42 + uint8_t volume_countdown; // Reloaded from NR42 + uint16_t lfsr; + bool narrow; + + uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz) + uint8_t __padding; + uint16_t counter; // A bit from this 14-bit register ticks LFSR + bool length_enabled; // NR44 + + uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of + // 1MHz. This variable keeps track of the alignment. + + } noise_channel; + +#define GB_SKIP_DIV_EVENT_INACTIVE 0 +#define GB_SKIP_DIV_EVENT_SKIPPED 1 +#define GB_SKIP_DIV_EVENT_SKIP 2 + uint8_t skip_div_event; + bool current_lfsr_sample; + uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch + uint8_t channel_1_restart_hold; + int8_t channel_4_delta; + bool channel_4_countdown_reloaded; + uint8_t channel_4_dmg_delayed_start; + uint16_t channel1_completed_addend; + + GB_envelope_clock_t square_envelope_clock[2]; + GB_envelope_clock_t noise_envelope_clock; +} GB_apu_t; + +typedef enum { + GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset + GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware + GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform + GB_HIGHPASS_MAX +} GB_highpass_mode_t; + +typedef struct { + unsigned sample_rate; + + double sample_cycles; // In 8 MHz units + double cycles_per_sample; + + // Samples are NOT normalized to MAX_CH_AMP * 4 at this stage! + unsigned cycles_since_render; + unsigned last_update[GB_N_CHANNELS]; + GB_sample_t current_sample[GB_N_CHANNELS]; + GB_sample_t summed_samples[GB_N_CHANNELS]; + double dac_discharge[GB_N_CHANNELS]; + + GB_highpass_mode_t highpass_mode; + double highpass_rate; + GB_double_sample_t highpass_diff; + + GB_sample_callback_t sample_callback; + + bool rate_set_in_clocks; + double interference_volume; + double interference_highpass; +} GB_apu_output_t; + +void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate); +void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */ +void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode); +void GB_set_interference_volume(GB_gameboy_t *gb, double volume); +void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); + +bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index); +void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); +uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg); +void GB_apu_div_event(GB_gameboy_t *gb); +void GB_apu_div_secondary_event(GB_gameboy_t *gb); +void GB_apu_init(GB_gameboy_t *gb); +void GB_apu_run(GB_gameboy_t *gb); +void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb); +void GB_borrow_sgb_border(GB_gameboy_t *gb); + +#endif /* apu_h */ diff --git a/src/engine/platform/sound/gb/gb.h b/src/engine/platform/sound/gb/gb.h new file mode 100644 index 00000000..c66a702b --- /dev/null +++ b/src/engine/platform/sound/gb/gb.h @@ -0,0 +1,677 @@ +#ifndef GB_h +#define GB_h +#define typeof __typeof__ +#include +#include +#include +#include + +#include "gb_struct_def.h" + +#include "apu.h" + +#define GB_STRUCT_VERSION 13 + +#define GB_MODEL_FAMILY_MASK 0xF00 +#define GB_MODEL_DMG_FAMILY 0x000 +#define GB_MODEL_MGB_FAMILY 0x100 +#define GB_MODEL_CGB_FAMILY 0x200 +#define GB_MODEL_PAL_BIT 0x40 +#define GB_MODEL_NO_SFC_BIT 0x80 + +#define GB_MODEL_PAL_BIT_OLD 0x1000 +#define GB_MODEL_NO_SFC_BIT_OLD 0x2000 + +#if __clang__ +#define unrolled _Pragma("unroll") +#elif __GNUC__ >= 8 +#define unrolled _Pragma("GCC unroll 8") +#else +#define unrolled +#endif + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define GB_BIG_ENDIAN +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define GB_LITTLE_ENDIAN +#else +#error Unable to detect endianess +#endif + +#ifdef GB_BIG_ENDIAN +#define LE16(x) __builtin_bswap16(x) +#define LE32(x) __builtin_bswap32(x) +#define LE64(x) __builtin_bswap64(x) +#define BE16(x) (x) +#define BE32(x) (x) +#define BE64(x) (x) +#else +#define LE16(x) (x) +#define LE32(x) (x) +#define LE64(x) (x) +#define BE16(x) __builtin_bswap16(x) +#define BE32(x) __builtin_bswap32(x) +#define BE64(x) __builtin_bswap64(x) +#endif + +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; }) +#endif + +typedef struct { + struct { + uint8_t r, g, b; + } colors[5]; +} GB_palette_t; + +extern const GB_palette_t GB_PALETTE_GREY; +extern const GB_palette_t GB_PALETTE_DMG; +extern const GB_palette_t GB_PALETTE_MGB; +extern const GB_palette_t GB_PALETTE_GBL; + +typedef union { + struct { + uint8_t seconds; + uint8_t minutes; + uint8_t hours; + uint8_t days; + uint8_t high; + }; + struct { + uint8_t seconds; + uint8_t minutes; + uint8_t hours:5; + uint8_t weekday:3; + uint8_t weeks; + } tpp1; + uint8_t data[5]; +} GB_rtc_time_t; + +typedef struct __attribute__((packed)) { + uint64_t last_rtc_second; + uint16_t minutes; + uint16_t days; + uint16_t alarm_minutes, alarm_days; + uint8_t alarm_enabled; +} GB_huc3_rtc_time_t; + +typedef enum { + // GB_MODEL_DMG_0 = 0x000, + // GB_MODEL_DMG_A = 0x001, + GB_MODEL_DMG_B = 0x002, + // GB_MODEL_DMG_C = 0x003, + GB_MODEL_SGB = 0x004, + GB_MODEL_SGB_NTSC = GB_MODEL_SGB, + GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT, + GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT, + GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC, + GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT, + // GB_MODEL_MGB = 0x100, + GB_MODEL_SGB2 = 0x101, + GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT, + // GB_MODEL_CGB_0 = 0x200, + // GB_MODEL_CGB_A = 0x201, + // GB_MODEL_CGB_B = 0x202, + GB_MODEL_CGB_C = 0x203, + // GB_MODEL_CGB_D = 0x204, + GB_MODEL_CGB_E = 0x205, + GB_MODEL_AGB = 0x206, +} GB_model_t; + +enum { + GB_REGISTER_AF, + GB_REGISTER_BC, + GB_REGISTER_DE, + GB_REGISTER_HL, + GB_REGISTER_SP, + GB_REGISTERS_16_BIT /* Count */ +}; + +/* Todo: Actually use these! */ +enum { + GB_CARRY_FLAG = 16, + GB_HALF_CARRY_FLAG = 32, + GB_SUBTRACT_FLAG = 64, + GB_ZERO_FLAG = 128, +}; + +typedef enum { + GB_BORDER_SGB, + GB_BORDER_NEVER, + GB_BORDER_ALWAYS, +} GB_border_mode_t; + +enum { + /* Joypad and Serial */ + GB_IO_JOYP = 0x00, // Joypad (R/W) + GB_IO_SB = 0x01, // Serial transfer data (R/W) + GB_IO_SC = 0x02, // Serial Transfer Control (R/W) + + /* Missing */ + + /* Timers */ + GB_IO_DIV = 0x04, // Divider Register (R/W) + GB_IO_TIMA = 0x05, // Timer counter (R/W) + GB_IO_TMA = 0x06, // Timer Modulo (R/W) + GB_IO_TAC = 0x07, // Timer Control (R/W) + + /* Missing */ + + GB_IO_IF = 0x0f, // Interrupt Flag (R/W) + + /* Sound */ + GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W) + GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W) + GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W) + GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only) + GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W) + /* NR20 does not exist */ + GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W) + GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W) + GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W) + GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W) + GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W) + GB_IO_NR31 = 0x1b, // Channel 3 Sound Length + GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W) + GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W) + GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W) + /* NR40 does not exist */ + GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W) + GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W) + GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W) + GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W) + GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W) + GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W) + GB_IO_NR52 = 0x26, // Sound on/off + + /* Missing */ + + GB_IO_WAV_START = 0x30, // Wave pattern start + GB_IO_WAV_END = 0x3f, // Wave pattern end + + /* Graphics */ + GB_IO_LCDC = 0x40, // LCD Control (R/W) + GB_IO_STAT = 0x41, // LCDC Status (R/W) + GB_IO_SCY = 0x42, // Scroll Y (R/W) + GB_IO_SCX = 0x43, // Scroll X (R/W) + GB_IO_LY = 0x44, // LCDC Y-Coordinate (R) + GB_IO_LYC = 0x45, // LY Compare (R/W) + GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W) + GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only + GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only + GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only + GB_IO_WY = 0x4a, // Window Y Position (R/W) + GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W) + // Has some undocumented compatibility flags written at boot. + // Unfortunately it is not readable or writable after boot has finished, so research of this + // register is quite limited. The value written to this register, however, can be controlled + // in some cases. + GB_IO_KEY0 = 0x4c, + + /* General CGB features */ + GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch + + /* Missing */ + + GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank + GB_IO_BANK = 0x50, // Write to disable the BIOS mapping + + /* CGB DMA */ + GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High + GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low + GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High + GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low + GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start + + /* IR */ + GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port + + /* Missing */ + + /* CGB Paletts */ + GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index + GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data + GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index + GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data + GB_IO_OPRI = 0x6c, // Affects object priority (X based or index based) + + /* Missing */ + + GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank + GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write) + GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write) + GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only + GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write) + GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes + GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes + GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only +}; + +typedef enum { + GB_LOG_BOLD = 1, + GB_LOG_DASHED_UNDERLINE = 2, + GB_LOG_UNDERLINE = 4, + GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE +} GB_log_attributes; + +typedef enum { + GB_BOOT_ROM_DMG0, + GB_BOOT_ROM_DMG, + GB_BOOT_ROM_MGB, + GB_BOOT_ROM_SGB, + GB_BOOT_ROM_SGB2, + GB_BOOT_ROM_CGB0, + GB_BOOT_ROM_CGB, + GB_BOOT_ROM_AGB, +} GB_boot_rom_t; + +typedef enum { + GB_RTC_MODE_SYNC_TO_HOST, + GB_RTC_MODE_ACCURATE, +} GB_rtc_mode_t; + +#define LCDC_PERIOD 70224 +#define CPU_FREQUENCY 0x400000 +#define SGB_NTSC_FREQUENCY (21477272 / 5) +#define SGB_PAL_FREQUENCY (21281370 / 5) +#define DIV_CYCLES (0x100) +#define INTERNAL_DIV_CYCLES (0x40000) + +#if !defined(MIN) +#define MIN(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; }) +#endif + +#if !defined(MAX) +#define MAX(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; }) +#endif + +typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb); +typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes); +typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb); +typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b); +typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on); +typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude); +typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send); +typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb); +typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb); +typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value); +typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row); +typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb); +typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb); +typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type); + +struct GB_breakpoint_s; +struct GB_watchpoint_s; + +typedef struct { + uint8_t pixel; // Color, 0-3 + uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG) + uint8_t priority; // Sprite priority – 0 in DMG, OAM index in CGB + bool bg_priority; // For sprite FIFO – the BG priority bit. For the BG FIFO – the CGB attributes priority bit +} GB_fifo_item_t; + +#define GB_FIFO_LENGTH 16 +typedef struct { + GB_fifo_item_t fifo[GB_FIFO_LENGTH]; + uint8_t read_end; + uint8_t write_end; +} GB_fifo_t; + +typedef struct { + uint32_t magic; + uint8_t track_count; + uint8_t first_track; + uint16_t load_address; + uint16_t init_address; + uint16_t play_address; + uint16_t sp; + uint8_t TMA; + uint8_t TAC; + char title[32]; + char author[32]; + char copyright[32]; +} GB_gbs_header_t; + +typedef struct { + uint8_t track_count; + uint8_t first_track; + char title[33]; + char author[33]; + char copyright[33]; +} GB_gbs_info_t; + +/* When state saving, each section is dumped independently of other sections. + This allows adding data to the end of the section without worrying about future compatibility. + Some other changes might be "safe" as well. + This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64 + bit platforms. */ + +struct GB_gameboy_s { + /* Registers */ + uint16_t pc; + union { + uint16_t registers[GB_REGISTERS_16_BIT]; + struct { + uint16_t af, + bc, + de, + hl, + sp; + }; + struct { +#ifdef GB_BIG_ENDIAN + uint8_t a, f, + b, c, + d, e, + h, l; +#else + uint8_t f, a, + c, b, + e, d, + l, h; +#endif + }; + + }; + uint8_t ime; + uint8_t interrupt_enable; + uint8_t cgb_ram_bank; + + /* CPU and General Hardware Flags*/ + GB_model_t model; + bool cgb_mode; + bool cgb_double_speed; + bool halted; + bool stopped; + bool boot_rom_finished; + bool ime_toggle; /* ei has delayed a effect.*/ + bool halt_bug; + bool just_halted; + + /* Misc state */ + bool infrared_input; + uint8_t extra_oam[0xff00 - 0xfea0]; + uint32_t ram_size; // Different between CGB and DMG + + int32_t ir_sensor; + bool effective_ir_input; + + /* DMA and HDMA */ + bool hdma_on; + bool hdma_on_hblank; + uint8_t hdma_steps_left; + int16_t hdma_cycles; // in 8MHz units + uint16_t hdma_current_src, hdma_current_dest; + + uint8_t dma_steps_left; + uint8_t dma_current_dest; + uint16_t dma_current_src; + int16_t dma_cycles; + bool is_dma_restarting; + uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */ + bool hdma_starting; + + /* MBC */ + uint16_t mbc_rom_bank; + uint8_t mbc_ram_bank; + uint32_t mbc_ram_size; + bool mbc_ram_enable; + union { + struct { + uint8_t bank_low:5; + uint8_t bank_high:2; + uint8_t mode:1; + } mbc1; + + struct { + uint8_t rom_bank:4; + } mbc2; + + struct { + uint8_t rom_bank:8; + uint8_t ram_bank:3; + } mbc3; + + struct { + uint8_t rom_bank_low; + uint8_t rom_bank_high:1; + uint8_t ram_bank:4; + } mbc5; + + struct { + uint8_t bank_low:6; + uint8_t bank_high:3; + bool mode:1; + bool ir_mode:1; + } huc1; + + struct { + uint8_t rom_bank:7; + uint8_t padding:1; + uint8_t ram_bank:4; + } huc3; + }; + uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ + bool camera_registers_mapped; + uint8_t camera_registers[0x36]; + uint8_t rumble_strength; + bool cart_ir; + + // TODO: move to huc3/mbc3/tpp1 struct when breaking save compat + uint8_t huc3_mode; + uint8_t huc3_access_index; + uint16_t huc3_minutes, huc3_days; + uint16_t huc3_alarm_minutes, huc3_alarm_days; + bool huc3_alarm_enabled; + uint8_t huc3_read; + uint8_t huc3_access_flags; + bool mbc3_rtc_mapped; + uint16_t tpp1_rom_bank; + uint8_t tpp1_ram_bank; + uint8_t tpp1_mode; + + + /* HRAM and HW Registers */ + uint8_t hram[0xFFFF - 0xFF80]; + uint8_t io_registers[0x80]; + + // oops + uint16_t div_counter; + uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */ + uint16_t serial_cycles; + uint16_t serial_length; + uint8_t double_speed_alignment; + uint8_t serial_count; + + GB_apu_t apu; + + + /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ + /* This data is reserved on reset and must come last in the struct */ + /* ROM */ + unsigned pending_cycles; + + /* Various RAMs */ + uint8_t *ram; + uint8_t *vram; + uint8_t *mbc_ram; + + /* I/O */ + uint32_t *screen; + uint32_t background_palettes_rgb[0x20]; + uint32_t sprite_palettes_rgb[0x20]; + const GB_palette_t *dmg_palette; + double light_temperature; + + /* Timing */ + uint64_t last_sync; + uint64_t cycles_since_last_sync; // In 8MHz units + GB_rtc_mode_t rtc_mode; + + /* Audio */ + GB_apu_output_t apu_output; + + /*** Debugger ***/ + volatile bool debug_stopped, debug_disable; + bool debug_fin_command, debug_next_command; + + /* SLD (Todo: merge with backtrace) */ + bool stack_leak_detection; + signed debug_call_depth; + uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */ + uint16_t addr_for_call_depth[0x200]; + + /* Backtrace */ + unsigned backtrace_size; + uint16_t backtrace_sps[0x200]; + struct { + uint16_t bank; + uint16_t addr; + } backtrace_returns[0x200]; + + /* Ticks command */ + uint64_t debugger_ticks; + + /* Undo */ + uint8_t *undo_state; + const char *undo_label; + + /* Rewind */ +#define GB_REWIND_FRAMES_PER_KEY 255 + size_t rewind_buffer_length; + struct { + uint8_t *key_state; + uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY]; + unsigned pos; + } *rewind_sequences; // lasts about 4 seconds + size_t rewind_pos; + + + /* Misc */ + bool turbo; + bool turbo_dont_skip; + bool disable_rendering; + uint8_t boot_rom[0x900]; + bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank + uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units + double clock_multiplier; + + /* Temporary state */ + bool wx_just_changed; + bool tile_sel_glitch; +}; + +#ifndef __printflike +/* Missing from Linux headers. */ +#define __printflike(fmtarg, firstvararg) \ +__attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#endif + +void GB_init(GB_gameboy_t *gb, GB_model_t model); +bool GB_is_inited(GB_gameboy_t *gb); +bool GB_is_cgb(GB_gameboy_t *gb); +bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 +bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd +GB_model_t GB_get_model(GB_gameboy_t *gb); +void GB_free(GB_gameboy_t *gb); +void GB_reset(GB_gameboy_t *gb); +void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model); + +/* Returns the time passed, in 8MHz ticks. */ +uint8_t GB_run(GB_gameboy_t *gb); +/* Returns the time passed since the last frame, in nanoseconds */ +uint64_t GB_run_frame(GB_gameboy_t *gb); + +typedef enum { + GB_DIRECT_ACCESS_ROM, + GB_DIRECT_ACCESS_RAM, + GB_DIRECT_ACCESS_CART_RAM, + GB_DIRECT_ACCESS_VRAM, + GB_DIRECT_ACCESS_HRAM, + GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */ + GB_DIRECT_ACCESS_BOOTROM, + GB_DIRECT_ACCESS_OAM, + GB_DIRECT_ACCESS_BGP, + GB_DIRECT_ACCESS_OBP, + GB_DIRECT_ACCESS_IE, +} GB_direct_access_t; + +/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank + is returned at *bank, even if only a portion of the memory is banked. */ +void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank); + +void *GB_get_user_data(GB_gameboy_t *gb); +void GB_set_user_data(GB_gameboy_t *gb, void *data); + +int GB_load_boot_rom(GB_gameboy_t *gb, const char *path); +void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size); +int GB_load_rom(GB_gameboy_t *gb, const char *path); +void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size); +int GB_load_isx(GB_gameboy_t *gb, const char *path); +int GB_load_gbs_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size, GB_gbs_info_t *info); +int GB_load_gbs(GB_gameboy_t *gb, const char *path, GB_gbs_info_t *info); +void GB_gbs_switch_track(GB_gameboy_t *gb, uint8_t track); + +int GB_save_battery_size(GB_gameboy_t *gb); +int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size); +int GB_save_battery(GB_gameboy_t *gb, const char *path); + +void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size); +void GB_load_battery(GB_gameboy_t *gb, const char *path); + +void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip); +void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled); + +void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3); +void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4); + +void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output); +void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode); + +void GB_set_infrared_input(GB_gameboy_t *gb, bool state); + +void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback); +void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback); +void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback); +void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback); +void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback); +void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback); +void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback); +void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback); +/* Called when a new boot ROM is needed. The callback should call GB_load_boot_rom or GB_load_boot_rom_from_buffer */ +void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback); + +void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette); + +/* These APIs are used when using internal clock */ +void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback); +void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback); + +/* These APIs are used when using external clock */ +bool GB_serial_get_data_bit(GB_gameboy_t *gb); +void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data); + +void GB_disconnect_serial(GB_gameboy_t *gb); + +/* For cartridges with an alarm clock */ +unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm + +/* RTC emulation mode */ +void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode); + +/* For integration with SFC/SNES emulators */ +void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback); +void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback); +void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback); +void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback); + +uint32_t GB_get_clock_rate(GB_gameboy_t *gb); +uint32_t GB_get_unmultiplied_clock_rate(GB_gameboy_t *gb); +void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier); + +unsigned GB_get_screen_width(GB_gameboy_t *gb); +unsigned GB_get_screen_height(GB_gameboy_t *gb); +double GB_get_usual_frame_rate(GB_gameboy_t *gb); +unsigned GB_get_player_count(GB_gameboy_t *gb); + +#endif /* GB_h */ diff --git a/src/engine/platform/sound/gb/gb_struct_def.h b/src/engine/platform/sound/gb/gb_struct_def.h new file mode 100644 index 00000000..0e0ebd12 --- /dev/null +++ b/src/engine/platform/sound/gb/gb_struct_def.h @@ -0,0 +1,5 @@ +#ifndef gb_struct_def_h +#define gb_struct_def_h +struct GB_gameboy_s; +typedef struct GB_gameboy_s GB_gameboy_t; +#endif