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