Mercurial > spc_convert
view snes_spc/SNES_SPC.h @ 0:e38dacceb958
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 05:53:11 -0700 |
parents | |
children |
line wrap: on
line source
1 // SNES SPC-700 APU emulator3 // snes_spc 0.9.04 #ifndef SNES_SPC_H5 #define SNES_SPC_H7 #include "SPC_DSP.h"8 #include "blargg_endian.h"10 struct SNES_SPC {11 public:12 typedef BOOST::uint8_t uint8_t;14 // Must be called once before using15 blargg_err_t init();17 // Sample pairs generated per second18 enum { sample_rate = 32000 };20 // Emulator use22 // Sets IPL ROM data. Library does not include ROM data. Most SPC music files23 // don't need ROM, but a full emulator must provide this.24 enum { rom_size = 0x40 };25 void init_rom( uint8_t const rom [rom_size] );27 // Sets destination for output samples28 typedef short sample_t;29 void set_output( sample_t* out, int out_size );31 // Number of samples written to output since last set32 int sample_count() const;34 // Resets SPC to power-on state. This resets your output buffer, so you must35 // call set_output() after this.36 void reset();38 // Emulates pressing reset switch on SNES. This resets your output buffer, so39 // you must call set_output() after this.40 void soft_reset();42 // 1024000 SPC clocks per second, sample pair every 32 clocks43 typedef int time_t;44 enum { clock_rate = 1024000 };45 enum { clocks_per_sample = 32 };47 // Emulated port read/write at specified time48 enum { port_count = 4 };49 int read_port ( time_t, int port );50 void write_port( time_t, int port, int data );52 // Runs SPC to end_time and starts a new time frame at 053 void end_frame( time_t end_time );55 // Sound control57 // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).58 // Reduces emulation accuracy.59 enum { voice_count = 8 };60 void mute_voices( int mask );62 // If true, prevents channels and global volumes from being phase-negated.63 // Only supported by fast DSP.64 void disable_surround( bool disable = true );66 // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.67 enum { tempo_unit = 0x100 };68 void set_tempo( int );70 // SPC music files72 // Loads SPC data into emulator73 enum { spc_min_file_size = 0x10180 };74 enum { spc_file_size = 0x10200 };75 blargg_err_t load_spc( void const* in, long size );77 // Clears echo region. Useful after loading an SPC as many have garbage in echo.78 void clear_echo();80 // Plays for count samples and write samples to out. Discards samples if out81 // is NULL. Count must be a multiple of 2 since output is stereo.82 blargg_err_t play( int count, sample_t* out );84 // Skips count samples. Several times faster than play() when using fast DSP.85 blargg_err_t skip( int count );87 // State save/load (only available with accurate DSP)89 #if !SPC_NO_COPY_STATE_FUNCS90 // Saves/loads state91 enum { state_size = 67 * 1024L }; // maximum space needed when saving92 typedef SPC_DSP::copy_func_t copy_func_t;93 void copy_state( unsigned char** io, copy_func_t );95 // Writes minimal header to spc_out96 static void init_header( void* spc_out );98 // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.99 // Does not set up SPC header; use init_header() for that.100 void save_spc( void* spc_out );102 // Returns true if new key-on events occurred since last check. Useful for103 // trimming silence while saving an SPC.104 bool check_kon();105 #endif107 public:108 BLARGG_DISABLE_NOTHROW110 typedef BOOST::uint16_t uint16_t;112 // Time relative to m_spc_time. Speeds up code a bit by eliminating need to113 // constantly add m_spc_time to time from CPU. CPU uses time that ends at114 // 0 to eliminate reloading end time every instruction. It pays off.115 typedef int rel_time_t;117 struct Timer118 {119 rel_time_t next_time; // time of next event120 int prescaler;121 int period;122 int divider;123 int enabled;124 int counter;125 };126 enum { reg_count = 0x10 };127 enum { timer_count = 3 };128 enum { extra_size = SPC_DSP::extra_size };130 enum { signature_size = 35 };132 private:133 SPC_DSP dsp;135 #if SPC_LESS_ACCURATE136 static signed char const reg_times_ [256];137 signed char reg_times [256];138 #endif140 struct state_t141 {142 Timer timers [timer_count];144 uint8_t smp_regs [2] [reg_count];146 struct147 {148 int pc;149 int a;150 int x;151 int y;152 int psw;153 int sp;154 } cpu_regs;156 rel_time_t dsp_time;157 time_t spc_time;158 bool echo_accessed;160 int tempo;161 int skipped_kon;162 int skipped_koff;163 const char* cpu_error;165 int extra_clocks;166 sample_t* buf_begin;167 sample_t const* buf_end;168 sample_t* extra_pos;169 sample_t extra_buf [extra_size];171 int rom_enabled;172 uint8_t rom [rom_size];173 uint8_t hi_ram [rom_size];175 unsigned char cycle_table [256];177 struct178 {179 // padding to neutralize address overflow180 union {181 uint8_t padding1 [0x100];182 uint16_t align; // makes compiler align data for 16-bit access183 } padding1 [1];184 uint8_t ram [0x10000];185 uint8_t padding2 [0x100];186 } ram;187 };188 state_t m;190 enum { rom_addr = 0xFFC0 };192 enum { skipping_time = 127 };194 // Value that padding should be filled with195 enum { cpu_pad_fill = 0xFF };197 enum {198 r_test = 0x0, r_control = 0x1,199 r_dspaddr = 0x2, r_dspdata = 0x3,200 r_cpuio0 = 0x4, r_cpuio1 = 0x5,201 r_cpuio2 = 0x6, r_cpuio3 = 0x7,202 r_f8 = 0x8, r_f9 = 0x9,203 r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,204 r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF205 };207 void timers_loaded();208 void enable_rom( int enable );209 void reset_buf();210 void save_extra();211 void load_regs( uint8_t const in [reg_count] );212 void ram_loaded();213 void regs_loaded();214 void reset_time_regs();215 void reset_common( int timer_counter_init );217 Timer* run_timer_ ( Timer* t, rel_time_t );218 Timer* run_timer ( Timer* t, rel_time_t );219 int dsp_read ( rel_time_t );220 void dsp_write ( int data, rel_time_t );221 void cpu_write_smp_reg_( int data, rel_time_t, int addr );222 void cpu_write_smp_reg ( int data, rel_time_t, int addr );223 void cpu_write_high ( int data, int i, rel_time_t );224 void cpu_write ( int data, int addr, rel_time_t );225 int cpu_read_smp_reg ( int i, rel_time_t );226 int cpu_read ( int addr, rel_time_t );227 unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );229 bool check_echo_access ( int addr );230 uint8_t* run_until_( time_t end_time );232 struct spc_file_t233 {234 char signature [signature_size];235 uint8_t has_id666;236 uint8_t version;237 uint8_t pcl, pch;238 uint8_t a;239 uint8_t x;240 uint8_t y;241 uint8_t psw;242 uint8_t sp;243 char text [212];244 uint8_t ram [0x10000];245 uint8_t dsp [128];246 uint8_t unused [0x40];247 uint8_t ipl_rom [0x40];248 };250 static char const signature [signature_size + 1];252 void save_regs( uint8_t out [reg_count] );253 };255 #include <assert.h>257 inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }259 inline int SNES_SPC::read_port( time_t t, int port )260 {261 assert( (unsigned) port < port_count );262 return run_until_( t ) [port];263 }265 inline void SNES_SPC::write_port( time_t t, int port, int data )266 {267 assert( (unsigned) port < port_count );268 run_until_( t ) [0x10 + port] = data;269 }271 inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }273 inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }275 #if !SPC_NO_COPY_STATE_FUNCS276 inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }277 #endif279 #endif