Path: blob/main/misc/emulator/xnes/snes9x/apu/SPC_DSP.h
28798 views
// Highly accurate SNES SPC-700 DSP emulator12// snes_spc 0.9.03#ifndef SPC_DSP_H4#define SPC_DSP_H56#include "blargg_common.h"78extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }910class SPC_DSP {11public:12typedef BOOST::uint8_t uint8_t;1314// Setup1516// Initializes DSP and has it use the 64K RAM provided17void init( void* ram_64k );1819// Sets destination for output samples. If out is NULL or out_size is 0,20// doesn't generate any.21typedef short sample_t;22void set_output( sample_t* out, int out_size );2324// Number of samples written to output since it was last set, always25// a multiple of 2. Undefined if more samples were generated than26// output buffer could hold.27int sample_count() const;2829// Emulation3031// Resets DSP to power-on state32void reset();3334// Emulates pressing reset switch on SNES35void soft_reset();3637// Reads/writes DSP registers. For accuracy, you must first call run()38// to catch the DSP up to present.39int read ( int addr ) const;40void write( int addr, int data );4142// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks43// a pair of samples is be generated.44void run( int clock_count );4546// Sound control4748// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).49// Reduces emulation accuracy.50enum { voice_count = 8 };51void mute_voices( int mask );5253// State5455// Resets DSP and uses supplied values to initialize registers56enum { register_count = 128 };57void load( uint8_t const regs [register_count] );5859// Saves/loads exact emulator state60enum { state_size = 640 }; // maximum space needed when saving61typedef dsp_copy_func_t copy_func_t;62void copy_state( unsigned char** io, copy_func_t );6364// Returns non-zero if new key-on events occurred since last call65bool check_kon();6667// Snes9x Accessor6869int stereo_switch;70int take_spc_snapshot;71int rom_enabled; // mirror72uint8_t *rom, *hi_ram; // mirror73void (*spc_snapshot_callback) (void);7475void set_spc_snapshot_callback( void (*callback) (void) );76void dump_spc_snapshot( void );77void set_stereo_switch( int );78uint8_t reg_value( int, int );79int envx_value( int );8081// DSP register addresses8283// Global registers84enum {85r_mvoll = 0x0C, r_mvolr = 0x1C,86r_evoll = 0x2C, r_evolr = 0x3C,87r_kon = 0x4C, r_koff = 0x5C,88r_flg = 0x6C, r_endx = 0x7C,89r_efb = 0x0D, r_pmon = 0x2D,90r_non = 0x3D, r_eon = 0x4D,91r_dir = 0x5D, r_esa = 0x6D,92r_edl = 0x7D,93r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F94};9596// Voice registers97enum {98v_voll = 0x00, v_volr = 0x01,99v_pitchl = 0x02, v_pitchh = 0x03,100v_srcn = 0x04, v_adsr0 = 0x05,101v_adsr1 = 0x06, v_gain = 0x07,102v_envx = 0x08, v_outx = 0x09103};104105public:106enum { extra_size = 16 };107sample_t* extra() { return m.extra; }108sample_t const* out_pos() const { return m.out; }109void disable_surround( bool ) { } // not supported110public:111BLARGG_DISABLE_NOTHROW112113typedef BOOST::int8_t int8_t;114typedef BOOST::int16_t int16_t;115116enum { echo_hist_size = 8 };117118enum env_mode_t { env_release, env_attack, env_decay, env_sustain };119enum { brr_buf_size = 12 };120struct voice_t121{122int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)123int buf_pos; // place in buffer where next samples will be decoded124int interp_pos; // relative fractional position in sample (0x1000 = 1.0)125int brr_addr; // address of current BRR block126int brr_offset; // current decoding offset in BRR block127uint8_t* regs; // pointer to voice's DSP registers128int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.129int kon_delay; // KON delay/current setup phase130env_mode_t env_mode;131int env; // current envelope level132int hidden_env; // used by GAIN mode 7, very obscure quirk133uint8_t t_envx_out;134int voice_number;135};136private:137enum { brr_block_size = 9 };138139struct state_t140{141uint8_t regs [register_count];142143// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)144int echo_hist [echo_hist_size * 2] [2];145int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]146147int every_other_sample; // toggles every sample148int kon; // KON value when last checked149int noise;150int counter;151int echo_offset; // offset from ESA in echo buffer152int echo_length; // number of bytes that echo_offset will stop at153int phase; // next clock cycle to run (0-31)154bool kon_check; // set when a new KON occurs155156// Hidden registers also written to when main register is written to157int new_kon;158uint8_t endx_buf;159uint8_t envx_buf;160uint8_t outx_buf;161162// Temporary state between clocks163164// read once per sample165int t_pmon;166int t_non;167int t_eon;168int t_dir;169int t_koff;170171// read a few clocks ahead then used172int t_brr_next_addr;173int t_adsr0;174int t_brr_header;175int t_brr_byte;176int t_srcn;177int t_esa;178int t_echo_enabled;179180// internal state that is recalculated every sample181int t_dir_addr;182int t_pitch;183int t_output;184int t_looped;185int t_echo_ptr;186187// left/right sums188int t_main_out [2];189int t_echo_out [2];190int t_echo_in [2];191192voice_t voices [voice_count];193194// non-emulation state195uint8_t* ram; // 64K shared RAM between DSP and SMP196int mute_mask;197sample_t* out;198sample_t* out_end;199sample_t* out_begin;200sample_t extra [extra_size];201};202state_t m;203204void init_counter();205void run_counters();206unsigned read_counter( int rate );207208int interpolate( voice_t const* v );209void run_envelope( voice_t* const v );210void decode_brr( voice_t* v );211212void misc_27();213void misc_28();214void misc_29();215void misc_30();216217void voice_output( voice_t const* v, int ch );218void voice_V1( voice_t* const );219void voice_V2( voice_t* const );220void voice_V3( voice_t* const );221void voice_V3a( voice_t* const );222void voice_V3b( voice_t* const );223void voice_V3c( voice_t* const );224void voice_V4( voice_t* const );225void voice_V5( voice_t* const );226void voice_V6( voice_t* const );227void voice_V7( voice_t* const );228void voice_V8( voice_t* const );229void voice_V9( voice_t* const );230void voice_V7_V4_V1( voice_t* const );231void voice_V8_V5_V2( voice_t* const );232void voice_V9_V6_V3( voice_t* const );233234void echo_read( int ch );235int echo_output( int ch );236void echo_write( int ch );237void echo_22();238void echo_23();239void echo_24();240void echo_25();241void echo_26();242void echo_27();243void echo_28();244void echo_29();245void echo_30();246247void soft_reset_common();248};249250#include <assert.h>251252inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; }253254inline int SPC_DSP::read( int addr ) const255{256assert( (unsigned) addr < register_count );257return m.regs [addr];258}259260inline void SPC_DSP::write( int addr, int data )261{262assert( (unsigned) addr < register_count );263264m.regs [addr] = (uint8_t) data;265switch ( addr & 0x0F )266{267case v_envx:268m.envx_buf = (uint8_t) data;269break;270271case v_outx:272m.outx_buf = (uint8_t) data;273break;274275case 0x0C:276if ( addr == r_kon )277m.new_kon = (uint8_t) data;278279if ( addr == r_endx ) // always cleared, regardless of data written280{281m.endx_buf = 0;282m.regs [r_endx] = 0;283}284break;285}286}287288inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }289290inline bool SPC_DSP::check_kon()291{292bool old = m.kon_check;293m.kon_check = 0;294return old;295}296297#if !SPC_NO_COPY_STATE_FUNCS298299class SPC_State_Copier {300SPC_DSP::copy_func_t func;301unsigned char** buf;302public:303SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }304void copy( void* state, size_t size );305int copy_int( int state, int size );306void skip( int count );307void extra();308};309310#define SPC_COPY( type, state )\311{\312state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\313assert( (BOOST::type) state == state );\314}315316#endif317318#endif319320321