summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-07-16 21:03:25 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-07-16 21:03:25 +0000
commit8552eff9e46ede8968ea13cc172e1c61856bee18 (patch)
tree7649c84039f0ee4801d56b7b9b38966c3731f79a
parent76fa0f7e30398e01d405b4391c30b99dca4c9288 (diff)
downloadrockbox-8552eff9e46ede8968ea13cc172e1c61856bee18.tar.gz
rockbox-8552eff9e46ede8968ea13cc172e1c61856bee18.zip
Make the SPC codec run like it used to on Coldfire before -Os crushed it. Build as a lib using the old -O option. Should not impact ARM targets.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13919 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/codecs/Makefile15
-rw-r--r--apps/codecs/spc.c567
-rw-r--r--apps/codecs/spc/Makefile44
-rw-r--r--apps/codecs/spc/SOURCES4
-rw-r--r--apps/codecs/spc/spc_cpu.c (renamed from apps/codecs/spc/Spc_Cpu.h)21
-rw-r--r--apps/codecs/spc/spc_dsp.c (renamed from apps/codecs/spc/Spc_Dsp.h)283
-rw-r--r--apps/codecs/spc/spc_emu.c378
-rw-r--r--apps/codecs/spc/spc_profiler.c64
-rw-r--r--apps/codecs/spc/spc_profiler.h47
9 files changed, 609 insertions, 814 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index 44caacf780..2c0a18295e 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -17,7 +17,7 @@ ifdef APPEXTRA
17endif 17endif
18 18
19ifdef SOFTWARECODECS 19ifdef SOFTWARECODECS
20 CODECLIBS = -lmad -la52 -lffmpegFLAC -lTremor -lwavpack -lmusepack -lalac -lfaad -lm4a -lspeex -ldemac -lwma 20 CODECLIBS = -lspc -lmad -la52 -lffmpegFLAC -lTremor -lwavpack -lmusepack -lalac -lfaad -lm4a -lspeex -ldemac -lwma
21endif 21endif
22 22
23# we "borrow" the plugin LDS file 23# we "borrow" the plugin LDS file
@@ -39,7 +39,7 @@ DIRS = .
39 39
40CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a 40CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a
41 41
42.PHONY: libmad liba52 libffmpegFLAC libTremor libspeex libwavpack libmusepack libalac libfaad libm4a libdemac libwma 42.PHONY: libspc libmad liba52 libffmpegFLAC libTremor libspeex libwavpack libmusepack libalac libfaad libm4a libdemac libwma
43 43
44OUTPUT = $(SOFTWARECODECS) 44OUTPUT = $(SOFTWARECODECS)
45 45
@@ -52,7 +52,7 @@ $(OBJDIR)/wav.elf : $(OBJDIR)/wav.o $(OBJDIR)/codec_crt0.o
52$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o $(OBJDIR)/codec_crt0.o 52$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o $(OBJDIR)/codec_crt0.o
53$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/codec_crt0.o 53$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/codec_crt0.o
54$(OBJDIR)/nsf.elf : $(OBJDIR)/nsf.o $(OBJDIR)/codec_crt0.o 54$(OBJDIR)/nsf.elf : $(OBJDIR)/nsf.o $(OBJDIR)/codec_crt0.o
55$(OBJDIR)/spc.elf : $(OBJDIR)/spc.o $(OBJDIR)/codec_crt0.o 55$(OBJDIR)/spc.elf : $(OBJDIR)/spc.o $(BUILDDIR)/libspc.a $(OBJDIR)/codec_crt0.o
56$(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o $(OBJDIR)/codec_crt0.o 56$(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o $(OBJDIR)/codec_crt0.o
57$(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a $(OBJDIR)/codec_crt0.o 57$(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a $(OBJDIR)/codec_crt0.o
58$(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a $(OBJDIR)/codec_crt0.o 58$(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a $(OBJDIR)/codec_crt0.o
@@ -140,6 +140,12 @@ $(BUILDDIR)/libcodec.a:
140$(LINKCODEC): $(LDS) 140$(LINKCODEC): $(LDS)
141 $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) -DCODEC $(INCLUDES) $(TARGET) $(DEFINES) -E -P - >$@ 141 $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) -DCODEC $(INCLUDES) $(TARGET) $(DEFINES) -E -P - >$@
142 142
143$(BUILDDIR)/libspc.a: libspc
144
145libspc:
146 $(SILENT)mkdir -p $(OBJDIR)/spc
147 $(call PRINTS,MAKE in spc)$(MAKE) -C spc OBJDIR=$(OBJDIR)/spc OUTPUT=$(BUILDDIR)/libspc.a
148
143$(BUILDDIR)/libmad.a: libmad 149$(BUILDDIR)/libmad.a: libmad
144 150
145libmad: 151libmad:
@@ -213,7 +219,8 @@ libdemac:
213 $(call PRINTS,MAKE in libdemac)$(MAKE) -C demac/libdemac OBJDIR=$(OBJDIR)/libdemac OUTPUT=$(BUILDDIR)/libdemac.a 219 $(call PRINTS,MAKE in libdemac)$(MAKE) -C demac/libdemac OBJDIR=$(OBJDIR)/libdemac OUTPUT=$(BUILDDIR)/libdemac.a
214 220
215clean: 221clean:
216 $(call PRINTS,cleaning codecs)rm -fr $(OBJDIR)/libmad $(BUILDDIR)/libmad.a $(OBJDIR)/liba52 $(BUILDDIR)/liba52.a $(OBJDIR)/libffmpegFLAC $(BUILDDIR)/libffmpegFLAC.a $(OBJDIR)/Tremor $(BUILDDIR)/libTremor.a $(OBJDIR)/libspeex $(BUILDDIR)/libSpeex.a $(OBJDIR)/libwavpack $(BUILDDIR)/libwavpack.a $(OBJDIR)/libmusepack $(BUILDDIR)/libmusepack.a $(OBJDIR)/libalac $(BUILDDIR)/libalac.a $(OBJDIR)/libfaad $(BUILDDIR)/libfaad.a $(OBJDIR)/libm4a $(BUILDDIR)/libm4a.a $(OBJDIR)/libdemac $(BUILDDIR)/libdemac.a $(OBJDIR)/libwma $(BUILDDIR)/libwma.a 222 $(call PRINTS,cleaning codecs)rm -fr $(OBJDIR)/spc $(BUILDDIR)/libspc.a $(OBJDIR)/libmad $(BUILDDIR)/libmad.a $(OBJDIR)/liba52 $(BUILDDIR)/liba52.a $(OBJDIR)/libffmpegFLAC $(BUILDDIR)/libffmpegFLAC.a $(OBJDIR)/Tremor $(BUILDDIR)/libTremor.a $(OBJDIR)/libspeex $(BUILDDIR)/libSpeex.a $(OBJDIR)/libwavpack $(BUILDDIR)/libwavpack.a $(OBJDIR)/libmusepack $(BUILDDIR)/libmusepack.a $(OBJDIR)/libalac $(BUILDDIR)/libalac.a $(OBJDIR)/libfaad $(BUILDDIR)/libfaad.a $(OBJDIR)/libm4a $(BUILDDIR)/libm4a.a $(OBJDIR)/libdemac $(BUILDDIR)/libdemac.a $(OBJDIR)/libwma $(BUILDDIR)/libwma.a
223 $(SILENT)$(MAKE) -C spc clean OBJDIR=$(OBJDIR)/spc
217 $(SILENT)$(MAKE) -C libmad clean OBJDIR=$(OBJDIR)/libmad 224 $(SILENT)$(MAKE) -C libmad clean OBJDIR=$(OBJDIR)/libmad
218 $(SILENT)$(MAKE) -C liba52 clean OBJDIR=$(OBJDIR)/liba52 225 $(SILENT)$(MAKE) -C liba52 clean OBJDIR=$(OBJDIR)/liba52
219 $(SILENT)$(MAKE) -C libffmpegFLAC clean OBJDIR=$(OBJDIR)/libffmpegFLAC 226 $(SILENT)$(MAKE) -C libffmpegFLAC clean OBJDIR=$(OBJDIR)/libffmpegFLAC
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 */
diff --git a/apps/codecs/spc/Makefile b/apps/codecs/spc/Makefile
new file mode 100644
index 0000000000..8929149ce2
--- /dev/null
+++ b/apps/codecs/spc/Makefile
@@ -0,0 +1,44 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10INCLUDES=-I$(APPSDIR) -I.. -I. -I$(FIRMDIR)/include -I$(FIRMDIR)/export \
11 -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(BUILDDIR)
12
13ifdef APPEXTRA
14 INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA)))
15endif
16
17SPCOPTS = -O -DROCKBOX
18
19CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET_INC) $(SPCOPTS) $(TARGET) \
20$(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} $(PROFILE_OPTS) -DCODEC=1
21
22# This sets up 'SRC' based on the files mentioned in SOURCES
23include $(TOOLSDIR)/makesrc.inc
24
25SOURCES = $(SRC)
26OBJS2 := $(SRC:%.c=$(OBJDIR)/%.o)
27OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
28DEPFILE = $(OBJDIR)/dep-spc
29DIRS =
30
31all: $(OUTPUT)
32
33$(OUTPUT): $(OBJS)
34 $(call PRINTS,AR+RANLIB $(@F))$(AR) ruv $@ $+ >/dev/null 2>&1
35 $(SILENT)$(RANLIB) $@
36
37include $(TOOLSDIR)/make.inc
38
39clean:
40 $(call PRINTS,cleaning spc)rm -f $(OBJS) $(OUTPUT) $(DEPFILE)
41
42ifneq ($(MAKECMDGOALS),clean)
43-include $(DEPFILE)
44endif
diff --git a/apps/codecs/spc/SOURCES b/apps/codecs/spc/SOURCES
new file mode 100644
index 0000000000..901232a6eb
--- /dev/null
+++ b/apps/codecs/spc/SOURCES
@@ -0,0 +1,4 @@
1spc_cpu.c
2spc_dsp.c
3spc_emu.c
4spc_profiler.c
diff --git a/apps/codecs/spc/Spc_Cpu.h b/apps/codecs/spc/spc_cpu.c
index b931ca2a3b..3b7c129ad8 100644
--- a/apps/codecs/spc/Spc_Cpu.h
+++ b/apps/codecs/spc/spc_cpu.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)
@@ -17,9 +18,15 @@
17 * KIND, either express or implied. 18 * KIND, either express or implied.
18 * 19 *
19 ****************************************************************************/ 20 ****************************************************************************/
20 21
21
22/* The CPU portion (shock!) */ 22/* The CPU portion (shock!) */
23#include "codec.h"
24#include "codecs.h"
25#include "spc_codec.h"
26#include "spc_profiler.h"
27
28#undef check
29#define check assert
23 30
24#define READ( addr ) (SPC_read( this, addr, spc_time_ )) 31#define READ( addr ) (SPC_read( this, addr, spc_time_ ))
25#define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ )) 32#define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ ))
@@ -103,9 +110,7 @@ enum { st_c = 0x01 };
103#define SET_SP( v ) (sp = RAM + 0x101 + (v)) 110#define SET_SP( v ) (sp = RAM + 0x101 + (v))
104#define GET_SP() (sp - 0x101 - RAM) 111#define GET_SP() (sp - 0x101 - RAM)
105 112
106static long CPU_run( THIS, long start_time ) ICODE_ATTR; 113long CPU_run( THIS, long start_time )
107
108static long CPU_run( THIS, long start_time )
109{ 114{
110#if 0 115#if 0
111 ENTER_TIMER(cpu); 116 ENTER_TIMER(cpu);
@@ -1035,3 +1040,9 @@ out_of_time:
1035#endif 1040#endif
1036 return spc_time_; 1041 return spc_time_;
1037} 1042}
1043
1044void CPU_Init( THIS )
1045{
1046 ci->memcpy( this->cycle_table, cycle_table, sizeof cycle_table );
1047}
1048
diff --git a/apps/codecs/spc/Spc_Dsp.h b/apps/codecs/spc/spc_dsp.c
index d670b20c52..6a8aaebc7f 100644
--- a/apps/codecs/spc/Spc_Dsp.h
+++ b/apps/codecs/spc/spc_dsp.c
@@ -20,196 +20,24 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22/* The DSP portion (awe!) */ 22/* The DSP portion (awe!) */
23#include "codec.h"
24#include "codecs.h"
25#include "spc_codec.h"
26#include "spc_profiler.h"
23 27
24enum { voice_count = 8 }; 28#ifdef CPU_COLDFIRE
25enum { register_count = 128 }; 29static int32_t fir_buf[FIR_BUF_HALF]
26 30 __attribute__ ((aligned (FIR_BUF_SIZE*2))) IBSS_ATTR;
27struct raw_voice_t
28{
29 int8_t volume [2];
30 uint8_t rate [2];
31 uint8_t waveform;
32 uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */
33 uint8_t gain; /* envelope gain (if not using ADSR) */
34 int8_t envx; /* current envelope level */
35 int8_t outx; /* current sample */
36 int8_t unused [6];
37};
38
39struct globals_t
40{
41 int8_t unused1 [12];
42 int8_t volume_0; /* 0C Main Volume Left (-.7) */
43 int8_t echo_feedback; /* 0D Echo Feedback (-.7) */
44 int8_t unused2 [14];
45 int8_t volume_1; /* 1C Main Volume Right (-.7) */
46 int8_t unused3 [15];
47 int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */
48 uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */
49 int8_t unused4 [14];
50 int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */
51 uint8_t noise_enables; /* 3D Noise output on/off for each voice */
52 int8_t unused5 [14];
53 uint8_t key_ons; /* 4C Key On for each voice */
54 uint8_t echo_ons; /* 4D Echo on/off for each voice */
55 int8_t unused6 [14];
56 uint8_t key_offs; /* 5C key off for each voice
57 (instantiates release mode) */
58 uint8_t wave_page; /* 5D source directory (wave table offsets) */
59 int8_t unused7 [14];
60 uint8_t flags; /* 6C flags and noise freq */
61 uint8_t echo_page; /* 6D */
62 int8_t unused8 [14];
63 uint8_t wave_ended; /* 7C */
64 uint8_t echo_delay; /* 7D ms >> 4 */
65 char unused9 [2];
66};
67
68enum state_t { /* -1, 0, +1 allows more efficient if statements */
69 state_decay = -1,
70 state_sustain = 0,
71 state_attack = +1,
72 state_release = 2
73};
74
75struct cache_entry_t
76{
77 int16_t const* samples;
78 unsigned end; /* past-the-end position */
79 unsigned loop; /* number of samples in loop */
80 unsigned start_addr;
81};
82
83enum { brr_block_size = 16 };
84
85struct voice_t
86{
87#if SPC_BRRCACHE
88 int16_t const* samples;
89 long wave_end;
90 int wave_loop;
91#else
92 int16_t samples [3 + brr_block_size + 1];
93 int block_header; /* header byte from current block */
94#endif 31#endif
95 uint8_t const* addr;
96 short volume [2];
97 long position;/* position in samples buffer, with 12-bit fraction */
98 short envx;
99 short env_mode;
100 short env_timer;
101 short key_on_delay;
102};
103 32
104#if SPC_BRRCACHE 33#if SPC_BRRCACHE
105/* a little extra for samples that go past end */ 34/* a little extra for samples that go past end */
106static int16_t BRRcache [0x20000 + 32]; 35int16_t BRRcache [0x20000 + 32];
107#endif
108
109enum { fir_buf_half = 8 };
110
111#ifdef CPU_COLDFIRE
112/* global because of the large aligment requirement for hardware masking -
113 * L-R interleaved 16-bit samples for easy loading and mac.w use.
114 */
115enum
116{
117 fir_buf_size = fir_buf_half * sizeof ( int32_t ),
118 fir_buf_mask = ~fir_buf_size
119};
120int32_t fir_buf[fir_buf_half]
121 __attribute__ ((aligned (fir_buf_size*2))) IBSS_ATTR;
122#endif /* CPU_COLDFIRE */
123
124struct Spc_Dsp
125{
126 union
127 {
128 struct raw_voice_t voice [voice_count];
129 uint8_t reg [register_count];
130 struct globals_t g;
131 int16_t align;
132 } r;
133
134 unsigned echo_pos;
135 int keys_down;
136 int noise_count;
137 uint16_t noise; /* also read as int16_t */
138
139#ifdef CPU_COLDFIRE
140 /* circularly hardware masked address */
141 int32_t *fir_ptr;
142 /* wrapped address just behind current position -
143 allows mac.w to increment and mask fir_ptr */
144 int32_t *last_fir_ptr;
145 /* copy of echo FIR constants as int16_t for use with mac.w */
146 int16_t fir_coeff[voice_count];
147#else
148 /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */
149 int fir_pos; /* (0 to 7) */
150 int fir_buf [fir_buf_half * 2] [2];
151 /* copy of echo FIR constants as int, for faster access */
152 int fir_coeff [voice_count];
153#endif
154
155 struct voice_t voice_state [voice_count];
156
157#if SPC_BRRCACHE
158 uint8_t oldsize;
159 struct cache_entry_t wave_entry [256];
160 struct cache_entry_t wave_entry_old [256];
161#endif
162};
163
164struct src_dir
165{
166 char start [2];
167 char loop [2];
168};
169
170static void DSP_reset( struct Spc_Dsp* this )
171{
172 this->keys_down = 0;
173 this->echo_pos = 0;
174 this->noise_count = 0;
175 this->noise = 2;
176
177 this->r.g.flags = 0xE0; /* reset, mute, echo off */
178 this->r.g.key_ons = 0;
179
180 memset( this->voice_state, 0, sizeof this->voice_state );
181
182 int i;
183 for ( i = voice_count; --i >= 0; )
184 {
185 struct voice_t* v = this->voice_state + i;
186 v->env_mode = state_release;
187 v->addr = ram.ram;
188 }
189
190 #if SPC_BRRCACHE
191 this->oldsize = 0;
192 for ( i = 0; i < 256; i++ )
193 this->wave_entry [i].start_addr = -1;
194 #endif
195
196#ifdef CPU_COLDFIRE
197 this->fir_ptr = fir_buf;
198 this->last_fir_ptr = &fir_buf [7];
199 memset( fir_buf, 0, sizeof fir_buf );
200#else
201 this->fir_pos = 0;
202 memset( this->fir_buf, 0, sizeof this->fir_buf );
203#endif 36#endif
204 37
205 assert( offsetof (struct globals_t,unused9 [2]) == register_count ); 38void DSP_write( struct Spc_Dsp* this, int i, int data )
206 assert( sizeof (this->r.voice) == register_count );
207}
208
209static void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR;
210static void DSP_write( struct Spc_Dsp* this, int i, int data )
211{ 39{
212 assert( (unsigned) i < register_count ); 40 assert( (unsigned) i < REGISTER_COUNT );
213 41
214 this->r.reg [i] = data; 42 this->r.reg [i] = data;
215 int high = i >> 4; 43 int high = i >> 4;
@@ -228,12 +56,6 @@ static void DSP_write( struct Spc_Dsp* this, int i, int data )
228 } 56 }
229} 57}
230 58
231static inline int DSP_read( struct Spc_Dsp* this, int i )
232{
233 assert( (unsigned) i < register_count );
234 return this->r.reg [i];
235}
236
237/* if ( n < -32768 ) out = -32768; */ 59/* if ( n < -32768 ) out = -32768; */
238/* if ( n > 32767 ) out = 32767; */ 60/* if ( n > 32767 ) out = 32767; */
239#define CLAMP16( n, out )\ 61#define CLAMP16( n, out )\
@@ -321,8 +143,8 @@ static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
321 int const left_shift = left_shifts [scale]; 143 int const left_shift = left_shifts [scale];
322 144
323 /* output position */ 145 /* output position */
324 out += brr_block_size; 146 out += BRR_BLOCK_SIZE;
325 int offset = -brr_block_size << 2; 147 int offset = -BRR_BLOCK_SIZE << 2;
326 148
327 do /* decode and filter 16 samples */ 149 do /* decode and filter 16 samples */
328 { 150 {
@@ -432,10 +254,10 @@ static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
432 { 254 {
433 voice->addr = RAM + start_addr; 255 voice->addr = RAM + start_addr;
434 /* BRR filter uses previous samples */ 256 /* BRR filter uses previous samples */
435 voice->samples [brr_block_size + 1] = 0; 257 voice->samples [BRR_BLOCK_SIZE + 1] = 0;
436 voice->samples [brr_block_size + 2] = 0; 258 voice->samples [BRR_BLOCK_SIZE + 2] = 0;
437 /* decode three samples immediately */ 259 /* decode three samples immediately */
438 voice->position = (brr_block_size + 3) * 0x1000 - 1; 260 voice->position = (BRR_BLOCK_SIZE + 3) * 0x1000 - 1;
439 voice->block_header = 0; /* "previous" BRR header */ 261 voice->block_header = 0; /* "previous" BRR header */
440 } 262 }
441 #else 263 #else
@@ -460,9 +282,7 @@ static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
460 } 282 }
461} 283}
462 284
463static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) 285void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
464 ICODE_ATTR;
465static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
466{ 286{
467 #undef RAM 287 #undef RAM
468#ifdef CPU_ARM 288#ifdef CPU_ARM
@@ -516,7 +336,7 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
516 #ifdef ROCKBOX_BIG_ENDIAN 336 #ifdef ROCKBOX_BIG_ENDIAN
517 /* Convert endiannesses before entering loops - these 337 /* Convert endiannesses before entering loops - these
518 get used alot */ 338 get used alot */
519 const uint32_t rates[voice_count] = 339 const uint32_t rates[VOICE_COUNT] =
520 { 340 {
521 GET_LE16A( this->r.voice[0].rate ) & 0x3FFF, 341 GET_LE16A( this->r.voice[0].rate ) & 0x3FFF,
522 GET_LE16A( this->r.voice[1].rate ) & 0x3FFF, 342 GET_LE16A( this->r.voice[1].rate ) & 0x3FFF,
@@ -531,7 +351,7 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
531 #define IF_RBE(...) __VA_ARGS__ 351 #define IF_RBE(...) __VA_ARGS__
532 #ifdef CPU_COLDFIRE 352 #ifdef CPU_COLDFIRE
533 /* Initialize mask register with the buffer address mask */ 353 /* Initialize mask register with the buffer address mask */
534 asm volatile ("move.l %[m], %%mask" : : [m]"i"(fir_buf_mask)); 354 asm volatile ("move.l %[m], %%mask" : : [m]"i"(FIR_BUF_MASK));
535 const int echo_wrap = (this->r.g.echo_delay & 15) * 0x800; 355 const int echo_wrap = (this->r.g.echo_delay & 15) * 0x800;
536 const int echo_start = this->r.g.echo_page * 0x100; 356 const int echo_start = this->r.g.echo_page * 0x100;
537 #endif /* CPU_COLDFIRE */ 357 #endif /* CPU_COLDFIRE */
@@ -757,9 +577,9 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
757#endif 577#endif
758 #if !SPC_BRRCACHE 578 #if !SPC_BRRCACHE
759 /* Decode BRR block */ 579 /* Decode BRR block */
760 if ( voice->position >= brr_block_size * 0x1000 ) 580 if ( voice->position >= BRR_BLOCK_SIZE * 0x1000 )
761 { 581 {
762 voice->position -= brr_block_size * 0x1000; 582 voice->position -= BRR_BLOCK_SIZE * 0x1000;
763 583
764 uint8_t const* addr = voice->addr; 584 uint8_t const* addr = voice->addr;
765 if ( addr >= RAM + 0x10000 ) 585 if ( addr >= RAM + 0x10000 )
@@ -805,13 +625,13 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
805 int const left_shift = left_shifts [scale]; 625 int const left_shift = left_shifts [scale];
806 626
807 /* previous samples */ 627 /* previous samples */
808 int smp2 = voice->samples [brr_block_size + 1]; 628 int smp2 = voice->samples [BRR_BLOCK_SIZE + 1];
809 int smp1 = voice->samples [brr_block_size + 2]; 629 int smp1 = voice->samples [BRR_BLOCK_SIZE + 2];
810 voice->samples [0] = voice->samples [brr_block_size]; 630 voice->samples [0] = voice->samples [BRR_BLOCK_SIZE];
811 631
812 /* output position */ 632 /* output position */
813 short* out = voice->samples + (1 + brr_block_size); 633 short* out = voice->samples + (1 + BRR_BLOCK_SIZE);
814 int offset = -brr_block_size << 2; 634 int offset = -BRR_BLOCK_SIZE << 2;
815 635
816 /* if next block has end flag set, 636 /* if next block has end flag set,
817 this block ends early (verified) */ 637 this block ends early (verified) */
@@ -820,9 +640,9 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
820 /* arrange for last 9 samples to be skipped */ 640 /* arrange for last 9 samples to be skipped */
821 int const skip = 9; 641 int const skip = 9;
822 out += (skip & 1); 642 out += (skip & 1);
823 voice->samples [skip] = voice->samples [brr_block_size]; 643 voice->samples [skip] = voice->samples [BRR_BLOCK_SIZE];
824 voice->position += skip * 0x1000; 644 voice->position += skip * 0x1000;
825 offset = (-brr_block_size + (skip & ~1)) << 2; 645 offset = (-BRR_BLOCK_SIZE + (skip & ~1)) << 2;
826 addr -= skip / 2; 646 addr -= skip / 2;
827 /* force sample to end on next decode */ 647 /* force sample to end on next decode */
828 voice->block_header = 1; 648 voice->block_header = 1;
@@ -1026,7 +846,7 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
1026 "asr.l %[sh], %[output] \r\n" 846 "asr.l %[sh], %[output] \r\n"
1027 "mac.l %[vvol_0], %[output], %%acc0 \r\n" 847 "mac.l %[vvol_0], %[output], %%acc0 \r\n"
1028 "mac.l %[vvol_1], %[output], %%acc1 \r\n" 848 "mac.l %[vvol_1], %[output], %%acc1 \r\n"
1029 : [output]"=&r"(amp_0) 849 : [output]"=&d"(amp_0)
1030 : [vvol_0]"r"((int)voice->volume[0]), 850 : [vvol_0]"r"((int)voice->volume[0]),
1031 [vvol_1]"r"((int)voice->volume[1]), 851 [vvol_1]"r"((int)voice->volume[1]),
1032 [sh]"d"(11) 852 [sh]"d"(11)
@@ -1252,12 +1072,12 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
1252 1072
1253 /* Keep last 8 samples */ 1073 /* Keep last 8 samples */
1254 int (* const fir_ptr) [2] = this->fir_buf + this->fir_pos; 1074 int (* const fir_ptr) [2] = this->fir_buf + this->fir_pos;
1255 this->fir_pos = (this->fir_pos + 1) & (fir_buf_half - 1); 1075 this->fir_pos = (this->fir_pos + 1) & (FIR_BUF_HALF - 1);
1256 fir_ptr [ 0] [0] = fb_0; 1076 fir_ptr [ 0] [0] = fb_0;
1257 fir_ptr [ 0] [1] = fb_1; 1077 fir_ptr [ 0] [1] = fb_1;
1258 /* duplicate at +8 eliminates wrap checking below */ 1078 /* duplicate at +8 eliminates wrap checking below */
1259 fir_ptr [fir_buf_half] [0] = fb_0; 1079 fir_ptr [FIR_BUF_HALF] [0] = fb_0;
1260 fir_ptr [fir_buf_half] [1] = fb_1; 1080 fir_ptr [FIR_BUF_HALF] [1] = fb_1;
1261 1081
1262 /* Apply FIR */ 1082 /* Apply FIR */
1263 fb_0 *= this->fir_coeff [0]; 1083 fb_0 *= this->fir_coeff [0];
@@ -1311,12 +1131,41 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
1311#endif 1131#endif
1312} 1132}
1313 1133
1314static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out ) 1134void DSP_reset( struct Spc_Dsp* this )
1315{ 1135{
1316 /* Should we just fill the buffer with silence? Flags won't be cleared */ 1136 this->keys_down = 0;
1317 /* during this run so it seems it should keep resetting every sample. */ 1137 this->echo_pos = 0;
1318 if ( this->r.g.flags & 0x80 ) 1138 this->noise_count = 0;
1319 DSP_reset( this ); 1139 this->noise = 2;
1320 1140
1321 DSP_run_( this, count, out ); 1141 this->r.g.flags = 0xE0; /* reset, mute, echo off */
1142 this->r.g.key_ons = 0;
1143
1144 ci->memset( this->voice_state, 0, sizeof this->voice_state );
1145
1146 int i;
1147 for ( i = VOICE_COUNT; --i >= 0; )
1148 {
1149 struct voice_t* v = this->voice_state + i;
1150 v->env_mode = state_release;
1151 v->addr = ram.ram;
1152 }
1153
1154 #if SPC_BRRCACHE
1155 this->oldsize = 0;
1156 for ( i = 0; i < 256; i++ )
1157 this->wave_entry [i].start_addr = -1;
1158 #endif
1159
1160#ifdef CPU_COLDFIRE
1161 this->fir_ptr = fir_buf;
1162 this->last_fir_ptr = &fir_buf [7];
1163 ci->memset( fir_buf, 0, sizeof fir_buf );
1164#else
1165 this->fir_pos = 0;
1166 ci->memset( this->fir_buf, 0, sizeof this->fir_buf );
1167#endif
1168
1169 assert( offsetof (struct globals_t,unused9 [2]) == REGISTER_COUNT );
1170 assert( sizeof (this->r.voice) == REGISTER_COUNT );
1322} 1171}
diff --git a/apps/codecs/spc/spc_emu.c b/apps/codecs/spc/spc_emu.c
new file mode 100644
index 0000000000..30aaf5d64b
--- /dev/null
+++ b/apps/codecs/spc/spc_emu.c
@@ -0,0 +1,378 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
11 * Copyright (C) 2004-2007 Shay Green (blargg)
12 * Copyright (C) 2002 Brad Martin
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "codec.h"
22#include "codecs.h"
23#include "spc_codec.h"
24#include "spc_profiler.h"
25
26/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
27/* DSP Based on Brad Martin's OpenSPC DSP emulator */
28/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
29
30struct cpu_ram_t ram;
31
32/**************** Timers ****************/
33
34void Timer_run_( struct Timer* t, long time )
35{
36 /* when disabled, next_tick should always be in the future */
37 assert( t->enabled );
38
39 int elapsed = ((time - t->next_tick) >> t->shift) + 1;
40 t->next_tick += elapsed << t->shift;
41
42 elapsed += t->count;
43 if ( elapsed >= t->period ) /* avoid unnecessary division */
44 {
45 int n = elapsed / t->period;
46 elapsed -= n * t->period;
47 t->counter = (t->counter + n) & 15;
48 }
49 t->count = elapsed;
50}
51
52/**************** SPC emulator ****************/
53/* 1.024 MHz clock / 32000 samples per second */
54
55static void SPC_enable_rom( THIS, int enable )
56{
57 if ( this->rom_enabled != enable )
58 {
59 this->rom_enabled = enable;
60 ci->memcpy( RAM + ROM_ADDR, (enable ? this->boot_rom : this->extra_ram), ROM_SIZE );
61 /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
62 }
63}
64
65void SPC_Init( THIS )
66{
67 this->timer [0].shift = 4 + 3; /* 8 kHz */
68 this->timer [1].shift = 4 + 3; /* 8 kHz */
69 this->timer [2].shift = 4; /* 8 kHz */
70
71 /* Put STOP instruction around memory to catch PC underflow/overflow. */
72 ci->memset( ram.padding1, 0xFF, sizeof ram.padding1 );
73 ci->memset( ram.padding2, 0xFF, sizeof ram.padding2 );
74
75 /* A few tracks read from the last four bytes of IPL ROM */
76 this->boot_rom [sizeof this->boot_rom - 2] = 0xC0;
77 this->boot_rom [sizeof this->boot_rom - 1] = 0xFF;
78 ci->memset( this->boot_rom, 0, sizeof this->boot_rom - 2 );
79}
80
81static void SPC_load_state( THIS, struct cpu_regs_t const* cpu_state,
82 const void* new_ram, const void* dsp_state )
83{
84 ci->memcpy(&(this->r),cpu_state,sizeof this->r);
85
86 /* ram */
87 ci->memcpy( RAM, new_ram, sizeof RAM );
88 ci->memcpy( this->extra_ram, RAM + ROM_ADDR, sizeof this->extra_ram );
89
90 /* boot rom (have to force enable_rom() to update it) */
91 this->rom_enabled = !(RAM [0xF1] & 0x80);
92 SPC_enable_rom( this, !this->rom_enabled );
93
94 /* dsp */
95 /* some SPCs rely on DSP immediately generating one sample */
96 this->extra_cycles = 32;
97 DSP_reset( &this->dsp );
98 int i;
99 for ( i = 0; i < REGISTER_COUNT; i++ )
100 DSP_write( &this->dsp, i, ((uint8_t const*) dsp_state) [i] );
101
102 /* timers */
103 for ( i = 0; i < TIMER_COUNT; i++ )
104 {
105 struct Timer* t = &this->timer [i];
106
107 t->next_tick = -EXTRA_CLOCKS;
108 t->enabled = (RAM [0xF1] >> i) & 1;
109 if ( !t->enabled )
110 t->next_tick = TIMER_DISABLED_TIME;
111 t->count = 0;
112 t->counter = RAM [0xFD + i] & 15;
113
114 int p = RAM [0xFA + i];
115 if ( !p )
116 p = 0x100;
117 t->period = p;
118 }
119
120 /* Handle registers which already give 0 when read by
121 setting RAM and not changing it.
122 Put STOP instruction in registers which can be read,
123 to catch attempted execution. */
124 RAM [0xF0] = 0;
125 RAM [0xF1] = 0;
126 RAM [0xF3] = 0xFF;
127 RAM [0xFA] = 0;
128 RAM [0xFB] = 0;
129 RAM [0xFC] = 0;
130 RAM [0xFD] = 0xFF;
131 RAM [0xFE] = 0xFF;
132 RAM [0xFF] = 0xFF;
133}
134
135static void clear_echo( THIS )
136{
137 if ( !(DSP_read( &this->dsp, 0x6C ) & 0x20) )
138 {
139 unsigned addr = 0x100 * DSP_read( &this->dsp, 0x6D );
140 size_t size = 0x800 * DSP_read( &this->dsp, 0x7D );
141 size_t max_size = sizeof RAM - addr;
142 if ( size > max_size )
143 size = sizeof RAM - addr;
144 ci->memset( RAM + addr, 0xFF, size );
145 }
146}
147
148int SPC_load_spc( THIS, const void* data, long size )
149{
150 struct spc_file_t const* spc = (struct spc_file_t const*) data;
151 struct cpu_regs_t regs;
152
153 if ( size < SPC_FILE_SIZE )
154 return -1;
155
156 if ( ci->memcmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
157 return -1;
158
159 regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
160 regs.a = spc->a;
161 regs.x = spc->x;
162 regs.y = spc->y;
163 regs.status = spc->status;
164 regs.sp = spc->sp;
165
166 if ( (unsigned long) size >= sizeof *spc )
167 ci->memcpy( this->boot_rom, spc->ipl_rom, sizeof this->boot_rom );
168
169 SPC_load_state( this, &regs, spc->ram, spc->dsp );
170
171 clear_echo(this);
172
173 return 0;
174}
175
176/**************** DSP interaction ****************/
177void SPC_run_dsp_( THIS, long time )
178{
179 /* divide by CLOCKS_PER_SAMPLE */
180 int count = ((time - this->next_dsp) >> 5) + 1;
181 int32_t* buf = this->sample_buf;
182 this->sample_buf = buf + count;
183 this->next_dsp += count * CLOCKS_PER_SAMPLE;
184 DSP_run( &this->dsp, count, buf );
185}
186
187int SPC_read( THIS, unsigned addr, long const time )
188{
189 int result = RAM [addr];
190
191 if ( ((unsigned) (addr - 0xF0)) < 0x10 )
192 {
193 assert( 0xF0 <= addr && addr <= 0xFF );
194
195 /* counters */
196 int i = addr - 0xFD;
197 if ( i >= 0 )
198 {
199 struct Timer* t = &this->timer [i];
200 Timer_run( t, time );
201 result = t->counter;
202 t->counter = 0;
203 }
204 /* dsp */
205 else if ( addr == 0xF3 )
206 {
207 SPC_run_dsp( this, time );
208 result = DSP_read( &this->dsp, RAM [0xF2] & 0x7F );
209 }
210 }
211 return result;
212}
213
214void SPC_write( THIS, unsigned addr, int data, long const time )
215{
216 /* first page is very common */
217 if ( addr < 0xF0 )
218 {
219 RAM [addr] = (uint8_t) data;
220 }
221 else switch ( addr )
222 {
223 /* RAM */
224 default:
225 if ( addr < ROM_ADDR )
226 {
227 RAM [addr] = (uint8_t) data;
228 }
229 else
230 {
231 this->extra_ram [addr - ROM_ADDR] = (uint8_t) data;
232 if ( !this->rom_enabled )
233 RAM [addr] = (uint8_t) data;
234 }
235 break;
236
237 /* DSP */
238 /*case 0xF2:*/ /* mapped to RAM */
239 case 0xF3: {
240 SPC_run_dsp( this, time );
241 int reg = RAM [0xF2];
242 if ( reg < REGISTER_COUNT ) {
243 DSP_write( &this->dsp, reg, data );
244 }
245 else {
246 /*dprintf( "DSP write to $%02X\n", (int) reg ); */
247 }
248 break;
249 }
250
251 case 0xF0: /* Test register */
252 /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
253 break;
254
255 /* Config */
256 case 0xF1:
257 {
258 int i;
259 /* timers */
260 for ( i = 0; i < TIMER_COUNT; i++ )
261 {
262 struct Timer * t = this->timer+i;
263 if ( !(data & (1 << i)) )
264 {
265 t->enabled = 0;
266 t->next_tick = TIMER_DISABLED_TIME;
267 }
268 else if ( !t->enabled )
269 {
270 /* just enabled */
271 t->enabled = 1;
272 t->counter = 0;
273 t->count = 0;
274 t->next_tick = time;
275 }
276 }
277
278 /* port clears */
279 if ( data & 0x10 )
280 {
281 RAM [0xF4] = 0;
282 RAM [0xF5] = 0;
283 }
284 if ( data & 0x20 )
285 {
286 RAM [0xF6] = 0;
287 RAM [0xF7] = 0;
288 }
289
290 SPC_enable_rom( this, (data & 0x80) != 0 );
291 break;
292 }
293
294 /* Ports */
295 case 0xF4:
296 case 0xF5:
297 case 0xF6:
298 case 0xF7:
299 /* to do: handle output ports */
300 break;
301
302 /* verified on SNES that these are read/write (RAM) */
303 /*case 0xF8: */
304 /*case 0xF9: */
305
306 /* Timers */
307 case 0xFA:
308 case 0xFB:
309 case 0xFC: {
310 int i = addr - 0xFA;
311 struct Timer* t = &this->timer [i];
312 if ( (t->period & 0xFF) != data )
313 {
314 Timer_run( t, time );
315 this->timer[i].period = data ? data : 0x100;
316 }
317 break;
318 }
319
320 /* Counters (cleared on write) */
321 case 0xFD:
322 case 0xFE:
323 case 0xFF:
324 /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
325 this->timer [addr - 0xFD].counter = 0;
326 break;
327 }
328}
329
330/**************** Sample generation ****************/
331int SPC_play( THIS, long count, int32_t* out )
332{
333 int i;
334 assert( count % 2 == 0 ); /* output is always in pairs of samples */
335
336 long start_time = -(count >> 1) * CLOCKS_PER_SAMPLE - EXTRA_CLOCKS;
337
338 /* DSP output is made on-the-fly when DSP registers are read or written */
339 this->sample_buf = out;
340 this->next_dsp = start_time + CLOCKS_PER_SAMPLE;
341
342 /* Localize timer next_tick times and run them to the present to prevent
343 a running but ignored timer's next_tick from getting too far behind
344 and overflowing. */
345 for ( i = 0; i < TIMER_COUNT; i++ )
346 {
347 struct Timer* t = &this->timer [i];
348 if ( t->enabled )
349 {
350 t->next_tick += start_time + EXTRA_CLOCKS;
351 Timer_run( t, start_time );
352 }
353 }
354
355 /* Run from start_time to 0, pre-advancing by extra cycles from last run */
356 this->extra_cycles = CPU_run( this, start_time + this->extra_cycles ) +
357 EXTRA_CLOCKS;
358 if ( this->extra_cycles < 0 )
359 {
360 /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
361 (int) CPU_read( r.pc ), (unsigned) r.pc ); */
362
363 return -1;
364 }
365
366 /* Catch DSP up to present */
367#if 0
368 ENTER_TIMER(cpu);
369#endif
370 SPC_run_dsp( this, -EXTRA_CLOCKS );
371#if 0
372 EXIT_TIMER(cpu);
373#endif
374 assert( this->next_dsp == CLOCKS_PER_SAMPLE - EXTRA_CLOCKS );
375 assert( this->sample_buf - out == count );
376
377 return 0;
378}
diff --git a/apps/codecs/spc/spc_profiler.c b/apps/codecs/spc/spc_profiler.c
new file mode 100644
index 0000000000..60e0ef7f82
--- /dev/null
+++ b/apps/codecs/spc/spc_profiler.c
@@ -0,0 +1,64 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
21/* DSP Based on Brad Martin's OpenSPC DSP emulator */
22/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
23
24#if defined(SPC_PROFILE) && defined(USEC_TIMER)
25
26#include "codec.h"
27#include "spc_codec.h"
28#define SPC_DEFINE_PROFILER_TIMERS
29#include "spc_profiler.h"
30
31void reset_profile_timers(void)
32{
33 RESET_TIMER(total);
34 RESET_TIMER(render);
35#if 0
36 RESET_TIMER(cpu);
37 RESET_TIMER(dsp);
38 RESET_TIMER(dsp_pregen);
39 RESET_TIMER(dsp_gen);
40 RESET_TIMER(dsp_mix);
41#endif
42}
43
44void print_timers(char * path)
45{
46 int logfd = ci->open("/spclog.txt",O_WRONLY|O_CREAT|O_APPEND);
47 ci->fdprintf(logfd,"%s:\t",path);
48 ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total));
49 PRINT_TIMER_PCT(render,total,"render");
50#if 0
51 PRINT_TIMER_PCT(cpu,total,"CPU");
52 PRINT_TIMER_PCT(dsp,total,"DSP");
53 ci->fdprintf(logfd,"(");
54 PRINT_TIMER_PCT(dsp_pregen,dsp,"pregen");
55 PRINT_TIMER_PCT(dsp_gen,dsp,"gen");
56 PRINT_TIMER_PCT(dsp_mix,dsp,"mix");
57#endif
58 ci->fdprintf(logfd,"\n");
59
60 ci->close(logfd);
61 logfd=-1;
62}
63
64#endif /* #if defined(SPC_PROFILE) && defined(USEC_TIMER) */
diff --git a/apps/codecs/spc/spc_profiler.h b/apps/codecs/spc/spc_profiler.h
index 99d3fdf16b..330d95bca7 100644
--- a/apps/codecs/spc/spc_profiler.h
+++ b/apps/codecs/spc/spc_profiler.h
@@ -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 * 11 *
@@ -17,11 +18,19 @@
17 ****************************************************************************/ 18 ****************************************************************************/
18 19
19/* a fun simple elapsed time profiler */ 20/* a fun simple elapsed time profiler */
21#ifndef _SPC_PROFILER_H_
22#define _SPC_PROFILER_H_
20 23
21#if defined(SPC_PROFILE) && defined(USEC_TIMER) 24#if defined(SPC_PROFILE) && defined(USEC_TIMER)
22 25
23#define CREATE_TIMER(name) static uint32_t spc_timer_##name##_start,\ 26#ifdef SPC_DEFINE_PROFILER_TIMERS
27#define CREATE_TIMER(name) uint32_t spc_timer_##name##_start,\
24 spc_timer_##name##_total 28 spc_timer_##name##_total
29#else
30#define CREATE_TIMER(name) extern uint32_t spc_timer_##name##_start,\
31 spc_timer_##name##_total
32#endif
33
25#define ENTER_TIMER(name) spc_timer_##name##_start=USEC_TIMER 34#define ENTER_TIMER(name) spc_timer_##name##_start=USEC_TIMER
26#define EXIT_TIMER(name) spc_timer_##name##_total+=\ 35#define EXIT_TIMER(name) spc_timer_##name##_total+=\
27 (USEC_TIMER-spc_timer_##name##_start) 36 (USEC_TIMER-spc_timer_##name##_start)
@@ -43,38 +52,8 @@ CREATE_TIMER(dsp_gen);
43CREATE_TIMER(dsp_mix); 52CREATE_TIMER(dsp_mix);
44#endif 53#endif
45 54
46static void reset_profile_timers(void) { 55void reset_profile_timers(void);
47 RESET_TIMER(total); 56void print_timers(char * path);
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 57
79#else 58#else
80 59
@@ -87,3 +66,5 @@ static void print_timers(char * path) {
87#define reset_profile_timers() 66#define reset_profile_timers()
88 67
89#endif 68#endif
69
70#endif /* _SPC_PROFILER_H_ */