1 // SNES SPC-700 APU emulator
2 
3 // snes_spc 0.9.0
4 #ifndef SNES_SPC_H
5 #define SNES_SPC_H
6 
7 #include "SPC_DSP.h"
8 #include "blargg_endian.h"
9 
10 struct SNES_SPC {
11 public:
12 	typedef BOOST::uint8_t uint8_t;
13 
14 	// Must be called once before using
15 	blargg_err_t init();
16 
17 	// Sample pairs generated per second
18 	enum { sample_rate = 32000 };
19 
20 // Emulator use
21 
22 	// Sets IPL ROM data. Library does not include ROM data. Most SPC music files
23 	// 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] );
26 
27 	// Sets destination for output samples
28 	typedef short sample_t;
29 	void set_output( sample_t* out, int out_size );
30 
31 	// Number of samples written to output since last set
32 	int sample_count() const;
33 
34 	// Resets SPC to power-on state. This resets your output buffer, so you must
35 	// call set_output() after this.
36 	void reset();
37 
38 	// Emulates pressing reset switch on SNES. This resets your output buffer, so
39 	// you must call set_output() after this.
40 	void soft_reset();
41 
42 	// 1024000 SPC clocks per second, sample pair every 32 clocks
43 	typedef int time_t;
44 	enum { clock_rate = 1024000 };
45 	enum { clocks_per_sample = 32 };
46 
47 	// Emulated port read/write at specified time
48 	enum { port_count = 4 };
49 	int  read_port ( time_t, int port );
50 	void write_port( time_t, int port, int data );
51 
52 	// Runs SPC to end_time and starts a new time frame at 0
53 	void end_frame( time_t end_time );
54 
55 // Sound control
56 
57 	// 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 );
61 
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 );
65 
66 	// Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
67 	enum { tempo_unit = 0x100 };
68 	void set_tempo( int );
69 
70 // SPC music files
71 
72 	// Loads SPC data into emulator
73 	enum { spc_min_file_size = 0x10180 };
74 	enum { spc_file_size     = 0x10200 };
75 	blargg_err_t load_spc( void const* in, long size );
76 
77 	// Clears echo region. Useful after loading an SPC as many have garbage in echo.
78 	void clear_echo();
79 
80 	// Plays for count samples and write samples to out. Discards samples if out
81 	// is NULL. Count must be a multiple of 2 since output is stereo.
82 	blargg_err_t play( int count, sample_t* out );
83 
84 	// Skips count samples. Several times faster than play() when using fast DSP.
85 	blargg_err_t skip( int count );
86 
87 // State save/load (only available with accurate DSP)
88 
89 #if !SPC_NO_COPY_STATE_FUNCS
90 	// Saves/loads state
91 	enum { state_size = 67 * 1024L }; // maximum space needed when saving
92 	typedef SPC_DSP::copy_func_t copy_func_t;
93 	void copy_state( unsigned char** io, copy_func_t );
94 
95 	// Writes minimal header to spc_out
96 	static void init_header( void* spc_out );
97 
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 );
101 
102 	// Returns true if new key-on events occurred since last check. Useful for
103 	// trimming silence while saving an SPC.
104 	bool check_kon();
105 #endif
106 
107 public:
108 	BLARGG_DISABLE_NOTHROW
109 
110 	typedef BOOST::uint16_t uint16_t;
111 
112 	// Time relative to m_spc_time. Speeds up code a bit by eliminating need to
113 	// constantly add m_spc_time to time from CPU. CPU uses time that ends at
114 	// 0 to eliminate reloading end time every instruction. It pays off.
115 	typedef int rel_time_t;
116 
117 	struct Timer
118 	{
119 		rel_time_t next_time; // time of next event
120 		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 };
129 
130 	enum { signature_size = 35 };
131 
132 private:
133 	SPC_DSP dsp;
134 
135 	#if SPC_LESS_ACCURATE
136 		static signed char const reg_times_ [256];
137 		signed char reg_times [256];
138 	#endif
139 
140 	struct state_t
141 	{
142 		Timer timers [timer_count];
143 
144 		uint8_t smp_regs [2] [reg_count];
145 
146 		struct
147 		{
148 			int pc;
149 			int a;
150 			int x;
151 			int y;
152 			int psw;
153 			int sp;
154 		} cpu_regs;
155 
156 		rel_time_t  dsp_time;
157 		time_t      spc_time;
158 		bool        echo_accessed;
159 
160 		int         tempo;
161 		int         skipped_kon;
162 		int         skipped_koff;
163 		const char* cpu_error;
164 
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];
170 
171 		int         rom_enabled;
172 		uint8_t     rom    [rom_size];
173 		uint8_t     hi_ram [rom_size];
174 
175 		unsigned char cycle_table [256];
176 
177 		struct
178 		{
179 			// padding to neutralize address overflow
180 			union {
181 				uint8_t padding1 [0x100];
182 				uint16_t align; // makes compiler align data for 16-bit access
183 			} padding1 [1];
184 			uint8_t ram [0x10000 + 0x100];
185 		} ram;
186 	};
187 	state_t m;
188 
189 	enum { rom_addr = 0xFFC0 };
190 
191 	enum { skipping_time = 127 };
192 
193 	// Value that padding should be filled with
194 	enum { cpu_pad_fill = 0xFF };
195 
196 	enum {
197         r_test     = 0x0, r_control  = 0x1,
198         r_dspaddr  = 0x2, r_dspdata  = 0x3,
199         r_cpuio0   = 0x4, r_cpuio1   = 0x5,
200         r_cpuio2   = 0x6, r_cpuio3   = 0x7,
201         r_f8       = 0x8, r_f9       = 0x9,
202         r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
203         r_t0out    = 0xD, r_t1out    = 0xE, r_t2out    = 0xF
204 	};
205 
206 	void timers_loaded();
207 	void enable_rom( int enable );
208 	void reset_buf();
209 	void save_extra();
210 	void load_regs( uint8_t const in [reg_count] );
211 	void ram_loaded();
212 	void regs_loaded();
213 	void reset_time_regs();
214 	void reset_common( int timer_counter_init );
215 
216 	Timer* run_timer_      ( Timer* t, rel_time_t );
217 	Timer* run_timer       ( Timer* t, rel_time_t );
218 	int dsp_read           ( rel_time_t );
219 	void dsp_write         ( int data, rel_time_t );
220 	void cpu_write_smp_reg_( int data, rel_time_t, int addr );
221 	void cpu_write_smp_reg ( int data, rel_time_t, int addr );
222 	void cpu_write_high    ( int data, int i, rel_time_t );
223 	void cpu_write         ( int data, int addr, rel_time_t );
224 	int cpu_read_smp_reg   ( int i, rel_time_t );
225 	int cpu_read           ( int addr, rel_time_t );
226 	unsigned CPU_mem_bit   ( uint8_t const* pc, rel_time_t );
227 
228 	bool check_echo_access ( int addr );
229 	uint8_t* run_until_( time_t end_time );
230 
231 	struct spc_file_t
232 	{
233 		char    signature [signature_size];
234 		uint8_t has_id666;
235 		uint8_t version;
236 		uint8_t pcl, pch;
237 		uint8_t a;
238 		uint8_t x;
239 		uint8_t y;
240 		uint8_t psw;
241 		uint8_t sp;
242 		char    text [212];
243 		uint8_t ram [0x10000];
244 		uint8_t dsp [128];
245 		uint8_t unused [0x40];
246 		uint8_t ipl_rom [0x40];
247 	};
248 
249 	static char const signature [signature_size + 1];
250 
251 	void save_regs( uint8_t out [reg_count] );
252 };
253 
254 #include <assert.h>
255 
sample_count()256 inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
257 
read_port(time_t t,int port)258 inline int SNES_SPC::read_port( time_t t, int port )
259 {
260 	assert( (unsigned) port < port_count );
261 	return run_until_( t ) [port];
262 }
263 
write_port(time_t t,int port,int data)264 inline void SNES_SPC::write_port( time_t t, int port, int data )
265 {
266 	assert( (unsigned) port < port_count );
267 	run_until_( t ) [0x10 + port] = data;
268 }
269 
mute_voices(int mask)270 inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }
271 
disable_surround(bool disable)272 inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
273 
274 #if !SPC_NO_COPY_STATE_FUNCS
check_kon()275 inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }
276 #endif
277 
278 #endif
279