Mercurial > spc_convert
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/snes_spc/SNES_SPC.h Fri Oct 21 05:53:11 2011 -0700 1.3 @@ -0,0 +1,279 @@ 1.4 +// SNES SPC-700 APU emulator 1.5 + 1.6 +// snes_spc 0.9.0 1.7 +#ifndef SNES_SPC_H 1.8 +#define SNES_SPC_H 1.9 + 1.10 +#include "SPC_DSP.h" 1.11 +#include "blargg_endian.h" 1.12 + 1.13 +struct SNES_SPC { 1.14 +public: 1.15 + typedef BOOST::uint8_t uint8_t; 1.16 + 1.17 + // Must be called once before using 1.18 + blargg_err_t init(); 1.19 + 1.20 + // Sample pairs generated per second 1.21 + enum { sample_rate = 32000 }; 1.22 + 1.23 +// Emulator use 1.24 + 1.25 + // Sets IPL ROM data. Library does not include ROM data. Most SPC music files 1.26 + // don't need ROM, but a full emulator must provide this. 1.27 + enum { rom_size = 0x40 }; 1.28 + void init_rom( uint8_t const rom [rom_size] ); 1.29 + 1.30 + // Sets destination for output samples 1.31 + typedef short sample_t; 1.32 + void set_output( sample_t* out, int out_size ); 1.33 + 1.34 + // Number of samples written to output since last set 1.35 + int sample_count() const; 1.36 + 1.37 + // Resets SPC to power-on state. This resets your output buffer, so you must 1.38 + // call set_output() after this. 1.39 + void reset(); 1.40 + 1.41 + // Emulates pressing reset switch on SNES. This resets your output buffer, so 1.42 + // you must call set_output() after this. 1.43 + void soft_reset(); 1.44 + 1.45 + // 1024000 SPC clocks per second, sample pair every 32 clocks 1.46 + typedef int time_t; 1.47 + enum { clock_rate = 1024000 }; 1.48 + enum { clocks_per_sample = 32 }; 1.49 + 1.50 + // Emulated port read/write at specified time 1.51 + enum { port_count = 4 }; 1.52 + int read_port ( time_t, int port ); 1.53 + void write_port( time_t, int port, int data ); 1.54 + 1.55 + // Runs SPC to end_time and starts a new time frame at 0 1.56 + void end_frame( time_t end_time ); 1.57 + 1.58 +// Sound control 1.59 + 1.60 + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). 1.61 + // Reduces emulation accuracy. 1.62 + enum { voice_count = 8 }; 1.63 + void mute_voices( int mask ); 1.64 + 1.65 + // If true, prevents channels and global volumes from being phase-negated. 1.66 + // Only supported by fast DSP. 1.67 + void disable_surround( bool disable = true ); 1.68 + 1.69 + // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. 1.70 + enum { tempo_unit = 0x100 }; 1.71 + void set_tempo( int ); 1.72 + 1.73 +// SPC music files 1.74 + 1.75 + // Loads SPC data into emulator 1.76 + enum { spc_min_file_size = 0x10180 }; 1.77 + enum { spc_file_size = 0x10200 }; 1.78 + blargg_err_t load_spc( void const* in, long size ); 1.79 + 1.80 + // Clears echo region. Useful after loading an SPC as many have garbage in echo. 1.81 + void clear_echo(); 1.82 + 1.83 + // Plays for count samples and write samples to out. Discards samples if out 1.84 + // is NULL. Count must be a multiple of 2 since output is stereo. 1.85 + blargg_err_t play( int count, sample_t* out ); 1.86 + 1.87 + // Skips count samples. Several times faster than play() when using fast DSP. 1.88 + blargg_err_t skip( int count ); 1.89 + 1.90 +// State save/load (only available with accurate DSP) 1.91 + 1.92 +#if !SPC_NO_COPY_STATE_FUNCS 1.93 + // Saves/loads state 1.94 + enum { state_size = 67 * 1024L }; // maximum space needed when saving 1.95 + typedef SPC_DSP::copy_func_t copy_func_t; 1.96 + void copy_state( unsigned char** io, copy_func_t ); 1.97 + 1.98 + // Writes minimal header to spc_out 1.99 + static void init_header( void* spc_out ); 1.100 + 1.101 + // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. 1.102 + // Does not set up SPC header; use init_header() for that. 1.103 + void save_spc( void* spc_out ); 1.104 + 1.105 + // Returns true if new key-on events occurred since last check. Useful for 1.106 + // trimming silence while saving an SPC. 1.107 + bool check_kon(); 1.108 +#endif 1.109 + 1.110 +public: 1.111 + BLARGG_DISABLE_NOTHROW 1.112 + 1.113 + typedef BOOST::uint16_t uint16_t; 1.114 + 1.115 + // Time relative to m_spc_time. Speeds up code a bit by eliminating need to 1.116 + // constantly add m_spc_time to time from CPU. CPU uses time that ends at 1.117 + // 0 to eliminate reloading end time every instruction. It pays off. 1.118 + typedef int rel_time_t; 1.119 + 1.120 + struct Timer 1.121 + { 1.122 + rel_time_t next_time; // time of next event 1.123 + int prescaler; 1.124 + int period; 1.125 + int divider; 1.126 + int enabled; 1.127 + int counter; 1.128 + }; 1.129 + enum { reg_count = 0x10 }; 1.130 + enum { timer_count = 3 }; 1.131 + enum { extra_size = SPC_DSP::extra_size }; 1.132 + 1.133 + enum { signature_size = 35 }; 1.134 + 1.135 +private: 1.136 + SPC_DSP dsp; 1.137 + 1.138 + #if SPC_LESS_ACCURATE 1.139 + static signed char const reg_times_ [256]; 1.140 + signed char reg_times [256]; 1.141 + #endif 1.142 + 1.143 + struct state_t 1.144 + { 1.145 + Timer timers [timer_count]; 1.146 + 1.147 + uint8_t smp_regs [2] [reg_count]; 1.148 + 1.149 + struct 1.150 + { 1.151 + int pc; 1.152 + int a; 1.153 + int x; 1.154 + int y; 1.155 + int psw; 1.156 + int sp; 1.157 + } cpu_regs; 1.158 + 1.159 + rel_time_t dsp_time; 1.160 + time_t spc_time; 1.161 + bool echo_accessed; 1.162 + 1.163 + int tempo; 1.164 + int skipped_kon; 1.165 + int skipped_koff; 1.166 + const char* cpu_error; 1.167 + 1.168 + int extra_clocks; 1.169 + sample_t* buf_begin; 1.170 + sample_t const* buf_end; 1.171 + sample_t* extra_pos; 1.172 + sample_t extra_buf [extra_size]; 1.173 + 1.174 + int rom_enabled; 1.175 + uint8_t rom [rom_size]; 1.176 + uint8_t hi_ram [rom_size]; 1.177 + 1.178 + unsigned char cycle_table [256]; 1.179 + 1.180 + struct 1.181 + { 1.182 + // padding to neutralize address overflow 1.183 + union { 1.184 + uint8_t padding1 [0x100]; 1.185 + uint16_t align; // makes compiler align data for 16-bit access 1.186 + } padding1 [1]; 1.187 + uint8_t ram [0x10000]; 1.188 + uint8_t padding2 [0x100]; 1.189 + } ram; 1.190 + }; 1.191 + state_t m; 1.192 + 1.193 + enum { rom_addr = 0xFFC0 }; 1.194 + 1.195 + enum { skipping_time = 127 }; 1.196 + 1.197 + // Value that padding should be filled with 1.198 + enum { cpu_pad_fill = 0xFF }; 1.199 + 1.200 + enum { 1.201 + r_test = 0x0, r_control = 0x1, 1.202 + r_dspaddr = 0x2, r_dspdata = 0x3, 1.203 + r_cpuio0 = 0x4, r_cpuio1 = 0x5, 1.204 + r_cpuio2 = 0x6, r_cpuio3 = 0x7, 1.205 + r_f8 = 0x8, r_f9 = 0x9, 1.206 + r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, 1.207 + r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF 1.208 + }; 1.209 + 1.210 + void timers_loaded(); 1.211 + void enable_rom( int enable ); 1.212 + void reset_buf(); 1.213 + void save_extra(); 1.214 + void load_regs( uint8_t const in [reg_count] ); 1.215 + void ram_loaded(); 1.216 + void regs_loaded(); 1.217 + void reset_time_regs(); 1.218 + void reset_common( int timer_counter_init ); 1.219 + 1.220 + Timer* run_timer_ ( Timer* t, rel_time_t ); 1.221 + Timer* run_timer ( Timer* t, rel_time_t ); 1.222 + int dsp_read ( rel_time_t ); 1.223 + void dsp_write ( int data, rel_time_t ); 1.224 + void cpu_write_smp_reg_( int data, rel_time_t, int addr ); 1.225 + void cpu_write_smp_reg ( int data, rel_time_t, int addr ); 1.226 + void cpu_write_high ( int data, int i, rel_time_t ); 1.227 + void cpu_write ( int data, int addr, rel_time_t ); 1.228 + int cpu_read_smp_reg ( int i, rel_time_t ); 1.229 + int cpu_read ( int addr, rel_time_t ); 1.230 + unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); 1.231 + 1.232 + bool check_echo_access ( int addr ); 1.233 + uint8_t* run_until_( time_t end_time ); 1.234 + 1.235 + struct spc_file_t 1.236 + { 1.237 + char signature [signature_size]; 1.238 + uint8_t has_id666; 1.239 + uint8_t version; 1.240 + uint8_t pcl, pch; 1.241 + uint8_t a; 1.242 + uint8_t x; 1.243 + uint8_t y; 1.244 + uint8_t psw; 1.245 + uint8_t sp; 1.246 + char text [212]; 1.247 + uint8_t ram [0x10000]; 1.248 + uint8_t dsp [128]; 1.249 + uint8_t unused [0x40]; 1.250 + uint8_t ipl_rom [0x40]; 1.251 + }; 1.252 + 1.253 + static char const signature [signature_size + 1]; 1.254 + 1.255 + void save_regs( uint8_t out [reg_count] ); 1.256 +}; 1.257 + 1.258 +#include <assert.h> 1.259 + 1.260 +inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; } 1.261 + 1.262 +inline int SNES_SPC::read_port( time_t t, int port ) 1.263 +{ 1.264 + assert( (unsigned) port < port_count ); 1.265 + return run_until_( t ) [port]; 1.266 +} 1.267 + 1.268 +inline void SNES_SPC::write_port( time_t t, int port, int data ) 1.269 +{ 1.270 + assert( (unsigned) port < port_count ); 1.271 + run_until_( t ) [0x10 + port] = data; 1.272 +} 1.273 + 1.274 +inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); } 1.275 + 1.276 +inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); } 1.277 + 1.278 +#if !SPC_NO_COPY_STATE_FUNCS 1.279 +inline bool SNES_SPC::check_kon() { return dsp.check_kon(); } 1.280 +#endif 1.281 + 1.282 +#endif