bsnes/Core/apu.h

165 lines
5.0 KiB
C
Raw Normal View History

2016-03-30 20:07:55 +00:00
#ifndef apu_h
#define apu_h
#include <stdbool.h>
#include <stdint.h>
2018-05-27 16:30:23 +00:00
#include <stddef.h>
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
/* Speed = 1 / Length (in seconds) */
#define DAC_DECAY_SPEED 20000
#define DAC_ATTACK_SPEED 20000
/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
2018-02-07 20:27:28 +00:00
#ifdef WIIU
2018-02-10 13:02:22 +00:00
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/
2018-02-15 23:26:37 +00:00
#define MAX_CH_AMP (0xFF0 / 2)
2018-02-07 20:27:28 +00:00
#else
2018-02-15 23:26:37 +00:00
#define MAX_CH_AMP 0xFF0
2018-02-07 20:27:28 +00:00
#endif
#define CH_STEP (MAX_CH_AMP/0xF/8)
#endif
2016-03-30 20:07:55 +00:00
/* APU ticks are 2MHz, triggered by an internal APU clock. */
2016-03-30 20:07:55 +00:00
typedef struct
{
int16_t left;
int16_t right;
} GB_sample_t;
2017-08-15 18:14:55 +00:00
typedef struct
{
double left;
double right;
} GB_double_sample_t;
enum GB_CHANNELS {
GB_SQUARE_1,
GB_SQUARE_2,
GB_WAVE,
GB_NOISE,
GB_N_CHANNELS
};
2016-03-30 20:07:55 +00:00
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
2016-03-30 20:07:55 +00:00
typedef struct
{
bool global_enable;
uint8_t apu_cycles;
uint8_t samples[GB_N_CHANNELS];
bool is_active[GB_N_CHANNELS];
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
// once more to generate 128Hz and 64Hz clocks
2017-08-11 14:57:08 +00:00
uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide
// need to divide the signal.
uint8_t square_sweep_countdown; // In 128Hz
2017-08-15 19:05:20 +00:00
uint8_t square_sweep_calculate_countdown; // In 2 MHz
uint16_t new_sweep_sample_legnth;
2017-09-21 15:18:10 +00:00
uint16_t shadow_sweep_sample_legnth;
2017-08-15 19:05:20 +00:00
bool sweep_enabled;
2017-09-21 15:18:10 +00:00
bool sweep_decreasing;
struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NRX2
uint8_t volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index; /* For save state compatibility,
highest bit is reused (See NR14/NR24's
write code)*/
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
bool length_enabled; // NRX4
} square_channels[2];
struct {
bool enable; // NR30
uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
uint8_t shift; // NR32
uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint8_t current_sample_index;
uint8_t current_sample; // Current sample before shifting.
int8_t wave_form[32];
bool wave_form_just_read;
} wave_channel;
2017-08-11 14:57:08 +00:00
struct {
uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NR42
uint8_t volume_countdown; // Reloaded from NR42
uint16_t lfsr;
bool narrow;
2017-08-11 14:57:08 +00:00
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length)
uint16_t sample_length; // From NR43, in APU ticks
bool length_enabled; // NR44
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
// 1MHz. This variable keeps track of the alignment.
2017-08-11 14:57:08 +00:00
} noise_channel;
bool skip_div_event;
2018-07-04 18:55:12 +00:00
bool current_lfsr_sample;
2016-03-30 20:07:55 +00:00
} GB_apu_t;
2017-08-15 18:14:55 +00:00
typedef enum {
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
2017-12-23 19:11:44 +00:00
GB_HIGHPASS_MAX
2017-08-15 18:14:55 +00:00
} GB_highpass_mode_t;
typedef struct {
unsigned sample_rate;
double sample_cycles; // In 8 MHz units
double cycles_per_sample;
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
unsigned cycles_since_render;
unsigned last_update[GB_N_CHANNELS];
GB_sample_t current_sample[GB_N_CHANNELS];
GB_sample_t summed_samples[GB_N_CHANNELS];
double dac_discharge[GB_N_CHANNELS];
2017-08-15 18:14:55 +00:00
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, unsigned cycles_per_sample); /* Cycles are in 8MHz units */
2017-08-15 18:14:55 +00:00
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
void GB_apu_div_event(GB_gameboy_t *gb);
void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
#endif
2016-03-30 20:07:55 +00:00
#endif /* apu_h */