summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Gashlin <agashlin@gmail.com>2007-02-14 03:34:55 +0000
committerAdam Gashlin <agashlin@gmail.com>2007-02-14 03:34:55 +0000
commitb73960d3b9e4bd84678202e84fabee7561b3c1ab (patch)
tree2cb5821f6a2a34c1ce6c05593cfe30dd9b12e824
parent9b9539c8d3349975127ff725c313d3b888f89ab6 (diff)
downloadrockbox-b73960d3b9e4bd84678202e84fabee7561b3c1ab.tar.gz
rockbox-b73960d3b9e4bd84678202e84fabee7561b3c1ab.zip
Adding SPC codec (FS #6542)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12298 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/codecs/Makefile1
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/spc.c845
-rw-r--r--apps/codecs/spc/Spc_Cpu.h1037
-rw-r--r--apps/codecs/spc/Spc_Dsp.h1107
-rw-r--r--apps/codecs/spc/spc_profiler.h89
-rw-r--r--apps/metadata.c5
-rw-r--r--apps/tree.c1
-rw-r--r--firmware/export/id3.h1
-rw-r--r--firmware/id3.c3
10 files changed, 3089 insertions, 1 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index e555d94f50..65c319bafe 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -52,6 +52,7 @@ $(OBJDIR)/wav.elf : $(OBJDIR)/wav.o
52$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o 52$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o
53$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o 53$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o
54$(OBJDIR)/nsf.elf : $(OBJDIR)/nsf.o 54$(OBJDIR)/nsf.elf : $(OBJDIR)/nsf.o
55$(OBJDIR)/spc.elf : $(OBJDIR)/spc.o
55$(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o 56$(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o
56$(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a 57$(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a
57$(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a 58$(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index b912dd98bf..123a9c0f3f 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -17,6 +17,7 @@ speex.c
17sid.c 17sid.c
18adx.c 18adx.c
19nsf.c 19nsf.c
20spc.c
20#if defined(HAVE_RECORDING) && !defined(SIMULATOR) 21#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
21/* encoders */ 22/* encoders */
22aiff_enc.c 23aiff_enc.c
diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c
new file mode 100644
index 0000000000..2dac1d4e61
--- /dev/null
+++ b/apps/codecs/spc.c
@@ -0,0 +1,845 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
10 * Copyright (C) 2004-2007 Shay Green (blargg)
11 * Copyright (C) 2002 Brad Martin
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21/* 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/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
24
25#include "codeclib.h"
26#include "inttypes.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 /* Disable echo processing */
55 #define SPC_NOECHO 1
56#endif
57
58/* Samples per channel per iteration */
59#ifdef CPU_COLDFIRE
60#define WAV_CHUNK_SIZE 1024
61#else
62#define WAV_CHUNK_SIZE 2048
63#endif
64
65/* simple profiling with USEC_TIMER */
66/*#define SPC_PROFILE*/
67
68#include "spc/spc_profiler.h"
69
70#define THIS struct Spc_Emu* const this
71
72/**************** Little-endian handling ****************/
73
74static inline unsigned get_le16( void const* p )
75{
76 return ((unsigned char const*) p) [1] * 0x100u +
77 ((unsigned char const*) p) [0];
78}
79
80static inline int get_le16s( void const* p )
81{
82 return ((signed char const*) p) [1] * 0x100 +
83 ((unsigned char const*) p) [0];
84}
85
86static inline void set_le16( void* p, unsigned n )
87{
88 ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
89 ((unsigned char*) p) [0] = (unsigned char) n;
90}
91
92#define GET_LE16( addr ) get_le16( addr )
93#define SET_LE16( addr, data ) set_le16( addr, data )
94
95#if ROCKBOX_LITTLE_ENDIAN
96 #define GET_LE16A( addr ) (*(uint16_t*) (addr))
97 #define GET_LE16SA( addr ) (*( int16_t*) (addr))
98 #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data))
99#else
100 #define GET_LE16A( addr ) get_le16 ( addr )
101 #define GET_LE16SA( addr ) get_le16s( addr )
102 #define SET_LE16A( addr, data ) set_le16 ( addr, data )
103#endif
104
105static struct
106{
107 union {
108 uint8_t padding1 [0x100];
109 uint16_t align;
110 } padding1 [1];
111 uint8_t ram [0x10000];
112 uint8_t padding2 [0x100];
113} ram;
114
115#include "spc/Spc_Dsp.h"
116
117#undef RAM
118#define RAM ram.ram
119
120/**************** Timers ****************/
121
122enum { timer_count = 3 };
123
124struct Timer
125{
126 long next_tick;
127 int period;
128 int count;
129 int shift;
130 int enabled;
131 int counter;
132};
133
134static void Timer_run_( struct Timer* t, long time ) ICODE_ATTR;
135static void Timer_run_( struct Timer* t, long time )
136{
137 /* when disabled, next_tick should always be in the future */
138 assert( t->enabled );
139
140 int elapsed = ((time - t->next_tick) >> t->shift) + 1;
141 t->next_tick += elapsed << t->shift;
142
143 elapsed += t->count;
144 if ( elapsed >= t->period ) /* avoid unnecessary division */
145 {
146 int n = elapsed / t->period;
147 elapsed -= n * t->period;
148 t->counter = (t->counter + n) & 15;
149 }
150 t->count = elapsed;
151}
152
153static inline void Timer_run( struct Timer* t, long time )
154{
155 if ( time >= t->next_tick )
156 Timer_run_( t, time );
157}
158
159/**************** SPC emulator ****************/
160/* 1.024 MHz clock / 32000 samples per second */
161enum { clocks_per_sample = 32 };
162
163enum { extra_clocks = clocks_per_sample / 2 };
164
165/* using this disables timer (since this will always be in the future) */
166enum { timer_disabled_time = 127 };
167
168enum { rom_size = 64 };
169enum { rom_addr = 0xFFC0 };
170
171struct cpu_regs_t
172{
173 long pc; /* more than 16 bits to allow overflow detection */
174 uint8_t a;
175 uint8_t x;
176 uint8_t y;
177 uint8_t status;
178 uint8_t sp;
179};
180
181
182struct Spc_Emu
183{
184 uint8_t cycle_table [0x100];
185 struct cpu_regs_t r;
186
187 int32_t* sample_buf;
188 long next_dsp;
189 int rom_enabled;
190 int extra_cycles;
191
192 struct Timer timer [timer_count];
193
194 /* large objects at end */
195 struct Spc_Dsp dsp;
196 uint8_t extra_ram [rom_size];
197 uint8_t boot_rom [rom_size];
198};
199
200static void SPC_enable_rom( THIS, int enable )
201{
202 if ( this->rom_enabled != enable )
203 {
204 this->rom_enabled = enable;
205 memcpy( RAM + rom_addr, (enable ? this->boot_rom : this->extra_ram), rom_size );
206 /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
207 }
208}
209
210static void SPC_Init( THIS )
211{
212 this->timer [0].shift = 4 + 3; /* 8 kHz */
213 this->timer [1].shift = 4 + 3; /* 8 kHz */
214 this->timer [2].shift = 4; /* 8 kHz */
215
216 /* Put STOP instruction around memory to catch PC underflow/overflow. */
217 memset( ram.padding1, 0xFF, sizeof ram.padding1 );
218 memset( ram.padding2, 0xFF, sizeof ram.padding2 );
219
220 /* A few tracks read from the last four bytes of IPL ROM */
221 this->boot_rom [sizeof this->boot_rom - 2] = 0xC0;
222 this->boot_rom [sizeof this->boot_rom - 1] = 0xFF;
223 memset( this->boot_rom, 0, sizeof this->boot_rom - 2 );
224}
225
226static void SPC_load_state( THIS, struct cpu_regs_t const* cpu_state,
227 const void* new_ram, const void* dsp_state )
228{
229 memcpy(&(this->r),cpu_state,sizeof this->r);
230
231 /* ram */
232 memcpy( RAM, new_ram, sizeof RAM );
233 memcpy( this->extra_ram, RAM + rom_addr, sizeof this->extra_ram );
234
235 /* boot rom (have to force enable_rom() to update it) */
236 this->rom_enabled = !(RAM [0xF1] & 0x80);
237 SPC_enable_rom( this, !this->rom_enabled );
238
239 /* dsp */
240 /* some SPCs rely on DSP immediately generating one sample */
241 this->extra_cycles = 32;
242 DSP_reset( &this->dsp );
243 int i;
244 for ( i = 0; i < register_count; i++ )
245 DSP_write( &this->dsp, i, ((uint8_t const*) dsp_state) [i] );
246
247 /* timers */
248 for ( i = 0; i < timer_count; i++ )
249 {
250 struct Timer* t = &this->timer [i];
251
252 t->next_tick = -extra_clocks;
253 t->enabled = (RAM [0xF1] >> i) & 1;
254 if ( !t->enabled )
255 t->next_tick = timer_disabled_time;
256 t->count = 0;
257 t->counter = RAM [0xFD + i] & 15;
258
259 int p = RAM [0xFA + i];
260 if ( !p )
261 p = 0x100;
262 t->period = p;
263 }
264
265 /* Handle registers which already give 0 when read by
266 setting RAM and not changing it.
267 Put STOP instruction in registers which can be read,
268 to catch attempted execution. */
269 RAM [0xF0] = 0;
270 RAM [0xF1] = 0;
271 RAM [0xF3] = 0xFF;
272 RAM [0xFA] = 0;
273 RAM [0xFB] = 0;
274 RAM [0xFC] = 0;
275 RAM [0xFD] = 0xFF;
276 RAM [0xFE] = 0xFF;
277 RAM [0xFF] = 0xFF;
278}
279
280static void clear_echo( THIS )
281{
282 if ( !(DSP_read( &this->dsp, 0x6C ) & 0x20) )
283 {
284 unsigned addr = 0x100 * DSP_read( &this->dsp, 0x6D );
285 size_t size = 0x800 * DSP_read( &this->dsp, 0x7D );
286 size_t max_size = sizeof RAM - addr;
287 if ( size > max_size )
288 size = sizeof RAM - addr;
289 memset( RAM + addr, 0xFF, size );
290 }
291}
292
293enum { spc_file_size = 0x10180 };
294
295struct spc_file_t
296{
297 char signature [27];
298 char unused [10];
299 uint8_t pc [2];
300 uint8_t a;
301 uint8_t x;
302 uint8_t y;
303 uint8_t status;
304 uint8_t sp;
305 char unused2 [212];
306 uint8_t ram [0x10000];
307 uint8_t dsp [128];
308 uint8_t ipl_rom [128];
309};
310
311static int SPC_load_spc( THIS, const void* data, long size )
312{
313 struct spc_file_t const* spc = (struct spc_file_t const*) data;
314 struct cpu_regs_t regs;
315
316 if ( size < spc_file_size )
317 return -1;
318
319 if ( memcmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
320 return -1;
321
322 regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
323 regs.a = spc->a;
324 regs.x = spc->x;
325 regs.y = spc->y;
326 regs.status = spc->status;
327 regs.sp = spc->sp;
328
329 if ( (unsigned long) size >= sizeof *spc )
330 memcpy( this->boot_rom, spc->ipl_rom, sizeof this->boot_rom );
331
332 SPC_load_state( this, &regs, spc->ram, spc->dsp );
333
334 clear_echo(this);
335
336 return 0;
337}
338
339/**************** DSP interaction ****************/
340
341static void SPC_run_dsp_( THIS, long time ) ICODE_ATTR;
342static void SPC_run_dsp_( THIS, long time )
343{
344 /* divide by clocks_per_sample */
345 int count = ((time - this->next_dsp) >> 5) + 1;
346 int32_t* buf = this->sample_buf;
347 this->sample_buf = buf + count;
348 this->next_dsp += count * clocks_per_sample;
349 DSP_run( &this->dsp, count, buf );
350}
351
352static inline void SPC_run_dsp( THIS, long time )
353{
354 if ( time >= this->next_dsp )
355 SPC_run_dsp_( this, time );
356}
357
358static int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR;
359static int SPC_read( THIS, unsigned addr, long const time )
360{
361 int result = RAM [addr];
362
363 if ( ((unsigned) (addr - 0xF0)) < 0x10 )
364 {
365 assert( 0xF0 <= addr && addr <= 0xFF );
366
367 /* counters */
368 int i = addr - 0xFD;
369 if ( i >= 0 )
370 {
371 struct Timer* t = &this->timer [i];
372 Timer_run( t, time );
373 result = t->counter;
374 t->counter = 0;
375 }
376 /* dsp */
377 else if ( addr == 0xF3 )
378 {
379 SPC_run_dsp( this, time );
380 result = DSP_read( &this->dsp, RAM [0xF2] & 0x7F );
381 }
382 }
383 return result;
384}
385
386static void SPC_write( THIS, unsigned addr, int data, long const time )
387 ICODE_ATTR;
388static void SPC_write( THIS, unsigned addr, int data, long const time )
389{
390 /* first page is very common */
391 if ( addr < 0xF0 )
392 {
393 RAM [addr] = (uint8_t) data;
394 }
395 else switch ( addr )
396 {
397 /* RAM */
398 default:
399 if ( addr < rom_addr )
400 {
401 RAM [addr] = (uint8_t) data;
402 }
403 else
404 {
405 this->extra_ram [addr - rom_addr] = (uint8_t) data;
406 if ( !this->rom_enabled )
407 RAM [addr] = (uint8_t) data;
408 }
409 break;
410
411 /* DSP */
412 /*case 0xF2:*/ /* mapped to RAM */
413 case 0xF3: {
414 SPC_run_dsp( this, time );
415 int reg = RAM [0xF2];
416 if ( reg < register_count ) {
417 DSP_write( &this->dsp, reg, data );
418 }
419 else {
420 /*dprintf( "DSP write to $%02X\n", (int) reg ); */
421 }
422 break;
423 }
424
425 case 0xF0: /* Test register */
426 /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
427 break;
428
429 /* Config */
430 case 0xF1:
431 {
432 int i;
433 /* timers */
434 for ( i = 0; i < timer_count; i++ )
435 {
436 struct Timer * t = this->timer+i;
437 if ( !(data & (1 << i)) )
438 {
439 t->enabled = 0;
440 t->next_tick = timer_disabled_time;
441 }
442 else if ( !t->enabled )
443 {
444 /* just enabled */
445 t->enabled = 1;
446 t->counter = 0;
447 t->count = 0;
448 t->next_tick = time;
449 }
450 }
451
452 /* port clears */
453 if ( data & 0x10 )
454 {
455 RAM [0xF4] = 0;
456 RAM [0xF5] = 0;
457 }
458 if ( data & 0x20 )
459 {
460 RAM [0xF6] = 0;
461 RAM [0xF7] = 0;
462 }
463
464 SPC_enable_rom( this, (data & 0x80) != 0 );
465 break;
466 }
467
468 /* Ports */
469 case 0xF4:
470 case 0xF5:
471 case 0xF6:
472 case 0xF7:
473 /* to do: handle output ports */
474 break;
475
476 /* verified on SNES that these are read/write (RAM) */
477 /*case 0xF8: */
478 /*case 0xF9: */
479
480 /* Timers */
481 case 0xFA:
482 case 0xFB:
483 case 0xFC: {
484 int i = addr - 0xFA;
485 struct Timer* t = &this->timer [i];
486 if ( (t->period & 0xFF) != data )
487 {
488 Timer_run( t, time );
489 this->timer[i].period = data ? data : 0x100;
490 }
491 break;
492 }
493
494 /* Counters (cleared on write) */
495 case 0xFD:
496 case 0xFE:
497 case 0xFF:
498 /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
499 this->timer [addr - 0xFD].counter = 0;
500 break;
501 }
502}
503
504#include "spc/Spc_Cpu.h"
505
506/**************** Sample generation ****************/
507
508static int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR;
509static int SPC_play( THIS, long count, int32_t* out )
510{
511 int i;
512 assert( count % 2 == 0 ); /* output is always in pairs of samples */
513
514 long start_time = -(count >> 1) * clocks_per_sample - extra_clocks;
515
516 /* DSP output is made on-the-fly when DSP registers are read or written */
517 this->sample_buf = out;
518 this->next_dsp = start_time + clocks_per_sample;
519
520 /* Localize timer next_tick times and run them to the present to prevent
521 a running but ignored timer's next_tick from getting too far behind
522 and overflowing. */
523 for ( i = 0; i < timer_count; i++ )
524 {
525 struct Timer* t = &this->timer [i];
526 if ( t->enabled )
527 {
528 t->next_tick += start_time + extra_clocks;
529 Timer_run( t, start_time );
530 }
531 }
532
533 /* Run from start_time to 0, pre-advancing by extra cycles from last run */
534 this->extra_cycles = CPU_run( this, start_time + this->extra_cycles ) +
535 extra_clocks;
536 if ( this->extra_cycles < 0 )
537 {
538 /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
539 (int) CPU_read( r.pc ), (unsigned) r.pc ); */
540
541 return -1;
542 }
543
544 /* Catch DSP up to present */
545#if 0
546 ENTER_TIMER(cpu);
547#endif
548 SPC_run_dsp( this, -extra_clocks );
549#if 0
550 EXIT_TIMER(cpu);
551#endif
552 assert( this->next_dsp == clocks_per_sample - extra_clocks );
553 assert( this->sample_buf - out == count );
554
555 return 0;
556}
557
558/**************** ID666 parsing ****************/
559
560struct {
561 unsigned char isBinary;
562 char song[32];
563 char game[32];
564 char dumper[16];
565 char comments[32];
566 int day,month,year;
567 unsigned long length;
568 unsigned long fade;
569 char artist[32];
570 unsigned char muted;
571 unsigned char emulator;
572} ID666;
573
574static int LoadID666(unsigned char *buf) {
575 unsigned char *ib=buf;
576 int isbinary = 1;
577 int i;
578
579 memcpy(ID666.song,ib,32);
580 ID666.song[31]=0;
581 ib+=32;
582
583 memcpy(ID666.game,ib,32);
584 ID666.game[31]=0;
585 ib+=32;
586
587 memcpy(ID666.dumper,ib,16);
588 ID666.dumper[15]=0;
589 ib+=16;
590
591 memcpy(ID666.comments,ib,32);
592 ID666.comments[31]=0;
593 ib+=32;
594
595 /* Ok, now comes the fun part. */
596
597 /* Date check */
598 if(ib[2] == '/' && ib[5] == '/' )
599 isbinary = 0;
600
601 /* Reserved bytes check */
602 if(ib[0xD2 - 0x2E - 112] >= '0' &&
603 ib[0xD2 - 0x2E - 112] <= '9' &&
604 ib[0xD3 - 0x2E - 112] == 0x00)
605 isbinary = 0;
606
607 /* is length & fade only digits? */
608 for (i=0;i<8 && (
609 (ib[0xA9 - 0x2E - 112+i]>='0'&&ib[0xA9 - 0x2E - 112+i]<='9') ||
610 ib[0xA9 - 0x2E - 112+i]=='\0');
611 i++);
612 if (i==8) isbinary=0;
613
614 ID666.isBinary = isbinary;
615
616 if(isbinary) {
617 DEBUGF("binary tag detected\n");
618 ID666.year=*ib;
619 ib++;
620 ID666.year|=*ib<<8;
621 ib++;
622 ID666.month=*ib;
623 ib++;
624 ID666.day=*ib;
625 ib++;
626
627 ib+=7;
628
629 ID666.length=*ib;
630 ib++;
631
632 ID666.length|=*ib<<8;
633 ib++;
634
635 ID666.length|=*ib<<16;
636 ID666.length*=1000;
637 ib++;
638
639 ID666.fade=*ib;
640 ib++;
641 ID666.fade|=*ib<<8;
642 ib++;
643 ID666.fade|=*ib<<16;
644 ib++;
645 ID666.fade|=*ib<<24;
646 ib++;
647
648 memcpy(ID666.artist,ib,32);
649 ID666.artist[31]=0;
650 ib+=32;
651
652 ID666.muted=*ib;
653 ib++;
654
655 ID666.emulator=*ib;
656 ib++;
657 } else {
658 int year, month, day;
659 unsigned long tmp;
660 char buf[64];
661
662 DEBUGF("text tag detected\n");
663
664 year=month=day=0;
665 ib+=11;
666
667 memcpy(buf, ib, 3);
668 buf[3] = 0;
669 tmp = 0;
670 for (i=0;i<3 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
671 ID666.length = tmp * 1000;
672 ib+=3;
673
674 memcpy(buf, ib, 5);
675 buf[5] = 0;
676 tmp = 0;
677 for (i=0;i<5 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
678 ID666.fade = tmp;
679 ib+=5;
680
681 memcpy(ID666.artist,ib,32);
682 ID666.artist[31]=0;
683 ib+=32;
684
685 /*I have no idea if this is right or not.*/
686 ID666.muted=*ib;
687 ib++;
688
689 memcpy(buf, ib, 1);
690 buf[1] = 0;
691 tmp = 0;
692 ib++;
693 }
694 return 1;
695}
696
697/**************** Codec ****************/
698
699static int32_t samples[WAV_CHUNK_SIZE*2] IBSS_ATTR;
700
701static struct Spc_Emu spc_emu IDATA_ATTR;
702
703enum {sample_rate = 32000};
704
705/* The main decoder loop */
706static int play_track( void )
707{
708 int sampleswritten=0;
709
710 unsigned long fadestartsample = ID666.length*(long long) sample_rate/1000;
711 unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) sample_rate/1000;
712 int fadedec = 0;
713 int fadevol = 0x7fffffffl;
714
715 if (fadeendsample>fadestartsample)
716 fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
717
718 ENTER_TIMER(total);
719
720 while ( 1 )
721 {
722 ci->yield();
723 if (ci->stop_codec || ci->new_track) {
724 break;
725 }
726
727 if (ci->seek_time) {
728 int curtime = sampleswritten*1000LL/sample_rate;
729 DEBUGF("seek to %d\ncurrently at %d\n",ci->seek_time,curtime);
730 if (ci->seek_time < curtime) {
731 DEBUGF("seek backwards = reset\n");
732 ci->seek_complete();
733 return 1;
734 }
735 ci->seek_complete();
736 }
737
738 ENTER_TIMER(render);
739 /* fill samples buffer */
740 if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,samples) )
741 assert( false );
742 EXIT_TIMER(render);
743
744 sampleswritten+=WAV_CHUNK_SIZE;
745
746 /* is track timed? */
747 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) {
748 unsigned long curtime = sampleswritten*1000LL/sample_rate;
749 unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE);
750
751 /* fade? */
752 if (curtime>ID666.length)
753 {
754 int i;
755 for (i=0;i<WAV_CHUNK_SIZE;i++) {
756 if (lasttimesample+i>fadestartsample) {
757 if (fadevol>0) {
758 samples[i] = (samples[i]*(fadevol>>24))>>7;
759 samples[i+WAV_CHUNK_SIZE] = (samples[i+WAV_CHUNK_SIZE]*(fadevol>>24))>>7;
760 } else samples[i]=samples[i+WAV_CHUNK_SIZE]=0;
761 fadevol-=fadedec;
762 }
763 }
764 }
765 /* end? */
766 if (lasttimesample>=fadeendsample)
767 break;
768 }
769
770 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
771
772 if (ci->global_settings->repeat_mode!=REPEAT_ONE)
773 ci->set_elapsed(sampleswritten*1000LL/sample_rate);
774 else
775 ci->set_elapsed(0);
776 }
777
778 EXIT_TIMER(total);
779
780 return 0;
781}
782
783/* this is the codec entry point */
784enum codec_status codec_main(void)
785{
786 memcpy( spc_emu.cycle_table, cycle_table, sizeof cycle_table );
787
788 do
789 {
790 DEBUGF("SPC: next_track\n");
791 if (codec_init()) {
792 return CODEC_ERROR;
793 }
794 DEBUGF("SPC: after init\n");
795
796 ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
797 ci->configure(DSP_SET_FREQUENCY, sample_rate);
798 ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
799
800 /* wait for track info to load */
801 while (!*ci->taginfo_ready && !ci->stop_codec)
802 ci->sleep(1);
803
804 /* Read the entire file */
805 DEBUGF("SPC: request initial buffer\n");
806 ci->configure(CODEC_SET_FILEBUF_WATERMARK, ci->filesize);
807
808 ci->seek_buffer(0);
809 size_t buffersize;
810 uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
811 if (!buffer) {
812 return CODEC_ERROR;
813 }
814
815 DEBUGF("SPC: read size = 0x%x\n",buffersize);
816 do
817 {
818 SPC_Init(&spc_emu);
819 if (SPC_load_spc(&spc_emu,buffer,buffersize)) {
820 DEBUGF("SPC load failure\n");
821 return CODEC_ERROR;
822 }
823
824 LoadID666(buffer+0x2e);
825
826 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) {
827 ID666.length=3*60*1000; /* 3 minutes */
828 ID666.fade=5*1000; /* 5 seconds */
829 }
830 ci->id3->length = ID666.length+ID666.fade;
831 ci->id3->title = ID666.song;
832 ci->id3->album = ID666.game;
833 ci->id3->artist = ID666.artist;
834
835 reset_profile_timers();
836 }
837
838 while ( play_track() );
839
840 print_timers(ci->id3->path);
841 }
842 while ( ci->request_next_track() );
843
844 return CODEC_OK;
845}
diff --git a/apps/codecs/spc/Spc_Cpu.h b/apps/codecs/spc/Spc_Cpu.h
new file mode 100644
index 0000000000..b931ca2a3b
--- /dev/null
+++ b/apps/codecs/spc/Spc_Cpu.h
@@ -0,0 +1,1037 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
10 * Copyright (C) 2004-2007 Shay Green (blargg)
11 * Copyright (C) 2002 Brad Martin
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22/* The CPU portion (shock!) */
23
24#define READ( addr ) (SPC_read( this, addr, spc_time_ ))
25#define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ ))
26
27#define READ_DP( addr ) READ( (addr) + dp )
28#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
29
30#define READ_PROG16( addr ) GET_LE16( RAM + (addr) )
31
32#define READ_PC( pc ) (*(pc))
33#define READ_PC16( pc ) GET_LE16( pc )
34
35#define SET_PC( n ) (pc = RAM + (n))
36#define GET_PC() (pc - RAM)
37
38static unsigned char const cycle_table [0x100] = {
39 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, /* 0 */
40 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, /* 1 */
41 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, /* 2 */
42 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, /* 3 */
43 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, /* 4 */
44 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, /* 5 */
45 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, /* 6 */
46 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, /* 7 */
47 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, /* 8 */
48 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,/* 9 */
49 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, /* A */
50 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, /* B */
51 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, /* C */
52 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, /* D */
53 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, /* E */
54 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 /* F */
55};
56
57#define MEM_BIT() CPU_mem_bit( this, pc, spc_time_ )
58
59static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
60 ICODE_ATTR;
61
62static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
63{
64 unsigned addr = READ_PC16( pc );
65 unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13);
66 return (t << 8) & 0x100;
67}
68
69/* status flags */
70enum { st_n = 0x80 };
71enum { st_v = 0x40 };
72enum { st_p = 0x20 };
73enum { st_b = 0x10 };
74enum { st_h = 0x08 };
75enum { st_i = 0x04 };
76enum { st_z = 0x02 };
77enum { st_c = 0x01 };
78
79#define IS_NEG (nz & 0x880)
80
81#define CALC_STATUS( out )\
82{\
83 out = status & ~(st_n | st_z | st_c);\
84 out |= (c >> 8) & st_c;\
85 out |= (dp >> 3) & st_p;\
86 if ( IS_NEG ) out |= st_n;\
87 if ( !(nz & 0xFF) ) out |= st_z;\
88}
89
90#define SET_STATUS( in )\
91{\
92 status = in & ~(st_n | st_z | st_c | st_p);\
93 c = in << 8;\
94 nz = ((in << 4) & 0x800) | (~in & st_z);\
95 dp = (in << 3) & 0x100;\
96}
97
98
99/* stack */
100#define PUSH( v ) (*--sp = (uint8_t) (v))
101#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
102#define POP() (*sp++)
103#define SET_SP( v ) (sp = RAM + 0x101 + (v))
104#define GET_SP() (sp - 0x101 - RAM)
105
106static long CPU_run( THIS, long start_time ) ICODE_ATTR;
107
108static long CPU_run( THIS, long start_time )
109{
110#if 0
111 ENTER_TIMER(cpu);
112#endif
113
114 register long spc_time_ = start_time;
115
116#ifdef CPU_ARM
117 uint8_t* const ram_ = ram.ram;
118 #undef RAM
119 #define RAM ram_
120#endif
121
122 int a = this->r.a;
123 int x = this->r.x;
124 int y = this->r.y;
125
126 uint8_t const* pc;
127 SET_PC( this->r.pc );
128
129 uint8_t* sp;
130 SET_SP( this->r.sp );
131
132 int status;
133 int c;
134 int nz;
135 unsigned dp;
136 {
137 int temp = this->r.status;
138 SET_STATUS( temp );
139 }
140
141 goto loop;
142
143 /* main loop */
144cbranch_taken_loop:
145 pc += *(int8_t const*) pc;
146 spc_time_ += 2;
147inc_pc_loop:
148 pc++;
149loop:
150 check( (unsigned) GET_PC() < 0x10000 );
151 check( (unsigned) GET_SP() < 0x100 );
152 check( (unsigned) a < 0x100 );
153 check( (unsigned) x < 0x100 );
154 check( (unsigned) y < 0x100 );
155
156 unsigned opcode = *pc;
157 int cycles = this->cycle_table [opcode];
158 unsigned data = *++pc;
159 if ( (spc_time_ += cycles) > 0 )
160 goto out_of_time;
161 switch ( opcode )
162 {
163
164/* Common instructions */
165
166#define BRANCH( cond )\
167{\
168 pc++;\
169 int offset = (int8_t) data;\
170 if ( cond ) {\
171 pc += offset;\
172 spc_time_ += 2;\
173 }\
174 goto loop;\
175}
176
177 case 0xF0: /* BEQ (most common) */
178 BRANCH( !(uint8_t) nz )
179
180 case 0xD0: /* BNE */
181 BRANCH( (uint8_t) nz )
182
183 case 0x3F: /* CALL */
184 PUSH16( GET_PC() + 2 );
185 SET_PC( READ_PC16( pc ) );
186 goto loop;
187
188 case 0x6F: /* RET */
189 SET_PC( POP() );
190 pc += POP() * 0x100;
191 goto loop;
192
193#define CASE( n ) case n:
194
195/* Define common address modes based on opcode for immediate mode. Execution
196 ends with data set to the address of the operand. */
197#define ADDR_MODES( op )\
198 CASE( op - 0x02 ) /* (X) */\
199 data = x + dp;\
200 pc--;\
201 goto end_##op;\
202 CASE( op + 0x0F ) /* (dp)+Y */\
203 data = READ_PROG16( data + dp ) + y;\
204 goto end_##op;\
205 CASE( op - 0x01 ) /* (dp+X) */\
206 data = READ_PROG16( ((uint8_t) (data + x)) + dp );\
207 goto end_##op;\
208 CASE( op + 0x0E ) /* abs+Y */\
209 data += y;\
210 goto abs_##op;\
211 CASE( op + 0x0D ) /* abs+X */\
212 data += x;\
213 CASE( op - 0x03 ) /* abs */\
214 abs_##op:\
215 data += 0x100 * READ_PC( ++pc );\
216 goto end_##op;\
217 CASE( op + 0x0C ) /* dp+X */\
218 data = (uint8_t) (data + x);\
219 CASE( op - 0x04 ) /* dp */\
220 data += dp;\
221 end_##op:
222
223/* 1. 8-bit Data Transmission Commands. Group I */
224
225 ADDR_MODES( 0xE8 ) /* MOV A,addr */
226 /*case 0xE4:*/ /* MOV a,dp (most common) */
227 mov_a_addr:
228 a = nz = READ( data );
229 goto inc_pc_loop;
230 case 0xBF: /* MOV A,(X)+ */
231 data = x + dp;
232 x = (uint8_t) (x + 1);
233 pc--;
234 goto mov_a_addr;
235
236 case 0xE8: /* MOV A,imm */
237 a = data;
238 nz = data;
239 goto inc_pc_loop;
240
241 case 0xF9: /* MOV X,dp+Y */
242 data = (uint8_t) (data + y);
243 case 0xF8: /* MOV X,dp */
244 data += dp;
245 goto mov_x_addr;
246 case 0xE9: /* MOV X,abs */
247 data = READ_PC16( pc );
248 pc++;
249 mov_x_addr:
250 data = READ( data );
251 case 0xCD: /* MOV X,imm */
252 x = data;
253 nz = data;
254 goto inc_pc_loop;
255
256 case 0xFB: /* MOV Y,dp+X */
257 data = (uint8_t) (data + x);
258 case 0xEB: /* MOV Y,dp */
259 data += dp;
260 goto mov_y_addr;
261 case 0xEC: /* MOV Y,abs */
262 data = READ_PC16( pc );
263 pc++;
264 mov_y_addr:
265 data = READ( data );
266 case 0x8D: /* MOV Y,imm */
267 y = data;
268 nz = data;
269 goto inc_pc_loop;
270
271/* 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 */
272
273 ADDR_MODES( 0xC8 ) /* MOV addr,A */
274 WRITE( data, a );
275 goto inc_pc_loop;
276
277 {
278 int temp;
279 case 0xCC: /* MOV abs,Y */
280 temp = y;
281 goto mov_abs_temp;
282 case 0xC9: /* MOV abs,X */
283 temp = x;
284 mov_abs_temp:
285 WRITE( READ_PC16( pc ), temp );
286 pc += 2;
287 goto loop;
288 }
289
290 case 0xD9: /* MOV dp+Y,X */
291 data = (uint8_t) (data + y);
292 case 0xD8: /* MOV dp,X */
293 WRITE( data + dp, x );
294 goto inc_pc_loop;
295
296 case 0xDB: /* MOV dp+X,Y */
297 data = (uint8_t) (data + x);
298 case 0xCB: /* MOV dp,Y */
299 WRITE( data + dp, y );
300 goto inc_pc_loop;
301
302 case 0xFA: /* MOV dp,dp */
303 data = READ( data + dp );
304 case 0x8F: /* MOV dp,#imm */
305 WRITE_DP( READ_PC( ++pc ), data );
306 goto inc_pc_loop;
307
308/* 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. */
309
310 case 0x7D: /* MOV A,X */
311 a = x;
312 nz = x;
313 goto loop;
314
315 case 0xDD: /* MOV A,Y */
316 a = y;
317 nz = y;
318 goto loop;
319
320 case 0x5D: /* MOV X,A */
321 x = a;
322 nz = a;
323 goto loop;
324
325 case 0xFD: /* MOV Y,A */
326 y = a;
327 nz = a;
328 goto loop;
329
330 case 0x9D: /* MOV X,SP */
331 x = nz = GET_SP();
332 goto loop;
333
334 case 0xBD: /* MOV SP,X */
335 SET_SP( x );
336 goto loop;
337
338 /*case 0xC6:*/ /* MOV (X),A (handled by MOV addr,A in group 2) */
339
340 case 0xAF: /* MOV (X)+,A */
341 WRITE_DP( x, a );
342 x++;
343 goto loop;
344
345/* 5. 8-BIT LOGIC OPERATION COMMANDS */
346
347#define LOGICAL_OP( op, func )\
348 ADDR_MODES( op ) /* addr */\
349 data = READ( data );\
350 case op: /* imm */\
351 nz = a func##= data;\
352 goto inc_pc_loop;\
353 { unsigned addr;\
354 case op + 0x11: /* X,Y */\
355 data = READ_DP( y );\
356 addr = x + dp;\
357 pc--;\
358 goto addr_##op;\
359 case op + 0x01: /* dp,dp */\
360 data = READ_DP( data );\
361 case op + 0x10: /*dp,imm*/\
362 addr = READ_PC( ++pc ) + dp;\
363 addr_##op:\
364 nz = data func READ( addr );\
365 WRITE( addr, nz );\
366 goto inc_pc_loop;\
367 }
368
369 LOGICAL_OP( 0x28, & ); /* AND */
370
371 LOGICAL_OP( 0x08, | ); /* OR */
372
373 LOGICAL_OP( 0x48, ^ ); /* EOR */
374
375/* 4. 8-BIT ARITHMETIC OPERATION COMMANDS */
376
377 ADDR_MODES( 0x68 ) /* CMP addr */
378 data = READ( data );
379 case 0x68: /* CMP imm */
380 nz = a - data;
381 c = ~nz;
382 nz &= 0xFF;
383 goto inc_pc_loop;
384
385 case 0x79: /* CMP (X),(Y) */
386 data = READ_DP( x );
387 nz = data - READ_DP( y );
388 c = ~nz;
389 nz &= 0xFF;
390 goto loop;
391
392 case 0x69: /* CMP (dp),(dp) */
393 data = READ_DP( data );
394 case 0x78: /* CMP dp,imm */
395 nz = READ_DP( READ_PC( ++pc ) ) - data;
396 c = ~nz;
397 nz &= 0xFF;
398 goto inc_pc_loop;
399
400 case 0x3E: /* CMP X,dp */
401 data += dp;
402 goto cmp_x_addr;
403 case 0x1E: /* CMP X,abs */
404 data = READ_PC16( pc );
405 pc++;
406 cmp_x_addr:
407 data = READ( data );
408 case 0xC8: /* CMP X,imm */
409 nz = x - data;
410 c = ~nz;
411 nz &= 0xFF;
412 goto inc_pc_loop;
413
414 case 0x7E: /* CMP Y,dp */
415 data += dp;
416 goto cmp_y_addr;
417 case 0x5E: /* CMP Y,abs */
418 data = READ_PC16( pc );
419 pc++;
420 cmp_y_addr:
421 data = READ( data );
422 case 0xAD: /* CMP Y,imm */
423 nz = y - data;
424 c = ~nz;
425 nz &= 0xFF;
426 goto inc_pc_loop;
427
428 {
429 int addr;
430 case 0xB9: /* SBC (x),(y) */
431 case 0x99: /* ADC (x),(y) */
432 pc--; /* compensate for inc later */
433 data = READ_DP( x );
434 addr = y + dp;
435 goto adc_addr;
436 case 0xA9: /* SBC dp,dp */
437 case 0x89: /* ADC dp,dp */
438 data = READ_DP( data );
439 case 0xB8: /* SBC dp,imm */
440 case 0x98: /* ADC dp,imm */
441 addr = READ_PC( ++pc ) + dp;
442 adc_addr:
443 nz = READ( addr );
444 goto adc_data;
445
446/* catch ADC and SBC together, then decode later based on operand */
447#undef CASE
448#define CASE( n ) case n: case (n) + 0x20:
449 ADDR_MODES( 0x88 ) /* ADC/SBC addr */
450 data = READ( data );
451 case 0xA8: /* SBC imm */
452 case 0x88: /* ADC imm */
453 addr = -1; /* A */
454 nz = a;
455 adc_data: {
456 if ( opcode & 0x20 )
457 data ^= 0xFF; /* SBC */
458 int carry = (c >> 8) & 1;
459 int ov = (nz ^ 0x80) + carry + (int8_t) data; /* sign-extend */
460 int hc = (nz & 15) + carry;
461 c = nz += data + carry;
462 hc = (nz & 15) - hc;
463 status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) |
464 ((hc >> 1) & st_h);
465 if ( addr < 0 ) {
466 a = (uint8_t) nz;
467 goto inc_pc_loop;
468 }
469 WRITE( addr, (uint8_t) nz );
470 goto inc_pc_loop;
471 }
472
473 }
474
475/* 6. ADDITION & SUBTRACTION COMMANDS */
476
477#define INC_DEC_REG( reg, n )\
478 nz = reg + n;\
479 reg = (uint8_t) nz;\
480 goto loop;
481
482 case 0xBC: INC_DEC_REG( a, 1 ) /* INC A */
483 case 0x3D: INC_DEC_REG( x, 1 ) /* INC X */
484 case 0xFC: INC_DEC_REG( y, 1 ) /* INC Y */
485
486 case 0x9C: INC_DEC_REG( a, -1 ) /* DEC A */
487 case 0x1D: INC_DEC_REG( x, -1 ) /* DEC X */
488 case 0xDC: INC_DEC_REG( y, -1 ) /* DEC Y */
489
490 case 0x9B: /* DEC dp+X */
491 case 0xBB: /* INC dp+X */
492 data = (uint8_t) (data + x);
493 case 0x8B: /* DEC dp */
494 case 0xAB: /* INC dp */
495 data += dp;
496 goto inc_abs;
497 case 0x8C: /* DEC abs */
498 case 0xAC: /* INC abs */
499 data = READ_PC16( pc );
500 pc++;
501 inc_abs:
502 nz = ((opcode >> 4) & 2) - 1;
503 nz += READ( data );
504 WRITE( data, (uint8_t) nz );
505 goto inc_pc_loop;
506
507/* 7. SHIFT, ROTATION COMMANDS */
508
509 case 0x5C: /* LSR A */
510 c = 0;
511 case 0x7C:{/* ROR A */
512 nz = ((c >> 1) & 0x80) | (a >> 1);
513 c = a << 8;
514 a = nz;
515 goto loop;
516 }
517
518 case 0x1C: /* ASL A */
519 c = 0;
520 case 0x3C:{/* ROL A */
521 int temp = (c >> 8) & 1;
522 c = a << 1;
523 nz = c | temp;
524 a = (uint8_t) nz;
525 goto loop;
526 }
527
528 case 0x0B: /* ASL dp */
529 c = 0;
530 data += dp;
531 goto rol_mem;
532 case 0x1B: /* ASL dp+X */
533 c = 0;
534 case 0x3B: /* ROL dp+X */
535 data = (uint8_t) (data + x);
536 case 0x2B: /* ROL dp */
537 data += dp;
538 goto rol_mem;
539 case 0x0C: /* ASL abs */
540 c = 0;
541 case 0x2C: /* ROL abs */
542 data = READ_PC16( pc );
543 pc++;
544 rol_mem:
545 nz = (c >> 8) & 1;
546 nz |= (c = READ( data ) << 1);
547 WRITE( data, (uint8_t) nz );
548 goto inc_pc_loop;
549
550 case 0x4B: /* LSR dp */
551 c = 0;
552 data += dp;
553 goto ror_mem;
554 case 0x5B: /* LSR dp+X */
555 c = 0;
556 case 0x7B: /* ROR dp+X */
557 data = (uint8_t) (data + x);
558 case 0x6B: /* ROR dp */
559 data += dp;
560 goto ror_mem;
561 case 0x4C: /* LSR abs */
562 c = 0;
563 case 0x6C: /* ROR abs */
564 data = READ_PC16( pc );
565 pc++;
566 ror_mem: {
567 int temp = READ( data );
568 nz = ((c >> 1) & 0x80) | (temp >> 1);
569 c = temp << 8;
570 WRITE( data, nz );
571 goto inc_pc_loop;
572 }
573
574 case 0x9F: /* XCN */
575 nz = a = (a >> 4) | (uint8_t) (a << 4);
576 goto loop;
577
578/* 8. 16-BIT TRANSMISION COMMANDS */
579
580 case 0xBA: /* MOVW YA,dp */
581 a = READ_DP( data );
582 nz = (a & 0x7F) | (a >> 1);
583 y = READ_DP( (uint8_t) (data + 1) );
584 nz |= y;
585 goto inc_pc_loop;
586
587 case 0xDA: /* MOVW dp,YA */
588 WRITE_DP( data, a );
589 WRITE_DP( (uint8_t) (data + 1), y );
590 goto inc_pc_loop;
591
592/* 9. 16-BIT OPERATION COMMANDS */
593
594 case 0x3A: /* INCW dp */
595 case 0x1A:{/* DECW dp */
596 data += dp;
597
598 /* low byte */
599 int temp = READ( data );
600 temp += ((opcode >> 4) & 2) - 1; /* +1 for INCW, -1 for DECW */
601 nz = ((temp >> 1) | temp) & 0x7F;
602 WRITE( data, (uint8_t) temp );
603
604 /* high byte */
605 data = ((uint8_t) (data + 1)) + dp;
606 temp >>= 8;
607 temp = (uint8_t) (temp + READ( data ));
608 nz |= temp;
609 WRITE( data, temp );
610
611 goto inc_pc_loop;
612 }
613
614 case 0x9A: /* SUBW YA,dp */
615 case 0x7A: /* ADDW YA,dp */
616 {
617 /* read 16-bit addend */
618 int temp = READ_DP( data );
619 int sign = READ_DP( (uint8_t) (data + 1) );
620 temp += 0x100 * sign;
621 status &= ~(st_v | st_h);
622
623 /* to do: fix half-carry for SUBW (it's probably wrong) */
624
625 /* for SUBW, negate and truncate to 16 bits */
626 if ( opcode & 0x80 ) {
627 temp = (temp ^ 0xFFFF) + 1;
628 sign = temp >> 8;
629 }
630
631 /* add low byte (A) */
632 temp += a;
633 a = (uint8_t) temp;
634 nz = (temp | (temp >> 1)) & 0x7F;
635
636 /* add high byte (Y) */
637 temp >>= 8;
638 c = y + temp;
639 nz = (nz | c) & 0xFF;
640
641 /* half-carry (temporary avoids CodeWarrior optimizer bug) */
642 unsigned hc = (c & 15) - (y & 15);
643 status |= (hc >> 4) & st_h;
644
645 /* overflow if sign of YA changed when previous sign
646 and addend sign were same */
647 status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v;
648
649 y = (uint8_t) c;
650
651 goto inc_pc_loop;
652 }
653
654 case 0x5A: { /* CMPW YA,dp */
655 int temp = a - READ_DP( data );
656 nz = ((temp >> 1) | temp) & 0x7F;
657 temp = y + (temp >> 8);
658 temp -= READ_DP( (uint8_t) (data + 1) );
659 nz |= temp;
660 c = ~temp;
661 nz &= 0xFF;
662 goto inc_pc_loop;
663 }
664
665/* 10. MULTIPLICATION & DIVISON COMMANDS */
666
667 case 0xCF: { /* MUL YA */
668 unsigned temp = y * a;
669 a = (uint8_t) temp;
670 nz = ((temp >> 1) | temp) & 0x7F;
671 y = temp >> 8;
672 nz |= y;
673 goto loop;
674 }
675
676 case 0x9E: /* DIV YA,X */
677 {
678 /* behavior based on SPC CPU tests */
679
680 status &= ~(st_h | st_v);
681
682 if ( (y & 15) >= (x & 15) )
683 status |= st_h;
684
685 if ( y >= x )
686 status |= st_v;
687
688 unsigned ya = y * 0x100 + a;
689 if ( y < x * 2 )
690 {
691 a = ya / x;
692 y = ya - a * x;
693 }
694 else
695 {
696 a = 255 - (ya - x * 0x200) / (256 - x);
697 y = x + (ya - x * 0x200) % (256 - x);
698 }
699
700 nz = (uint8_t) a;
701 a = (uint8_t) a;
702
703 goto loop;
704 }
705
706/* 11. DECIMAL COMPENSATION COMMANDS */
707
708 /* seem unused */
709 /* case 0xDF: */ /* DAA */
710 /* case 0xBE: */ /* DAS */
711
712/* 12. BRANCHING COMMANDS */
713
714 case 0x2F: /* BRA rel */
715 pc += (int8_t) data;
716 goto inc_pc_loop;
717
718 case 0x30: /* BMI */
719 BRANCH( IS_NEG )
720
721 case 0x10: /* BPL */
722 BRANCH( !IS_NEG )
723
724 case 0xB0: /* BCS */
725 BRANCH( c & 0x100 )
726
727 case 0x90: /* BCC */
728 BRANCH( !(c & 0x100) )
729
730 case 0x70: /* BVS */
731 BRANCH( status & st_v )
732
733 case 0x50: /* BVC */
734 BRANCH( !(status & st_v) )
735
736 case 0x03: /* BBS dp.bit,rel */
737 case 0x23:
738 case 0x43:
739 case 0x63:
740 case 0x83:
741 case 0xA3:
742 case 0xC3:
743 case 0xE3:
744 pc++;
745 if ( (READ_DP( data ) >> (opcode >> 5)) & 1 )
746 goto cbranch_taken_loop;
747 goto inc_pc_loop;
748
749 case 0x13: /* BBC dp.bit,rel */
750 case 0x33:
751 case 0x53:
752 case 0x73:
753 case 0x93:
754 case 0xB3:
755 case 0xD3:
756 case 0xF3:
757 pc++;
758 if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) )
759 goto cbranch_taken_loop;
760 goto inc_pc_loop;
761
762 case 0xDE: /* CBNE dp+X,rel */
763 data = (uint8_t) (data + x);
764 /* fall through */
765 case 0x2E: /* CBNE dp,rel */
766 pc++;
767 if ( READ_DP( data ) != a )
768 goto cbranch_taken_loop;
769 goto inc_pc_loop;
770
771 case 0xFE: /* DBNZ Y,rel */
772 y = (uint8_t) (y - 1);
773 BRANCH( y )
774
775 case 0x6E: { /* DBNZ dp,rel */
776 pc++;
777 unsigned temp = READ_DP( data ) - 1;
778 WRITE_DP( (uint8_t) data, (uint8_t) temp );
779 if ( temp )
780 goto cbranch_taken_loop;
781 goto inc_pc_loop;
782 }
783
784 case 0x1F: /* JMP (abs+X) */
785 SET_PC( READ_PC16( pc ) + x );
786 /* fall through */
787 case 0x5F: /* JMP abs */
788 SET_PC( READ_PC16( pc ) );
789 goto loop;
790
791/* 13. SUB-ROUTINE CALL RETURN COMMANDS */
792
793 case 0x0F:{/* BRK */
794 check( 0 ); /* untested */
795 PUSH16( GET_PC() + 1 );
796 SET_PC( READ_PROG16( 0xFFDE ) ); /* vector address verified */
797 int temp;
798 CALC_STATUS( temp );
799 PUSH( temp );
800 status = (status | st_b) & ~st_i;
801 goto loop;
802 }
803
804 case 0x4F: /* PCALL offset */
805 PUSH16( GET_PC() + 1 );
806 SET_PC( 0xFF00 + data );
807 goto loop;
808
809 case 0x01: /* TCALL n */
810 case 0x11:
811 case 0x21:
812 case 0x31:
813 case 0x41:
814 case 0x51:
815 case 0x61:
816 case 0x71:
817 case 0x81:
818 case 0x91:
819 case 0xA1:
820 case 0xB1:
821 case 0xC1:
822 case 0xD1:
823 case 0xE1:
824 case 0xF1:
825 PUSH16( GET_PC() );
826 SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
827 goto loop;
828
829/* 14. STACK OPERATION COMMANDS */
830
831 {
832 int temp;
833 case 0x7F: /* RET1 */
834 temp = POP();
835 SET_PC( POP() );
836 pc += POP() << 8;
837 goto set_status;
838 case 0x8E: /* POP PSW */
839 temp = POP();
840 set_status:
841 SET_STATUS( temp );
842 goto loop;
843 }
844
845 case 0x0D: { /* PUSH PSW */
846 int temp;
847 CALC_STATUS( temp );
848 PUSH( temp );
849 goto loop;
850 }
851
852 case 0x2D: /* PUSH A */
853 PUSH( a );
854 goto loop;
855
856 case 0x4D: /* PUSH X */
857 PUSH( x );
858 goto loop;
859
860 case 0x6D: /* PUSH Y */
861 PUSH( y );
862 goto loop;
863
864 case 0xAE: /* POP A */
865 a = POP();
866 goto loop;
867
868 case 0xCE: /* POP X */
869 x = POP();
870 goto loop;
871
872 case 0xEE: /* POP Y */
873 y = POP();
874 goto loop;
875
876/* 15. BIT OPERATION COMMANDS */
877
878 case 0x02: /* SET1 */
879 case 0x22:
880 case 0x42:
881 case 0x62:
882 case 0x82:
883 case 0xA2:
884 case 0xC2:
885 case 0xE2:
886 case 0x12: /* CLR1 */
887 case 0x32:
888 case 0x52:
889 case 0x72:
890 case 0x92:
891 case 0xB2:
892 case 0xD2:
893 case 0xF2: {
894 data += dp;
895 int bit = 1 << (opcode >> 5);
896 int mask = ~bit;
897 if ( opcode & 0x10 )
898 bit = 0;
899 WRITE( data, (READ( data ) & mask) | bit );
900 goto inc_pc_loop;
901 }
902
903 case 0x0E: /* TSET1 abs */
904 case 0x4E:{/* TCLR1 abs */
905 data = READ_PC16( pc );
906 pc += 2;
907 unsigned temp = READ( data );
908 nz = temp & a;
909 temp &= ~a;
910 if ( !(opcode & 0x40) )
911 temp |= a;
912 WRITE( data, temp );
913 goto loop;
914 }
915
916 case 0x4A: /* AND1 C,mem.bit */
917 c &= MEM_BIT();
918 pc += 2;
919 goto loop;
920
921 case 0x6A: /* AND1 C,/mem.bit */
922 check( 0 ); /* untested */
923 c &= ~MEM_BIT();
924 pc += 2;
925 goto loop;
926
927 case 0x0A: /* OR1 C,mem.bit */
928 check( 0 ); /* untested */
929 c |= MEM_BIT();
930 pc += 2;
931 goto loop;
932
933 case 0x2A: /* OR1 C,/mem.bit */
934 check( 0 ); /* untested */
935 c |= ~MEM_BIT();
936 pc += 2;
937 goto loop;
938
939 case 0x8A: /* EOR1 C,mem.bit */
940 c ^= MEM_BIT();
941 pc += 2;
942 goto loop;
943
944 case 0xEA: { /* NOT1 mem.bit */
945 data = READ_PC16( pc );
946 pc += 2;
947 unsigned temp = READ( data & 0x1FFF );
948 temp ^= 1 << (data >> 13);
949 WRITE( data & 0x1FFF, temp );
950 goto loop;
951 }
952
953 case 0xCA: { /* MOV1 mem.bit,C */
954 data = READ_PC16( pc );
955 pc += 2;
956 unsigned temp = READ( data & 0x1FFF );
957 unsigned bit = data >> 13;
958 temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit);
959 WRITE( data & 0x1FFF, temp );
960 goto loop;
961 }
962
963 case 0xAA: /* MOV1 C,mem.bit */
964 c = MEM_BIT();
965 pc += 2;
966 goto loop;
967
968/* 16. PROGRAM STATUS FLAG OPERATION COMMANDS */
969
970 case 0x60: /* CLRC */
971 c = 0;
972 goto loop;
973
974 case 0x80: /* SETC */
975 c = ~0;
976 goto loop;
977
978 case 0xED: /* NOTC */
979 c ^= 0x100;
980 goto loop;
981
982 case 0xE0: /* CLRV */
983 status &= ~(st_v | st_h);
984 goto loop;
985
986 case 0x20: /* CLRP */
987 dp = 0;
988 goto loop;
989
990 case 0x40: /* SETP */
991 dp = 0x100;
992 goto loop;
993
994 case 0xA0: /* EI */
995 check( 0 ); /* untested */
996 status |= st_i;
997 goto loop;
998
999 case 0xC0: /* DI */
1000 check( 0 ); /* untested */
1001 status &= ~st_i;
1002 goto loop;
1003
1004/* 17. OTHER COMMANDS */
1005
1006 case 0x00: /* NOP */
1007 goto loop;
1008
1009 /*case 0xEF:*/ /* SLEEP */
1010 /*case 0xFF:*/ /* STOP */
1011 case 0xFF:
1012 c |= 1; /* force switch table to have 256 entries,
1013 hopefully helping optimizer */
1014 } /* switch */
1015
1016 /* unhandled instructions fall out of switch so emulator can catch them */
1017
1018out_of_time:
1019 /* undo partial execution of opcode */
1020 spc_time_ -= this->cycle_table [*--pc];
1021 {
1022 int temp;
1023 CALC_STATUS( temp );
1024 this->r.status = (uint8_t) temp;
1025 }
1026
1027 this->r.pc = GET_PC();
1028 this->r.sp = (uint8_t) GET_SP();
1029 this->r.a = (uint8_t) a;
1030 this->r.x = (uint8_t) x;
1031 this->r.y = (uint8_t) y;
1032
1033#if 0
1034 EXIT_TIMER(cpu);
1035#endif
1036 return spc_time_;
1037}
diff --git a/apps/codecs/spc/Spc_Dsp.h b/apps/codecs/spc/Spc_Dsp.h
new file mode 100644
index 0000000000..b297630b42
--- /dev/null
+++ b/apps/codecs/spc/Spc_Dsp.h
@@ -0,0 +1,1107 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
10 * Copyright (C) 2004-2007 Shay Green (blargg)
11 * Copyright (C) 2002 Brad Martin
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21/* The DSP portion (awe!) */
22
23enum { voice_count = 8 };
24enum { register_count = 128 };
25
26struct raw_voice_t
27{
28 int8_t volume [2];
29 uint8_t rate [2];
30 uint8_t waveform;
31 uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */
32 uint8_t gain; /* envelope gain (if not using ADSR) */
33 int8_t envx; /* current envelope level */
34 int8_t outx; /* current sample */
35 int8_t unused [6];
36};
37
38struct globals_t
39{
40 int8_t unused1 [12];
41 int8_t volume_0; /* 0C Main Volume Left (-.7) */
42 int8_t echo_feedback; /* 0D Echo Feedback (-.7) */
43 int8_t unused2 [14];
44 int8_t volume_1; /* 1C Main Volume Right (-.7) */
45 int8_t unused3 [15];
46 int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */
47 uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */
48 int8_t unused4 [14];
49 int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */
50 uint8_t noise_enables; /* 3D Noise output on/off for each voice */
51 int8_t unused5 [14];
52 uint8_t key_ons; /* 4C Key On for each voice */
53 uint8_t echo_ons; /* 4D Echo on/off for each voice */
54 int8_t unused6 [14];
55 uint8_t key_offs; /* 5C key off for each voice
56 (instantiates release mode) */
57 uint8_t wave_page; /* 5D source directory (wave table offsets) */
58 int8_t unused7 [14];
59 uint8_t flags; /* 6C flags and noise freq */
60 uint8_t echo_page; /* 6D */
61 int8_t unused8 [14];
62 uint8_t wave_ended; /* 7C */
63 uint8_t echo_delay; /* 7D ms >> 4 */
64 char unused9 [2];
65};
66
67enum state_t { /* -1, 0, +1 allows more efficient if statements */
68 state_decay = -1,
69 state_sustain = 0,
70 state_attack = +1,
71 state_release = 2
72};
73
74struct cache_entry_t
75{
76 int16_t const* samples;
77 unsigned end; /* past-the-end position */
78 unsigned loop; /* number of samples in loop */
79 unsigned start_addr;
80};
81
82enum { brr_block_size = 16 };
83
84struct voice_t
85{
86#if SPC_BRRCACHE
87 int16_t const* samples;
88 long wave_end;
89 int wave_loop;
90#else
91 int16_t samples [3 + brr_block_size + 1];
92 int block_header; /* header byte from current block */
93#endif
94 uint8_t const* addr;
95 short volume [2];
96 long position;/* position in samples buffer, with 12-bit fraction */
97 short envx;
98 short env_mode;
99 short env_timer;
100 short key_on_delay;
101};
102
103#if SPC_BRRCACHE
104/* a little extra for samples that go past end */
105static int16_t BRRcache [0x20000 + 32];
106#endif
107
108enum { fir_buf_half = 8 };
109
110struct Spc_Dsp
111{
112 union
113 {
114 struct raw_voice_t voice [voice_count];
115 uint8_t reg [register_count];
116 struct globals_t g;
117 int16_t align;
118 } r;
119
120 unsigned echo_pos;
121 int keys_down;
122 int noise_count;
123 uint16_t noise; /* also read as int16_t */
124
125 /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */
126 int fir_pos; /* (0 to 7) */
127 int fir_buf [fir_buf_half * 2] [2];
128 /* copy of echo FIR constants as int, for faster access */
129 int fir_coeff [voice_count];
130
131 struct voice_t voice_state [voice_count];
132
133#if SPC_BRRCACHE
134 uint8_t oldsize;
135 struct cache_entry_t wave_entry [256];
136 struct cache_entry_t wave_entry_old [256];
137#endif
138};
139
140struct src_dir
141{
142 char start [2];
143 char loop [2];
144};
145
146static void DSP_reset( struct Spc_Dsp* this )
147{
148 this->keys_down = 0;
149 this->echo_pos = 0;
150 this->noise_count = 0;
151 this->noise = 2;
152 this->fir_pos = 0;
153
154 this->r.g.flags = 0xE0; /* reset, mute, echo off */
155 this->r.g.key_ons = 0;
156
157 memset( this->voice_state, 0, sizeof this->voice_state );
158
159 int i;
160 for ( i = voice_count; --i >= 0; )
161 {
162 struct voice_t* v = this->voice_state + i;
163 v->env_mode = state_release;
164 v->addr = ram.ram;
165 }
166
167 #if SPC_BRRCACHE
168 this->oldsize = 0;
169 for ( i = 0; i < 256; i++ )
170 this->wave_entry [i].start_addr = -1;
171 #endif
172
173 memset( this->fir_buf, 0, sizeof this->fir_buf );
174 assert( offsetof (struct globals_t,unused9 [2]) == register_count );
175 assert( sizeof (this->r.voice) == register_count );
176}
177
178static void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR;
179static void DSP_write( struct Spc_Dsp* this, int i, int data )
180{
181 assert( (unsigned) i < register_count );
182
183 this->r.reg [i] = data;
184 int high = i >> 4;
185 int low = i & 0x0F;
186 if ( low < 2 ) /* voice volumes */
187 {
188 int left = *(int8_t const*) &this->r.reg [i & ~1];
189 int right = *(int8_t const*) &this->r.reg [i | 1];
190 struct voice_t* v = this->voice_state + high;
191 v->volume [0] = left;
192 v->volume [1] = right;
193 }
194 else if ( low == 0x0F ) /* fir coefficients */
195 {
196 this->fir_coeff [7 - high] = (int8_t) data; /* sign-extend */
197 }
198}
199
200static inline int DSP_read( struct Spc_Dsp* this, int i )
201{
202 assert( (unsigned) i < register_count );
203 return this->r.reg [i];
204}
205
206/* if ( n < -32768 ) out = -32768; */
207/* if ( n > 32767 ) out = 32767; */
208#define CLAMP16( n, out )\
209{\
210 if ( (int16_t) n != n )\
211 out = 0x7FFF ^ (n >> 31);\
212}
213
214#if SPC_BRRCACHE
215static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
216 struct voice_t* voice,
217 struct raw_voice_t const* const raw_voice ) ICODE_ATTR;
218static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
219 struct voice_t* voice,
220 struct raw_voice_t const* const raw_voice )
221{
222 /* setup same variables as where decode_brr() is called from */
223 #undef RAM
224 #define RAM ram.ram
225 struct src_dir const* const sd =
226 (struct src_dir*) &RAM [this->r.g.wave_page * 0x100];
227 struct cache_entry_t* const wave_entry =
228 &this->wave_entry [raw_voice->waveform];
229
230 /* the following block can be put in place of the call to
231 decode_brr() below
232 */
233 {
234 DEBUGF( "decode at %08x (wave #%d)\n",
235 start_addr, raw_voice->waveform );
236
237 /* see if in cache */
238 int i;
239 for ( i = 0; i < this->oldsize; i++ )
240 {
241 struct cache_entry_t* e = &this->wave_entry_old [i];
242 if ( e->start_addr == start_addr )
243 {
244 DEBUGF( "found in wave_entry_old (oldsize=%d)\n",
245 this->oldsize );
246 *wave_entry = *e;
247 goto wave_in_cache;
248 }
249 }
250
251 wave_entry->start_addr = start_addr;
252
253 uint8_t const* const loop_ptr =
254 RAM + GET_LE16A( sd [raw_voice->waveform].loop );
255 short* loop_start = 0;
256
257 short* out = BRRcache + start_addr * 2;
258 wave_entry->samples = out;
259 *out++ = 0;
260 int smp1 = 0;
261 int smp2 = 0;
262
263 uint8_t const* addr = RAM + start_addr;
264 int block_header;
265 do
266 {
267 if ( addr == loop_ptr )
268 {
269 loop_start = out;
270 DEBUGF( "loop at %08x (wave #%d)\n", addr - RAM, raw_voice->waveform );
271 }
272
273 /* header */
274 block_header = *addr;
275 addr += 9;
276 voice->addr = addr;
277 int const filter = (block_header & 0x0C) - 0x08;
278
279 /* scaling
280 (invalid scaling gives -4096 for neg nybble, 0 for pos) */
281 static unsigned char const right_shifts [16] = {
282 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 29,
283 };
284 static unsigned char const left_shifts [16] = {
285 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
286 };
287 int const scale = block_header >> 4;
288 int const right_shift = right_shifts [scale];
289 int const left_shift = left_shifts [scale];
290
291 /* output position */
292 out += brr_block_size;
293 int offset = -brr_block_size << 2;
294
295 do /* decode and filter 16 samples */
296 {
297 /* Get nybble, sign-extend, then scale
298 get byte, select which nybble, sign-extend, then shift based
299 on scaling. also handles invalid scaling values. */
300 int delta = (int) (int8_t) (addr [offset >> 3] << (offset & 4))
301 >> right_shift << left_shift;
302
303 out [offset >> 2] = smp2;
304
305 if ( filter == 0 ) /* mode 0x08 (30-90% of the time) */
306 {
307 delta -= smp2 >> 1;
308 delta += smp2 >> 5;
309 smp2 = smp1;
310 delta += smp1;
311 delta += (-smp1 - (smp1 >> 1)) >> 5;
312 }
313 else
314 {
315 if ( filter == -4 ) /* mode 0x04 */
316 {
317 delta += smp1 >> 1;
318 delta += (-smp1) >> 5;
319 }
320 else if ( filter > -4 ) /* mode 0x0C */
321 {
322 delta -= smp2 >> 1;
323 delta += (smp2 + (smp2 >> 1)) >> 4;
324 delta += smp1;
325 delta += (-smp1 * 13) >> 7;
326 }
327 smp2 = smp1;
328 }
329
330 CLAMP16( delta, delta );
331 smp1 = (int16_t) (delta * 2); /* sign-extend */
332 }
333 while ( (offset += 4) != 0 );
334
335 /* if next block has end flag set, this block ends early */
336 /* (verified) */
337 if ( (block_header & 3) != 3 && (*addr & 3) == 1 )
338 {
339 /* skip last 9 samples */
340 out -= 9;
341 goto early_end;
342 }
343 }
344 while ( !(block_header & 1) && addr < RAM + 0x10000 );
345
346 out [0] = smp2;
347 out [1] = smp1;
348
349 early_end:
350 wave_entry->end = (out - 1 - wave_entry->samples) << 12;
351
352 wave_entry->loop = 0;
353 if ( (block_header & 2) )
354 {
355 if ( loop_start )
356 {
357 int loop = out - loop_start;
358 wave_entry->loop = loop;
359 wave_entry->end += 0x3000;
360 out [2] = loop_start [2];
361 out [3] = loop_start [3];
362 out [4] = loop_start [4];
363 }
364 else
365 {
366 DEBUGF( "loop point outside initial wave\n" );
367 }
368 }
369
370 DEBUGF( "end at %08x (wave #%d)\n", addr - RAM, raw_voice->waveform );
371
372 /* add to cache */
373 this->wave_entry_old [this->oldsize++] = *wave_entry;
374wave_in_cache:;
375 }
376}
377#endif
378
379static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
380 struct src_dir const* const sd,
381 struct raw_voice_t const* const raw_voice,
382 const int key_on_delay, const int vbit) ICODE_ATTR;
383static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
384 struct src_dir const* const sd,
385 struct raw_voice_t const* const raw_voice,
386 const int key_on_delay, const int vbit) {
387 #undef RAM
388 #define RAM ram.ram
389 int const env_rate_init = 0x7800;
390 voice->key_on_delay = key_on_delay;
391 if ( key_on_delay == 0 )
392 {
393 this->keys_down |= vbit;
394 voice->envx = 0;
395 voice->env_mode = state_attack;
396 voice->env_timer = env_rate_init; /* TODO: inaccurate? */
397 unsigned start_addr = GET_LE16A( sd [raw_voice->waveform].start );
398 #if !SPC_BRRCACHE
399 {
400 voice->addr = RAM + start_addr;
401 /* BRR filter uses previous samples */
402 voice->samples [brr_block_size + 1] = 0;
403 voice->samples [brr_block_size + 2] = 0;
404 /* decode three samples immediately */
405 voice->position = (brr_block_size + 3) * 0x1000 - 1;
406 voice->block_header = 0; /* "previous" BRR header */
407 }
408 #else
409 {
410 voice->position = 3 * 0x1000 - 1;
411 struct cache_entry_t* const wave_entry =
412 &this->wave_entry [raw_voice->waveform];
413
414 /* predecode BRR if not already */
415 if ( wave_entry->start_addr != start_addr )
416 {
417 /* the following line can be replaced by the indicated block
418 in decode_brr() */
419 decode_brr( this, start_addr, voice, raw_voice );
420 }
421
422 voice->samples = wave_entry->samples;
423 voice->wave_end = wave_entry->end;
424 voice->wave_loop = wave_entry->loop;
425 }
426 #endif
427 }
428}
429
430static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
431 ICODE_ATTR;
432static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
433{
434 #undef RAM
435#ifdef CPU_ARM
436 uint8_t* const ram_ = ram.ram;
437 #define RAM ram_
438#else
439 #define RAM ram.ram
440#endif
441#if 0
442 EXIT_TIMER(cpu);
443 ENTER_TIMER(dsp);
444#endif
445
446 /* Here we check for keys on/off. Docs say that successive writes
447 to KON/KOF must be separated by at least 2 Ts periods or risk
448 being neglected. Therefore DSP only looks at these during an
449 update, and not at the time of the write. Only need to do this
450 once however, since the regs haven't changed over the whole
451 period we need to catch up with. */
452
453 {
454 int key_ons = this->r.g.key_ons;
455 int key_offs = this->r.g.key_offs;
456 /* keying on a voice resets that bit in ENDX */
457 this->r.g.wave_ended &= ~key_ons;
458 /* key_off bits prevent key_on from being acknowledged */
459 this->r.g.key_ons = key_ons & key_offs;
460
461 /* process key events outside loop, since they won't re-occur */
462 struct voice_t* voice = this->voice_state + 8;
463 int vbit = 0x80;
464 do
465 {
466 --voice;
467 if ( key_offs & vbit )
468 {
469 voice->env_mode = state_release;
470 voice->key_on_delay = 0;
471 }
472 else if ( key_ons & vbit )
473 {
474 voice->key_on_delay = 8;
475 }
476 }
477 while ( (vbit >>= 1) != 0 );
478 }
479
480 struct src_dir const* const sd =
481 (struct src_dir*) &RAM [this->r.g.wave_page * 0x100];
482
483#if !SPC_NOINTERP
484 int const slow_gaussian = (this->r.g.pitch_mods >> 1) |
485 this->r.g.noise_enables;
486#endif
487 /* (g.flags & 0x40) ? 30 : 14 */
488 int const global_muting = ((this->r.g.flags & 0x40) >> 2) + 14;
489
490 /* scaling to offset quietage */
491 int const global_vol_0 = this->r.g.volume_0 * 3;
492 int const global_vol_1 = this->r.g.volume_1 * 3;
493
494 /* each rate divides exactly into 0x7800 without remainder */
495 int const env_rate_init = 0x7800;
496 static unsigned short const env_rates [0x20] ICONST_ATTR =
497 {
498 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
499 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
500 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
501 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
502 };
503
504 do /* one pair of output samples per iteration */
505 {
506 /* Noise */
507 if ( this->r.g.noise_enables )
508 {
509 if ( (this->noise_count -=
510 env_rates [this->r.g.flags & 0x1F]) <= 0 )
511 {
512 this->noise_count = env_rate_init;
513 int feedback = (this->noise << 13) ^ (this->noise << 14);
514 this->noise = (feedback & 0x8000) ^ (this->noise >> 1 & ~1);
515 }
516 }
517
518#if !SPC_NOECHO
519 int echo_0 = 0;
520 int echo_1 = 0;
521#endif
522 long prev_outx = 0; /* TODO: correct value for first channel? */
523 int chans_0 = 0;
524 int chans_1 = 0;
525 /* TODO: put raw_voice pointer in voice_t? */
526 struct raw_voice_t * raw_voice = this->r.voice;
527 struct voice_t* voice = this->voice_state;
528 int vbit = 1;
529 for ( ; vbit < 0x100; vbit <<= 1, ++voice, ++raw_voice )
530 {
531 /* pregen involves checking keyon, etc */
532#if 0
533 ENTER_TIMER(dsp_pregen);
534#endif
535
536 /* Key on events are delayed */
537 int key_on_delay = voice->key_on_delay;
538
539 if ( --key_on_delay >= 0 ) /* <1% of the time */
540 {
541 key_on(this,voice,sd,raw_voice,key_on_delay,vbit);
542 }
543
544 if ( !(this->keys_down & vbit) ) /* Silent channel */
545 {
546 silent_chan:
547 raw_voice->envx = 0;
548 raw_voice->outx = 0;
549 prev_outx = 0;
550 continue;
551 }
552
553 /* Envelope */
554 {
555 int const env_range = 0x800;
556 int env_mode = voice->env_mode;
557 int adsr0 = raw_voice->adsr [0];
558 int env_timer;
559 if ( env_mode != state_release ) /* 99% of the time */
560 {
561 env_timer = voice->env_timer;
562 if ( adsr0 & 0x80 ) /* 79% of the time */
563 {
564 int adsr1 = raw_voice->adsr [1];
565 if ( env_mode == state_sustain ) /* 74% of the time */
566 {
567 if ( (env_timer -= env_rates [adsr1 & 0x1F]) > 0 )
568 goto write_env_timer;
569
570 int envx = voice->envx;
571 envx--; /* envx *= 255 / 256 */
572 envx -= envx >> 8;
573 voice->envx = envx;
574 /* TODO: should this be 8? */
575 raw_voice->envx = envx >> 4;
576 goto init_env_timer;
577 }
578 else if ( env_mode < 0 ) /* 25% state_decay */
579 {
580 int envx = voice->envx;
581 if ( (env_timer -=
582 env_rates [(adsr0 >> 3 & 0x0E) + 0x10]) <= 0 )
583 {
584 envx--; /* envx *= 255 / 256 */
585 envx -= envx >> 8;
586 voice->envx = envx;
587 /* TODO: should this be 8? */
588 raw_voice->envx = envx >> 4;
589 env_timer = env_rate_init;
590 }
591
592 int sustain_level = adsr1 >> 5;
593 if ( envx <= (sustain_level + 1) * 0x100 )
594 voice->env_mode = state_sustain;
595
596 goto write_env_timer;
597 }
598 else /* state_attack */
599 {
600 int t = adsr0 & 0x0F;
601 if ( (env_timer -= env_rates [t * 2 + 1]) > 0 )
602 goto write_env_timer;
603
604 int envx = voice->envx;
605
606 int const step = env_range / 64;
607 envx += step;
608 if ( t == 15 )
609 envx += env_range / 2 - step;
610
611 if ( envx >= env_range )
612 {
613 envx = env_range - 1;
614 voice->env_mode = state_decay;
615 }
616 voice->envx = envx;
617 /* TODO: should this be 8? */
618 raw_voice->envx = envx >> 4;
619 goto init_env_timer;
620 }
621 }
622 else /* gain mode */
623 {
624 int t = raw_voice->gain;
625 if ( t < 0x80 )
626 {
627 raw_voice->envx = t;
628 voice->envx = t << 4;
629 goto env_end;
630 }
631 else
632 {
633 if ( (env_timer -= env_rates [t & 0x1F]) > 0 )
634 goto write_env_timer;
635
636 int envx = voice->envx;
637 int mode = t >> 5;
638 if ( mode <= 5 ) /* decay */
639 {
640 int step = env_range / 64;
641 if ( mode == 5 ) /* exponential */
642 {
643 envx--; /* envx *= 255 / 256 */
644 step = envx >> 8;
645 }
646 if ( (envx -= step) < 0 )
647 {
648 envx = 0;
649 if ( voice->env_mode == state_attack )
650 voice->env_mode = state_decay;
651 }
652 }
653 else /* attack */
654 {
655 int const step = env_range / 64;
656 envx += step;
657 if ( mode == 7 &&
658 envx >= env_range * 3 / 4 + step )
659 envx += env_range / 256 - step;
660
661 if ( envx >= env_range )
662 envx = env_range - 1;
663 }
664 voice->envx = envx;
665 /* TODO: should this be 8? */
666 raw_voice->envx = envx >> 4;
667 goto init_env_timer;
668 }
669 }
670 }
671 else /* state_release */
672 {
673 int envx = voice->envx;
674 if ( (envx -= env_range / 256) > 0 )
675 {
676 voice->envx = envx;
677 raw_voice->envx = envx >> 8;
678 goto env_end;
679 }
680 else
681 {
682 /* bit was set, so this clears it */
683 this->keys_down ^= vbit;
684 voice->envx = 0;
685 goto silent_chan;
686 }
687 }
688 init_env_timer:
689 env_timer = env_rate_init;
690 write_env_timer:
691 voice->env_timer = env_timer;
692 env_end:;
693 }
694#if 0
695 EXIT_TIMER(dsp_pregen);
696
697 ENTER_TIMER(dsp_gen);
698#endif
699 #if !SPC_BRRCACHE
700 /* Decode BRR block */
701 if ( voice->position >= brr_block_size * 0x1000 )
702 {
703 voice->position -= brr_block_size * 0x1000;
704
705 uint8_t const* addr = voice->addr;
706 if ( addr >= RAM + 0x10000 )
707 addr -= 0x10000;
708
709 /* action based on previous block's header */
710 if ( voice->block_header & 1 )
711 {
712 addr = RAM + GET_LE16A( sd [raw_voice->waveform].loop );
713 this->r.g.wave_ended |= vbit;
714 if ( !(voice->block_header & 2) ) /* 1% of the time */
715 {
716 /* first block was end block;
717 don't play anything (verified) */
718 /* bit was set, so this clears it */
719 this->keys_down ^= vbit;
720
721 /* since voice->envx is 0,
722 samples and position don't matter */
723 raw_voice->envx = 0;
724 voice->envx = 0;
725 goto skip_decode;
726 }
727 }
728
729 /* header */
730 int const block_header = *addr;
731 addr += 9;
732 voice->addr = addr;
733 voice->block_header = block_header;
734 int const filter = (block_header & 0x0C) - 0x08;
735
736 /* scaling (invalid scaling gives -4096 for neg nybble,
737 0 for pos) */
738 static unsigned char const right_shifts [16] = {
739 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 29,
740 };
741 static unsigned char const left_shifts [16] = {
742 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
743 };
744 int const scale = block_header >> 4;
745 int const right_shift = right_shifts [scale];
746 int const left_shift = left_shifts [scale];
747
748 /* previous samples */
749 int smp2 = voice->samples [brr_block_size + 1];
750 int smp1 = voice->samples [brr_block_size + 2];
751 voice->samples [0] = voice->samples [brr_block_size];
752
753 /* output position */
754 short* out = voice->samples + (1 + brr_block_size);
755 int offset = -brr_block_size << 2;
756
757 /* if next block has end flag set,
758 this block ends early (verified) */
759 if ( (block_header & 3) != 3 && (*addr & 3) == 1 )
760 {
761 /* arrange for last 9 samples to be skipped */
762 int const skip = 9;
763 out += (skip & 1);
764 voice->samples [skip] = voice->samples [brr_block_size];
765 voice->position += skip * 0x1000;
766 offset = (-brr_block_size + (skip & ~1)) << 2;
767 addr -= skip / 2;
768 /* force sample to end on next decode */
769 voice->block_header = 1;
770 }
771
772 do /* decode and filter 16 samples */
773 {
774 /* Get nybble, sign-extend, then scale
775 get byte, select which nybble, sign-extend, then shift
776 based on scaling. also handles invalid scaling values.*/
777 int delta = (int) (int8_t) (addr [offset >> 3] <<
778 (offset & 4)) >> right_shift << left_shift;
779
780 out [offset >> 2] = smp2;
781
782 if ( filter == 0 ) /* mode 0x08 (30-90% of the time) */
783 {
784 delta -= smp2 >> 1;
785 delta += smp2 >> 5;
786 smp2 = smp1;
787 delta += smp1;
788 delta += (-smp1 - (smp1 >> 1)) >> 5;
789 }
790 else
791 {
792 if ( filter == -4 ) /* mode 0x04 */
793 {
794 delta += smp1 >> 1;
795 delta += (-smp1) >> 5;
796 }
797 else if ( filter > -4 ) /* mode 0x0C */
798 {
799 delta -= smp2 >> 1;
800 delta += (smp2 + (smp2 >> 1)) >> 4;
801 delta += smp1;
802 delta += (-smp1 * 13) >> 7;
803 }
804 smp2 = smp1;
805 }
806
807 CLAMP16( delta, delta );
808 smp1 = (int16_t) (delta * 2); /* sign-extend */
809 }
810 while ( (offset += 4) != 0 );
811
812 out [0] = smp2;
813 out [1] = smp1;
814
815 skip_decode:;
816 }
817 #endif
818
819 /* Get rate (with possible modulation) */
820 int rate = GET_LE16A( raw_voice->rate ) & 0x3FFF;
821 if ( this->r.g.pitch_mods & vbit )
822 rate = (rate * (prev_outx + 32768)) >> 15;
823
824 #if !SPC_NOINTERP
825 /* Interleved gauss table (to improve cache coherency). */
826 /* gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] */
827 static short const gauss [512] =
828 {
829370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
830339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
831311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
832283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
833257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
834233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
835210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
836188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
837168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
838150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
839132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
840117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
841102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,
842 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,
843 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,
844 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,
845 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,
846 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,
847 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,
848 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,
849 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,
850 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,
851 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,
852 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,
853 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,
854 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,
855 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,
856 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,
857 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,
858 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,
859 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,
860 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,
861 };
862
863 /* Gaussian interpolation using most recent 4 samples */
864 long position = voice->position;
865 voice->position += rate;
866 short const* interp = voice->samples + (position >> 12);
867 int offset = position >> 4 & 0xFF;
868
869 /* Only left half of gaussian kernel is in table, so we must mirror
870 for right half */
871 short const* fwd = gauss + offset * 2;
872 short const* rev = gauss + 510 - offset * 2;
873
874 /* Use faster gaussian interpolation when exact result isn't needed
875 by pitch modulator of next channel */
876 int amp_0, amp_1;
877 if ( !(slow_gaussian & vbit) ) /* 99% of the time */
878 {
879 /* Main optimization is lack of clamping. Not a problem since
880 output never goes more than +/- 16 outside 16-bit range and
881 things are clamped later anyway. Other optimization is to
882 preserve fractional accuracy, eliminating several masks. */
883 int output = (((fwd [0] * interp [0] +
884 fwd [1] * interp [1] +
885 rev [1] * interp [2] +
886 rev [0] * interp [3] ) >> 11) * voice->envx) >> 11;
887
888 /* duplicated here to give compiler more to run in parallel */
889 amp_0 = voice->volume [0] * output;
890 amp_1 = voice->volume [1] * output;
891 raw_voice->outx = output >> 8;
892 }
893 else
894 {
895 int output = *(int16_t*) &this->noise;
896 if ( !(this->r.g.noise_enables & vbit) )
897 {
898 output = (fwd [0] * interp [0]) & ~0xFFF;
899 output = (output + fwd [1] * interp [1]) & ~0xFFF;
900 output = (output + rev [1] * interp [2]) >> 12;
901 output = (int16_t) (output * 2);
902 output += ((rev [0] * interp [3]) >> 12) * 2;
903 CLAMP16( output, output );
904 }
905 output = (output * voice->envx) >> 11 & ~1;
906
907 /* duplicated here to give compiler more to run in parallel */
908 amp_0 = voice->volume [0] * output;
909 amp_1 = voice->volume [1] * output;
910 prev_outx = output;
911 raw_voice->outx = (int8_t) (output >> 8);
912 }
913 #else
914 /* two-point linear interpolation */
915 #ifdef CPU_COLDFIRE
916 int32_t output = (int16_t)this->noise;
917
918 if ( (this->r.g.noise_enables & vbit) == 0 )
919 {
920 uint32_t f = voice->position;
921 int32_t y1;
922 asm (
923 "move.l %[f], %[y0] \n" /* separate fraction */
924 "and.l #0xfff, %[f] \n" /* and whole parts */
925 "lsr.l %[sh], %[y0] \n"
926 "move.l 2(%[s], %[y0].l*2), %[y1] \n" /* load two samples */
927 "move.l %[y1], %[y0] \n" /* separate samples */
928 "ext.l %[y1] \n" /* y0=s[1], y1=s[2] */
929 "swap %[y0] \n"
930 "ext.l %[y0] \n"
931 "sub.l %[y0], %[y1] \n" /* diff = y1 - y0 */
932 "muls.l %[f], %[y1] \n" /* y0 += f*diff */
933 "asr.l %[sh], %[y1] \n"
934 "add.l %[y1], %[y0] \n"
935 : [f]"+&d"(f), [y0]"=&d"(output), [y1]"=&d"(y1)
936 : [s]"a"(voice->samples), [sh]"r"(12)
937 );
938 }
939
940 voice->position += rate;
941 #else
942
943 /* Try this one out on ARM and see - similar to above but the asm
944 on coldfire removes a redundant register load worth 1 or 2%;
945 switching to loading two samples at once may help too. That's
946 done above and while 6 to 7% faster on cf over two 16 bit loads
947 it makes it endian dependant.
948
949 measured small improvement (~1.5%) - hcs
950 */
951
952 int output;
953
954 if ( (this->r.g.noise_enables & vbit) == 0 )
955 {
956 int const fraction = voice->position & 0xfff;
957 short const* const pos = (voice->samples + (voice->position >> 12)) + 1;
958 output = pos[0] + ((fraction * (pos[1] - pos[0])) >> 12);
959 } else {
960 output = *(int16_t *)&this->noise;
961 }
962
963 voice->position += rate;
964
965 /* old version */
966#if 0
967 int fraction = voice->position & 0xFFF;
968 short const* const pos = voice->samples + (voice->position >> 12);
969 voice->position += rate;
970 int output =
971 (pos [2] * fraction + pos [1] * (0x1000 - fraction)) >> 12;
972 /* no interpolation (hardly faster, and crappy sounding) */
973 /*int output = pos [0];*/
974 if ( this->r.g.noise_enables & vbit )
975 output = *(int16_t*) &this->noise;
976#endif
977 #endif /* CPU_COLDFIRE */
978
979 output = (output * voice->envx) >> 11;
980
981 /* duplicated here to give compiler more to run in parallel */
982 int amp_0 = voice->volume [0] * output;
983 int amp_1 = voice->volume [1] * output;
984
985 prev_outx = output;
986 raw_voice->outx = (int8_t) (output >> 8);
987 #endif
988
989 #if SPC_BRRCACHE
990 if ( voice->position >= voice->wave_end )
991 {
992 long loop_len = voice->wave_loop << 12;
993 voice->position -= loop_len;
994 this->r.g.wave_ended |= vbit;
995 if ( !loop_len )
996 {
997 this->keys_down ^= vbit;
998 raw_voice->envx = 0;
999 voice->envx = 0;
1000 }
1001 }
1002 #endif
1003#if 0
1004 EXIT_TIMER(dsp_gen);
1005
1006 ENTER_TIMER(dsp_mix);
1007#endif
1008 chans_0 += amp_0;
1009 chans_1 += amp_1;
1010 #if !SPC_NOECHO
1011 if ( this->r.g.echo_ons & vbit )
1012 {
1013 echo_0 += amp_0;
1014 echo_1 += amp_1;
1015 }
1016 #endif
1017#if 0
1018 EXIT_TIMER(dsp_mix);
1019#endif
1020 }
1021 /* end of voice loop */
1022
1023 #if !SPC_NOECHO
1024 /* Read feedback from echo buffer */
1025 int echo_pos = this->echo_pos;
1026 uint8_t* const echo_ptr = RAM +
1027 ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF);
1028 echo_pos += 4;
1029 if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 )
1030 echo_pos = 0;
1031 this->echo_pos = echo_pos;
1032 int fb_0 = GET_LE16SA( echo_ptr );
1033 int fb_1 = GET_LE16SA( echo_ptr + 2 );
1034
1035 /* Keep last 8 samples */
1036 int (* const fir_ptr) [2] = this->fir_buf + this->fir_pos;
1037 this->fir_pos = (this->fir_pos + 1) & (fir_buf_half - 1);
1038 fir_ptr [ 0] [0] = fb_0;
1039 fir_ptr [ 0] [1] = fb_1;
1040 /* duplicate at +8 eliminates wrap checking below */
1041 fir_ptr [fir_buf_half] [0] = fb_0;
1042 fir_ptr [fir_buf_half] [1] = fb_1;
1043
1044 /* Apply FIR */
1045 fb_0 *= this->fir_coeff [0];
1046 fb_1 *= this->fir_coeff [0];
1047
1048 #define DO_PT( i )\
1049 fb_0 += fir_ptr [i] [0] * this->fir_coeff [i];\
1050 fb_1 += fir_ptr [i] [1] * this->fir_coeff [i];
1051
1052 DO_PT( 1 )
1053 DO_PT( 2 )
1054 DO_PT( 3 )
1055 DO_PT( 4 )
1056 DO_PT( 5 )
1057 DO_PT( 6 )
1058 DO_PT( 7 )
1059
1060 /* Generate output */
1061 int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0)
1062 >> global_muting;
1063 int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1)
1064 >> global_muting;
1065 CLAMP16( amp_0, amp_0 );
1066 out_buf [0] = amp_0 * (1 << 8);
1067 CLAMP16( amp_1, amp_1 );
1068 out_buf [WAV_CHUNK_SIZE] = amp_1 * (1 << 8);
1069 out_buf ++;
1070
1071 /* Feedback into echo buffer */
1072 int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14);
1073 int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14);
1074 if ( !(this->r.g.flags & 0x20) )
1075 {
1076 CLAMP16( e0, e0 );
1077 SET_LE16A( echo_ptr , e0 );
1078 CLAMP16( e1, e1 );
1079 SET_LE16A( echo_ptr + 2, e1 );
1080 }
1081 #else
1082 /* Generate output */
1083 int amp_0 = (chans_0 * global_vol_0) >> global_muting;
1084 int amp_1 = (chans_1 * global_vol_1) >> global_muting;
1085 CLAMP16( amp_0, amp_0 );
1086 out_buf [0] = amp_0 * (1 << 8);
1087 CLAMP16( amp_1, amp_1 );
1088 out_buf [WAV_CHUNK_SIZE] = amp_1 * (1 << 8);
1089 out_buf ++;
1090 #endif
1091 }
1092 while ( --count );
1093#if 0
1094 EXIT_TIMER(dsp);
1095 ENTER_TIMER(cpu);
1096#endif
1097}
1098
1099static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out )
1100{
1101 /* Should we just fill the buffer with silence? Flags won't be cleared */
1102 /* during this run so it seems it should keep resetting every sample. */
1103 if ( this->r.g.flags & 0x80 )
1104 DSP_reset( this );
1105
1106 DSP_run_( this, count, out );
1107}
diff --git a/apps/codecs/spc/spc_profiler.h b/apps/codecs/spc/spc_profiler.h
new file mode 100644
index 0000000000..99d3fdf16b
--- /dev/null
+++ b/apps/codecs/spc/spc_profiler.h
@@ -0,0 +1,89 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19/* a fun simple elapsed time profiler */
20
21#if defined(SPC_PROFILE) && defined(USEC_TIMER)
22
23#define CREATE_TIMER(name) static uint32_t spc_timer_##name##_start,\
24 spc_timer_##name##_total
25#define ENTER_TIMER(name) spc_timer_##name##_start=USEC_TIMER
26#define EXIT_TIMER(name) spc_timer_##name##_total+=\
27 (USEC_TIMER-spc_timer_##name##_start)
28#define READ_TIMER(name) (spc_timer_##name##_total)
29#define RESET_TIMER(name) spc_timer_##name##_total=0
30
31#define PRINT_TIMER_PCT(bname,tname,nstr) ci->fdprintf( \
32 logfd,"%10ld ",READ_TIMER(bname));\
33 ci->fdprintf(logfd,"(%3d%%) " nstr "\t",\
34 ((uint64_t)READ_TIMER(bname))*100/READ_TIMER(tname))
35
36CREATE_TIMER(total);
37CREATE_TIMER(render);
38#if 0
39CREATE_TIMER(cpu);
40CREATE_TIMER(dsp);
41CREATE_TIMER(dsp_pregen);
42CREATE_TIMER(dsp_gen);
43CREATE_TIMER(dsp_mix);
44#endif
45
46static void reset_profile_timers(void) {
47 RESET_TIMER(total);
48 RESET_TIMER(render);
49#if 0
50 RESET_TIMER(cpu);
51 RESET_TIMER(dsp);
52 RESET_TIMER(dsp_pregen);
53 RESET_TIMER(dsp_gen);
54 RESET_TIMER(dsp_mix);
55#endif
56}
57
58static int logfd=-1;
59
60static void print_timers(char * path) {
61 logfd = ci->open("/spclog.txt",O_WRONLY|O_CREAT|O_APPEND);
62 ci->fdprintf(logfd,"%s:\t",path);
63 ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total));
64 PRINT_TIMER_PCT(render,total,"render");
65#if 0
66 PRINT_TIMER_PCT(cpu,total,"CPU");
67 PRINT_TIMER_PCT(dsp,total,"DSP");
68 ci->fdprintf(logfd,"(");
69 PRINT_TIMER_PCT(dsp_pregen,dsp,"pregen");
70 PRINT_TIMER_PCT(dsp_gen,dsp,"gen");
71 PRINT_TIMER_PCT(dsp_mix,dsp,"mix");
72#endif
73 ci->fdprintf(logfd,"\n");
74
75 ci->close(logfd);
76 logfd=-1;
77}
78
79#else
80
81#define CREATE_TIMER(name)
82#define ENTER_TIMER(name)
83#define EXIT_TIMER(name)
84#define READ_TIMER(name)
85#define RESET_TIMER(name)
86#define print_timers(path)
87#define reset_profile_timers()
88
89#endif
diff --git a/apps/metadata.c b/apps/metadata.c
index 81b2a3f713..33b82874cb 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -2229,7 +2229,10 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
2229 return false; 2229 return false;
2230 } 2230 }
2231 break; 2231 break;
2232 2232 case AFMT_SPC:
2233 track->id3.filesize = filesize(fd);
2234 track->id3.genre = 36;
2235 break;
2233 case AFMT_ADX: 2236 case AFMT_ADX:
2234 if (!get_adx_metadata(fd, &(track->id3))) 2237 if (!get_adx_metadata(fd, &(track->id3)))
2235 { 2238 {
diff --git a/apps/tree.c b/apps/tree.c
index 0e40206ff9..f4bf54b9f0 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -111,6 +111,7 @@ const struct filetype filetypes[] = {
111 { "adx", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 111 { "adx", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
112 { "nsf", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 112 { "nsf", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
113 { "nsfe", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 113 { "nsfe", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
114 { "spc", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
114#endif 115#endif
115 { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, 116 { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
116 { "m3u8", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, 117 { "m3u8", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
index c016bd520c..980d3b4d94 100644
--- a/firmware/export/id3.h
+++ b/firmware/export/id3.h
@@ -52,6 +52,7 @@ enum
52 AFMT_ADX, /* ADX File Format */ 52 AFMT_ADX, /* ADX File Format */
53 AFMT_NSF, /* NESM (NES Sound Format) */ 53 AFMT_NSF, /* NESM (NES Sound Format) */
54 AFMT_SPEEX, /* Ogg Speex speech */ 54 AFMT_SPEEX, /* Ogg Speex speech */
55 AFMT_SPC, /* SPC700 save state */
55#endif 56#endif
56 57
57 /* add new formats at any index above this line to have a sensible order - 58 /* add new formats at any index above this line to have a sensible order -
diff --git a/firmware/id3.c b/firmware/id3.c
index bdcf8e526b..0547bed3af 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -104,6 +104,9 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
104 /* Speex File Format */ 104 /* Speex File Format */
105 [AFMT_SPEEX] = 105 [AFMT_SPEEX] =
106 AFMT_ENTRY("Speex","speex", NULL, "spx\0" ), 106 AFMT_ENTRY("Speex","speex", NULL, "spx\0" ),
107 /* SPC700 Save State */
108 [AFMT_SPC] =
109 AFMT_ENTRY("SPC", "spc", NULL, "spc\0" ),
107#endif 110#endif
108}; 111};
109 112