1 // SPC emulation support: init, sample buffering, reset, SPC loading
2 
3 // Game_Music_Emu 0.6.0. http://www.slack.net/~ant/
4 
5 #include "Snes_Spc.h"
6 
7 #include <string.h>
8 
9 /* Copyright (C) 2004-2007 Shay Green. This module is free software; you
10 can redistribute it and/or modify it under the terms of the GNU Lesser
11 General Public License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version. This
13 module is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16 details. You should have received a copy of the GNU Lesser General Public
17 License along with this module; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 
20 #include "blargg_source.h"
21 
22 #define RAM         (m.ram.ram)
23 #define REGS        (m.smp_regs [0])
24 #define REGS_IN     (m.smp_regs [1])
25 
26 // (n ? n : 256)
27 #define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
28 
29 
30 //// Init
31 
init()32 blargg_err_t Snes_Spc::init()
33 {
34 	memset( &m, 0, sizeof m );
35 	dsp.init( RAM );
36 
37 	m.tempo = tempo_unit;
38 
39 	// Most SPC music doesn't need ROM, and almost all the rest only rely
40 	// on these two bytes
41 	m.rom [0x3E] = 0xFF;
42 	m.rom [0x3F] = 0xC0;
43 
44 	static unsigned char const cycle_table [128] =
45 	{//   01   23   45   67   89   AB   CD   EF
46 	    0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0
47 	    0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1
48 	    0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2
49 	    0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3
50 	    0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4
51 	    0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5
52 	    0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6
53 	    0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7
54 	    0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8
55 	    0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9
56 	    0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A
57 	    0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B
58 	    0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C
59 	    0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D
60 	    0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
61 	    0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
62 	};
63 
64 	// unpack cycle table
65 	for ( int i = 0; i < 128; i++ )
66 	{
67 		int n = cycle_table [i];
68 		m.cycle_table [i * 2 + 0] = n >> 4;
69 		m.cycle_table [i * 2 + 1] = n & 0x0F;
70 	}
71 
72 	#if SPC_LESS_ACCURATE
73 		memcpy( reg_times, reg_times_, sizeof reg_times );
74 	#endif
75 
76 	reset();
77 	return 0;
78 }
79 
init_rom(uint8_t const in[rom_size])80 void Snes_Spc::init_rom( uint8_t const in [rom_size] )
81 {
82 	memcpy( m.rom, in, sizeof m.rom );
83 }
84 
set_tempo(int t)85 void Snes_Spc::set_tempo( int t )
86 {
87 	m.tempo = t;
88 	int const timer2_shift = 4; // 64 kHz
89 	int const other_shift  = 3; //  8 kHz
90 
91 	#if SPC_DISABLE_TEMPO
92 		m.timers [2].prescaler = timer2_shift;
93 		m.timers [1].prescaler = timer2_shift + other_shift;
94 		m.timers [0].prescaler = timer2_shift + other_shift;
95 	#else
96 		if ( !t )
97 			t = 1;
98 		int const timer2_rate  = 1 << timer2_shift;
99 		int rate = (timer2_rate * tempo_unit + (t >> 1)) / t;
100 		if ( rate < timer2_rate / 4 )
101 			rate = timer2_rate / 4; // max 4x tempo
102 		m.timers [2].prescaler = rate;
103 		m.timers [1].prescaler = rate << other_shift;
104 		m.timers [0].prescaler = rate << other_shift;
105 	#endif
106 }
107 
108 // Timer registers have been loaded. Applies these to the timers. Does not
109 // reset timer prescalers or dividers.
timers_loaded()110 void Snes_Spc::timers_loaded()
111 {
112 	int i;
113 	for ( i = 0; i < timer_count; i++ )
114 	{
115 		Timer* t = &m.timers [i];
116 		t->period  = IF_0_THEN_256( REGS [r_t0target + i] );
117 		t->enabled = REGS [r_control] >> i & 1;
118 		t->counter = REGS_IN [r_t0out + i] & 0x0F;
119 	}
120 
121 	set_tempo( m.tempo );
122 }
123 
124 // Loads registers from unified 16-byte format
load_regs(uint8_t const in[reg_count])125 void Snes_Spc::load_regs( uint8_t const in [reg_count] )
126 {
127 	memcpy( REGS, in, reg_count );
128 	memcpy( REGS_IN, REGS, reg_count );
129 
130 	// These always read back as 0
131 	REGS_IN [r_test    ] = 0;
132 	REGS_IN [r_control ] = 0;
133 	REGS_IN [r_t0target] = 0;
134 	REGS_IN [r_t1target] = 0;
135 	REGS_IN [r_t2target] = 0;
136 }
137 
138 // RAM was just loaded from SPC, with $F0-$FF containing SMP registers
139 // and timer counts. Copies these to proper registers.
ram_loaded()140 void Snes_Spc::ram_loaded()
141 {
142 	m.rom_enabled = 0;
143 	load_regs( &RAM [0xF0] );
144 
145 	// Put STOP instruction around memory to catch PC underflow/overflow
146 	memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
147 	memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 );
148 }
149 
150 // Registers were just loaded. Applies these new values.
regs_loaded()151 void Snes_Spc::regs_loaded()
152 {
153 	enable_rom( REGS [r_control] & 0x80 );
154 	timers_loaded();
155 }
156 
reset_time_regs()157 void Snes_Spc::reset_time_regs()
158 {
159 	m.cpu_error     = 0;
160 	m.echo_accessed = 0;
161 	m.spc_time      = 0;
162 	m.dsp_time      = 0;
163 	#if SPC_LESS_ACCURATE
164 		m.dsp_time = clocks_per_sample + 1;
165 	#endif
166 
167 	for ( int i = 0; i < timer_count; i++ )
168 	{
169 		Timer* t = &m.timers [i];
170 		t->next_time = 1;
171 		t->divider   = 0;
172 	}
173 
174 	regs_loaded();
175 
176 	m.extra_clocks = 0;
177 	reset_buf();
178 }
179 
reset_common(int timer_counter_init)180 void Snes_Spc::reset_common( int timer_counter_init )
181 {
182 	int i;
183 	for ( i = 0; i < timer_count; i++ )
184 		REGS_IN [r_t0out + i] = timer_counter_init;
185 
186 	// Run IPL ROM
187 	memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
188 	m.cpu_regs.pc = rom_addr;
189 
190 	REGS [r_test   ] = 0x0A;
191 	REGS [r_control] = 0xB0; // ROM enabled, clear ports
192 	for ( i = 0; i < port_count; i++ )
193 		REGS_IN [r_cpuio0 + i] = 0;
194 
195 	reset_time_regs();
196 }
197 
soft_reset()198 void Snes_Spc::soft_reset()
199 {
200 	reset_common( 0 );
201 	dsp.soft_reset();
202 }
203 
reset()204 void Snes_Spc::reset()
205 {
206 	memset( RAM, 0xFF, 0x10000 );
207 	ram_loaded();
208 	reset_common( 0x0F );
209 	dsp.reset();
210 }
211 
212 char const Snes_Spc::signature [signature_size + 1] =
213 		"SNES-SPC700 Sound File Data v0.30\x1A\x1A";
214 
load_spc(void const * data,long size)215 blargg_err_t Snes_Spc::load_spc( void const* data, long size )
216 {
217 	spc_file_t const* const spc = (spc_file_t const*) data;
218 
219 	// be sure compiler didn't insert any padding into fle_t
220 	assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 );
221 
222 	// Check signature and file size
223 	if ( size < signature_size || memcmp( spc, signature, 27 ) )
224 		return "Not an SPC file";
225 
226 	if ( size < spc_min_file_size )
227 		return "Corrupt SPC file";
228 
229 	// CPU registers
230 	m.cpu_regs.pc  = spc->pch * 0x100 + spc->pcl;
231 	m.cpu_regs.a   = spc->a;
232 	m.cpu_regs.x   = spc->x;
233 	m.cpu_regs.y   = spc->y;
234 	m.cpu_regs.psw = spc->psw;
235 	m.cpu_regs.sp  = spc->sp;
236 
237 	// RAM and registers
238 	memcpy( RAM, spc->ram, 0x10000 );
239 	ram_loaded();
240 
241 	// DSP registers
242 	dsp.load( spc->dsp );
243 
244 	reset_time_regs();
245 
246 	return 0;
247 }
248 
clear_echo()249 void Snes_Spc::clear_echo()
250 {
251 	if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) )
252 	{
253 		int addr = 0x100 * dsp.read( Spc_Dsp::r_esa );
254 		int end  = addr + 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F);
255 		if ( end > 0x10000 )
256 			end = 0x10000;
257 		memset( &RAM [addr], 0xFF, end - addr );
258 	}
259 }
260 
261 
262 //// Sample output
263 
reset_buf()264 void Snes_Spc::reset_buf()
265 {
266 	// Start with half extra buffer of silence
267 	sample_t* out = m.extra_buf;
268 	while ( out < &m.extra_buf [extra_size / 2] )
269 		*out++ = 0;
270 
271 	m.extra_pos = out;
272 	m.buf_begin = 0;
273 
274 	dsp.set_output( 0, 0 );
275 }
276 
set_output(sample_t * out,int size)277 void Snes_Spc::set_output( sample_t* out, int size )
278 {
279 	require( (size & 1) == 0 ); // size must be even
280 
281 	m.extra_clocks &= clocks_per_sample - 1;
282 	if ( out )
283 	{
284 		sample_t const* out_end = out + size;
285 		m.buf_begin = out;
286 		m.buf_end   = out_end;
287 
288 		// Copy extra to output
289 		sample_t const* in = m.extra_buf;
290 		while ( in < m.extra_pos && out < out_end )
291 			*out++ = *in++;
292 
293 		// Handle output being full already
294 		if ( out >= out_end )
295 		{
296 			// Have DSP write to remaining extra space
297 			out     = dsp.extra();
298 			out_end = &dsp.extra() [extra_size];
299 
300 			// Copy any remaining extra samples as if DSP wrote them
301 			while ( in < m.extra_pos )
302 				*out++ = *in++;
303 			assert( out <= out_end );
304 		}
305 
306 		dsp.set_output( out, out_end - out );
307 	}
308 	else
309 	{
310 		reset_buf();
311 	}
312 }
313 
save_extra()314 void Snes_Spc::save_extra()
315 {
316 	// Get end pointers
317 	sample_t const* main_end = m.buf_end;     // end of data written to buf
318 	sample_t const* dsp_end  = dsp.out_pos(); // end of data written to dsp.extra()
319 	if ( m.buf_begin <= dsp_end && dsp_end <= main_end )
320 	{
321 		main_end = dsp_end;
322 		dsp_end  = dsp.extra(); // nothing in DSP's extra
323 	}
324 
325 	// Copy any extra samples at these ends into extra_buf
326 	sample_t* out = m.extra_buf;
327 	sample_t const* in;
328 	for ( in = m.buf_begin + sample_count(); in < main_end; in++ )
329 		*out++ = *in;
330 	for ( in = dsp.extra(); in < dsp_end ; in++ )
331 		*out++ = *in;
332 
333 	m.extra_pos = out;
334 	assert( out <= &m.extra_buf [extra_size] );
335 }
336 
play(int count,sample_t * out)337 blargg_err_t Snes_Spc::play( int count, sample_t* out )
338 {
339 	require( (count & 1) == 0 ); // must be even
340 	if ( count )
341 	{
342 		set_output( out, count );
343 		end_frame( count * (clocks_per_sample / 2) );
344 	}
345 
346 	const char* err = m.cpu_error;
347 	m.cpu_error = 0;
348 	return err;
349 }
350 
skip(int count)351 blargg_err_t Snes_Spc::skip( int count )
352 {
353 	#if SPC_LESS_ACCURATE
354 	if ( count > 2 * sample_rate * 2 )
355 	{
356 		set_output( 0, 0 );
357 
358 		// Skip a multiple of 4 samples
359 		time_t end = count;
360 		count = (count & 3) + 1 * sample_rate * 2;
361 		end = (end - count) * (clocks_per_sample / 2);
362 
363 		m.skipped_kon  = 0;
364 		m.skipped_koff = 0;
365 
366 		// Preserve DSP and timer synchronization
367 		// TODO: verify that this really preserves it
368 		int old_dsp_time = m.dsp_time + m.spc_time;
369 		m.dsp_time = end - m.spc_time + skipping_time;
370 		end_frame( end );
371 		m.dsp_time = m.dsp_time - skipping_time + old_dsp_time;
372 
373 		dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon );
374 		dsp.write( Spc_Dsp::r_kon , m.skipped_kon );
375 		clear_echo();
376 	}
377 	#endif
378 
379 	return play( count, 0 );
380 }
381