mirror of
https://github.com/LIJI32/SameBoy.git
synced 2026-10-12 02:06:38 +00:00
2642 lines
109 KiB
C
2642 lines
109 KiB
C
#include <stdint.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include "gb.h"
|
|
|
|
/* Band limited synthesis loosely based on: http://www.slack.net/~ant/bl-synth/ */
|
|
static int32_t band_limited_steps[GB_BAND_LIMITED_PHASES][GB_BAND_LIMITED_WIDTH];
|
|
|
|
static void __attribute__((constructor)) band_limited_init(void)
|
|
{
|
|
const unsigned master_size = GB_BAND_LIMITED_WIDTH * GB_BAND_LIMITED_PHASES;
|
|
double *master = malloc(master_size * sizeof(*master));
|
|
memset(master, 0, master_size * sizeof(*master));
|
|
|
|
const double lowpass = 15.0 / 16.0; // 1.0 means using Nyquist as the exact cutoff
|
|
const double to_angle = M_PI / GB_BAND_LIMITED_PHASES * lowpass;
|
|
double sum = 0;
|
|
nounroll for (signed i = 0; i < master_size; i++) {
|
|
// Exact Blackman window
|
|
const double a0 = 7938 / 18608.0;
|
|
const double a1 = 9240 / 18608.0;
|
|
const double a2 = 1430 / 18608.0;
|
|
double window_angle = (2.0 * M_PI * i) / (master_size);
|
|
double window = a0 - a1 * cos(window_angle) + a2 * cos(2 * window_angle);
|
|
|
|
double angle = (i - (signed)master_size / 2) * to_angle;
|
|
sum += master[i] = (angle == 0? 1 : sin(angle) / angle) * window;
|
|
}
|
|
|
|
nounroll for (signed i = 0; i < master_size; i++) {
|
|
master[i] /= sum;
|
|
}
|
|
|
|
nounroll for (signed phase = 0; phase < GB_BAND_LIMITED_PHASES; phase++) {
|
|
int32_t error = GB_BAND_LIMITED_ONE;
|
|
nounroll for (signed i = 0; i < GB_BAND_LIMITED_WIDTH; i++) {
|
|
double sum = 0;
|
|
nounroll for (signed j = 0; j < GB_BAND_LIMITED_PHASES; j++) {
|
|
signed index = i * GB_BAND_LIMITED_PHASES - phase + j;
|
|
if (index >= 0) {
|
|
sum += master[index];
|
|
}
|
|
}
|
|
int32_t cur = sum * GB_BAND_LIMITED_ONE;
|
|
error -= cur;
|
|
band_limited_steps[phase][i] = cur;
|
|
}
|
|
|
|
// Make sure the deltas sum to 1.0
|
|
band_limited_steps[phase][GB_BAND_LIMITED_WIDTH / 2] += error;
|
|
}
|
|
free(master);
|
|
}
|
|
|
|
static void band_limited_update(GB_band_limited_t *band_limited, const GB_sample_t *input, unsigned phase)
|
|
{
|
|
if (input->packed == band_limited->input.packed) return;
|
|
unsigned delay = phase / GB_BAND_LIMITED_PHASES;
|
|
phase = phase & (GB_BAND_LIMITED_PHASES - 1);
|
|
|
|
struct {
|
|
signed left, right;
|
|
} delta = {
|
|
.left = input->left - band_limited->input.left,
|
|
.right = input->right - band_limited->input.right,
|
|
};
|
|
band_limited->input.packed = input->packed;
|
|
|
|
for (unsigned i = 0; i < GB_BAND_LIMITED_WIDTH; i++) {
|
|
unsigned offset = (i + band_limited->pos + delay) & (sizeof(band_limited->buffer) / sizeof(band_limited->buffer[0]) - 1);
|
|
band_limited->buffer[offset].left += delta.left * band_limited_steps[phase][i];
|
|
band_limited->buffer[offset].right += delta.right * band_limited_steps[phase][i];
|
|
}
|
|
}
|
|
|
|
static void band_limited_update_unfiltered(GB_band_limited_t *band_limited, const GB_sample_t *input, unsigned delay)
|
|
{
|
|
if (input->packed == band_limited->input.packed) return;
|
|
|
|
struct {
|
|
signed left, right;
|
|
} delta = {
|
|
.left = input->left - band_limited->input.left,
|
|
.right = input->right - band_limited->input.right,
|
|
};
|
|
band_limited->input.packed = input->packed;
|
|
|
|
unsigned offset = (band_limited->pos + delay) & (sizeof(band_limited->buffer) / sizeof(band_limited->buffer[0]) - 1);
|
|
band_limited->buffer[offset].left += delta.left * GB_BAND_LIMITED_ONE;
|
|
band_limited->buffer[offset].right += delta.right * GB_BAND_LIMITED_ONE;
|
|
}
|
|
|
|
static void band_limited_read(GB_band_limited_t *band_limited, GB_sample_t *output, uint32_t multiplier)
|
|
{
|
|
band_limited->output.left += band_limited->buffer[band_limited->pos].left;
|
|
band_limited->output.right += band_limited->buffer[band_limited->pos].right;
|
|
|
|
band_limited->buffer[band_limited->pos].left = band_limited->buffer[band_limited->pos].right = 0;
|
|
band_limited->pos = (band_limited->pos + 1) & (sizeof(band_limited->buffer) / sizeof(band_limited->buffer[0]) - 1);
|
|
|
|
output->left = band_limited->output.left * multiplier / GB_BAND_LIMITED_ONE;
|
|
output->right = band_limited->output.right * multiplier / GB_BAND_LIMITED_ONE;
|
|
|
|
/* This hueristic will mute the channel if it's only playing an amplitude of 1 or 2 units, usually
|
|
caused by rounding errors when the channel is playing a single frequency above Nyquist. */
|
|
|
|
unsigned diff = abs(output->left - band_limited->last_output.left);
|
|
if (diff > 4) {
|
|
band_limited->silence_detection = 0;
|
|
band_limited->last_output.packed = output->packed;
|
|
return;
|
|
}
|
|
|
|
diff = abs(output->right - band_limited->last_output.right);
|
|
if (diff > 4) {
|
|
band_limited->silence_detection = 0;
|
|
band_limited->last_output.packed = output->packed;
|
|
return;
|
|
}
|
|
|
|
if (band_limited->silence_detection == 4000) {
|
|
output->packed = band_limited->last_output.packed;
|
|
}
|
|
else {
|
|
band_limited->silence_detection++;
|
|
}
|
|
}
|
|
|
|
static inline uint32_t sample_fraction_multiply(GB_gameboy_t *gb, unsigned multiplier)
|
|
{
|
|
if (unlikely(multiplier == 0)) return 0;
|
|
if (likely(multiplier < GB_QUICK_MULTIPLY_COUNT + 1)) {
|
|
return gb->apu_output.quick_fraction_multiply_cache[multiplier - 1];
|
|
}
|
|
return gb->apu_output.quick_fraction_multiply_cache[0] * multiplier;
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, GB_channel_t index)
|
|
{
|
|
if (gb->model > GB_MODEL_CGB_E) {
|
|
/* 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;
|
|
|
|
nodefault;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, GB_channel_t 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;
|
|
|
|
nodefault;
|
|
}
|
|
}
|
|
|
|
static void update_sample(GB_gameboy_t *gb, GB_channel_t index, int8_t value, unsigned cycles_offset)
|
|
{
|
|
if (gb->model > GB_MODEL_CGB_E) {
|
|
/* 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;
|
|
int8_t silence = 0;
|
|
if (index == GB_WAVE) {
|
|
/* For some reason, channel 3 is inverted on the AGB, and has a different "silence" value */
|
|
value ^= 0xF;
|
|
silence = 7 * 2;
|
|
}
|
|
|
|
uint8_t bias = agb_bias_for_channel(gb, index);
|
|
|
|
bool left = gb->io_registers[GB_IO_NR51] & (0x10 << index);
|
|
bool right = gb->io_registers[GB_IO_NR51] & (1 << index);
|
|
|
|
GB_sample_t output = {
|
|
.left = (0xF - (left? value * 2 + bias : silence)) * left_volume,
|
|
.right = (0xF - (right? value * 2 + bias : silence)) * right_volume
|
|
};
|
|
|
|
if (unlikely(gb->apu_output.channel_muted[index])) {
|
|
output.left = output.right = 0;
|
|
}
|
|
|
|
if (unlikely(gb->apu_output.max_cycles_per_sample == 1)) {
|
|
band_limited_update_unfiltered(&gb->apu_output.band_limited[index], &output, cycles_offset);
|
|
}
|
|
else {
|
|
band_limited_update(&gb->apu_output.band_limited[index],
|
|
&output,
|
|
(((gb->apu_output.sample_fraction + sample_fraction_multiply(gb, cycles_offset)) >> 8) * GB_BAND_LIMITED_PHASES) >> 20);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (value == 0 && gb->apu.samples[index] == 0) 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 = {0, 0};
|
|
if (likely(!gb->apu_output.channel_muted[index])) {
|
|
output = (GB_sample_t){(0xF - value * 2) * left_volume, (0xF - value * 2) * right_volume};
|
|
}
|
|
if (unlikely(gb->apu_output.max_cycles_per_sample == 1)) {
|
|
band_limited_update_unfiltered(&gb->apu_output.band_limited[index], &output, cycles_offset);
|
|
}
|
|
else {
|
|
band_limited_update(&gb->apu_output.band_limited[index],
|
|
&output,
|
|
(((gb->apu_output.sample_fraction + sample_fraction_multiply(gb, cycles_offset)) >> 8) * GB_BAND_LIMITED_PHASES) >> 20);
|
|
}
|
|
}
|
|
}
|
|
|
|
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_CGB_E) {
|
|
ret -= MAX_CH_AMP / 5;
|
|
}
|
|
else {
|
|
ret -= MAX_CH_AMP / 12;
|
|
}
|
|
}
|
|
if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE) {
|
|
ret += MAX_CH_AMP / 7;
|
|
if ((gb->io_registers[GB_IO_STAT] & 3) == 3 && gb->model <= GB_MODEL_CGB_E) {
|
|
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 (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_E && (gb->io_registers[GB_IO_RP] & 1)) {
|
|
ret += MAX_CH_AMP / 10;
|
|
}
|
|
|
|
if (!GB_is_cgb(gb)) {
|
|
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_CGB_E) {
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
GB_sample_t channel_output;
|
|
band_limited_read(&gb->apu_output.band_limited[i], &channel_output, multiplier);
|
|
|
|
output.left += channel_output.left;
|
|
output.right += channel_output.right;
|
|
}
|
|
gb->apu_output.cycles_since_render = 0;
|
|
if (unlikely(gb->apu_output.sample_fraction < (1 << 28))) {
|
|
gb->apu_output.sample_fraction = 0;
|
|
}
|
|
else {
|
|
gb->apu_output.sample_fraction -= 1 << 28;
|
|
}
|
|
|
|
if (gb->sgb && gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) return;
|
|
|
|
GB_sample_t filtered_output = gb->apu_output.highpass_mode?
|
|
(GB_sample_t) {output.left - (int16_t)gb->apu_output.highpass_diff.left,
|
|
output.right - (int16_t)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 - (output.left - gb->apu_output.highpass_diff.left) * gb->apu_output.highpass_rate,
|
|
output.right - (output.right - gb->apu_output.highpass_diff.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_DAC_enabled(gb, i)) {
|
|
if (mask & 1) {
|
|
left_volume += ((gb->io_registers[GB_IO_NR50] & 7) + 1) * CH_STEP * 0xF;
|
|
}
|
|
if (mask & 0x10) {
|
|
right_volume += (((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1) * CH_STEP * 0xF;
|
|
}
|
|
}
|
|
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);
|
|
if (unlikely(gb->apu_output.output_file && gb->apu_output.output_format != GB_AUDIO_FORMAT_VGM)) {
|
|
#ifdef GB_BIG_ENDIAN
|
|
if (gb->apu_output.output_format == GB_AUDIO_FORMAT_WAV) {
|
|
filtered_output.left = LE16(filtered_output.left);
|
|
filtered_output.right = LE16(filtered_output.right);
|
|
}
|
|
#endif
|
|
if (fwrite(&filtered_output, sizeof(filtered_output), 1, gb->apu_output.output_file) != 1) {
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
gb->apu_output.output_error = errno;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void update_square_sample(GB_gameboy_t *gb, GB_channel_t index, unsigned cycles)
|
|
{
|
|
if (gb->apu.square_channels[index].sample_surpressed) {
|
|
if (gb->model > GB_MODEL_CGB_E) {
|
|
update_sample(gb, index, gb->apu.samples[index], 0);
|
|
}
|
|
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,
|
|
cycles);
|
|
}
|
|
|
|
static inline void update_wave_sample(GB_gameboy_t *gb, unsigned cycles)
|
|
{
|
|
if (gb->apu.wave_channel.current_sample_index & 1) {
|
|
update_sample(gb, GB_WAVE,
|
|
(gb->apu.wave_channel.current_sample_byte & 0xF) >> gb->apu.wave_channel.shift,
|
|
cycles);
|
|
}
|
|
else {
|
|
update_sample(gb, GB_WAVE,
|
|
(gb->apu.wave_channel.current_sample_byte >> 4) >> gb->apu.wave_channel.shift,
|
|
cycles);
|
|
}
|
|
}
|
|
|
|
static inline void set_envelope_clock(GB_envelope_clock_t *clock, bool value, bool direction, uint8_t volume)
|
|
{
|
|
if (clock->clock == value) return;
|
|
if (value) {
|
|
clock->clock = true;
|
|
clock->should_lock = (volume == 0xF && direction) || (volume == 0x0 && !direction);
|
|
}
|
|
else {
|
|
clock->clock = false;
|
|
clock->locked |= clock->should_lock;
|
|
}
|
|
}
|
|
|
|
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 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) {
|
|
set_envelope_clock(lock, false, 0, 0);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* Note: on pre-CGB models *some* of these are non-deterministic. Specifically,
|
|
$x0 writes seem to be non-deterministic while $x8 always work as expected.
|
|
TODO: Might be useful to find which cases are non-deterministic, and allow
|
|
the debugger to issue warnings when they're used. I suspect writes to/from
|
|
$xF are guaranteed to be deterministic. */
|
|
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, GB_channel_t index)
|
|
{
|
|
set_envelope_clock(&gb->apu.square_channels[index].envelope_clock, false, 0, 0);
|
|
if (gb->apu.square_channels[index].envelope_clock.locked) return;
|
|
uint8_t nrx2 = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
|
|
|
|
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 {
|
|
/* Note: CGB-0 behavior is instance specific and non-deterministic. Emulated behavior follows my CGB-0,
|
|
except that my CGB-0 sometimes yields "1" for 8->7 and 4->3 transitions. */
|
|
uint8_t mask;
|
|
if (unlikely(gb->model == GB_MODEL_CGB_0)) {
|
|
if (gb->apu.square_channels[GB_SQUARE_2].current_volume == 1 && (gb->io_registers[GB_IO_NR22] & 8)) {
|
|
mask = 0x1F;
|
|
}
|
|
else {
|
|
mask = 0x3F;
|
|
}
|
|
}
|
|
else {
|
|
mask = 0x3F;
|
|
}
|
|
gb->apu.pcm_mask[0] &= (gb->apu.square_channels[GB_SQUARE_2].current_volume << 4) | mask;
|
|
}
|
|
}
|
|
|
|
set_envelope_clock(&gb->apu.square_channels[index].envelope_clock, false, 0, 0);
|
|
|
|
if (nrx2 & 8) {
|
|
gb->apu.square_channels[index].current_volume++;
|
|
}
|
|
else {
|
|
gb->apu.square_channels[index].current_volume--;
|
|
}
|
|
|
|
if (gb->apu.is_active[index]) {
|
|
update_square_sample(gb, index, 0);
|
|
}
|
|
}
|
|
|
|
static void tick_noise_envelope(GB_gameboy_t *gb)
|
|
{
|
|
set_envelope_clock(&gb->apu.noise_channel.envelope_clock, false, 0, 0);
|
|
if (gb->apu.noise_channel.envelope_clock.locked) return;
|
|
|
|
uint8_t nr42 = gb->io_registers[GB_IO_NR42];
|
|
if (!(nr42 & 7)) return;
|
|
|
|
if (gb->cgb_double_speed) {
|
|
gb->apu.pcm_mask[1] &= (gb->apu.noise_channel.current_volume << 4) | 0x1F;
|
|
}
|
|
|
|
if (nr42 & 8) {
|
|
gb->apu.noise_channel.current_volume++;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.current_volume--;
|
|
}
|
|
|
|
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 sweep_calculation_done(GB_gameboy_t *gb, unsigned cycles)
|
|
{
|
|
/* 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 * 2 - cycles);
|
|
}
|
|
gb->apu.channel1_completed_addend = gb->apu.sweep_length_addend;
|
|
}
|
|
|
|
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;
|
|
// TODO: this is a hack because DIV write timing is inaccurate. Will probably break on odd mode.
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer = 1 + gb->apu.lf_div;
|
|
if (!gb->cgb_double_speed && gb->during_div_write) {
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer = 1;
|
|
}
|
|
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;
|
|
if (gb->apu.square_sweep_calculate_countdown == 0) {
|
|
gb->apu.square_sweep_instant_calculation_done = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
noinline void GB_apu_delayed_envelope_tick(GB_gameboy_t *gb)
|
|
{
|
|
gb->apu.pending_envelope_tick = false;
|
|
if (!gb->apu.global_enable) return;
|
|
|
|
GB_apu_run(gb, true);
|
|
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF;
|
|
|
|
|
|
unrolled for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; i++) {
|
|
if (gb->apu.square_channels[i].envelope_clock.clock) {
|
|
tick_square_envelope(gb, i);
|
|
}
|
|
}
|
|
|
|
if (gb->apu.noise_channel.envelope_clock.clock) {
|
|
tick_noise_envelope(gb);
|
|
}
|
|
}
|
|
|
|
noinline void GB_apu_div_event(GB_gameboy_t *gb)
|
|
{
|
|
GB_apu_run(gb, true);
|
|
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF;
|
|
|
|
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_1; i <= GB_SQUARE_2; i++) {
|
|
if (!gb->apu.square_channels[i].envelope_clock.clock) {
|
|
gb->apu.square_channels[i].volume_countdown--;
|
|
gb->apu.square_channels[i].volume_countdown &= 7;
|
|
}
|
|
}
|
|
if (!gb->apu.noise_channel.envelope_clock.clock) {
|
|
gb->apu.noise_channel.volume_countdown--;
|
|
gb->apu.noise_channel.volume_countdown &= 7;
|
|
}
|
|
}
|
|
|
|
if (gb->cgb_double_speed && (gb->model == GB_MODEL_CGB_D || gb->model == GB_MODEL_CGB_E)) {
|
|
gb->apu.pending_envelope_tick = true;
|
|
}
|
|
else {
|
|
unrolled for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; i++) {
|
|
if (gb->apu.square_channels[i].envelope_clock.clock) {
|
|
tick_square_envelope(gb, i);
|
|
}
|
|
}
|
|
|
|
if (gb->apu.noise_channel.envelope_clock.clock) {
|
|
tick_noise_envelope(gb);
|
|
}
|
|
}
|
|
|
|
if ((gb->apu.div_divider & 1) == 1) {
|
|
unrolled for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; 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) {
|
|
if (gb->apu.is_active[GB_WAVE] && gb->model > GB_MODEL_CGB_E) {
|
|
if (gb->apu.wave_channel.sample_countdown == 0) {
|
|
gb->apu.wave_channel.current_sample_byte =
|
|
gb->io_registers[GB_IO_WAV_START + (((gb->apu.wave_channel.current_sample_index + 1) & 0xF) >> 1)];
|
|
}
|
|
else if (gb->apu.wave_channel.sample_countdown == 9) {
|
|
// TODO: wtf?
|
|
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START];
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
noinline void GB_apu_div_secondary_event(GB_gameboy_t *gb)
|
|
{
|
|
GB_apu_run(gb, true);
|
|
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF;
|
|
|
|
if (!gb->apu.global_enable) return;
|
|
unrolled for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; 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) {
|
|
set_envelope_clock(&gb->apu.square_channels[i].envelope_clock,
|
|
(gb->apu.square_channels[i].volume_countdown = nrx2 & 7),
|
|
nrx2 & 8,
|
|
gb->apu.square_channels[i].current_volume);
|
|
|
|
}
|
|
}
|
|
|
|
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0) {
|
|
set_envelope_clock(&gb->apu.noise_channel.envelope_clock,
|
|
(gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7),
|
|
gb->io_registers[GB_IO_NR42] & 8,
|
|
gb->apu.noise_channel.current_volume);
|
|
}
|
|
}
|
|
|
|
static void update_lfsr(GB_gameboy_t *gb, unsigned cycles_offset)
|
|
{
|
|
gb->apu.noise_channel.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
|
|
if (gb->apu.is_active[GB_NOISE]) {
|
|
update_sample(gb, GB_NOISE,
|
|
gb->apu.noise_channel.current_lfsr_sample ?
|
|
gb->apu.noise_channel.current_volume : 0,
|
|
cycles_offset);
|
|
}
|
|
}
|
|
|
|
static void step_lfsr(GB_gameboy_t *gb, unsigned cycles_offset)
|
|
{
|
|
gb->apu.lfsr_bit_7_before_step = gb->apu.noise_channel.lfsr & 0x80;
|
|
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;
|
|
}
|
|
|
|
update_lfsr(gb, cycles_offset);
|
|
gb->apu.lfsr_stepped_in_narrow = gb->apu.noise_channel.narrow;
|
|
}
|
|
|
|
void GB_apu_run(GB_gameboy_t *gb, bool force)
|
|
{
|
|
uint32_t clock_rate = GB_get_clock_rate(gb);
|
|
bool orig_force = force;
|
|
|
|
restart:;
|
|
uint16_t cycles = gb->apu.apu_cycles;
|
|
|
|
if (force ||
|
|
(cycles + gb->apu_output.cycles_since_render >= gb->apu_output.max_cycles_per_sample) ||
|
|
(gb->apu_output.sample_cycles >= clock_rate) ||
|
|
(gb->apu.square_sweep_calculate_countdown || gb->apu.channel_1_restart_hold || gb->apu.square_sweep_calculate_countdown_reload_timer) ||
|
|
(gb->model <= GB_MODEL_CGB_E && (gb->apu.wave_channel.bugged_read_countdown || (gb->apu.wave_channel.enable && gb->apu.wave_channel.pulsed)))) {
|
|
force = true;
|
|
}
|
|
if (!force) {
|
|
return;
|
|
}
|
|
|
|
/* Force renders to never be more than max_cycles_per_sample apart by spliting runs. */
|
|
while (cycles + gb->apu_output.cycles_since_render > gb->apu_output.max_cycles_per_sample) {
|
|
/* We're already past max_cycles_per_sample. This can happen when changing clock rates, etc.
|
|
Let this sample render normally. */
|
|
if (unlikely(gb->apu_output.cycles_since_render > gb->apu_output.max_cycles_per_sample)) break;
|
|
|
|
gb->apu.apu_cycles = gb->apu_output.max_cycles_per_sample - gb->apu_output.cycles_since_render;
|
|
|
|
if (gb->apu.apu_cycles) {
|
|
// Run for just enough cycles to reach max_cycles_per_sample
|
|
cycles -= gb->apu.apu_cycles;
|
|
GB_apu_run(gb, true);
|
|
// Re-evaluate force if needed
|
|
if (!orig_force) {
|
|
force = false;
|
|
gb->apu.apu_cycles = cycles;
|
|
goto restart;
|
|
}
|
|
// Check if we need another batch
|
|
continue;
|
|
}
|
|
|
|
// Render if needed
|
|
if (gb->apu_output.sample_cycles >= clock_rate) {
|
|
gb->apu_output.sample_cycles -= clock_rate;
|
|
render(gb);
|
|
}
|
|
break;
|
|
}
|
|
|
|
gb->apu.apu_cycles = 0;
|
|
if (!cycles) {
|
|
/* This can happen in pre-CGB stop mode */
|
|
while (unlikely(gb->apu_output.sample_cycles >= clock_rate)) {
|
|
gb->apu_output.sample_cycles -= clock_rate;
|
|
render(gb);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (unlikely(gb->apu.wave_channel.bugged_read_countdown)) {
|
|
uint16_t cycles_left = cycles;
|
|
while (cycles_left) {
|
|
cycles_left--;
|
|
if (--gb->apu.wave_channel.bugged_read_countdown == 0) {
|
|
gb->apu.wave_channel.current_sample_byte =
|
|
gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)];
|
|
if (gb->apu.is_active[GB_WAVE]) {
|
|
update_wave_sample(gb, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool start_ch4 = false;
|
|
if (likely(!gb->stopped || GB_is_cgb(gb))) {
|
|
if (gb->apu.noise_channel.dmg_delayed_start) {
|
|
if (gb->apu.noise_channel.dmg_delayed_start == cycles) {
|
|
gb->apu.noise_channel.dmg_delayed_start = 0;
|
|
start_ch4 = true;
|
|
}
|
|
else if (gb->apu.noise_channel.dmg_delayed_start > cycles) {
|
|
gb->apu.noise_channel.dmg_delayed_start -= cycles;
|
|
}
|
|
else {
|
|
/* Split it into two */
|
|
cycles -= gb->apu.noise_channel.dmg_delayed_start;
|
|
gb->apu.apu_cycles = gb->apu.noise_channel.dmg_delayed_start;
|
|
GB_apu_run(gb, true);
|
|
}
|
|
}
|
|
/* To align the square signal to 1MHz */
|
|
gb->apu.lf_div ^= cycles & 1;
|
|
gb->apu.noise_channel.alignment += cycles;
|
|
|
|
unsigned sweep_cycles = cycles / 2;
|
|
if ((cycles & 1) && !gb->apu.lf_div) {
|
|
sweep_cycles++;
|
|
}
|
|
|
|
if (gb->apu.square_sweep_calculate_countdown_reload_timer > sweep_cycles) {
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer -= sweep_cycles;
|
|
sweep_cycles = 0;
|
|
}
|
|
else {
|
|
if (gb->apu.square_sweep_calculate_countdown_reload_timer && !gb->apu.square_sweep_calculate_countdown && gb->apu.square_sweep_instant_calculation_done) {
|
|
sweep_calculation_done(gb, cycles);
|
|
}
|
|
gb->apu.square_sweep_instant_calculation_done = false;
|
|
sweep_cycles -= gb->apu.square_sweep_calculate_countdown_reload_timer;
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer = 0;
|
|
}
|
|
|
|
if (gb->apu.square_sweep_calculate_countdown &&
|
|
(((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep))) { // Calculation is paused if the lower bits are 0
|
|
if (gb->apu.square_sweep_calculate_countdown > sweep_cycles) {
|
|
gb->apu.square_sweep_calculate_countdown -= sweep_cycles;
|
|
}
|
|
else {
|
|
gb->apu.square_sweep_calculate_countdown = 0;
|
|
sweep_calculation_done(gb, cycles);
|
|
}
|
|
}
|
|
|
|
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]) {
|
|
uint16_t cycles_left = cycles;
|
|
if (unlikely(gb->apu.square_channels[i].delay)) {
|
|
if (gb->apu.square_channels[i].delay < cycles_left) {
|
|
gb->apu.square_channels[i].delay = 0;
|
|
}
|
|
else {
|
|
gb->apu.square_channels[i].delay -= cycles_left;
|
|
}
|
|
}
|
|
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;
|
|
gb->apu.square_channels[i].sample_surpressed = false;
|
|
if (cycles_left == 0 && gb->apu.samples[i] == 0) {
|
|
gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F;
|
|
}
|
|
gb->apu.square_channels[i].did_tick = true;
|
|
update_square_sample(gb, i, cycles - cycles_left);
|
|
|
|
uint8_t duty = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6;
|
|
uint8_t edge_sample_index = inline_const(uint8_t[], {7, 7, 5, 1})[duty];
|
|
if (gb->apu.square_channels[i].current_sample_index == edge_sample_index) {
|
|
gb->apu_output.edge_triggered[i] = true;
|
|
}
|
|
}
|
|
gb->apu.square_channels[i].just_reloaded = cycles_left == 0;
|
|
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]) {
|
|
uint16_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_byte =
|
|
gb->io_registers[GB_IO_WAV_START + (gb->apu.wave_channel.current_sample_index >> 1)];
|
|
update_wave_sample(gb, cycles - cycles_left);
|
|
gb->apu.wave_channel.wave_form_just_read = true;
|
|
if (gb->apu.wave_channel.current_sample_index == 0) {
|
|
gb->apu_output.edge_triggered[GB_WAVE] = true;
|
|
}
|
|
}
|
|
if (cycles_left) {
|
|
gb->apu.wave_channel.sample_countdown -= cycles_left;
|
|
gb->apu.wave_channel.wave_form_just_read = false;
|
|
}
|
|
}
|
|
else if (gb->apu.wave_channel.enable && gb->apu.wave_channel.pulsed && gb->model <= GB_MODEL_CGB_E) {
|
|
uint16_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;
|
|
if (cycles_left) {
|
|
gb->apu.wave_channel.current_sample_byte =
|
|
gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)];
|
|
}
|
|
else {
|
|
gb->apu.wave_channel.bugged_read_countdown = 1;
|
|
}
|
|
}
|
|
if (cycles_left) {
|
|
gb->apu.wave_channel.sample_countdown -= cycles_left;
|
|
}
|
|
if (gb->apu.wave_channel.sample_countdown == 0) {
|
|
gb->apu.wave_channel.bugged_read_countdown = 2;
|
|
}
|
|
}
|
|
|
|
// TODO: verify these conditions one a DMG somehow
|
|
if (gb->apu.noise_counter_active || gb->apu.noise_background_counter_active) {
|
|
uint16_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;
|
|
}
|
|
// This while doesn't get an unlikely because the noise channel steps frequently enough
|
|
while (cycles_left >= gb->apu.noise_channel.counter_countdown) {
|
|
cycles_left -= gb->apu.noise_channel.counter_countdown;
|
|
gb->apu.noise_channel.counter_countdown = divisor;
|
|
uint16_t mask = 1 << (gb->io_registers[GB_IO_NR43] >> 4);
|
|
bool old_bit = gb->apu.noise_channel.counter & mask;
|
|
gb->apu.noise_channel.counter++;
|
|
gb->apu.noise_channel.counter &= 0x3FFF;
|
|
gb->apu.noise_channel.did_step_counter = true;
|
|
bool new_bit = gb->apu.noise_channel.counter & mask;
|
|
|
|
/* Step LFSR */
|
|
if (new_bit && !old_bit && gb->apu.is_active[GB_NOISE]) {
|
|
if (cycles_left == 0 && gb->apu.samples[GB_NOISE] == 0 && !gb->cgb_double_speed) {
|
|
gb->apu.pcm_mask[1] &= 0x0F;
|
|
}
|
|
step_lfsr(gb, cycles - cycles_left);
|
|
}
|
|
}
|
|
if (cycles_left) {
|
|
if (likely(gb->apu.noise_counter_active || gb->apu.noise_background_counter_active)) {
|
|
gb->apu.noise_channel.counter_countdown -= cycles_left;
|
|
gb->apu.noise_channel.countdown_reloaded = false;
|
|
}
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.countdown_reloaded = true;
|
|
gb->apu_output.edge_triggered[GB_NOISE] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gb->apu_output.sample_rate) {
|
|
gb->apu_output.cycles_since_render += cycles;
|
|
gb->apu_output.sample_fraction += sample_fraction_multiply(gb, cycles);
|
|
assert(gb->apu_output.sample_fraction < (4 << 28));
|
|
|
|
if (gb->apu_output.sample_cycles >= clock_rate) {
|
|
gb->apu_output.sample_cycles -= clock_rate;
|
|
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));
|
|
gb->apu.apu_cycles_in_2mhz = true;
|
|
gb->apu.lf_div = 1;
|
|
gb->apu.wave_channel.shift = 4;
|
|
/* 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;
|
|
}
|
|
gb->apu.square_channels[GB_SQUARE_1].sample_countdown = -1;
|
|
gb->apu.square_channels[GB_SQUARE_2].sample_countdown = -1;
|
|
}
|
|
|
|
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
|
|
{
|
|
GB_apu_run(gb, true);
|
|
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 (!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) {
|
|
return 0xFF;
|
|
}
|
|
if (gb->model > GB_MODEL_CGB_E) {
|
|
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 noinline void nr10_write_glitch(GB_gameboy_t *gb, uint8_t value)
|
|
{
|
|
// TODO: Check all of these in APU odd mode
|
|
if (gb->model <= GB_MODEL_CGB_C) {
|
|
if (gb->apu.square_sweep_calculate_countdown_reload_timer == 1 && !gb->apu.lf_div) {
|
|
if (gb->cgb_double_speed) {
|
|
/* This is some instance-specific data corruption. It might also be affect by revision.
|
|
At least for my CGB-0 (haven't tested any other CGB-0s), the '3' case is non-deterministic. */
|
|
static const uint8_t corruption[8] = {7, 7, 5, 7, 3, 3, 5, 7}; // Two of my CGB-Cs, CGB-A
|
|
// static const uint8_t corruption[8] = {7, 7, 1, 3, 3, 3, 5, 7}; // My other CGB-C, Coffee Bat's CGB-C
|
|
// static const uint8_t corruption[8] = {7, 1, 1, 3, 3, 5, 5, 7}; // My CGB-B
|
|
// static const uint8_t corruption[8] = {7, 7, 1, *, 3, 3, 5, 7}; // My CGB-0
|
|
|
|
// static const uint8_t corruption[8] = {7, 5, 1, 3, 3, 1, 5, 7}; // PinoBatch's CGB-B
|
|
// static const uint8_t corruption[8] = {7, 5, 1, 3, 3, *, 5, 7}; // GenericHeroGuy CGB-C
|
|
|
|
|
|
// TODO: How does this affect actual frequency calculation?
|
|
|
|
gb->apu.square_sweep_calculate_countdown = corruption[gb->apu.square_sweep_calculate_countdown & 7];
|
|
/* TODO: the value of 1 needs special handling, but it doesn't occur with the instance I'm emulating here */
|
|
}
|
|
}
|
|
else if (gb->apu.square_sweep_calculate_countdown_reload_timer > 1) {
|
|
if (gb->cgb_double_speed) {
|
|
// TODO: How does this affect actual frequency calculation?
|
|
gb->apu.square_sweep_calculate_countdown = value & 7;
|
|
}
|
|
}
|
|
else if (gb->apu.square_sweep_calculate_countdown) {
|
|
// No clue why 1 is a special case here
|
|
bool should_zombie_step = false;
|
|
if (!(gb->io_registers[GB_IO_NR10] & 7)) {
|
|
should_zombie_step = gb->apu.lf_div ^ gb->cgb_double_speed;
|
|
}
|
|
else if (gb->cgb_double_speed && gb->apu.square_sweep_calculate_countdown == 1) {
|
|
should_zombie_step = true;
|
|
}
|
|
|
|
if (should_zombie_step) {
|
|
gb->apu.square_sweep_calculate_countdown--;
|
|
if (gb->apu.square_sweep_calculate_countdown <= 1) {
|
|
gb->apu.square_sweep_calculate_countdown = 0;
|
|
sweep_calculation_done(gb, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (gb->apu.square_sweep_calculate_countdown_reload_timer == 2) {
|
|
// Countdown just reloaded, re-reload it
|
|
gb->apu.square_sweep_calculate_countdown = value & 0x7;
|
|
if (!gb->apu.square_sweep_calculate_countdown) {
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer = 0;
|
|
}
|
|
else {
|
|
// TODO: How does this affect actual frequency calculation?
|
|
}
|
|
}
|
|
if ((value & 7) && !(gb->io_registers[GB_IO_NR10] & 7) && !gb->apu.lf_div && gb->apu.square_sweep_calculate_countdown > 1) {
|
|
// TODO: Another odd glitch? Ditto
|
|
gb->apu.square_sweep_calculate_countdown--;
|
|
if (!gb->apu.square_sweep_calculate_countdown) {
|
|
sweep_calculation_done(gb, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void prepare_noise_start(GB_gameboy_t *gb)
|
|
{
|
|
/*
|
|
TODO: When restarting a channel right after starting it, before it has the chance to tick the counter, things
|
|
behave differently. Only certain behaviors of this edge case are emulated.
|
|
*/
|
|
|
|
/*
|
|
TODO: Restarting a channel in double speed mode under CGB-C and older is not accurate if the divisor is 0 or 1.
|
|
Specifically in the 0 case, the initial LFSR value seems to be deterministic, but dependant on various
|
|
parameters. It is neither 0 or the equaly unexplained 0x0055.
|
|
*/
|
|
gb->apu.noise_counter_active = gb->io_registers[GB_IO_NR42] & 0xF8; // Resets on APU off and DAC disable
|
|
bool was_started_with_dac_disabled = gb->apu.noise_started_with_dac_disabled;
|
|
gb->apu.noise_started_with_dac_disabled = !gb->apu.noise_counter_active;
|
|
unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07);
|
|
bool was_background_counting = gb->apu.noise_background_counter_active;
|
|
gb->apu.noise_background_counter_active = true;
|
|
bool instant_step = false;
|
|
bool div_1_glitch = false;
|
|
|
|
if (divisor > 1 && gb->apu.noise_channel.counter_countdown == 1) {
|
|
gb->apu.noise_channel.counter++;
|
|
gb->apu.noise_channel.counter &= 0x3FFF;
|
|
}
|
|
else if (divisor > 1 && gb->apu.noise_channel.counter_countdown == 2 && gb->apu.is_active[GB_NOISE] && gb->model <= GB_MODEL_CGB_C && gb->cgb_double_speed) {
|
|
gb->apu.noise_channel.counter++;
|
|
gb->apu.noise_channel.counter &= 0x3FFF;
|
|
}
|
|
else if (gb->apu.noise_channel.counter_countdown == 2 &&
|
|
(gb->apu.noise_channel.alignment & 3) == 0 &&
|
|
gb->apu.is_active[GB_NOISE]) {
|
|
if (divisor == 0) {
|
|
divisor = 8;
|
|
}
|
|
else if (divisor == 1) {
|
|
if (!gb->apu.noise_channel.did_step_counter) {
|
|
div_1_glitch = true;
|
|
}
|
|
|
|
uint16_t mask = 1 << (gb->io_registers[GB_IO_NR43] >> 4);
|
|
bool old_bit = gb->apu.noise_channel.counter & mask;
|
|
gb->apu.noise_channel.counter++;
|
|
gb->apu.noise_channel.counter &= 0x3FFF;
|
|
bool new_bit = gb->apu.noise_channel.counter & mask;
|
|
|
|
if ((new_bit && !old_bit)) {
|
|
instant_step = true;
|
|
}
|
|
}
|
|
}
|
|
gb->apu.noise_channel.counter_countdown = divisor == 0? 6 : divisor * 4 + 6;
|
|
if (gb->apu.noise_channel.alignment & 1) {
|
|
if (!divisor) {
|
|
if (gb->model <= GB_MODEL_CGB_C) {
|
|
gb->apu.noise_channel.counter_countdown++;
|
|
}
|
|
else if (was_background_counting) {
|
|
gb->apu.noise_channel.counter_countdown--;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.counter_countdown++;
|
|
}
|
|
}
|
|
else {
|
|
if (gb->apu.noise_channel.alignment & 2) {
|
|
if (divisor == 1 && !gb->apu.is_active[GB_NOISE]) {
|
|
gb->apu.noise_channel.counter_countdown++;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.counter_countdown -= 3;
|
|
}
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.counter_countdown--;
|
|
if (divisor == 1 && gb->apu.is_active[GB_NOISE]) {
|
|
gb->apu.noise_channel.counter_countdown -= 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (divisor) {
|
|
if (gb->apu.noise_channel.alignment & 2) {
|
|
if (gb->cgb_double_speed && gb->model <= GB_MODEL_CGB_C && divisor == 1) {
|
|
gb->apu.noise_channel.counter_countdown += 2;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.counter_countdown -= 2;
|
|
}
|
|
}
|
|
else if (divisor > 1 && (!gb->cgb_double_speed || gb->model > GB_MODEL_CGB_C)) {
|
|
gb->apu.noise_channel.counter_countdown -= 4;
|
|
}
|
|
/* TODO: This quirk seems way too specific */
|
|
else if (divisor == 1 && gb->apu.is_active[GB_NOISE] && !(gb->io_registers[GB_IO_NR43] & 0xf0)) {
|
|
gb->apu.noise_channel.counter_countdown -= 4;
|
|
}
|
|
}
|
|
else if (gb->cgb_double_speed && gb->model <= GB_MODEL_CGB_C) {
|
|
gb->apu.noise_channel.counter_countdown += 2;
|
|
}
|
|
}
|
|
|
|
/* Background counting glitches */
|
|
/* TODO: Double speed mode not tested */
|
|
if (divisor > 1) {
|
|
if (!gb->apu.noise_counter_active && !(gb->apu.noise_channel.alignment & 3)) {
|
|
gb->apu.noise_channel.counter_countdown += 4;
|
|
}
|
|
}
|
|
else {
|
|
if (was_background_counting && !gb->apu.is_active[GB_NOISE] && !(gb->apu.noise_channel.alignment & 3)) {
|
|
if (divisor == 0) {
|
|
if (was_started_with_dac_disabled) { // TODO: Why is it different?
|
|
gb->apu.noise_channel.counter_countdown += 28;
|
|
}
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.counter_countdown -= 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO: This is weird, is the clock going out of sync? */
|
|
if (!divisor && gb->model <= GB_MODEL_CGB_C && was_background_counting && !gb->apu.is_active[GB_NOISE] && gb->cgb_double_speed) {
|
|
gb->apu.noise_channel.counter_countdown--;
|
|
}
|
|
if (div_1_glitch) {
|
|
gb->apu.noise_channel.counter_countdown -= 4;
|
|
}
|
|
|
|
if (!divisor && gb->apu.is_active[GB_NOISE] && (gb->apu.noise_channel.alignment & 3) == 3) {
|
|
/* TODO: I have no clue where this number comes from, but this number is confirmed for this edge case even for
|
|
side LFSR, despite being seemingly arbitrary. */
|
|
gb->apu.noise_channel.lfsr = 0x0055;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.lfsr = 0;
|
|
}
|
|
if (instant_step) {
|
|
step_lfsr(gb, 0);
|
|
}
|
|
}
|
|
|
|
static void nr43_write(GB_gameboy_t *gb, uint8_t new)
|
|
{
|
|
/*
|
|
NR43 writes cause glitch signals to the LFSR. They are often non-deterministic, and
|
|
they're revision and instance specific. This implementation is trying to emulate a
|
|
simplified and deterministic "variant" of specific instances of revisions I own.
|
|
|
|
For more details:
|
|
https://github.com/LIJI32/SameBoy/issues/397#issuecomment-3733625631
|
|
*/
|
|
|
|
/*
|
|
TODO: Non-determinism aside, this is currently only 100% accurate in CGB-E mode, where
|
|
my specific CGB-E is currently emulated. My CGB-D, under rare cases, samples a second
|
|
intermediate value, and this is not currently emulated. AGB revisions are extremely
|
|
glitchy, and are hard to research.
|
|
|
|
Due to FF-write glitches in pre-CGB-D revisions, all writes (even no-change writes) go
|
|
through 3 intermediate values by definition. Also, the effective counter value used is
|
|
ORed with the next (or previous, timing needs to be verified) value.
|
|
*/
|
|
bool old_narrow = gb->apu.noise_channel.narrow;
|
|
gb->apu.noise_channel.narrow = new & 8;
|
|
uint8_t old = gb->io_registers[GB_IO_NR43];
|
|
gb->io_registers[GB_IO_NR43] = new;
|
|
|
|
if ((old & 0xF0) == (new & 0xF0)) return;
|
|
|
|
uint16_t effective_counter = gb->apu.noise_channel.counter;
|
|
if (gb->model <= GB_MODEL_CGB_C && gb->apu.noise_channel.countdown_reloaded) {
|
|
effective_counter |= (effective_counter - 1) & 0x3FFF;
|
|
}
|
|
bool old_bit = (effective_counter >> (old >> 4)) & 1;
|
|
|
|
uint8_t glitch_value = (old & 0x7F) | (new & 0x80);
|
|
bool glitch_bit = (effective_counter >> (glitch_value >> 4)) & 1;
|
|
bool new_bit = (effective_counter >> (new >> 4)) & 1;
|
|
bool force_glitch = false;
|
|
|
|
if (gb->model == GB_MODEL_CGB_D) {
|
|
if (new_bit && glitch_bit && old_bit) {
|
|
if ((old ^ new) & 0x70) {
|
|
force_glitch = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gb->model > GB_MODEL_CGB_E) {
|
|
/* AGB behavior is very glitchy and incosistent. It can have 2 intermediate values for
|
|
NR43, and sometimes even 3, and the pattern isn't very consistent. This is a *very*
|
|
rough approximation of the behavior.
|
|
|
|
Due to having so up to 3 intermediate value, glitch behavior is complicated to look
|
|
into, so currently CGB-E behavior is arbitrarily used if a glitch occurs. */
|
|
|
|
uint8_t glitch_value2 = 0;
|
|
if (new >= 0x80 && old >= 0x80) {
|
|
glitch_value = (old & 0xCF) | (new & 0x30);
|
|
glitch_value2 = (old & 0x8F) | (new & 0x70);
|
|
}
|
|
else {
|
|
glitch_value = (old & 0xDF) | (new & 0x20);
|
|
glitch_value2 = (old & 0xCF) | (new & 0x30);
|
|
}
|
|
glitch_bit = (gb->apu.noise_channel.counter >> (glitch_value >> 4)) & 1;
|
|
uint8_t glitch_bit2 = (gb->apu.noise_channel.counter >> (glitch_value2 >> 4)) & 1;
|
|
if (glitch_bit != glitch_bit2) {
|
|
if (new_bit == old_bit) {
|
|
glitch_bit = !new_bit;
|
|
}
|
|
else if (!glitch_bit && old_bit) {
|
|
force_glitch = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Step LFSR */
|
|
|
|
if ((old_bit == new_bit && new_bit != glitch_bit) || force_glitch) {
|
|
/* Glitching write. Has two categories, both have non-deterministic
|
|
variants. These are the most common variants of the two categories,
|
|
which are deterministic. */
|
|
if (new_bit) {
|
|
/* Category 1 */
|
|
if (gb->model >= GB_MODEL_CGB_E) {
|
|
if (!(new & 0x80)) {
|
|
step_lfsr(gb, 0);
|
|
}
|
|
else {
|
|
/* Only happens under this odd condition */
|
|
uint8_t t1 = (old >> 4) & 7;
|
|
uint8_t t2 = (new >> 4) & 7;
|
|
|
|
if ((t1 ^ 7) + t2 > 7 || ((t1 ^ 7) & t2)) {
|
|
/* Copy bit 8 to bit 7 */
|
|
gb->apu.noise_channel.lfsr &= ~0x80;
|
|
gb->apu.noise_channel.lfsr |= (gb->apu.noise_channel.lfsr >> 1) & 0x80;
|
|
|
|
/* All specific cases have non-deterministic behaviors involved */
|
|
if ((t1 == 0 || t1 == 4) && t2 == 3) {
|
|
gb->apu.noise_channel.lfsr &= (gb->apu.noise_channel.lfsr >> 1) | 0x545;
|
|
update_lfsr(gb, 0);
|
|
}
|
|
else if (t1 == 2 && t2 == 3) {
|
|
uint16_t mask = 0x555;
|
|
if ((gb->apu.noise_channel.lfsr & 0xC) == 0xC) {
|
|
mask |= 8;
|
|
}
|
|
if ((gb->apu.noise_channel.lfsr & 0xC00) == 0xC00) {
|
|
mask |= 0x800;
|
|
}
|
|
|
|
gb->apu.noise_channel.lfsr &= (gb->apu.noise_channel.lfsr >> 1) | mask;
|
|
update_lfsr(gb, 0);
|
|
}
|
|
if (!gb->apu.noise_channel.narrow && old_narrow && gb->apu.lfsr_stepped_in_narrow) {
|
|
/* TODO: Behaves weirder in non-deterministic t1 == 0/4 scenarios? */
|
|
if (gb->apu.lfsr_bit_7_before_step) {
|
|
gb->apu.noise_channel.lfsr |= 0x40;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.lfsr &= ~0x40;
|
|
}
|
|
}
|
|
gb->apu.noise_channel.lfsr |= gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
|
|
/* TODO: verify */
|
|
gb->apu.lfsr_stepped_in_narrow = gb->apu.noise_channel.narrow;
|
|
}
|
|
}
|
|
}
|
|
else if (gb->model == GB_MODEL_CGB_D) {
|
|
static const uint8_t glitch_map_l2h[8 * 8] = {
|
|
[000] = 0x00, 0x01, 0x01, 0x21, 0x02, 0x21,
|
|
[010] = 0x03, 0x00, 0x21, 0x01, 0x04, 0x04,
|
|
[020] = 0x05, 0x01, 0x00, 0x01, 0x04, 0x21,
|
|
[030] = 0x03, 0x05, 0x05, 0x00, 0x01, 0x01,
|
|
[040] = 0x05, 0x01, 0x01, 0x21, 0x00, 0x01,
|
|
[050] = 0x05, 0x05, 0x21, 0x01, 0x05, 0x00,
|
|
[060] = 0x05, 0x01, 0x05, 0x01, 0x05, 0x01,
|
|
[070] = 0x03, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
};
|
|
|
|
/* The following transitions are a bit non-deterministic (except under forced glitched):
|
|
1 -> c, 2 -> c, 3 -> c, 3 -> d */
|
|
|
|
static const uint8_t glitch_map_h2l[8 * 8] = {
|
|
[000] = 0x00, 0x27, 0x26, 0x37, 0x21, 0x38, 0x01, 0x01,
|
|
[010] = 0x01, 0x00, 0x38, 0x21, 0x21, 0x21, 0x01, 0x01,
|
|
[020] = 0x01, 0x27, 0x00, 0x28, 0x21, 0x38, 0x01, 0x01,
|
|
[030] = 0x01, 0x02, 0x01, 0x00, 0x31, 0x21, 0x01, 0x01,
|
|
[040] = 0x06, 0x28, 0x28, 0x38, 0x00, 0x27, 0x01, 0x01,
|
|
[050] = 0x01, 0x03, 0x38, 0x21, 0x01, 0x00, 0x01, 0x01,
|
|
};
|
|
/* The following transitions are a bit non-deterministic (except under forced glitched):
|
|
8 -> 5, 2 -> 9, a -> 3, c -> 3 */
|
|
|
|
const uint8_t *glitch_map = old & 0x80? &glitch_map_h2l[0] : &glitch_map_l2h[0];
|
|
|
|
|
|
unsigned glitch = glitch_map[((old & 0x70) >> 1) | ((new & 0x70) >> 4)];
|
|
if (force_glitch) {
|
|
if (!((new ^ old) & 0x80)) {
|
|
glitch = glitch & 0x20? 5 : 0;
|
|
}
|
|
else if (!(new & 0x80)) {
|
|
glitch = glitch & 0x10? 5 : 0;
|
|
}
|
|
else if ((glitch & 0xF) == 1 || (glitch & 0xF) == 4) {
|
|
glitch = 5;
|
|
}
|
|
else {
|
|
glitch = 0;
|
|
}
|
|
}
|
|
else {
|
|
glitch &= 0xF;
|
|
}
|
|
uint16_t old_lfsr = gb->apu.noise_channel.lfsr;
|
|
uint16_t lfsr_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
|
|
switch (glitch) {
|
|
case 6: // Like 2, but conditional
|
|
case 4: // Like 2, but conditional
|
|
if ((gb->apu.noise_channel.lfsr & (glitch == 4? 0x60 : 0x40)) != 0x40) { // Todo check wide mode
|
|
case 2: // And bit 1 with bit 0 before doing glitch 1
|
|
if (!(gb->apu.noise_channel.lfsr & 1)) {
|
|
gb->apu.noise_channel.lfsr &= ~2;
|
|
}
|
|
}
|
|
case 1: // Step and set the LFSR bit
|
|
case 8: // Step and set the LFSR bit conditionally
|
|
step_lfsr(gb, 0);
|
|
case 5: // Just set LFSR
|
|
if ((glitch != 8) || (old_lfsr & 3) != 2) {
|
|
gb->apu.noise_channel.lfsr |= lfsr_mask;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.lfsr |= old_lfsr & lfsr_mask;
|
|
}
|
|
break;
|
|
|
|
case 7: // Step and OR the LFSR bit with its old value
|
|
step_lfsr(gb, 0);
|
|
gb->apu.noise_channel.lfsr |= old_lfsr & lfsr_mask;
|
|
break;
|
|
|
|
case 3: // A bit of a mess
|
|
step_lfsr(gb, 0);
|
|
gb->apu.noise_channel.lfsr &= old_lfsr;
|
|
gb->apu.noise_channel.lfsr |= old_lfsr & 1;
|
|
gb->apu.noise_channel.lfsr |= lfsr_mask;
|
|
update_lfsr(gb, 0);
|
|
break;
|
|
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Category 2 */
|
|
if (gb->model >= GB_MODEL_CGB_E) {
|
|
static const uint8_t glitch_map[8 * 8] = {
|
|
/* 8 9 A B C D */
|
|
[002] = 4, [003] = 2, [004] = 2, [005] = 2, // 0
|
|
[012] = 2, [013] = 4, [014] = 2, [015] = 2, // 1
|
|
[020] = 1, [021] = 2, [023] = 1, [024] = 5, [025] = 3, // 2
|
|
[034] = 2, [035] = 2, // 3
|
|
[041] = 2, [042] = 2, [043] = 2, // 4
|
|
[050] = 6, [052] = 2, [053] = 2, // 5
|
|
};
|
|
|
|
/* The following transitions are a bit non-deterministic:
|
|
2 -> 8, 0 -> A, 2 -> C */
|
|
|
|
unsigned glitch = new & 0x80? glitch_map[((old & 0x70) >> 1) | ((new & 0x70) >> 4)] : 0;
|
|
switch (glitch) {
|
|
case 1: /* Step, followed by bit 1 &= bit 0 */
|
|
case 6: /* Variant of type 1: LFSR bit - 1 glitched by LFSR bit, LFSR bit - 2, and bit 0 */
|
|
step_lfsr(gb, 0);
|
|
if (glitch == 6) {
|
|
/* TODO: Verify wide mode */
|
|
if ((gb->apu.noise_channel.narrow &&
|
|
((gb->apu.noise_channel.lfsr & 0x71) == 0x20)) ||
|
|
(gb->apu.noise_channel.lfsr & 0x71) == 0x61) {
|
|
gb->apu.noise_channel.lfsr &= ~0x20;
|
|
}
|
|
if ((gb->apu.noise_channel.lfsr & 0x7001) == 0x2000 ||
|
|
(gb->apu.noise_channel.lfsr & 0x7001) == 0x6001) {
|
|
gb->apu.noise_channel.lfsr &= ~0x2000;
|
|
}
|
|
}
|
|
if ((gb->apu.noise_channel.lfsr & 0x3) == 2) {
|
|
gb->apu.noise_channel.lfsr &= ~2;
|
|
}
|
|
break;
|
|
case 2: { /* Step, bitwise AND with previous, except for bit 0 */
|
|
uint16_t prev = gb->apu.noise_channel.lfsr;
|
|
step_lfsr(gb, 0);
|
|
gb->apu.noise_channel.lfsr &= prev | 1;
|
|
break;
|
|
}
|
|
|
|
case 5:; /* Non deterministic variant of type 3:
|
|
The LFSR is unset if bit 0 & 1 are 0b10.
|
|
Bit 3 is complex and non-deterministic (TODO: wide mode) */
|
|
if ((gb->apu.noise_channel.lfsr & 0x3) == 2) {
|
|
gb->apu.noise_channel.lfsr &= gb->apu.noise_channel.narrow? ~0x4040 : ~0x4000;
|
|
}
|
|
|
|
if ((gb->apu.noise_channel.lfsr & 0x19) == 8) {
|
|
gb->apu.noise_channel.lfsr &= ~8;
|
|
}
|
|
|
|
case 3: /* No step, bit 0 = bit 1, some other bits have AND glitches with next*/
|
|
gb->apu.noise_channel.lfsr &= ~1;
|
|
gb->apu.noise_channel.lfsr |= (gb->apu.noise_channel.lfsr >> 1) & 1;
|
|
|
|
update_lfsr(gb, 0);
|
|
/* TODO: verify */
|
|
gb->apu.lfsr_stepped_in_narrow = gb->apu.noise_channel.narrow;
|
|
break;
|
|
|
|
case 4: { /* Step, bit 1 &= bit 0, LFSR bit -1 &= LFSR bit */
|
|
uint16_t prev = gb->apu.noise_channel.lfsr;
|
|
step_lfsr(gb, 0);
|
|
gb->apu.noise_channel.lfsr &= prev | (gb->apu.noise_channel.narrow? ~0x2022 : ~0x2002);
|
|
break;
|
|
}
|
|
|
|
default: /* No glitch, plain step*/
|
|
step_lfsr(gb, 0);
|
|
break;
|
|
|
|
}
|
|
}
|
|
else {
|
|
step_lfsr(gb, 0);
|
|
}
|
|
}
|
|
}
|
|
else if (!old_bit && new_bit) {
|
|
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;
|
|
if ((new & 0xf0) <= 0x20 && glitch_bit && !(effective_counter & 8)) { // No clue why that specific bit is tested
|
|
// Non-deterministic, not fully tested for revision differences and wide mode
|
|
// Step twice?
|
|
step_lfsr(gb, 0);
|
|
gb->apu.noise_channel.lfsr &= ~(gb->apu.noise_channel.narrow? 0x4040 : 0x4000);
|
|
gb->apu.noise_channel.lfsr |= (gb->apu.noise_channel.lfsr & (gb->apu.noise_channel.narrow? 0x2020 : 0x2000)) << 1;
|
|
}
|
|
}
|
|
else {
|
|
step_lfsr(gb, 0);
|
|
}
|
|
}
|
|
else if (gb->model <= GB_MODEL_CGB_C) {
|
|
if ((new & 0xf0) <= 0x20 && !glitch_bit && !new_bit && !old_bit && (effective_counter & 8)) { // No clue why that specific bit is tested
|
|
// Step twice?
|
|
step_lfsr(gb, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|
{
|
|
GB_apu_run(gb, true);
|
|
if (!gb->apu.global_enable && reg != GB_IO_NR52 && reg < GB_IO_WAV_START && (GB_is_cgb(gb) ||
|
|
(
|
|
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 ((!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) || gb->model > GB_MODEL_CGB_E) {
|
|
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--;) {
|
|
int8_t sample = gb->apu.samples[i];
|
|
gb->apu.samples[i] = 0x10; // Invalidate to force update
|
|
update_sample(gb, i, sample, 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;
|
|
gb->apu.apu_cycles_in_2mhz = true;
|
|
}
|
|
|
|
if (!GB_is_cgb(gb) && (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: {
|
|
if (unlikely(gb->apu.square_sweep_calculate_countdown || gb->apu.square_sweep_calculate_countdown_reload_timer)) {
|
|
nr10_write_glitch(gb, value);
|
|
}
|
|
bool old_negate = gb->io_registers[GB_IO_NR10] & 8;
|
|
gb->io_registers[GB_IO_NR10] = value;
|
|
if (gb->model <= GB_MODEL_CGB_C) {
|
|
old_negate = true;
|
|
}
|
|
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: {
|
|
GB_channel_t 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: {
|
|
GB_channel_t 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_channels[index].envelope_clock);
|
|
update_square_sample(gb, index, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case GB_IO_NR13:
|
|
case GB_IO_NR23: {
|
|
GB_channel_t 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;
|
|
if (gb->apu.square_channels[index].just_reloaded) {
|
|
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GB_IO_NR14:
|
|
case GB_IO_NR24: {
|
|
GB_channel_t 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 from ≥$700 to <$700, 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] && (gb->io_registers[reg] & 0x7) == 7 && (value & 7) != 7) {
|
|
/* 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].did_tick &&
|
|
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;
|
|
gb->apu.square_channels[index].sample_surpressed = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (gb->apu.square_channels[index].just_reloaded) {
|
|
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 1;
|
|
}
|
|
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_channels[index].envelope_clock.locked = false;
|
|
gb->apu.square_channels[index].envelope_clock.clock = false;
|
|
gb->apu.square_channels[index].did_tick = false;
|
|
bool force_unsurpressed = false;
|
|
if (!gb->apu.is_active[index]) {
|
|
if (gb->model == GB_MODEL_CGB_E || gb->model == GB_MODEL_CGB_D) {
|
|
if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - gb->apu.square_channels[index].delay) / 2) & 0x400)) {
|
|
gb->apu.square_channels[index].current_sample_index++;
|
|
gb->apu.square_channels[index].current_sample_index &= 0x7;
|
|
force_unsurpressed = true;
|
|
}
|
|
}
|
|
gb->apu.square_channels[index].delay = 6 + gb->apu.lf_div * (gb->model < GB_MODEL_CGB_D && gb->cgb_double_speed? 1 : -1);
|
|
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + gb->apu.square_channels[index].delay;
|
|
}
|
|
else {
|
|
unsigned extra_delay = 0;
|
|
if (gb->model == GB_MODEL_CGB_E || gb->model == GB_MODEL_CGB_D) {
|
|
if (!gb->apu.square_channels[index].just_reloaded && !(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1 - gb->apu.square_channels[index].delay) / 2) & 0x400)) {
|
|
gb->apu.square_channels[index].current_sample_index++;
|
|
gb->apu.square_channels[index].current_sample_index &= 0x7;
|
|
gb->apu.square_channels[index].sample_surpressed = false;
|
|
}
|
|
/* 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].sample_surpressed)) {
|
|
extra_delay += 2;
|
|
}
|
|
}
|
|
/* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/
|
|
gb->apu.square_channels[index].delay = 4 - gb->apu.lf_div + extra_delay;
|
|
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + gb->apu.square_channels[index].delay;
|
|
}
|
|
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 takes 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, 0);
|
|
}
|
|
|
|
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);
|
|
gb->apu.square_channels[index].sample_surpressed = true && !force_unsurpressed;
|
|
}
|
|
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.square_sweep_instant_calculation_done = false;
|
|
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;
|
|
if ((gb->apu.lf_div ^ !gb->cgb_double_speed) && gb->model <= GB_MODEL_CGB_C) {
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer = 3;
|
|
}
|
|
else {
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer = 2;
|
|
}
|
|
gb->apu.unshifted_sweep = false;
|
|
if (!was_active) {
|
|
gb->apu.square_sweep_calculate_countdown_reload_timer++;
|
|
}
|
|
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 + (GB_is_cgb(gb) && gb->model != GB_MODEL_CGB_D) * 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_is_cgb(gb) && gb->model <= GB_MODEL_CGB_B)) && // Current value is irrelevant on CGB-B and older
|
|
!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.wave_channel.pulsed = false;
|
|
if (gb->apu.is_active[GB_WAVE]) {
|
|
// Todo: I assume this happens on pre-CGB models; test this with an audible test
|
|
if (gb->apu.wave_channel.sample_countdown == 0 && gb->model <= GB_MODEL_CGB_E) {
|
|
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START + (gb->pc & 0xF)];
|
|
}
|
|
else if (gb->apu.wave_channel.wave_form_just_read && gb->model <= GB_MODEL_CGB_C) {
|
|
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START + (GB_IO_NR30 & 0xF)];
|
|
}
|
|
}
|
|
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 = inline_const(uint8_t[], {4, 0, 1, 2})[(value >> 5) & 3];
|
|
if (gb->apu.is_active[GB_WAVE]) {
|
|
update_wave_sample(gb, 0);
|
|
}
|
|
break;
|
|
case GB_IO_NR33:
|
|
gb->apu.wave_channel.sample_length &= ~0xFF;
|
|
gb->apu.wave_channel.sample_length |= value & 0xFF;
|
|
if (gb->apu.wave_channel.bugged_read_countdown == 1) { // Just reloaded countdown
|
|
/* TODO: not verified with a test ROM yet */
|
|
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF;
|
|
}
|
|
break;
|
|
case GB_IO_NR34:
|
|
gb->apu.wave_channel.sample_length &= 0xFF;
|
|
gb->apu.wave_channel.sample_length |= (value & 7) << 8;
|
|
if (value & 0x80) {
|
|
gb->apu.wave_channel.pulsed = true;
|
|
/* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU
|
|
reads from it. */
|
|
if (!GB_is_cgb(gb) &&
|
|
gb->apu.is_active[GB_WAVE] &&
|
|
gb->apu.wave_channel.sample_countdown == 0) {
|
|
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.
|
|
|
|
For DMG-B emulation I emulate the most common behavior, which blargg's tests expect (not my own DMG-B, which fails it)
|
|
For MGB emulation, I emulate my Game Boy Light, which happens to be deterministic.
|
|
|
|
Additionally, I believe DMGs, including those we behave differently than emulated,
|
|
are all deterministic. */
|
|
if (offset < 4 && gb->model != GB_MODEL_MGB) {
|
|
gb->io_registers[GB_IO_WAV_START] = gb->io_registers[GB_IO_WAV_START + offset];
|
|
}
|
|
else {
|
|
memcpy(gb->io_registers + GB_IO_WAV_START,
|
|
gb->io_registers + GB_IO_WAV_START + (offset & ~3),
|
|
4);
|
|
}
|
|
}
|
|
gb->apu.wave_channel.current_sample_index = 0;
|
|
if (gb->apu.is_active[GB_WAVE] && gb->apu.wave_channel.sample_countdown == 0) {
|
|
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START];
|
|
}
|
|
if (gb->apu.wave_channel.enable) {
|
|
gb->apu.is_active[GB_WAVE] = true;
|
|
update_sample(gb, GB_WAVE,
|
|
(gb->apu.wave_channel.current_sample_byte >> 4) >> gb->apu.wave_channel.shift,
|
|
0);
|
|
}
|
|
gb->apu.wave_channel.sample_countdown = (gb->apu.wave_channel.sample_length ^ 0x7FF) + 3;
|
|
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_is_cgb(gb) && gb->model <= GB_MODEL_CGB_B)) && // Current value is irrelevant on CGB-B and older
|
|
!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;
|
|
|
|
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 */
|
|
if (gb->apu.is_active[GB_NOISE] && gb->io_registers[GB_IO_NR43] & 7) {
|
|
if (gb->apu.noise_channel.counter_countdown <= 2) {
|
|
gb->apu.noise_channel.counter++;
|
|
}
|
|
gb->apu.noise_background_counter_active = false;
|
|
}
|
|
|
|
gb->io_registers[reg] = value;
|
|
gb->apu.is_active[GB_NOISE] = false;
|
|
update_sample(gb, GB_NOISE, 0, 0);
|
|
gb->apu.noise_counter_active = false;
|
|
}
|
|
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_channel.envelope_clock);
|
|
update_sample(gb, GB_NOISE,
|
|
gb->apu.noise_channel.current_lfsr_sample ?
|
|
gb->apu.noise_channel.current_volume : 0,
|
|
0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GB_IO_NR43: {
|
|
if (gb->apu.noise_channel.countdown_reloaded) {
|
|
unsigned divisor = (value & 0x07) << 2;
|
|
if (!divisor) divisor = 2;
|
|
if (gb->model > GB_MODEL_CGB_C) {
|
|
gb->apu.noise_channel.counter_countdown =
|
|
divisor + (divisor == 2? 0 : inline_const(uint8_t[], {2, 1, 0, 3})[(gb->apu.noise_channel.alignment) & 3]);
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.counter_countdown =
|
|
divisor + (divisor == 2? 0 : inline_const(uint8_t[], {2, 1, 4, 3})[(gb->apu.noise_channel.alignment) & 3]);
|
|
}
|
|
}
|
|
if (gb->model <= GB_MODEL_CGB_C) {
|
|
/* TODO: CGB≤C (and DMG) have various unemulated quirks when you write to NR43 just as the counter reloads */
|
|
if (gb->apu.noise_channel.countdown_reloaded) {
|
|
bool old_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
|
|
bool glitch_bit = (gb->apu.noise_channel.counter >> 7) & 1;
|
|
bool new_bit = (gb->apu.noise_channel.counter >> (value >> 4)) & 1;
|
|
|
|
if (!old_bit && new_bit && glitch_bit) {
|
|
uint16_t previous_counter = (gb->apu.noise_channel.counter - 1) & 0x3FFF;
|
|
bool old_bit = (previous_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
|
|
bool glitch_bit = (previous_counter >> 7) & 1;
|
|
bool new_bit = (previous_counter >> (value >> 4)) & 1;
|
|
if (old_bit && !new_bit && glitch_bit) {
|
|
step_lfsr(gb, 0);
|
|
}
|
|
}
|
|
}
|
|
nr43_write(gb, 0xff);
|
|
}
|
|
nr43_write(gb, value);
|
|
|
|
break;
|
|
}
|
|
|
|
case GB_IO_NR44: {
|
|
if (value & 0x80) {
|
|
gb->apu.noise_channel.envelope_clock.locked = false;
|
|
gb->apu.noise_channel.envelope_clock.clock = false;
|
|
if (!GB_is_cgb(gb) && (gb->apu.noise_channel.alignment & 3) != 0) {
|
|
gb->apu.noise_channel.dmg_delayed_start = 6;
|
|
}
|
|
else {
|
|
gb->apu.noise_channel.lfsr = 0;
|
|
prepare_noise_start(gb);
|
|
|
|
gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4;
|
|
gb->apu.noise_channel.current_lfsr_sample = false;
|
|
gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7;
|
|
gb->apu.noise_channel.did_step_counter = (gb->apu.noise_channel.alignment & 3) == 2;
|
|
|
|
if (gb->io_registers[GB_IO_NR42] & 0xF8) {
|
|
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;
|
|
}
|
|
}
|
|
gb->io_registers[reg] = value;
|
|
}
|
|
|
|
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
|
|
{
|
|
if (gb->apu_output.sample_rate != sample_rate) {
|
|
GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
|
|
}
|
|
gb->apu_output.sample_rate = sample_rate;
|
|
if (sample_rate) {
|
|
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
|
gb->apu_output.max_cycles_per_sample = ceil(GB_get_clock_rate(gb) / 2.0 / sample_rate);
|
|
gb->apu_output.quick_fraction_multiply_cache[0] = round(sample_rate * 2.0 / GB_get_clock_rate(gb) * (1 << 28));
|
|
for (unsigned i = 1; i < GB_QUICK_MULTIPLY_COUNT; i++) {
|
|
gb->apu_output.quick_fraction_multiply_cache[i] = gb->apu_output.quick_fraction_multiply_cache[0] * (i + 1);
|
|
}
|
|
}
|
|
else {
|
|
gb->apu_output.max_cycles_per_sample = 0x400;
|
|
}
|
|
}
|
|
|
|
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample)
|
|
{
|
|
GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
|
|
if (cycles_per_sample == 0) {
|
|
GB_set_sample_rate(gb, 0);
|
|
return;
|
|
}
|
|
gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2;
|
|
gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample);
|
|
gb->apu_output.max_cycles_per_sample = ceil(cycles_per_sample / 4);
|
|
|
|
gb->apu_output.quick_fraction_multiply_cache[0] = round(gb->apu_output.sample_rate * 2.0 / GB_get_clock_rate(gb) * (1 << 28));
|
|
for (unsigned i = 1; i < GB_QUICK_MULTIPLY_COUNT; i++) {
|
|
gb->apu_output.quick_fraction_multiply_cache[i] = gb->apu_output.quick_fraction_multiply_cache[0] * (i + 1);
|
|
}
|
|
}
|
|
|
|
unsigned GB_get_sample_rate(GB_gameboy_t *gb)
|
|
{
|
|
return gb->apu_output.sample_rate;
|
|
}
|
|
|
|
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_set_interference_volume(GB_gameboy_t *gb, double volume)
|
|
{
|
|
gb->apu_output.interference_volume = volume;
|
|
}
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint32_t format_chunk; // = BE32('FORM')
|
|
uint32_t size; // = BE32(file size - 8)
|
|
uint32_t format; // = BE32('AIFC')
|
|
|
|
uint32_t fver_chunk; // = BE32('FVER')
|
|
uint32_t fver_size; // = BE32(4)
|
|
uint32_t fver;
|
|
|
|
uint32_t comm_chunk; // = BE32('COMM')
|
|
uint32_t comm_size; // = BE32(0x18)
|
|
|
|
uint16_t channels; // = BE16(2)
|
|
uint32_t samples_per_channel; // = BE32(total number of samples / 2)
|
|
uint16_t bit_depth; // = BE16(16)
|
|
uint16_t frequency_exponent;
|
|
uint64_t frequency_significand;
|
|
uint32_t compression_type; // = 'NONE' (BE) or 'twos' (LE)
|
|
uint16_t compression_name; // = 0
|
|
|
|
uint32_t ssnd_chunk; // = BE32('SSND')
|
|
uint32_t ssnd_size; // = BE32(length of samples - 8)
|
|
uint32_t ssnd_offset; // = 0
|
|
uint32_t ssnd_block; // = 0
|
|
} aiff_header_t;
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint32_t marker; // = BE32('RIFF')
|
|
uint32_t size; // = LE32(file size - 8)
|
|
uint32_t type; // = BE32('WAVE')
|
|
|
|
uint32_t fmt_chunk; // = BE32('fmt ')
|
|
uint32_t fmt_size; // = LE16(16)
|
|
uint16_t format; // = LE16(1)
|
|
uint16_t channels; // = LE16(2)
|
|
uint32_t sample_rate; // = LE32(sample_rate)
|
|
uint32_t byte_rate; // = LE32(sample_rate * 4)
|
|
uint16_t frame_size; // = LE32(4)
|
|
uint16_t bit_depth; // = LE16(16)
|
|
|
|
uint32_t data_chunk; // = BE32('data')
|
|
uint32_t data_size; // = LE32(length of samples)
|
|
} wav_header_t;
|
|
|
|
#define VGM_HEADER_SIZE 0xC0
|
|
#define VGM_DATA_OFFSET (VGM_HEADER_SIZE - 0x34)
|
|
#define VGM_SAMPLE_RATE 44100
|
|
|
|
static void vgm_set_le32(uint8_t *header, size_t offset, uint32_t value)
|
|
{
|
|
value = LE32(value);
|
|
memcpy(header + offset, &value, sizeof(value));
|
|
}
|
|
|
|
static void vgm_get_header(GB_gameboy_t *gb, uint8_t header[VGM_HEADER_SIZE], uint32_t eof_offset)
|
|
{
|
|
memset(header, 0, VGM_HEADER_SIZE);
|
|
header[0] = 'V';
|
|
header[1] = 'g';
|
|
header[2] = 'm';
|
|
header[3] = ' ';
|
|
vgm_set_le32(header, 0x04, eof_offset);
|
|
vgm_set_le32(header, 0x08, 0x00000161);
|
|
vgm_set_le32(header, 0x18, gb->apu_output.vgm_total_samples);
|
|
vgm_set_le32(header, 0x34, VGM_DATA_OFFSET);
|
|
vgm_set_le32(header, 0x80, gb->apu_output.vgm_clock_rate | (GB_is_cgb(gb) ? 0x80000000u : 0));
|
|
}
|
|
|
|
static bool vgm_write_data(GB_gameboy_t *gb, const void *data, size_t size)
|
|
{
|
|
if (!gb->apu_output.output_file) return false;
|
|
if (fwrite(data, size, 1, gb->apu_output.output_file) != 1) {
|
|
gb->apu_output.output_error = errno ?: EIO;
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool vgm_flush_wait(GB_gameboy_t *gb)
|
|
{
|
|
while (gb->apu_output.vgm_pending_samples) {
|
|
if (gb->apu_output.vgm_pending_samples <= 16) {
|
|
uint8_t command = 0x70 + gb->apu_output.vgm_pending_samples - 1;
|
|
gb->apu_output.vgm_pending_samples = 0;
|
|
if (!vgm_write_data(gb, &command, sizeof(command))) return false;
|
|
}
|
|
else {
|
|
uint16_t samples = MIN(gb->apu_output.vgm_pending_samples, 0xFFFF);
|
|
uint8_t command[] = {0x61, samples & 0xFF, samples >> 8};
|
|
gb->apu_output.vgm_pending_samples -= samples;
|
|
if (!vgm_write_data(gb, command, sizeof(command))) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool vgm_write_register(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|
{
|
|
if (!vgm_flush_wait(gb)) return false;
|
|
uint8_t command[] = {0xB3, reg - GB_IO_NR10, value};
|
|
return vgm_write_data(gb, command, sizeof(command));
|
|
}
|
|
|
|
static uint8_t vgm_get_trigger_register(GB_gameboy_t *gb, uint8_t reg, GB_channel_t channel)
|
|
{
|
|
uint8_t value = gb->io_registers[reg] & 0x7F;
|
|
if (gb->apu.is_active[channel]) {
|
|
value |= 0x80;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static bool vgm_write_apu_snapshot(GB_gameboy_t *gb)
|
|
{
|
|
if (!vgm_write_register(gb, GB_IO_NR52, gb->apu.global_enable ? 0x80 : 0)) return false;
|
|
|
|
/* Wave RAM is preserved even when the APU is disabled. */
|
|
for (unsigned i = GB_IO_WAV_START; i <= GB_IO_WAV_END; i++) {
|
|
if (!vgm_write_register(gb, i, gb->io_registers[i])) return false;
|
|
}
|
|
|
|
if (!gb->apu.global_enable) return true;
|
|
|
|
static const uint8_t registers[] = {
|
|
GB_IO_NR50, GB_IO_NR51,
|
|
GB_IO_NR10, GB_IO_NR11, GB_IO_NR12, GB_IO_NR13,
|
|
GB_IO_NR21, GB_IO_NR22, GB_IO_NR23,
|
|
GB_IO_NR30, GB_IO_NR31, GB_IO_NR32, GB_IO_NR33,
|
|
GB_IO_NR41, GB_IO_NR42, GB_IO_NR43,
|
|
};
|
|
|
|
for (unsigned i = 0; i < sizeof(registers) / sizeof(registers[0]); i++) {
|
|
if (!vgm_write_register(gb, registers[i], gb->io_registers[registers[i]])) return false;
|
|
}
|
|
|
|
if (!vgm_write_register(gb, GB_IO_NR14,
|
|
vgm_get_trigger_register(gb, GB_IO_NR14, GB_SQUARE_1))) return false;
|
|
if (!vgm_write_register(gb, GB_IO_NR24,
|
|
vgm_get_trigger_register(gb, GB_IO_NR24, GB_SQUARE_2))) return false;
|
|
if (!vgm_write_register(gb, GB_IO_NR34,
|
|
vgm_get_trigger_register(gb, GB_IO_NR34, GB_WAVE))) return false;
|
|
if (!vgm_write_register(gb, GB_IO_NR44,
|
|
vgm_get_trigger_register(gb, GB_IO_NR44, GB_NOISE))) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void noinline vgm_advance(GB_gameboy_t *gb, uint8_t cycles)
|
|
{
|
|
uint64_t clock_rate = (uint64_t)gb->apu_output.vgm_clock_rate * 2;
|
|
if (!clock_rate) return;
|
|
uint64_t sample_fraction = gb->apu_output.vgm_sample_fraction + (uint64_t)cycles * VGM_SAMPLE_RATE;
|
|
uint32_t samples = sample_fraction / clock_rate;
|
|
gb->apu_output.vgm_sample_fraction = sample_fraction % clock_rate;
|
|
gb->apu_output.vgm_pending_samples += samples;
|
|
gb->apu_output.vgm_total_samples += samples;
|
|
}
|
|
|
|
void GB_apu_vgm_advance(GB_gameboy_t *gb, uint8_t cycles)
|
|
{
|
|
if (likely(!gb->apu_output.output_file)) return;
|
|
if (likely(gb->apu_output.output_format != GB_AUDIO_FORMAT_VGM)) return;
|
|
vgm_advance(gb, cycles);
|
|
}
|
|
|
|
void GB_apu_vgm_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|
{
|
|
if (unlikely(gb->apu_output.output_file && gb->apu_output.output_format == GB_AUDIO_FORMAT_VGM)) {
|
|
vgm_write_register(gb, reg, value);
|
|
}
|
|
}
|
|
|
|
|
|
int GB_start_audio_recording(GB_gameboy_t *gb, const char *path, GB_audio_format_t format)
|
|
{
|
|
if (format != GB_AUDIO_FORMAT_VGM && gb->apu_output.sample_rate == 0) {
|
|
return EINVAL;
|
|
}
|
|
|
|
if (gb->apu_output.output_file) {
|
|
GB_stop_audio_recording(gb);
|
|
}
|
|
gb->apu_output.output_file = fopen(path, "wb");
|
|
if (!gb->apu_output.output_file) return errno;
|
|
|
|
gb->apu_output.output_format = format;
|
|
switch (format) {
|
|
case GB_AUDIO_FORMAT_RAW:
|
|
return 0;
|
|
case GB_AUDIO_FORMAT_VGM: {
|
|
gb->apu_output.vgm_pending_samples = 0;
|
|
gb->apu_output.vgm_total_samples = 0;
|
|
gb->apu_output.vgm_sample_fraction = 0;
|
|
gb->apu_output.vgm_clock_rate = GB_get_clock_rate(gb) ?: CPU_FREQUENCY;
|
|
uint8_t header[VGM_HEADER_SIZE];
|
|
vgm_get_header(gb, header, 0);
|
|
if (fwrite(header, sizeof(header), 1, gb->apu_output.output_file) != 1) {
|
|
int ret = errno ?: EIO;
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
return ret;
|
|
}
|
|
if (!vgm_write_apu_snapshot(gb)) {
|
|
int ret = gb->apu_output.output_error ?: EIO;
|
|
if (gb->apu_output.output_file) {
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
}
|
|
gb->apu_output.output_error = 0;
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
case GB_AUDIO_FORMAT_AIFF: {
|
|
aiff_header_t header = {0,};
|
|
if (fwrite(&header, sizeof(header), 1, gb->apu_output.output_file) != 1) {
|
|
int ret = errno ?: EIO;
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
case GB_AUDIO_FORMAT_WAV: {
|
|
wav_header_t header = {0,};
|
|
if (fwrite(&header, sizeof(header), 1, gb->apu_output.output_file) != 1) {
|
|
int ret = errno ?: EIO;
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
default:
|
|
fclose(gb->apu_output.output_file);
|
|
gb->apu_output.output_file = NULL;
|
|
return EINVAL;
|
|
}
|
|
}
|
|
int GB_stop_audio_recording(GB_gameboy_t *gb)
|
|
{
|
|
if (!gb->apu_output.output_file) {
|
|
int ret = gb->apu_output.output_error ?: -1;
|
|
gb->apu_output.output_error = 0;
|
|
return ret;
|
|
}
|
|
gb->apu_output.output_error = 0;
|
|
switch (gb->apu_output.output_format) {
|
|
case GB_AUDIO_FORMAT_RAW:
|
|
break;
|
|
case GB_AUDIO_FORMAT_VGM: {
|
|
if (!vgm_flush_wait(gb)) break;
|
|
uint8_t end_command = 0x66;
|
|
if (!vgm_write_data(gb, &end_command, sizeof(end_command))) break;
|
|
long file_size = ftell(gb->apu_output.output_file);
|
|
if (file_size < 0) {
|
|
gb->apu_output.output_error = errno ?: EIO;
|
|
break;
|
|
}
|
|
uint8_t header[VGM_HEADER_SIZE];
|
|
vgm_get_header(gb, header, file_size - 4);
|
|
if (fseek(gb->apu_output.output_file, 0, SEEK_SET) != 0 ||
|
|
fwrite(header, sizeof(header), 1, gb->apu_output.output_file) != 1) {
|
|
gb->apu_output.output_error = errno ?: EIO;
|
|
}
|
|
break;
|
|
}
|
|
case GB_AUDIO_FORMAT_AIFF: {
|
|
size_t file_size = ftell(gb->apu_output.output_file);
|
|
size_t frames = (file_size - sizeof(aiff_header_t)) / sizeof(GB_sample_t);
|
|
aiff_header_t header = {
|
|
.format_chunk = BE32('FORM'),
|
|
.size = BE32(file_size - 8),
|
|
.format = BE32('AIFC'),
|
|
|
|
.fver_chunk = BE32('FVER'),
|
|
.fver_size = BE32(4),
|
|
.fver = BE32(0xA2805140),
|
|
|
|
.comm_chunk = BE32('COMM'),
|
|
.comm_size = BE32(0x18),
|
|
.channels = BE16(2),
|
|
.samples_per_channel = BE32(frames),
|
|
.bit_depth = BE16(16),
|
|
#ifdef GB_BIG_ENDIAN
|
|
.compression_type = 'NONE',
|
|
#else
|
|
.compression_type = 'twos',
|
|
#endif
|
|
.compression_name = 0,
|
|
.ssnd_chunk = BE32('SSND'),
|
|
.ssnd_size = BE32(frames * sizeof(GB_sample_t) - 8),
|
|
.ssnd_offset = 0,
|
|
.ssnd_block = 0,
|
|
};
|
|
|
|
uint64_t significand = gb->apu_output.sample_rate;
|
|
uint16_t exponent = 0x403E;
|
|
while ((int64_t)significand > 0) {
|
|
significand <<= 1;
|
|
exponent--;
|
|
}
|
|
header.frequency_exponent = BE16(exponent);
|
|
header.frequency_significand = BE64(significand);
|
|
|
|
fseek(gb->apu_output.output_file, 0, SEEK_SET);
|
|
if (fwrite(&header, sizeof(header), 1, gb->apu_output.output_file) != 1) {
|
|
gb->apu_output.output_error = errno;
|
|
}
|
|
break;
|
|
}
|
|
case GB_AUDIO_FORMAT_WAV: {
|
|
size_t file_size = ftell(gb->apu_output.output_file);
|
|
size_t frames = (file_size - sizeof(wav_header_t)) / sizeof(GB_sample_t);
|
|
wav_header_t header = {
|
|
.marker = BE32('RIFF'),
|
|
.size = LE32(file_size - 8),
|
|
.type = BE32('WAVE'),
|
|
|
|
.fmt_chunk = BE32('fmt '),
|
|
.fmt_size = LE16(16),
|
|
.format = LE16(1),
|
|
.channels = LE16(2),
|
|
.sample_rate = LE32(gb->apu_output.sample_rate),
|
|
.byte_rate = LE32(gb->apu_output.sample_rate * 4),
|
|
.frame_size = LE32(4),
|
|
.bit_depth = LE16(16),
|
|
|
|
.data_chunk = BE32('data'),
|
|
.data_size = LE32(frames * sizeof(GB_sample_t)),
|
|
};
|
|
|
|
fseek(gb->apu_output.output_file, 0, SEEK_SET);
|
|
if (fwrite(&header, sizeof(header), 1, gb->apu_output.output_file) != 1) {
|
|
gb->apu_output.output_error = errno;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (gb->apu_output.output_file) {
|
|
if (fclose(gb->apu_output.output_file) != 0 && !gb->apu_output.output_error) {
|
|
gb->apu_output.output_error = errno ?: EIO;
|
|
}
|
|
gb->apu_output.output_file = NULL;
|
|
}
|
|
|
|
int ret = gb->apu_output.output_error;
|
|
gb->apu_output.output_error = 0;
|
|
return ret;
|
|
}
|
|
|
|
|
|
void GB_set_channel_muted(GB_gameboy_t *gb, GB_channel_t channel, bool muted)
|
|
{
|
|
assert(channel < GB_N_CHANNELS);
|
|
gb->apu_output.channel_muted[channel] = muted;
|
|
}
|
|
|
|
bool GB_is_channel_muted(GB_gameboy_t *gb, GB_channel_t channel)
|
|
{
|
|
return gb->apu_output.channel_muted[channel];
|
|
}
|
|
|
|
// Note: this intentionally does not check to see if the channel is muted.
|
|
uint8_t GB_get_channel_volume(GB_gameboy_t *gb, GB_channel_t channel)
|
|
{
|
|
switch (channel) {
|
|
case GB_SQUARE_1:
|
|
case GB_SQUARE_2:
|
|
return gb->apu.square_channels[channel].current_volume;
|
|
|
|
case GB_WAVE:
|
|
return inline_const(uint8_t[], {0xF, 8, 4, 0, 0})[gb->apu.wave_channel.shift];
|
|
|
|
case GB_NOISE:
|
|
return gb->apu.noise_channel.current_volume;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
uint8_t GB_get_channel_amplitude(GB_gameboy_t *gb, GB_channel_t channel)
|
|
{
|
|
return gb->apu.is_active[channel] ? gb->apu.samples[channel] : 0;
|
|
}
|
|
|
|
uint16_t GB_get_channel_period(GB_gameboy_t *gb, GB_channel_t channel)
|
|
{
|
|
switch (channel) {
|
|
case GB_SQUARE_1:
|
|
case GB_SQUARE_2:
|
|
return gb->apu.square_channels[channel].sample_length;
|
|
|
|
case GB_WAVE:
|
|
return gb->apu.wave_channel.sample_length;
|
|
|
|
case GB_NOISE:
|
|
return (gb->io_registers[GB_IO_NR43] & 7) << (gb->io_registers[GB_IO_NR43] >> 4);
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// wave_table is a user allocated uint8_t[32] array
|
|
void GB_get_apu_wave_table(GB_gameboy_t *gb, uint8_t *wave_table)
|
|
{
|
|
for (unsigned i = GB_IO_WAV_START; i <= GB_IO_WAV_END; i++) {
|
|
wave_table[2 * (i - GB_IO_WAV_START)] = gb->io_registers[i] >> 4;
|
|
wave_table[2 * (i - GB_IO_WAV_START) + 1] = gb->io_registers[i] & 0xF;
|
|
}
|
|
}
|
|
|
|
bool GB_get_channel_edge_triggered(GB_gameboy_t *gb, GB_channel_t channel)
|
|
{
|
|
bool edge_triggered = gb->apu_output.edge_triggered[channel];
|
|
gb->apu_output.edge_triggered[channel] = false;
|
|
return edge_triggered;
|
|
}
|