summaryrefslogtreecommitdiff
path: root/apps/codecs/spc.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/spc.c')
-rw-r--r--apps/codecs/spc.c567
1 files changed, 12 insertions, 555 deletions
diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c
index 6c791b46a1..f2890cd4a4 100644
--- a/apps/codecs/spc.c
+++ b/apps/codecs/spc.c
@@ -5,6 +5,7 @@
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$
8 * 9 *
9 * Copyright (C) 2006-2007 Adam Gashlin (hcs) 10 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
10 * Copyright (C) 2004-2007 Shay Green (blargg) 11 * Copyright (C) 2004-2007 Shay Green (blargg)
@@ -21,555 +22,11 @@
21/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ 22/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
22/* DSP Based on Brad Martin's OpenSPC DSP emulator */ 23/* DSP Based on Brad Martin's OpenSPC DSP emulator */
23/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */ 24/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
24
25#include "codeclib.h" 25#include "codeclib.h"
26#include "inttypes.h" 26#include "spc/spc_codec.h"
27#include "system.h"
28
29/* rather than comment out asserts, just define NDEBUG */
30#define NDEBUG
31#include <assert.h>
32#undef check
33#define check assert
34
35CODEC_HEADER
36
37#ifdef CPU_ARM
38 #undef ICODE_ATTR
39 #define ICODE_ATTR
40
41 #undef IDATA_ATTR
42 #define IDATA_ATTR
43#endif
44
45/* TGB is the only target fast enough for gaussian and realtime BRR decode */
46/* echo is almost fast enough but not quite */
47#ifndef TOSHIBA_GIGABEAT_F
48 /* Cache BRR waves */
49 #define SPC_BRRCACHE 1
50
51 /* Disable gaussian interpolation */
52 #define SPC_NOINTERP 1
53
54#ifndef CPU_COLDFIRE
55 /* Disable echo processing */
56 #define SPC_NOECHO 1
57#else
58 /* Enable echo processing */
59 #define SPC_NOECHO 0
60#endif
61#else
62 /* Don't cache BRR waves */
63 #define SPC_BRRCACHE 0
64
65 /* Allow gaussian interpolation */
66 #define SPC_NOINTERP 0
67
68 /* Allow echo processing */
69 #define SPC_NOECHO 0
70#endif
71
72/* Samples per channel per iteration */
73#ifdef CPU_COLDFIRE
74#define WAV_CHUNK_SIZE 1024
75#else
76#define WAV_CHUNK_SIZE 2048
77#endif
78
79/* simple profiling with USEC_TIMER */
80/*#define SPC_PROFILE*/
81
82#include "spc/spc_profiler.h" 27#include "spc/spc_profiler.h"
83 28
84#define THIS struct Spc_Emu* const this 29CODEC_HEADER
85
86/**************** Little-endian handling ****************/
87
88static inline unsigned get_le16( void const* p )
89{
90 return ((unsigned char const*) p) [1] * 0x100u +
91 ((unsigned char const*) p) [0];
92}
93
94static inline int get_le16s( void const* p )
95{
96 return ((signed char const*) p) [1] * 0x100 +
97 ((unsigned char const*) p) [0];
98}
99
100static inline void set_le16( void* p, unsigned n )
101{
102 ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
103 ((unsigned char*) p) [0] = (unsigned char) n;
104}
105
106#define GET_LE16( addr ) get_le16( addr )
107#define SET_LE16( addr, data ) set_le16( addr, data )
108#define INT16A( addr ) (*(uint16_t*) (addr))
109#define INT16SA( addr ) (*(int16_t*) (addr))
110
111#ifdef ROCKBOX_LITTLE_ENDIAN
112 #define GET_LE16A( addr ) (*(uint16_t*) (addr))
113 #define GET_LE16SA( addr ) (*( int16_t*) (addr))
114 #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data))
115#else
116 #define GET_LE16A( addr ) get_le16 ( addr )
117 #define GET_LE16SA( addr ) get_le16s( addr )
118 #define SET_LE16A( addr, data ) set_le16 ( addr, data )
119#endif
120
121static struct
122{
123 union {
124 uint8_t padding1 [0x100];
125 uint16_t align;
126 } padding1 [1];
127 uint8_t ram [0x10000];
128 uint8_t padding2 [0x100];
129} ram;
130
131#include "spc/Spc_Dsp.h"
132
133#undef RAM
134#define RAM ram.ram
135
136/**************** Timers ****************/
137
138enum { timer_count = 3 };
139
140struct Timer
141{
142 long next_tick;
143 int period;
144 int count;
145 int shift;
146 int enabled;
147 int counter;
148};
149
150static void Timer_run_( struct Timer* t, long time ) ICODE_ATTR;
151static void Timer_run_( struct Timer* t, long time )
152{
153 /* when disabled, next_tick should always be in the future */
154 assert( t->enabled );
155
156 int elapsed = ((time - t->next_tick) >> t->shift) + 1;
157 t->next_tick += elapsed << t->shift;
158
159 elapsed += t->count;
160 if ( elapsed >= t->period ) /* avoid unnecessary division */
161 {
162 int n = elapsed / t->period;
163 elapsed -= n * t->period;
164 t->counter = (t->counter + n) & 15;
165 }
166 t->count = elapsed;
167}
168
169static inline void Timer_run( struct Timer* t, long time )
170{
171 if ( time >= t->next_tick )
172 Timer_run_( t, time );
173}
174
175/**************** SPC emulator ****************/
176/* 1.024 MHz clock / 32000 samples per second */
177enum { clocks_per_sample = 32 };
178
179enum { extra_clocks = clocks_per_sample / 2 };
180
181/* using this disables timer (since this will always be in the future) */
182enum { timer_disabled_time = 127 };
183
184enum { rom_size = 64 };
185enum { rom_addr = 0xFFC0 };
186
187struct cpu_regs_t
188{
189 long pc; /* more than 16 bits to allow overflow detection */
190 uint8_t a;
191 uint8_t x;
192 uint8_t y;
193 uint8_t status;
194 uint8_t sp;
195};
196
197
198struct Spc_Emu
199{
200 uint8_t cycle_table [0x100];
201 struct cpu_regs_t r;
202
203 int32_t* sample_buf;
204 long next_dsp;
205 int rom_enabled;
206 int extra_cycles;
207
208 struct Timer timer [timer_count];
209
210 /* large objects at end */
211 struct Spc_Dsp dsp;
212 uint8_t extra_ram [rom_size];
213 uint8_t boot_rom [rom_size];
214};
215
216static void SPC_enable_rom( THIS, int enable )
217{
218 if ( this->rom_enabled != enable )
219 {
220 this->rom_enabled = enable;
221 memcpy( RAM + rom_addr, (enable ? this->boot_rom : this->extra_ram), rom_size );
222 /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
223 }
224}
225
226static void SPC_Init( THIS )
227{
228 this->timer [0].shift = 4 + 3; /* 8 kHz */
229 this->timer [1].shift = 4 + 3; /* 8 kHz */
230 this->timer [2].shift = 4; /* 8 kHz */
231
232 /* Put STOP instruction around memory to catch PC underflow/overflow. */
233 memset( ram.padding1, 0xFF, sizeof ram.padding1 );
234 memset( ram.padding2, 0xFF, sizeof ram.padding2 );
235
236 /* A few tracks read from the last four bytes of IPL ROM */
237 this->boot_rom [sizeof this->boot_rom - 2] = 0xC0;
238 this->boot_rom [sizeof this->boot_rom - 1] = 0xFF;
239 memset( this->boot_rom, 0, sizeof this->boot_rom - 2 );
240}
241
242static void SPC_load_state( THIS, struct cpu_regs_t const* cpu_state,
243 const void* new_ram, const void* dsp_state )
244{
245 memcpy(&(this->r),cpu_state,sizeof this->r);
246
247 /* ram */
248 memcpy( RAM, new_ram, sizeof RAM );
249 memcpy( this->extra_ram, RAM + rom_addr, sizeof this->extra_ram );
250
251 /* boot rom (have to force enable_rom() to update it) */
252 this->rom_enabled = !(RAM [0xF1] & 0x80);
253 SPC_enable_rom( this, !this->rom_enabled );
254
255 /* dsp */
256 /* some SPCs rely on DSP immediately generating one sample */
257 this->extra_cycles = 32;
258 DSP_reset( &this->dsp );
259 int i;
260 for ( i = 0; i < register_count; i++ )
261 DSP_write( &this->dsp, i, ((uint8_t const*) dsp_state) [i] );
262
263 /* timers */
264 for ( i = 0; i < timer_count; i++ )
265 {
266 struct Timer* t = &this->timer [i];
267
268 t->next_tick = -extra_clocks;
269 t->enabled = (RAM [0xF1] >> i) & 1;
270 if ( !t->enabled )
271 t->next_tick = timer_disabled_time;
272 t->count = 0;
273 t->counter = RAM [0xFD + i] & 15;
274
275 int p = RAM [0xFA + i];
276 if ( !p )
277 p = 0x100;
278 t->period = p;
279 }
280
281 /* Handle registers which already give 0 when read by
282 setting RAM and not changing it.
283 Put STOP instruction in registers which can be read,
284 to catch attempted execution. */
285 RAM [0xF0] = 0;
286 RAM [0xF1] = 0;
287 RAM [0xF3] = 0xFF;
288 RAM [0xFA] = 0;
289 RAM [0xFB] = 0;
290 RAM [0xFC] = 0;
291 RAM [0xFD] = 0xFF;
292 RAM [0xFE] = 0xFF;
293 RAM [0xFF] = 0xFF;
294}
295
296static void clear_echo( THIS )
297{
298 if ( !(DSP_read( &this->dsp, 0x6C ) & 0x20) )
299 {
300 unsigned addr = 0x100 * DSP_read( &this->dsp, 0x6D );
301 size_t size = 0x800 * DSP_read( &this->dsp, 0x7D );
302 size_t max_size = sizeof RAM - addr;
303 if ( size > max_size )
304 size = sizeof RAM - addr;
305 memset( RAM + addr, 0xFF, size );
306 }
307}
308
309enum { spc_file_size = 0x10180 };
310
311struct spc_file_t
312{
313 char signature [27];
314 char unused [10];
315 uint8_t pc [2];
316 uint8_t a;
317 uint8_t x;
318 uint8_t y;
319 uint8_t status;
320 uint8_t sp;
321 char unused2 [212];
322 uint8_t ram [0x10000];
323 uint8_t dsp [128];
324 uint8_t ipl_rom [128];
325};
326
327static int SPC_load_spc( THIS, const void* data, long size )
328{
329 struct spc_file_t const* spc = (struct spc_file_t const*) data;
330 struct cpu_regs_t regs;
331
332 if ( size < spc_file_size )
333 return -1;
334
335 if ( memcmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
336 return -1;
337
338 regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
339 regs.a = spc->a;
340 regs.x = spc->x;
341 regs.y = spc->y;
342 regs.status = spc->status;
343 regs.sp = spc->sp;
344
345 if ( (unsigned long) size >= sizeof *spc )
346 memcpy( this->boot_rom, spc->ipl_rom, sizeof this->boot_rom );
347
348 SPC_load_state( this, &regs, spc->ram, spc->dsp );
349
350 clear_echo(this);
351
352 return 0;
353}
354
355/**************** DSP interaction ****************/
356
357static void SPC_run_dsp_( THIS, long time ) ICODE_ATTR;
358static void SPC_run_dsp_( THIS, long time )
359{
360 /* divide by clocks_per_sample */
361 int count = ((time - this->next_dsp) >> 5) + 1;
362 int32_t* buf = this->sample_buf;
363 this->sample_buf = buf + count;
364 this->next_dsp += count * clocks_per_sample;
365 DSP_run( &this->dsp, count, buf );
366}
367
368static inline void SPC_run_dsp( THIS, long time )
369{
370 if ( time >= this->next_dsp )
371 SPC_run_dsp_( this, time );
372}
373
374static int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR;
375static int SPC_read( THIS, unsigned addr, long const time )
376{
377 int result = RAM [addr];
378
379 if ( ((unsigned) (addr - 0xF0)) < 0x10 )
380 {
381 assert( 0xF0 <= addr && addr <= 0xFF );
382
383 /* counters */
384 int i = addr - 0xFD;
385 if ( i >= 0 )
386 {
387 struct Timer* t = &this->timer [i];
388 Timer_run( t, time );
389 result = t->counter;
390 t->counter = 0;
391 }
392 /* dsp */
393 else if ( addr == 0xF3 )
394 {
395 SPC_run_dsp( this, time );
396 result = DSP_read( &this->dsp, RAM [0xF2] & 0x7F );
397 }
398 }
399 return result;
400}
401
402static void SPC_write( THIS, unsigned addr, int data, long const time )
403 ICODE_ATTR;
404static void SPC_write( THIS, unsigned addr, int data, long const time )
405{
406 /* first page is very common */
407 if ( addr < 0xF0 )
408 {
409 RAM [addr] = (uint8_t) data;
410 }
411 else switch ( addr )
412 {
413 /* RAM */
414 default:
415 if ( addr < rom_addr )
416 {
417 RAM [addr] = (uint8_t) data;
418 }
419 else
420 {
421 this->extra_ram [addr - rom_addr] = (uint8_t) data;
422 if ( !this->rom_enabled )
423 RAM [addr] = (uint8_t) data;
424 }
425 break;
426
427 /* DSP */
428 /*case 0xF2:*/ /* mapped to RAM */
429 case 0xF3: {
430 SPC_run_dsp( this, time );
431 int reg = RAM [0xF2];
432 if ( reg < register_count ) {
433 DSP_write( &this->dsp, reg, data );
434 }
435 else {
436 /*dprintf( "DSP write to $%02X\n", (int) reg ); */
437 }
438 break;
439 }
440
441 case 0xF0: /* Test register */
442 /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
443 break;
444
445 /* Config */
446 case 0xF1:
447 {
448 int i;
449 /* timers */
450 for ( i = 0; i < timer_count; i++ )
451 {
452 struct Timer * t = this->timer+i;
453 if ( !(data & (1 << i)) )
454 {
455 t->enabled = 0;
456 t->next_tick = timer_disabled_time;
457 }
458 else if ( !t->enabled )
459 {
460 /* just enabled */
461 t->enabled = 1;
462 t->counter = 0;
463 t->count = 0;
464 t->next_tick = time;
465 }
466 }
467
468 /* port clears */
469 if ( data & 0x10 )
470 {
471 RAM [0xF4] = 0;
472 RAM [0xF5] = 0;
473 }
474 if ( data & 0x20 )
475 {
476 RAM [0xF6] = 0;
477 RAM [0xF7] = 0;
478 }
479
480 SPC_enable_rom( this, (data & 0x80) != 0 );
481 break;
482 }
483
484 /* Ports */
485 case 0xF4:
486 case 0xF5:
487 case 0xF6:
488 case 0xF7:
489 /* to do: handle output ports */
490 break;
491
492 /* verified on SNES that these are read/write (RAM) */
493 /*case 0xF8: */
494 /*case 0xF9: */
495
496 /* Timers */
497 case 0xFA:
498 case 0xFB:
499 case 0xFC: {
500 int i = addr - 0xFA;
501 struct Timer* t = &this->timer [i];
502 if ( (t->period & 0xFF) != data )
503 {
504 Timer_run( t, time );
505 this->timer[i].period = data ? data : 0x100;
506 }
507 break;
508 }
509
510 /* Counters (cleared on write) */
511 case 0xFD:
512 case 0xFE:
513 case 0xFF:
514 /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
515 this->timer [addr - 0xFD].counter = 0;
516 break;
517 }
518}
519
520#include "spc/Spc_Cpu.h"
521
522/**************** Sample generation ****************/
523
524static int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR;
525static int SPC_play( THIS, long count, int32_t* out )
526{
527 int i;
528 assert( count % 2 == 0 ); /* output is always in pairs of samples */
529
530 long start_time = -(count >> 1) * clocks_per_sample - extra_clocks;
531
532 /* DSP output is made on-the-fly when DSP registers are read or written */
533 this->sample_buf = out;
534 this->next_dsp = start_time + clocks_per_sample;
535
536 /* Localize timer next_tick times and run them to the present to prevent
537 a running but ignored timer's next_tick from getting too far behind
538 and overflowing. */
539 for ( i = 0; i < timer_count; i++ )
540 {
541 struct Timer* t = &this->timer [i];
542 if ( t->enabled )
543 {
544 t->next_tick += start_time + extra_clocks;
545 Timer_run( t, start_time );
546 }
547 }
548
549 /* Run from start_time to 0, pre-advancing by extra cycles from last run */
550 this->extra_cycles = CPU_run( this, start_time + this->extra_cycles ) +
551 extra_clocks;
552 if ( this->extra_cycles < 0 )
553 {
554 /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
555 (int) CPU_read( r.pc ), (unsigned) r.pc ); */
556
557 return -1;
558 }
559
560 /* Catch DSP up to present */
561#if 0
562 ENTER_TIMER(cpu);
563#endif
564 SPC_run_dsp( this, -extra_clocks );
565#if 0
566 EXIT_TIMER(cpu);
567#endif
568 assert( this->next_dsp == clocks_per_sample - extra_clocks );
569 assert( this->sample_buf - out == count );
570
571 return 0;
572}
573 30
574/**************** ID666 parsing ****************/ 31/**************** ID666 parsing ****************/
575 32
@@ -733,15 +190,15 @@ static int32_t samples[WAV_CHUNK_SIZE*2] IBSS_ATTR;
733 190
734static struct Spc_Emu spc_emu IDATA_ATTR; 191static struct Spc_Emu spc_emu IDATA_ATTR;
735 192
736enum {sample_rate = 32000}; 193enum {SAMPLE_RATE = 32000};
737 194
738/* The main decoder loop */ 195/* The main decoder loop */
739static int play_track( void ) 196static int play_track( void )
740{ 197{
741 int sampleswritten=0; 198 int sampleswritten=0;
742 199
743 unsigned long fadestartsample = ID666.length*(long long) sample_rate/1000; 200 unsigned long fadestartsample = ID666.length*(long long) SAMPLE_RATE/1000;
744 unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) sample_rate/1000; 201 unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000;
745 int fadedec = 0; 202 int fadedec = 0;
746 int fadevol = 0x7fffffffl; 203 int fadevol = 0x7fffffffl;
747 204
@@ -758,7 +215,7 @@ static int play_track( void )
758 } 215 }
759 216
760 if (ci->seek_time) { 217 if (ci->seek_time) {
761 int curtime = sampleswritten*1000LL/sample_rate; 218 int curtime = sampleswritten*1000LL/SAMPLE_RATE;
762 DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime); 219 DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime);
763 if (ci->seek_time < curtime) { 220 if (ci->seek_time < curtime) {
764 DEBUGF("seek backwards = reset\n"); 221 DEBUGF("seek backwards = reset\n");
@@ -778,7 +235,7 @@ static int play_track( void )
778 235
779 /* is track timed? */ 236 /* is track timed? */
780 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) { 237 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) {
781 unsigned long curtime = sampleswritten*1000LL/sample_rate; 238 unsigned long curtime = sampleswritten*1000LL/SAMPLE_RATE;
782 unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE); 239 unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE);
783 240
784 /* fade? */ 241 /* fade? */
@@ -811,7 +268,7 @@ static int play_track( void )
811 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE); 268 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
812 269
813 if (ci->global_settings->repeat_mode!=REPEAT_ONE) 270 if (ci->global_settings->repeat_mode!=REPEAT_ONE)
814 ci->set_elapsed(sampleswritten*1000LL/sample_rate); 271 ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE);
815 else 272 else
816 ci->set_elapsed(0); 273 ci->set_elapsed(0);
817 } 274 }
@@ -824,11 +281,11 @@ static int play_track( void )
824/* this is the codec entry point */ 281/* this is the codec entry point */
825enum codec_status codec_main(void) 282enum codec_status codec_main(void)
826{ 283{
827 memcpy( spc_emu.cycle_table, cycle_table, sizeof cycle_table );
828
829#ifdef CPU_COLDFIRE 284#ifdef CPU_COLDFIRE
285 /* signed integer mode with saturation */
830 coldfire_set_macsr(EMAC_SATURATE); 286 coldfire_set_macsr(EMAC_SATURATE);
831#endif 287#endif
288 CPU_Init(&spc_emu);
832 289
833 do 290 do
834 { 291 {
@@ -839,7 +296,7 @@ enum codec_status codec_main(void)
839 DEBUGF("SPC: after init\n"); 296 DEBUGF("SPC: after init\n");
840 297
841 ci->configure(DSP_SET_SAMPLE_DEPTH, 24); 298 ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
842 ci->configure(DSP_SET_FREQUENCY, sample_rate); 299 ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
843 ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); 300 ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
844 301
845 /* wait for track info to load */ 302 /* wait for track info to load */