diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-07-16 21:03:25 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-07-16 21:03:25 +0000 |
commit | 8552eff9e46ede8968ea13cc172e1c61856bee18 (patch) | |
tree | 7649c84039f0ee4801d56b7b9b38966c3731f79a | |
parent | 76fa0f7e30398e01d405b4391c30b99dca4c9288 (diff) | |
download | rockbox-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/Makefile | 15 | ||||
-rw-r--r-- | apps/codecs/spc.c | 567 | ||||
-rw-r--r-- | apps/codecs/spc/Makefile | 44 | ||||
-rw-r--r-- | apps/codecs/spc/SOURCES | 4 | ||||
-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.c | 378 | ||||
-rw-r--r-- | apps/codecs/spc/spc_profiler.c | 64 | ||||
-rw-r--r-- | apps/codecs/spc/spc_profiler.h | 47 |
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 | |||
17 | endif | 17 | endif |
18 | 18 | ||
19 | ifdef SOFTWARECODECS | 19 | ifdef 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 |
21 | endif | 21 | endif |
22 | 22 | ||
23 | # we "borrow" the plugin LDS file | 23 | # we "borrow" the plugin LDS file |
@@ -39,7 +39,7 @@ DIRS = . | |||
39 | 39 | ||
40 | CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a | 40 | CODECDEPS = $(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 | ||
44 | OUTPUT = $(SOFTWARECODECS) | 44 | OUTPUT = $(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 | |||
145 | libspc: | ||
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 | ||
145 | libmad: | 151 | libmad: |
@@ -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 | ||
215 | clean: | 221 | clean: |
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 | |||
35 | CODEC_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 | 29 | CODEC_HEADER |
85 | |||
86 | /**************** Little-endian handling ****************/ | ||
87 | |||
88 | static inline unsigned get_le16( void const* p ) | ||
89 | { | ||
90 | return ((unsigned char const*) p) [1] * 0x100u + | ||
91 | ((unsigned char const*) p) [0]; | ||
92 | } | ||
93 | |||
94 | static inline int get_le16s( void const* p ) | ||
95 | { | ||
96 | return ((signed char const*) p) [1] * 0x100 + | ||
97 | ((unsigned char const*) p) [0]; | ||
98 | } | ||
99 | |||
100 | static 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 | |||
121 | static 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 | |||
138 | enum { timer_count = 3 }; | ||
139 | |||
140 | struct Timer | ||
141 | { | ||
142 | long next_tick; | ||
143 | int period; | ||
144 | int count; | ||
145 | int shift; | ||
146 | int enabled; | ||
147 | int counter; | ||
148 | }; | ||
149 | |||
150 | static void Timer_run_( struct Timer* t, long time ) ICODE_ATTR; | ||
151 | static 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 | |||
169 | static 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 */ | ||
177 | enum { clocks_per_sample = 32 }; | ||
178 | |||
179 | enum { extra_clocks = clocks_per_sample / 2 }; | ||
180 | |||
181 | /* using this disables timer (since this will always be in the future) */ | ||
182 | enum { timer_disabled_time = 127 }; | ||
183 | |||
184 | enum { rom_size = 64 }; | ||
185 | enum { rom_addr = 0xFFC0 }; | ||
186 | |||
187 | struct 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 | |||
198 | struct 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 | |||
216 | static 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 | |||
226 | static 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 | |||
242 | static 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 | |||
296 | static 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 | |||
309 | enum { spc_file_size = 0x10180 }; | ||
310 | |||
311 | struct 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 | |||
327 | static 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, ®s, spc->ram, spc->dsp ); | ||
349 | |||
350 | clear_echo(this); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | /**************** DSP interaction ****************/ | ||
356 | |||
357 | static void SPC_run_dsp_( THIS, long time ) ICODE_ATTR; | ||
358 | static 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 | |||
368 | static inline void SPC_run_dsp( THIS, long time ) | ||
369 | { | ||
370 | if ( time >= this->next_dsp ) | ||
371 | SPC_run_dsp_( this, time ); | ||
372 | } | ||
373 | |||
374 | static int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR; | ||
375 | static 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 | |||
402 | static void SPC_write( THIS, unsigned addr, int data, long const time ) | ||
403 | ICODE_ATTR; | ||
404 | static 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 | |||
524 | static int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR; | ||
525 | static 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 | ||
734 | static struct Spc_Emu spc_emu IDATA_ATTR; | 191 | static struct Spc_Emu spc_emu IDATA_ATTR; |
735 | 192 | ||
736 | enum {sample_rate = 32000}; | 193 | enum {SAMPLE_RATE = 32000}; |
737 | 194 | ||
738 | /* The main decoder loop */ | 195 | /* The main decoder loop */ |
739 | static int play_track( void ) | 196 | static 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 */ |
825 | enum codec_status codec_main(void) | 282 | enum 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 | |||
10 | INCLUDES=-I$(APPSDIR) -I.. -I. -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ | ||
11 | -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(BUILDDIR) | ||
12 | |||
13 | ifdef APPEXTRA | ||
14 | INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) | ||
15 | endif | ||
16 | |||
17 | SPCOPTS = -O -DROCKBOX | ||
18 | |||
19 | CFLAGS = $(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 | ||
23 | include $(TOOLSDIR)/makesrc.inc | ||
24 | |||
25 | SOURCES = $(SRC) | ||
26 | OBJS2 := $(SRC:%.c=$(OBJDIR)/%.o) | ||
27 | OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2)) | ||
28 | DEPFILE = $(OBJDIR)/dep-spc | ||
29 | DIRS = | ||
30 | |||
31 | all: $(OUTPUT) | ||
32 | |||
33 | $(OUTPUT): $(OBJS) | ||
34 | $(call PRINTS,AR+RANLIB $(@F))$(AR) ruv $@ $+ >/dev/null 2>&1 | ||
35 | $(SILENT)$(RANLIB) $@ | ||
36 | |||
37 | include $(TOOLSDIR)/make.inc | ||
38 | |||
39 | clean: | ||
40 | $(call PRINTS,cleaning spc)rm -f $(OBJS) $(OUTPUT) $(DEPFILE) | ||
41 | |||
42 | ifneq ($(MAKECMDGOALS),clean) | ||
43 | -include $(DEPFILE) | ||
44 | endif | ||
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 @@ | |||
1 | spc_cpu.c | ||
2 | spc_dsp.c | ||
3 | spc_emu.c | ||
4 | spc_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 | ||
106 | static long CPU_run( THIS, long start_time ) ICODE_ATTR; | 113 | long CPU_run( THIS, long start_time ) |
107 | |||
108 | static 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 | |||
1044 | void 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 | ||
24 | enum { voice_count = 8 }; | 28 | #ifdef CPU_COLDFIRE |
25 | enum { register_count = 128 }; | 29 | static int32_t fir_buf[FIR_BUF_HALF] |
26 | 30 | __attribute__ ((aligned (FIR_BUF_SIZE*2))) IBSS_ATTR; | |
27 | struct 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 | |||
39 | struct 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 | |||
68 | enum 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 | |||
75 | struct 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 | |||
83 | enum { brr_block_size = 16 }; | ||
84 | |||
85 | struct 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 */ |
106 | static int16_t BRRcache [0x20000 + 32]; | 35 | int16_t BRRcache [0x20000 + 32]; |
107 | #endif | ||
108 | |||
109 | enum { 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 | */ | ||
115 | enum | ||
116 | { | ||
117 | fir_buf_size = fir_buf_half * sizeof ( int32_t ), | ||
118 | fir_buf_mask = ~fir_buf_size | ||
119 | }; | ||
120 | int32_t fir_buf[fir_buf_half] | ||
121 | __attribute__ ((aligned (fir_buf_size*2))) IBSS_ATTR; | ||
122 | #endif /* CPU_COLDFIRE */ | ||
123 | |||
124 | struct 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 | |||
164 | struct src_dir | ||
165 | { | ||
166 | char start [2]; | ||
167 | char loop [2]; | ||
168 | }; | ||
169 | |||
170 | static 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 ); | 38 | void DSP_write( struct Spc_Dsp* this, int i, int data ) |
206 | assert( sizeof (this->r.voice) == register_count ); | ||
207 | } | ||
208 | |||
209 | static void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR; | ||
210 | static 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 | ||
231 | static 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 | ||
463 | static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) | 285 | void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) |
464 | ICODE_ATTR; | ||
465 | static 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 | ||
1314 | static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out ) | 1134 | void 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 | |||
30 | struct cpu_ram_t ram; | ||
31 | |||
32 | /**************** Timers ****************/ | ||
33 | |||
34 | void 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 | |||
55 | static 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 | |||
65 | void 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 | |||
81 | static 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 | |||
135 | static 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 | |||
148 | int 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, ®s, spc->ram, spc->dsp ); | ||
170 | |||
171 | clear_echo(this); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | /**************** DSP interaction ****************/ | ||
177 | void 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 | |||
187 | int 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 | |||
214 | void 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 ****************/ | ||
331 | int 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 | |||
31 | void 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 | |||
44 | void 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); | |||
43 | CREATE_TIMER(dsp_mix); | 52 | CREATE_TIMER(dsp_mix); |
44 | #endif | 53 | #endif |
45 | 54 | ||
46 | static void reset_profile_timers(void) { | 55 | void reset_profile_timers(void); |
47 | RESET_TIMER(total); | 56 | void 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 | |||
58 | static int logfd=-1; | ||
59 | |||
60 | static 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_ */ | ||