comparison snes_spc/SNES_SPC_misc.cpp @ 0:e38dacceb958

initial import
author Robert McIntyre <rlm@mit.edu>
date Fri, 21 Oct 2011 05:53:11 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:e38dacceb958
1 // SPC emulation support: init, sample buffering, reset, SPC loading
2
3 // snes_spc 0.9.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
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
80 void SNES_SPC::init_rom( uint8_t const in [rom_size] )
81 {
82 memcpy( m.rom, in, sizeof m.rom );
83 }
84
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.
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
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.
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.
151 void SNES_SPC::regs_loaded()
152 {
153 enable_rom( REGS [r_control] & 0x80 );
154 timers_loaded();
155 }
156
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
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
198 void SNES_SPC::soft_reset()
199 {
200 reset_common( 0 );
201 dsp.soft_reset();
202 }
203
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
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
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
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
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
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
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
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 }