summaryrefslogtreecommitdiff
path: root/apps/codecs/spc
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 /apps/codecs/spc
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
Diffstat (limited to 'apps/codecs/spc')
-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
7 files changed, 586 insertions, 255 deletions
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_ */