summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libspc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libspc')
-rw-r--r--lib/rbcodec/codecs/libspc/SOURCES4
-rw-r--r--lib/rbcodec/codecs/libspc/libspc.make18
-rw-r--r--lib/rbcodec/codecs/libspc/spc_codec.h491
-rw-r--r--lib/rbcodec/codecs/libspc/spc_cpu.c1049
-rw-r--r--lib/rbcodec/codecs/libspc/spc_dsp.c1594
-rw-r--r--lib/rbcodec/codecs/libspc/spc_emu.c397
-rw-r--r--lib/rbcodec/codecs/libspc/spc_profiler.c66
-rw-r--r--lib/rbcodec/codecs/libspc/spc_profiler.h72
8 files changed, 3691 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libspc/SOURCES b/lib/rbcodec/codecs/libspc/SOURCES
new file mode 100644
index 0000000000..901232a6eb
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/SOURCES
@@ -0,0 +1,4 @@
1spc_cpu.c
2spc_dsp.c
3spc_emu.c
4spc_profiler.c
diff --git a/lib/rbcodec/codecs/libspc/libspc.make b/lib/rbcodec/codecs/libspc/libspc.make
new file mode 100644
index 0000000000..a005f7914a
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/libspc.make
@@ -0,0 +1,18 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10# libspc
11SPCLIB := $(CODECDIR)/libspc.a
12SPCLIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libspc/SOURCES)
13SPCLIB_OBJ := $(call c2obj, $(SPCLIB_SRC))
14OTHER_SRC += $(SPCLIB_SRC)
15
16$(SPCLIB): $(SPCLIB_OBJ)
17 $(SILENT)$(shell rm -f $@)
18 $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
diff --git a/lib/rbcodec/codecs/libspc/spc_codec.h b/lib/rbcodec/codecs/libspc/spc_codec.h
new file mode 100644
index 0000000000..7f6b6e2e9f
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/spc_codec.h
@@ -0,0 +1,491 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS)
11 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
12 * Copyright (C) 2004-2007 Shay Green (blargg)
13 * Copyright (C) 2002 Brad Martin
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
26/* DSP Based on Brad Martin's OpenSPC DSP emulator */
27/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
28
29#ifndef _SPC_CODEC_H_
30#define _SPC_CODEC_H_
31
32/* rather than comment out asserts, just define NDEBUG */
33#ifndef NDEBUG
34#define NDEBUG
35#endif
36#include <assert.h>
37
38/** Basic configuration options **/
39
40#ifndef ARM_ARCH
41#define ARM_ARCH 0
42#endif
43
44#define SPC_DUAL_CORE 1
45
46#if !defined(SPC_DUAL_CORE) || NUM_CORES == 1
47#undef SPC_DUAL_CORE
48#define SPC_DUAL_CORE 0
49#endif
50
51/* Only some targets are fast enough for gaussian and realtime BRR decode */
52#if CONFIG_CPU == S3C2440 || CONFIG_CPU == IMX31L || \
53 CONFIG_CPU == AS3525 || CONFIG_CPU == AS3525v2 || \
54 defined(CPU_S5L870X) || \
55 (CONFIG_PLATFORM & PLATFORM_HOSTED) || MEMORYSIZE <= 2
56 /* Don't cache BRR waves */
57 #define SPC_BRRCACHE 0
58
59 /* Allow gaussian interpolation */
60 #define SPC_NOINTERP 0
61
62 /* Allow echo processing */
63 #define SPC_NOECHO 0
64#elif defined(CPU_COLDFIRE)
65 /* Cache BRR waves */
66 #define SPC_BRRCACHE 1
67
68 /* Disable gaussian interpolation */
69 #define SPC_NOINTERP 1
70
71 /* Allow echo processing */
72 #define SPC_NOECHO 0
73#elif defined (CPU_PP) && SPC_DUAL_CORE
74 /* Cache BRR waves */
75 #define SPC_BRRCACHE 1
76
77 /* Disable gaussian interpolation */
78 #define SPC_NOINTERP 1
79
80 /* Allow echo processing */
81 #define SPC_NOECHO 0
82#else
83 /* Cache BRR waves */
84 #define SPC_BRRCACHE 1
85
86 /* Disable gaussian interpolation */
87 #define SPC_NOINTERP 1
88
89 /* Disable echo processing */
90 #define SPC_NOECHO 1
91#endif
92
93#if (CONFIG_CPU == MCF5250)
94#define IBSS_ATTR_SPC IBSS_ATTR
95#define ICODE_ATTR_SPC ICODE_ATTR
96#define ICONST_ATTR_SPC ICONST_ATTR
97/* Not enough IRAM available to move further data to it. */
98#define IBSS_ATTR_SPC_LARGE_IRAM
99
100#elif (CONFIG_CPU == PP5020)
101/* spc is slower on PP5020 when moving data to IRAM. */
102#define IBSS_ATTR_SPC
103#define ICODE_ATTR_SPC
104#define ICONST_ATTR_SPC
105/* Not enough IRAM available to move further data to it. */
106#define IBSS_ATTR_SPC_LARGE_IRAM
107
108#elif (CONFIG_CPU == PP5022) || (CONFIG_CPU == PP5024)
109#define IBSS_ATTR_SPC IBSS_ATTR
110#define ICODE_ATTR_SPC ICODE_ATTR
111#define ICONST_ATTR_SPC ICONST_ATTR
112/* Not enough IRAM available to move further data to it. */
113#define IBSS_ATTR_SPC_LARGE_IRAM
114
115#elif defined(CPU_S5L870X)
116#define IBSS_ATTR_SPC IBSS_ATTR
117#define ICODE_ATTR_SPC ICODE_ATTR
118#define ICONST_ATTR_SPC ICONST_ATTR
119/* Very large IRAM. Move even more data to it. */
120#define IBSS_ATTR_SPC_LARGE_IRAM IBSS_ATTR
121
122#else
123#define IBSS_ATTR_SPC IBSS_ATTR
124#define ICODE_ATTR_SPC ICODE_ATTR
125#define ICONST_ATTR_SPC ICONST_ATTR
126/* Not enough IRAM available to move further data to it. */
127#define IBSS_ATTR_SPC_LARGE_IRAM
128#endif
129
130#if SPC_DUAL_CORE
131 #undef SHAREDBSS_ATTR
132 #define SHAREDBSS_ATTR __attribute__ ((section(".ibss")))
133 #undef SHAREDDATA_ATTR
134 #define SHAREDDATA_ATTR __attribute__((section(".idata")))
135#endif
136
137/* Samples per channel per iteration */
138#if defined(CPU_PP) && NUM_CORES == 1
139#define WAV_CHUNK_SIZE 2048
140#else
141#define WAV_CHUNK_SIZE 1024
142#endif
143
144/**************** Little-endian handling ****************/
145
146static inline unsigned get_le16( void const* p )
147{
148 return ((unsigned char const*) p) [1] * 0x100u +
149 ((unsigned char const*) p) [0];
150}
151
152static inline int get_le16s( void const* p )
153{
154 return ((signed char const*) p) [1] * 0x100 +
155 ((unsigned char const*) p) [0];
156}
157
158static inline void set_le16( void* p, unsigned n )
159{
160 ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
161 ((unsigned char*) p) [0] = (unsigned char) n;
162}
163
164#define GET_LE16( addr ) get_le16( addr )
165#define GET_LE16A( addr ) get_le16( addr )
166#define SET_LE16( addr, data ) set_le16( addr, data )
167#define INT16A( addr ) (*(uint16_t*) (addr))
168#define INT16SA( addr ) (*(int16_t*) (addr))
169
170#ifdef ROCKBOX_LITTLE_ENDIAN
171 #define GET_LE16SA( addr ) (*( int16_t*) (addr))
172 #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data))
173#else
174 #define GET_LE16SA( addr ) get_le16s( addr )
175 #define SET_LE16A( addr, data ) set_le16 ( addr, data )
176#endif
177
178struct Spc_Emu;
179#define THIS struct Spc_Emu* const this
180
181/* The CPU portion (shock!) */
182
183struct cpu_regs_t
184{
185 long pc; /* more than 16 bits to allow overflow detection */
186 uint8_t a;
187 uint8_t x;
188 uint8_t y;
189 uint8_t status;
190 uint8_t sp;
191};
192
193struct src_dir
194{
195 uint16_t start;
196 uint16_t loop;
197};
198
199struct cpu_ram_t
200{
201 union {
202 uint8_t padding1 [0x100];
203 uint16_t align;
204 } padding1 [1];
205 union {
206 uint8_t ram [0x10000];
207 struct src_dir sd [0x10000/sizeof(struct src_dir)];
208 };
209 uint8_t padding2 [0x100];
210};
211
212#undef RAM
213#define RAM ram.ram
214extern struct cpu_ram_t ram;
215
216long CPU_run( THIS, long start_time ) ICODE_ATTR_SPC;
217void CPU_Init( THIS );
218
219/* The DSP portion (awe!) */
220enum { VOICE_COUNT = 8 };
221enum { REGISTER_COUNT = 128 };
222
223struct raw_voice_t
224{
225 int8_t volume [2];
226 uint8_t rate [2];
227 uint8_t waveform;
228 uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */
229 uint8_t gain; /* envelope gain (if not using ADSR) */
230 int8_t envx; /* current envelope level */
231 int8_t outx; /* current sample */
232 int8_t unused [6];
233};
234
235struct globals_t
236{
237 int8_t unused1 [12];
238 int8_t volume_0; /* 0C Main Volume Left (-.7) */
239 int8_t echo_feedback; /* 0D Echo Feedback (-.7) */
240 int8_t unused2 [14];
241 int8_t volume_1; /* 1C Main Volume Right (-.7) */
242 int8_t unused3 [15];
243 int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */
244 uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */
245 int8_t unused4 [14];
246 int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */
247 uint8_t noise_enables; /* 3D Noise output on/off for each voice */
248 int8_t unused5 [14];
249 uint8_t key_ons; /* 4C Key On for each voice */
250 uint8_t echo_ons; /* 4D Echo on/off for each voice */
251 int8_t unused6 [14];
252 uint8_t key_offs; /* 5C key off for each voice
253 (instantiates release mode) */
254 uint8_t wave_page; /* 5D source directory (wave table offsets) */
255 int8_t unused7 [14];
256 uint8_t flags; /* 6C flags and noise freq */
257 uint8_t echo_page; /* 6D */
258 int8_t unused8 [14];
259 uint8_t wave_ended; /* 7C */
260 uint8_t echo_delay; /* 7D ms >> 4 */
261 char unused9 [2];
262};
263
264enum state_t
265{ /* -1, 0, +1 allows more efficient if statements */
266 state_decay = -1,
267 state_sustain = 0,
268 state_attack = +1,
269 state_release = 2
270};
271
272struct cache_entry_t
273{
274 int16_t const* samples;
275 unsigned end; /* past-the-end position */
276 unsigned loop; /* number of samples in loop */
277 unsigned start_addr;
278};
279
280enum { BRR_BLOCK_SIZE = 16 };
281enum { BRR_CACHE_SIZE = 0x20000 + 32} ;
282
283struct voice_t
284{
285#if SPC_BRRCACHE
286 int16_t const* samples;
287 long wave_end;
288 int wave_loop;
289#else
290 int16_t samples [3 + BRR_BLOCK_SIZE + 1];
291 int block_header; /* header byte from current block */
292#endif
293 uint8_t const* addr;
294 short volume [2];
295 long position;/* position in samples buffer, with 12-bit fraction */
296 short envx;
297 short env_mode;
298 short env_timer;
299 short key_on_delay;
300};
301
302#if SPC_BRRCACHE
303/* a little extra for samples that go past end */
304extern int16_t BRRcache [BRR_CACHE_SIZE];
305#endif
306
307enum { FIR_BUF_HALF = 8 };
308
309#if defined(CPU_COLDFIRE)
310/* global because of the large aligment requirement for hardware masking -
311 * L-R interleaved 16-bit samples for easy loading and mac.w use.
312 */
313enum
314{
315 FIR_BUF_CNT = FIR_BUF_HALF,
316 FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ),
317 FIR_BUF_ALIGN = FIR_BUF_SIZE * 2,
318 FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1))
319};
320#elif defined (CPU_ARM)
321#if ARM_ARCH >= 6
322enum
323{
324 FIR_BUF_CNT = FIR_BUF_HALF * 2,
325 FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ),
326 FIR_BUF_ALIGN = FIR_BUF_SIZE,
327 FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1))
328};
329#else
330enum
331{
332 FIR_BUF_CNT = FIR_BUF_HALF * 2 * 2,
333 FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ),
334 FIR_BUF_ALIGN = FIR_BUF_SIZE,
335 FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) * 2 - 1))
336};
337#endif /* ARM_ARCH */
338#endif /* CPU_* */
339
340struct Spc_Dsp
341{
342 union
343 {
344 struct raw_voice_t voice [VOICE_COUNT];
345 uint8_t reg [REGISTER_COUNT];
346 struct globals_t g;
347 int16_t align;
348 } r;
349
350 unsigned echo_pos;
351 int keys_down;
352 int noise_count;
353 uint16_t noise; /* also read as int16_t */
354
355#if defined(CPU_COLDFIRE)
356 /* FIR history is interleaved. Hardware handles wrapping by mask.
357 * |LR|LR|LR|LR|LR|LR|LR|LR| */
358 int32_t *fir_ptr;
359 /* wrapped address just behind current position -
360 allows mac.w to increment and mask fir_ptr */
361 int32_t *last_fir_ptr;
362 /* copy of echo FIR constants as int16_t for use with mac.w */
363 int16_t fir_coeff [VOICE_COUNT];
364#elif defined (CPU_ARM)
365 /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */
366 int32_t *fir_ptr;
367#if ARM_ARCH >= 6
368 /* FIR history is interleaved with guard to eliminate wrap checking
369 * when convolving.
370 * |LR|LR|LR|LR|LR|LR|LR|LR|--|--|--|--|--|--|--|--| */
371 /* copy of echo FIR constants as int16_t, loaded as int32 for
372 * halfword, packed multiples */
373 int16_t fir_coeff [VOICE_COUNT];
374#else
375 /* FIR history is interleaved with guard to eliminate wrap checking
376 * when convolving.
377 * |LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|...
378 * |--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--| */
379 /* copy of echo FIR constants as int32_t, for faster access */
380 int32_t fir_coeff [VOICE_COUNT];
381#endif /* ARM_ARCH */
382#else /* Unoptimized CPU */
383 /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */
384 int fir_pos; /* (0 to 7) */
385 int fir_buf [FIR_BUF_HALF * 2] [2];
386 /* copy of echo FIR constants as int, for faster access */
387 int fir_coeff [VOICE_COUNT];
388#endif
389
390 struct voice_t voice_state [VOICE_COUNT];
391
392#if SPC_BRRCACHE
393 uint8_t oldsize;
394 struct cache_entry_t wave_entry [256];
395 struct cache_entry_t wave_entry_old [256];
396#endif
397};
398
399void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) ICODE_ATTR_SPC;
400void DSP_reset( struct Spc_Dsp* this );
401
402static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out )
403{
404 /* Should we just fill the buffer with silence? Flags won't be cleared */
405 /* during this run so it seems it should keep resetting every sample. */
406 if ( this->r.g.flags & 0x80 )
407 DSP_reset( this );
408
409 DSP_run_( this, count, out );
410}
411
412/**************** SPC emulator ****************/
413/* 1.024 MHz clock / 32000 samples per second */
414enum { CLOCKS_PER_SAMPLE = 32 };
415
416enum { EXTRA_CLOCKS = CLOCKS_PER_SAMPLE / 2 };
417
418/* using this disables timer (since this will always be in the future) */
419enum { TIMER_DISABLED_TIME = 127 };
420
421enum { ROM_SIZE = 64 };
422enum { ROM_ADDR = 0xFFC0 };
423
424enum { TIMER_COUNT = 3 };
425
426struct Timer
427{
428 long next_tick;
429 int period;
430 int count;
431 int shift;
432 int enabled;
433 int counter;
434};
435
436struct Spc_Emu
437{
438 uint8_t cycle_table [0x100];
439 struct cpu_regs_t r;
440
441 int32_t* sample_buf;
442 long next_dsp;
443 int rom_enabled;
444 int extra_cycles;
445
446 struct Timer timer [TIMER_COUNT];
447
448 /* large objects at end */
449 struct Spc_Dsp dsp;
450 uint8_t extra_ram [ROM_SIZE];
451 uint8_t boot_rom [ROM_SIZE];
452};
453
454enum { SPC_FILE_SIZE = 0x10180 };
455
456struct spc_file_t
457{
458 char signature [27];
459 char unused [10];
460 uint8_t pc [2];
461 uint8_t a;
462 uint8_t x;
463 uint8_t y;
464 uint8_t status;
465 uint8_t sp;
466 char unused2 [212];
467 uint8_t ram [0x10000];
468 uint8_t dsp [128];
469 uint8_t ipl_rom [128];
470};
471
472void SPC_Init( THIS );
473
474int SPC_load_spc( THIS, const void* data, long size );
475
476/**************** DSP interaction ****************/
477void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR_SPC;
478
479static inline int DSP_read( struct Spc_Dsp* this, int i )
480{
481 assert( (unsigned) i < REGISTER_COUNT );
482 return this->r.reg [i];
483}
484
485int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR_SPC;
486void SPC_write( THIS, unsigned addr, int data, long const time ) ICODE_ATTR_SPC;
487
488/**************** Sample generation ****************/
489int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR_SPC;
490
491#endif /* _SPC_CODEC_H_ */
diff --git a/lib/rbcodec/codecs/libspc/spc_cpu.c b/lib/rbcodec/codecs/libspc/spc_cpu.c
new file mode 100644
index 0000000000..23dcc257de
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/spc_cpu.c
@@ -0,0 +1,1049 @@
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 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24/* The CPU portion (shock!) */
25#include "codeclib.h"
26#include "spc_codec.h"
27#include "spc_profiler.h"
28
29#undef check
30#define check assert
31
32#define READ( addr ) (SPC_read( this, addr, spc_time_ ))
33#define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ ))
34
35#define READ_DP( addr ) READ( (addr) + dp )
36#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
37
38#define READ_PROG16( addr ) GET_LE16( RAM + (addr) )
39
40#define READ_PC( pc ) (*(pc))
41#define READ_PC16( pc ) GET_LE16( pc )
42
43#define SET_PC( n ) (pc = RAM + (n))
44#define GET_PC() (pc - RAM)
45
46static unsigned char const cycle_table [0x100] = {
47 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, /* 0 */
48 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, /* 1 */
49 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, /* 2 */
50 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, /* 3 */
51 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, /* 4 */
52 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, /* 5 */
53 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, /* 6 */
54 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, /* 7 */
55 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, /* 8 */
56 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,/* 9 */
57 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, /* A */
58 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, /* B */
59 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, /* C */
60 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, /* D */
61 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, /* E */
62 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 /* F */
63};
64
65#define MEM_BIT() CPU_mem_bit( this, pc, spc_time_ )
66
67static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
68 ICODE_ATTR_SPC;
69
70static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
71{
72 unsigned addr = READ_PC16( pc );
73 unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13);
74 return (t << 8) & 0x100;
75}
76
77/* status flags */
78enum { st_n = 0x80 };
79enum { st_v = 0x40 };
80enum { st_p = 0x20 };
81enum { st_b = 0x10 };
82enum { st_h = 0x08 };
83enum { st_i = 0x04 };
84enum { st_z = 0x02 };
85enum { st_c = 0x01 };
86
87#define IS_NEG (nz & 0x880)
88
89#define CALC_STATUS( out )\
90{\
91 out = status & ~(st_n | st_z | st_c);\
92 out |= (c >> 8) & st_c;\
93 out |= (dp >> 3) & st_p;\
94 if ( IS_NEG ) out |= st_n;\
95 if ( !(nz & 0xFF) ) out |= st_z;\
96}
97
98#define SET_STATUS( in )\
99{\
100 status = in & ~(st_n | st_z | st_c | st_p);\
101 c = in << 8;\
102 nz = ((in << 4) & 0x800) | (~in & st_z);\
103 dp = (in << 3) & 0x100;\
104}
105
106
107/* stack */
108#define PUSH( v ) (*--sp = (uint8_t) (v))
109#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
110#define POP() (*sp++)
111#define SET_SP( v ) (sp = RAM + 0x101 + (v))
112#define GET_SP() (sp - 0x101 - RAM)
113
114long CPU_run( THIS, long start_time )
115{
116#if 0
117 ENTER_TIMER(cpu);
118#endif
119
120 register long spc_time_ = start_time;
121
122#ifdef CPU_ARM
123 uint8_t* const ram_ = ram.ram;
124 #undef RAM
125 #define RAM ram_
126#endif
127
128 int a = this->r.a;
129 int x = this->r.x;
130 int y = this->r.y;
131
132 uint8_t const* pc;
133 SET_PC( this->r.pc );
134
135 uint8_t* sp;
136 SET_SP( this->r.sp );
137
138 int status;
139 int c;
140 int nz;
141 unsigned dp;
142 {
143 int temp = this->r.status;
144 SET_STATUS( temp );
145 }
146
147 goto loop;
148
149 /* main loop */
150cbranch_taken_loop:
151 pc += *(int8_t const*) pc;
152 spc_time_ += 2;
153inc_pc_loop:
154 pc++;
155loop:
156 check( (unsigned) GET_PC() < 0x10000 );
157 check( (unsigned) GET_SP() < 0x100 );
158 check( (unsigned) a < 0x100 );
159 check( (unsigned) x < 0x100 );
160 check( (unsigned) y < 0x100 );
161
162 unsigned opcode = *pc;
163 int cycles = this->cycle_table [opcode];
164 unsigned data = *++pc;
165 if ( (spc_time_ += cycles) > 0 )
166 goto out_of_time;
167 switch ( opcode )
168 {
169
170/* Common instructions */
171
172#define BRANCH( cond )\
173{\
174 pc++;\
175 int offset = (int8_t) data;\
176 if ( cond ) {\
177 pc += offset;\
178 spc_time_ += 2;\
179 }\
180 goto loop;\
181}
182
183 case 0xF0: /* BEQ (most common) */
184 BRANCH( !(uint8_t) nz )
185
186 case 0xD0: /* BNE */
187 BRANCH( (uint8_t) nz )
188
189 case 0x3F: /* CALL */
190 PUSH16( GET_PC() + 2 );
191 SET_PC( READ_PC16( pc ) );
192 goto loop;
193
194 case 0x6F: /* RET */
195 SET_PC( POP() );
196 pc += POP() * 0x100;
197 goto loop;
198
199#define CASE( n ) case n:
200
201/* Define common address modes based on opcode for immediate mode. Execution
202 ends with data set to the address of the operand. */
203#define ADDR_MODES( op )\
204 CASE( op - 0x02 ) /* (X) */\
205 data = x + dp;\
206 pc--;\
207 goto end_##op;\
208 CASE( op + 0x0F ) /* (dp)+Y */\
209 data = READ_PROG16( data + dp ) + y;\
210 goto end_##op;\
211 CASE( op - 0x01 ) /* (dp+X) */\
212 data = READ_PROG16( ((uint8_t) (data + x)) + dp );\
213 goto end_##op;\
214 CASE( op + 0x0E ) /* abs+Y */\
215 data += y;\
216 goto abs_##op;\
217 CASE( op + 0x0D ) /* abs+X */\
218 data += x;\
219 CASE( op - 0x03 ) /* abs */\
220 abs_##op:\
221 data += 0x100 * READ_PC( ++pc );\
222 goto end_##op;\
223 CASE( op + 0x0C ) /* dp+X */\
224 data = (uint8_t) (data + x);\
225 CASE( op - 0x04 ) /* dp */\
226 data += dp;\
227 end_##op:
228
229/* 1. 8-bit Data Transmission Commands. Group I */
230
231 ADDR_MODES( 0xE8 ) /* MOV A,addr */
232 /*case 0xE4:*/ /* MOV a,dp (most common) */
233 mov_a_addr:
234 a = nz = READ( data );
235 goto inc_pc_loop;
236 case 0xBF: /* MOV A,(X)+ */
237 data = x + dp;
238 x = (uint8_t) (x + 1);
239 pc--;
240 goto mov_a_addr;
241
242 case 0xE8: /* MOV A,imm */
243 a = data;
244 nz = data;
245 goto inc_pc_loop;
246
247 case 0xF9: /* MOV X,dp+Y */
248 data = (uint8_t) (data + y);
249 case 0xF8: /* MOV X,dp */
250 data += dp;
251 goto mov_x_addr;
252 case 0xE9: /* MOV X,abs */
253 data = READ_PC16( pc );
254 pc++;
255 mov_x_addr:
256 data = READ( data );
257 case 0xCD: /* MOV X,imm */
258 x = data;
259 nz = data;
260 goto inc_pc_loop;
261
262 case 0xFB: /* MOV Y,dp+X */
263 data = (uint8_t) (data + x);
264 case 0xEB: /* MOV Y,dp */
265 data += dp;
266 goto mov_y_addr;
267 case 0xEC: /* MOV Y,abs */
268 data = READ_PC16( pc );
269 pc++;
270 mov_y_addr:
271 data = READ( data );
272 case 0x8D: /* MOV Y,imm */
273 y = data;
274 nz = data;
275 goto inc_pc_loop;
276
277/* 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 */
278
279 ADDR_MODES( 0xC8 ) /* MOV addr,A */
280 WRITE( data, a );
281 goto inc_pc_loop;
282
283 {
284 int temp;
285 case 0xCC: /* MOV abs,Y */
286 temp = y;
287 goto mov_abs_temp;
288 case 0xC9: /* MOV abs,X */
289 temp = x;
290 mov_abs_temp:
291 WRITE( READ_PC16( pc ), temp );
292 pc += 2;
293 goto loop;
294 }
295
296 case 0xD9: /* MOV dp+Y,X */
297 data = (uint8_t) (data + y);
298 case 0xD8: /* MOV dp,X */
299 WRITE( data + dp, x );
300 goto inc_pc_loop;
301
302 case 0xDB: /* MOV dp+X,Y */
303 data = (uint8_t) (data + x);
304 case 0xCB: /* MOV dp,Y */
305 WRITE( data + dp, y );
306 goto inc_pc_loop;
307
308 case 0xFA: /* MOV dp,dp */
309 data = READ( data + dp );
310 case 0x8F: /* MOV dp,#imm */
311 WRITE_DP( READ_PC( ++pc ), data );
312 goto inc_pc_loop;
313
314/* 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. */
315
316 case 0x7D: /* MOV A,X */
317 a = x;
318 nz = x;
319 goto loop;
320
321 case 0xDD: /* MOV A,Y */
322 a = y;
323 nz = y;
324 goto loop;
325
326 case 0x5D: /* MOV X,A */
327 x = a;
328 nz = a;
329 goto loop;
330
331 case 0xFD: /* MOV Y,A */
332 y = a;
333 nz = a;
334 goto loop;
335
336 case 0x9D: /* MOV X,SP */
337 x = nz = GET_SP();
338 goto loop;
339
340 case 0xBD: /* MOV SP,X */
341 SET_SP( x );
342 goto loop;
343
344 /*case 0xC6:*/ /* MOV (X),A (handled by MOV addr,A in group 2) */
345
346 case 0xAF: /* MOV (X)+,A */
347 WRITE_DP( x, a );
348 x++;
349 goto loop;
350
351/* 5. 8-BIT LOGIC OPERATION COMMANDS */
352
353#define LOGICAL_OP( op, func )\
354 ADDR_MODES( op ) /* addr */\
355 data = READ( data );\
356 case op: /* imm */\
357 nz = a func##= data;\
358 goto inc_pc_loop;\
359 { unsigned addr;\
360 case op + 0x11: /* X,Y */\
361 data = READ_DP( y );\
362 addr = x + dp;\
363 pc--;\
364 goto addr_##op;\
365 case op + 0x01: /* dp,dp */\
366 data = READ_DP( data );\
367 case op + 0x10: /*dp,imm*/\
368 addr = READ_PC( ++pc ) + dp;\
369 addr_##op:\
370 nz = data func READ( addr );\
371 WRITE( addr, nz );\
372 goto inc_pc_loop;\
373 }
374
375 LOGICAL_OP( 0x28, & ); /* AND */
376
377 LOGICAL_OP( 0x08, | ); /* OR */
378
379 LOGICAL_OP( 0x48, ^ ); /* EOR */
380
381/* 4. 8-BIT ARITHMETIC OPERATION COMMANDS */
382
383 ADDR_MODES( 0x68 ) /* CMP addr */
384 data = READ( data );
385 case 0x68: /* CMP imm */
386 nz = a - data;
387 c = ~nz;
388 nz &= 0xFF;
389 goto inc_pc_loop;
390
391 case 0x79: /* CMP (X),(Y) */
392 data = READ_DP( x );
393 nz = data - READ_DP( y );
394 c = ~nz;
395 nz &= 0xFF;
396 goto loop;
397
398 case 0x69: /* CMP (dp),(dp) */
399 data = READ_DP( data );
400 case 0x78: /* CMP dp,imm */
401 nz = READ_DP( READ_PC( ++pc ) ) - data;
402 c = ~nz;
403 nz &= 0xFF;
404 goto inc_pc_loop;
405
406 case 0x3E: /* CMP X,dp */
407 data += dp;
408 goto cmp_x_addr;
409 case 0x1E: /* CMP X,abs */
410 data = READ_PC16( pc );
411 pc++;
412 cmp_x_addr:
413 data = READ( data );
414 case 0xC8: /* CMP X,imm */
415 nz = x - data;
416 c = ~nz;
417 nz &= 0xFF;
418 goto inc_pc_loop;
419
420 case 0x7E: /* CMP Y,dp */
421 data += dp;
422 goto cmp_y_addr;
423 case 0x5E: /* CMP Y,abs */
424 data = READ_PC16( pc );
425 pc++;
426 cmp_y_addr:
427 data = READ( data );
428 case 0xAD: /* CMP Y,imm */
429 nz = y - data;
430 c = ~nz;
431 nz &= 0xFF;
432 goto inc_pc_loop;
433
434 {
435 int addr;
436 case 0xB9: /* SBC (x),(y) */
437 case 0x99: /* ADC (x),(y) */
438 pc--; /* compensate for inc later */
439 data = READ_DP( x );
440 addr = y + dp;
441 goto adc_addr;
442 case 0xA9: /* SBC dp,dp */
443 case 0x89: /* ADC dp,dp */
444 data = READ_DP( data );
445 case 0xB8: /* SBC dp,imm */
446 case 0x98: /* ADC dp,imm */
447 addr = READ_PC( ++pc ) + dp;
448 adc_addr:
449 nz = READ( addr );
450 goto adc_data;
451
452/* catch ADC and SBC together, then decode later based on operand */
453#undef CASE
454#define CASE( n ) case n: case (n) + 0x20:
455 ADDR_MODES( 0x88 ) /* ADC/SBC addr */
456 data = READ( data );
457 case 0xA8: /* SBC imm */
458 case 0x88: /* ADC imm */
459 addr = -1; /* A */
460 nz = a;
461 adc_data: {
462 if ( opcode & 0x20 )
463 data ^= 0xFF; /* SBC */
464 int carry = (c >> 8) & 1;
465 int ov = (nz ^ 0x80) + carry + (int8_t) data; /* sign-extend */
466 int hc = (nz & 15) + carry;
467 c = nz += data + carry;
468 hc = (nz & 15) - hc;
469 status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) |
470 ((hc >> 1) & st_h);
471 if ( addr < 0 ) {
472 a = (uint8_t) nz;
473 goto inc_pc_loop;
474 }
475 WRITE( addr, (uint8_t) nz );
476 goto inc_pc_loop;
477 }
478
479 }
480
481/* 6. ADDITION & SUBTRACTION COMMANDS */
482
483#define INC_DEC_REG( reg, n )\
484 nz = reg + n;\
485 reg = (uint8_t) nz;\
486 goto loop;
487
488 case 0xBC: INC_DEC_REG( a, 1 ) /* INC A */
489 case 0x3D: INC_DEC_REG( x, 1 ) /* INC X */
490 case 0xFC: INC_DEC_REG( y, 1 ) /* INC Y */
491
492 case 0x9C: INC_DEC_REG( a, -1 ) /* DEC A */
493 case 0x1D: INC_DEC_REG( x, -1 ) /* DEC X */
494 case 0xDC: INC_DEC_REG( y, -1 ) /* DEC Y */
495
496 case 0x9B: /* DEC dp+X */
497 case 0xBB: /* INC dp+X */
498 data = (uint8_t) (data + x);
499 case 0x8B: /* DEC dp */
500 case 0xAB: /* INC dp */
501 data += dp;
502 goto inc_abs;
503 case 0x8C: /* DEC abs */
504 case 0xAC: /* INC abs */
505 data = READ_PC16( pc );
506 pc++;
507 inc_abs:
508 nz = ((opcode >> 4) & 2) - 1;
509 nz += READ( data );
510 WRITE( data, (uint8_t) nz );
511 goto inc_pc_loop;
512
513/* 7. SHIFT, ROTATION COMMANDS */
514
515 case 0x5C: /* LSR A */
516 c = 0;
517 case 0x7C:{/* ROR A */
518 nz = ((c >> 1) & 0x80) | (a >> 1);
519 c = a << 8;
520 a = nz;
521 goto loop;
522 }
523
524 case 0x1C: /* ASL A */
525 c = 0;
526 case 0x3C:{/* ROL A */
527 int temp = (c >> 8) & 1;
528 c = a << 1;
529 nz = c | temp;
530 a = (uint8_t) nz;
531 goto loop;
532 }
533
534 case 0x0B: /* ASL dp */
535 c = 0;
536 data += dp;
537 goto rol_mem;
538 case 0x1B: /* ASL dp+X */
539 c = 0;
540 case 0x3B: /* ROL dp+X */
541 data = (uint8_t) (data + x);
542 case 0x2B: /* ROL dp */
543 data += dp;
544 goto rol_mem;
545 case 0x0C: /* ASL abs */
546 c = 0;
547 case 0x2C: /* ROL abs */
548 data = READ_PC16( pc );
549 pc++;
550 rol_mem:
551 nz = (c >> 8) & 1;
552 nz |= (c = READ( data ) << 1);
553 WRITE( data, (uint8_t) nz );
554 goto inc_pc_loop;
555
556 case 0x4B: /* LSR dp */
557 c = 0;
558 data += dp;
559 goto ror_mem;
560 case 0x5B: /* LSR dp+X */
561 c = 0;
562 case 0x7B: /* ROR dp+X */
563 data = (uint8_t) (data + x);
564 case 0x6B: /* ROR dp */
565 data += dp;
566 goto ror_mem;
567 case 0x4C: /* LSR abs */
568 c = 0;
569 case 0x6C: /* ROR abs */
570 data = READ_PC16( pc );
571 pc++;
572 ror_mem: {
573 int temp = READ( data );
574 nz = ((c >> 1) & 0x80) | (temp >> 1);
575 c = temp << 8;
576 WRITE( data, nz );
577 goto inc_pc_loop;
578 }
579
580 case 0x9F: /* XCN */
581 nz = a = (a >> 4) | (uint8_t) (a << 4);
582 goto loop;
583
584/* 8. 16-BIT TRANSMISION COMMANDS */
585
586 case 0xBA: /* MOVW YA,dp */
587 a = READ_DP( data );
588 nz = (a & 0x7F) | (a >> 1);
589 y = READ_DP( (uint8_t) (data + 1) );
590 nz |= y;
591 goto inc_pc_loop;
592
593 case 0xDA: /* MOVW dp,YA */
594 WRITE_DP( data, a );
595 WRITE_DP( (uint8_t) (data + 1), y );
596 goto inc_pc_loop;
597
598/* 9. 16-BIT OPERATION COMMANDS */
599
600 case 0x3A: /* INCW dp */
601 case 0x1A:{/* DECW dp */
602 data += dp;
603
604 /* low byte */
605 int temp = READ( data );
606 temp += ((opcode >> 4) & 2) - 1; /* +1 for INCW, -1 for DECW */
607 nz = ((temp >> 1) | temp) & 0x7F;
608 WRITE( data, (uint8_t) temp );
609
610 /* high byte */
611 data = ((uint8_t) (data + 1)) + dp;
612 temp >>= 8;
613 temp = (uint8_t) (temp + READ( data ));
614 nz |= temp;
615 WRITE( data, temp );
616
617 goto inc_pc_loop;
618 }
619
620 case 0x9A: /* SUBW YA,dp */
621 case 0x7A: /* ADDW YA,dp */
622 {
623 /* read 16-bit addend */
624 int temp = READ_DP( data );
625 int sign = READ_DP( (uint8_t) (data + 1) );
626 temp += 0x100 * sign;
627 status &= ~(st_v | st_h);
628
629 /* to do: fix half-carry for SUBW (it's probably wrong) */
630
631 /* for SUBW, negate and truncate to 16 bits */
632 if ( opcode & 0x80 ) {
633 temp = (temp ^ 0xFFFF) + 1;
634 sign = temp >> 8;
635 }
636
637 /* add low byte (A) */
638 temp += a;
639 a = (uint8_t) temp;
640 nz = (temp | (temp >> 1)) & 0x7F;
641
642 /* add high byte (Y) */
643 temp >>= 8;
644 c = y + temp;
645 nz = (nz | c) & 0xFF;
646
647 /* half-carry (temporary avoids CodeWarrior optimizer bug) */
648 unsigned hc = (c & 15) - (y & 15);
649 status |= (hc >> 4) & st_h;
650
651 /* overflow if sign of YA changed when previous sign
652 and addend sign were same */
653 status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v;
654
655 y = (uint8_t) c;
656
657 goto inc_pc_loop;
658 }
659
660 case 0x5A: { /* CMPW YA,dp */
661 int temp = a - READ_DP( data );
662 nz = ((temp >> 1) | temp) & 0x7F;
663 temp = y + (temp >> 8);
664 temp -= READ_DP( (uint8_t) (data + 1) );
665 nz |= temp;
666 c = ~temp;
667 nz &= 0xFF;
668 goto inc_pc_loop;
669 }
670
671/* 10. MULTIPLICATION & DIVISON COMMANDS */
672
673 case 0xCF: { /* MUL YA */
674 unsigned temp = y * a;
675 a = (uint8_t) temp;
676 nz = ((temp >> 1) | temp) & 0x7F;
677 y = temp >> 8;
678 nz |= y;
679 goto loop;
680 }
681
682 case 0x9E: /* DIV YA,X */
683 {
684 /* behavior based on SPC CPU tests */
685
686 status &= ~(st_h | st_v);
687
688 if ( (y & 15) >= (x & 15) )
689 status |= st_h;
690
691 if ( y >= x )
692 status |= st_v;
693
694 unsigned ya = y * 0x100 + a;
695 if ( y < x * 2 )
696 {
697 a = ya / x;
698 y = ya - a * x;
699 }
700 else
701 {
702 a = 255 - (ya - x * 0x200) / (256 - x);
703 y = x + (ya - x * 0x200) % (256 - x);
704 }
705
706 nz = (uint8_t) a;
707 a = (uint8_t) a;
708
709 goto loop;
710 }
711
712/* 11. DECIMAL COMPENSATION COMMANDS */
713
714 /* seem unused */
715 /* case 0xDF: */ /* DAA */
716 /* case 0xBE: */ /* DAS */
717
718/* 12. BRANCHING COMMANDS */
719
720 case 0x2F: /* BRA rel */
721 pc += (int8_t) data;
722 goto inc_pc_loop;
723
724 case 0x30: /* BMI */
725 BRANCH( IS_NEG )
726
727 case 0x10: /* BPL */
728 BRANCH( !IS_NEG )
729
730 case 0xB0: /* BCS */
731 BRANCH( c & 0x100 )
732
733 case 0x90: /* BCC */
734 BRANCH( !(c & 0x100) )
735
736 case 0x70: /* BVS */
737 BRANCH( status & st_v )
738
739 case 0x50: /* BVC */
740 BRANCH( !(status & st_v) )
741
742 case 0x03: /* BBS dp.bit,rel */
743 case 0x23:
744 case 0x43:
745 case 0x63:
746 case 0x83:
747 case 0xA3:
748 case 0xC3:
749 case 0xE3:
750 pc++;
751 if ( (READ_DP( data ) >> (opcode >> 5)) & 1 )
752 goto cbranch_taken_loop;
753 goto inc_pc_loop;
754
755 case 0x13: /* BBC dp.bit,rel */
756 case 0x33:
757 case 0x53:
758 case 0x73:
759 case 0x93:
760 case 0xB3:
761 case 0xD3:
762 case 0xF3:
763 pc++;
764 if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) )
765 goto cbranch_taken_loop;
766 goto inc_pc_loop;
767
768 case 0xDE: /* CBNE dp+X,rel */
769 data = (uint8_t) (data + x);
770 /* fall through */
771 case 0x2E: /* CBNE dp,rel */
772 pc++;
773 if ( READ_DP( data ) != a )
774 goto cbranch_taken_loop;
775 goto inc_pc_loop;
776
777 case 0xFE: /* DBNZ Y,rel */
778 y = (uint8_t) (y - 1);
779 BRANCH( y )
780
781 case 0x6E: { /* DBNZ dp,rel */
782 pc++;
783 unsigned temp = READ_DP( data ) - 1;
784 WRITE_DP( (uint8_t) data, (uint8_t) temp );
785 if ( temp )
786 goto cbranch_taken_loop;
787 goto inc_pc_loop;
788 }
789
790 case 0x1F: /* JMP (abs+X) */
791 SET_PC( READ_PC16( pc ) + x );
792 /* fall through */
793 case 0x5F: /* JMP abs */
794 SET_PC( READ_PC16( pc ) );
795 goto loop;
796
797/* 13. SUB-ROUTINE CALL RETURN COMMANDS */
798
799 case 0x0F:{/* BRK */
800 check( 0 ); /* untested */
801 PUSH16( GET_PC() + 1 );
802 SET_PC( READ_PROG16( 0xFFDE ) ); /* vector address verified */
803 int temp;
804 CALC_STATUS( temp );
805 PUSH( temp );
806 status = (status | st_b) & ~st_i;
807 goto loop;
808 }
809
810 case 0x4F: /* PCALL offset */
811 PUSH16( GET_PC() + 1 );
812 SET_PC( 0xFF00 + data );
813 goto loop;
814
815 case 0x01: /* TCALL n */
816 case 0x11:
817 case 0x21:
818 case 0x31:
819 case 0x41:
820 case 0x51:
821 case 0x61:
822 case 0x71:
823 case 0x81:
824 case 0x91:
825 case 0xA1:
826 case 0xB1:
827 case 0xC1:
828 case 0xD1:
829 case 0xE1:
830 case 0xF1:
831 PUSH16( GET_PC() );
832 SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
833 goto loop;
834
835/* 14. STACK OPERATION COMMANDS */
836
837 {
838 int temp;
839 case 0x7F: /* RET1 */
840 temp = POP();
841 SET_PC( POP() );
842 pc += POP() << 8;
843 goto set_status;
844 case 0x8E: /* POP PSW */
845 temp = POP();
846 set_status:
847 SET_STATUS( temp );
848 goto loop;
849 }
850
851 case 0x0D: { /* PUSH PSW */
852 int temp;
853 CALC_STATUS( temp );
854 PUSH( temp );
855 goto loop;
856 }
857
858 case 0x2D: /* PUSH A */
859 PUSH( a );
860 goto loop;
861
862 case 0x4D: /* PUSH X */
863 PUSH( x );
864 goto loop;
865
866 case 0x6D: /* PUSH Y */
867 PUSH( y );
868 goto loop;
869
870 case 0xAE: /* POP A */
871 a = POP();
872 goto loop;
873
874 case 0xCE: /* POP X */
875 x = POP();
876 goto loop;
877
878 case 0xEE: /* POP Y */
879 y = POP();
880 goto loop;
881
882/* 15. BIT OPERATION COMMANDS */
883
884 case 0x02: /* SET1 */
885 case 0x22:
886 case 0x42:
887 case 0x62:
888 case 0x82:
889 case 0xA2:
890 case 0xC2:
891 case 0xE2:
892 case 0x12: /* CLR1 */
893 case 0x32:
894 case 0x52:
895 case 0x72:
896 case 0x92:
897 case 0xB2:
898 case 0xD2:
899 case 0xF2: {
900 data += dp;
901 int bit = 1 << (opcode >> 5);
902 int mask = ~bit;
903 if ( opcode & 0x10 )
904 bit = 0;
905 WRITE( data, (READ( data ) & mask) | bit );
906 goto inc_pc_loop;
907 }
908
909 case 0x0E: /* TSET1 abs */
910 case 0x4E:{/* TCLR1 abs */
911 data = READ_PC16( pc );
912 pc += 2;
913 unsigned temp = READ( data );
914 nz = temp & a;
915 temp &= ~a;
916 if ( !(opcode & 0x40) )
917 temp |= a;
918 WRITE( data, temp );
919 goto loop;
920 }
921
922 case 0x4A: /* AND1 C,mem.bit */
923 c &= MEM_BIT();
924 pc += 2;
925 goto loop;
926
927 case 0x6A: /* AND1 C,/mem.bit */
928 check( 0 ); /* untested */
929 c &= ~MEM_BIT();
930 pc += 2;
931 goto loop;
932
933 case 0x0A: /* OR1 C,mem.bit */
934 check( 0 ); /* untested */
935 c |= MEM_BIT();
936 pc += 2;
937 goto loop;
938
939 case 0x2A: /* OR1 C,/mem.bit */
940 check( 0 ); /* untested */
941 c |= ~MEM_BIT();
942 pc += 2;
943 goto loop;
944
945 case 0x8A: /* EOR1 C,mem.bit */
946 c ^= MEM_BIT();
947 pc += 2;
948 goto loop;
949
950 case 0xEA: { /* NOT1 mem.bit */
951 data = READ_PC16( pc );
952 pc += 2;
953 unsigned temp = READ( data & 0x1FFF );
954 temp ^= 1 << (data >> 13);
955 WRITE( data & 0x1FFF, temp );
956 goto loop;
957 }
958
959 case 0xCA: { /* MOV1 mem.bit,C */
960 data = READ_PC16( pc );
961 pc += 2;
962 unsigned temp = READ( data & 0x1FFF );
963 unsigned bit = data >> 13;
964 temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit);
965 WRITE( data & 0x1FFF, temp );
966 goto loop;
967 }
968
969 case 0xAA: /* MOV1 C,mem.bit */
970 c = MEM_BIT();
971 pc += 2;
972 goto loop;
973
974/* 16. PROGRAM STATUS FLAG OPERATION COMMANDS */
975
976 case 0x60: /* CLRC */
977 c = 0;
978 goto loop;
979
980 case 0x80: /* SETC */
981 c = ~0;
982 goto loop;
983
984 case 0xED: /* NOTC */
985 c ^= 0x100;
986 goto loop;
987
988 case 0xE0: /* CLRV */
989 status &= ~(st_v | st_h);
990 goto loop;
991
992 case 0x20: /* CLRP */
993 dp = 0;
994 goto loop;
995
996 case 0x40: /* SETP */
997 dp = 0x100;
998 goto loop;
999
1000 case 0xA0: /* EI */
1001 check( 0 ); /* untested */
1002 status |= st_i;
1003 goto loop;
1004
1005 case 0xC0: /* DI */
1006 check( 0 ); /* untested */
1007 status &= ~st_i;
1008 goto loop;
1009
1010/* 17. OTHER COMMANDS */
1011
1012 case 0x00: /* NOP */
1013 goto loop;
1014
1015 /*case 0xEF:*/ /* SLEEP */
1016 /*case 0xFF:*/ /* STOP */
1017 case 0xFF:
1018 c |= 1; /* force switch table to have 256 entries,
1019 hopefully helping optimizer */
1020 } /* switch */
1021
1022 /* unhandled instructions fall out of switch so emulator can catch them */
1023
1024out_of_time:
1025 /* undo partial execution of opcode */
1026 spc_time_ -= this->cycle_table [*--pc];
1027 {
1028 int temp;
1029 CALC_STATUS( temp );
1030 this->r.status = (uint8_t) temp;
1031 }
1032
1033 this->r.pc = GET_PC();
1034 this->r.sp = (uint8_t) GET_SP();
1035 this->r.a = (uint8_t) a;
1036 this->r.x = (uint8_t) x;
1037 this->r.y = (uint8_t) y;
1038
1039#if 0
1040 EXIT_TIMER(cpu);
1041#endif
1042 return spc_time_;
1043}
1044
1045void CPU_Init( THIS )
1046{
1047 ci->memcpy( this->cycle_table, cycle_table, sizeof cycle_table );
1048}
1049
diff --git a/lib/rbcodec/codecs/libspc/spc_dsp.c b/lib/rbcodec/codecs/libspc/spc_dsp.c
new file mode 100644
index 0000000000..6350c4c331
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/spc_dsp.c
@@ -0,0 +1,1594 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS)
11 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
12 * Copyright (C) 2004-2007 Shay Green (blargg)
13 * Copyright (C) 2002 Brad Martin
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25/* The DSP portion (awe!) */
26#include "codeclib.h"
27#include "spc_codec.h"
28#include "spc_profiler.h"
29
30#if defined(CPU_COLDFIRE) || defined (CPU_ARM)
31int32_t fir_buf[FIR_BUF_CNT] IBSS_ATTR_SPC
32 __attribute__((aligned(FIR_BUF_ALIGN*1)));
33#endif
34#if SPC_BRRCACHE
35/* a little extra for samples that go past end */
36int16_t BRRcache [BRR_CACHE_SIZE] CACHEALIGN_ATTR;
37#endif
38
39void DSP_write( struct Spc_Dsp* this, int i, int data )
40{
41 assert( (unsigned) i < REGISTER_COUNT );
42
43 this->r.reg [i] = data;
44 int high = i >> 4;
45 int low = i & 0x0F;
46 if ( low < 2 ) /* voice volumes */
47 {
48 int left = *(int8_t const*) &this->r.reg [i & ~1];
49 int right = *(int8_t const*) &this->r.reg [i | 1];
50 struct voice_t* v = this->voice_state + high;
51 v->volume [0] = left;
52 v->volume [1] = right;
53 }
54 else if ( low == 0x0F ) /* fir coefficients */
55 {
56 this->fir_coeff [7 - high] = (int8_t) data; /* sign-extend */
57 }
58}
59
60#define CLAMP16( n ) clip_sample_16( n )
61
62#if SPC_BRRCACHE
63static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
64 struct voice_t* voice,
65 struct raw_voice_t const* const raw_voice ) ICODE_ATTR_SPC;
66static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
67 struct voice_t* voice,
68 struct raw_voice_t const* const raw_voice )
69{
70 /* setup same variables as where decode_brr() is called from */
71 #undef RAM
72 #define RAM ram.ram
73
74 struct src_dir const* const sd =
75 &ram.sd[this->r.g.wave_page * 0x100/sizeof(struct src_dir)];
76 struct cache_entry_t* const wave_entry =
77 &this->wave_entry [raw_voice->waveform];
78
79 /* the following block can be put in place of the call to
80 decode_brr() below
81 */
82 {
83 DEBUGF( "decode at %08x (wave #%d)\n",
84 start_addr, raw_voice->waveform );
85
86 /* see if in cache */
87 int i;
88 for ( i = 0; i < this->oldsize; i++ )
89 {
90 struct cache_entry_t* e = &this->wave_entry_old [i];
91 if ( e->start_addr == start_addr )
92 {
93 DEBUGF( "found in wave_entry_old (oldsize=%d)\n",
94 this->oldsize );
95 *wave_entry = *e;
96 goto wave_in_cache;
97 }
98 }
99
100 wave_entry->start_addr = start_addr;
101
102 uint8_t const* const loop_ptr =
103 RAM + letoh16(sd[raw_voice->waveform].loop);
104 short* loop_start = 0;
105
106 short* out = BRRcache + start_addr * 2;
107 wave_entry->samples = out;
108 *out++ = 0;
109 int smp1 = 0;
110 int smp2 = 0;
111
112 uint8_t const* addr = RAM + start_addr;
113 int block_header;
114 do
115 {
116 if ( addr == loop_ptr )
117 {
118 loop_start = out;
119 DEBUGF( "loop at %08lx (wave #%d)\n",
120 (unsigned long)(addr - RAM), raw_voice->waveform );
121 }
122
123 /* header */
124 block_header = *addr;
125 addr += 9;
126 voice->addr = addr;
127 int const filter = (block_header & 0x0C) - 0x08;
128
129 /* scaling
130 (invalid scaling gives -4096 for neg nybble, 0 for pos) */
131 static unsigned char const right_shifts [16] = {
132 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 29,
133 };
134 static unsigned char const left_shifts [16] = {
135 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
136 };
137 int const scale = block_header >> 4;
138 int const right_shift = right_shifts [scale];
139 int const left_shift = left_shifts [scale];
140
141 /* output position */
142 out += BRR_BLOCK_SIZE;
143 int offset = -BRR_BLOCK_SIZE << 2;
144
145 do /* decode and filter 16 samples */
146 {
147 /* Get nybble, sign-extend, then scale
148 get byte, select which nybble, sign-extend, then shift based
149 on scaling. also handles invalid scaling values. */
150 int delta = (int) (int8_t) (addr [offset >> 3] << (offset & 4))
151 >> right_shift << left_shift;
152
153 out [offset >> 2] = smp2;
154
155 if ( filter == 0 ) /* mode 0x08 (30-90% of the time) */
156 {
157 delta -= smp2 >> 1;
158 delta += smp2 >> 5;
159 smp2 = smp1;
160 delta += smp1;
161 delta += (-smp1 - (smp1 >> 1)) >> 5;
162 }
163 else
164 {
165 if ( filter == -4 ) /* mode 0x04 */
166 {
167 delta += smp1 >> 1;
168 delta += (-smp1) >> 5;
169 }
170 else if ( filter > -4 ) /* mode 0x0C */
171 {
172 delta -= smp2 >> 1;
173 delta += (smp2 + (smp2 >> 1)) >> 4;
174 delta += smp1;
175 delta += (-smp1 * 13) >> 7;
176 }
177 smp2 = smp1;
178 }
179
180 delta = CLAMP16( delta );
181 smp1 = (int16_t) (delta * 2); /* sign-extend */
182 }
183 while ( (offset += 4) != 0 );
184
185 /* if next block has end flag set, this block ends early */
186 /* (verified) */
187 if ( (block_header & 3) != 3 && (*addr & 3) == 1 )
188 {
189 /* skip last 9 samples */
190 out -= 9;
191 goto early_end;
192 }
193 }
194 while ( !(block_header & 1) && addr < RAM + 0x10000 );
195
196 out [0] = smp2;
197 out [1] = smp1;
198
199 early_end:
200 wave_entry->end = (out - 1 - wave_entry->samples) << 12;
201
202 wave_entry->loop = 0;
203 if ( (block_header & 2) )
204 {
205 if ( loop_start )
206 {
207 int loop = out - loop_start;
208 wave_entry->loop = loop;
209 wave_entry->end += 0x3000;
210 out [2] = loop_start [2];
211 out [3] = loop_start [3];
212 out [4] = loop_start [4];
213 }
214 else
215 {
216 DEBUGF( "loop point outside initial wave\n" );
217 }
218 }
219
220 DEBUGF( "end at %08lx (wave #%d)\n",
221 (unsigned long)(addr - RAM), raw_voice->waveform );
222
223 /* add to cache */
224 this->wave_entry_old [this->oldsize++] = *wave_entry;
225wave_in_cache:;
226 }
227}
228#endif
229
230static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
231 struct src_dir const* const sd,
232 struct raw_voice_t const* const raw_voice,
233 const int key_on_delay, const int vbit) ICODE_ATTR_SPC;
234static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
235 struct src_dir const* const sd,
236 struct raw_voice_t const* const raw_voice,
237 const int key_on_delay, const int vbit) {
238 #undef RAM
239 #define RAM ram.ram
240 int const env_rate_init = 0x7800;
241 voice->key_on_delay = key_on_delay;
242 if ( key_on_delay == 0 )
243 {
244 this->keys_down |= vbit;
245 voice->envx = 0;
246 voice->env_mode = state_attack;
247 voice->env_timer = env_rate_init; /* TODO: inaccurate? */
248 unsigned start_addr = letoh16(sd[raw_voice->waveform].start);
249 #if !SPC_BRRCACHE
250 {
251 voice->addr = RAM + start_addr;
252 /* BRR filter uses previous samples */
253 voice->samples [BRR_BLOCK_SIZE + 1] = 0;
254 voice->samples [BRR_BLOCK_SIZE + 2] = 0;
255 /* decode three samples immediately */
256 voice->position = (BRR_BLOCK_SIZE + 3) * 0x1000 - 1;
257 voice->block_header = 0; /* "previous" BRR header */
258 }
259 #else
260 {
261 voice->position = 3 * 0x1000 - 1;
262 struct cache_entry_t* const wave_entry =
263 &this->wave_entry [raw_voice->waveform];
264
265 /* predecode BRR if not already */
266 if ( wave_entry->start_addr != start_addr )
267 {
268 /* the following line can be replaced by the indicated block
269 in decode_brr() */
270 decode_brr( this, start_addr, voice, raw_voice );
271 }
272
273 voice->samples = wave_entry->samples;
274 voice->wave_end = wave_entry->end;
275 voice->wave_loop = wave_entry->loop;
276 }
277 #endif
278 }
279}
280
281void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
282{
283 #undef RAM
284#if defined(CPU_ARM) && !SPC_BRRCACHE
285 uint8_t* const ram_ = ram.ram;
286 #define RAM ram_
287#else
288 #define RAM ram.ram
289#endif
290#if 0
291 EXIT_TIMER(cpu);
292 ENTER_TIMER(dsp);
293#endif
294
295 /* Here we check for keys on/off. Docs say that successive writes
296 to KON/KOF must be separated by at least 2 Ts periods or risk
297 being neglected. Therefore DSP only looks at these during an
298 update, and not at the time of the write. Only need to do this
299 once however, since the regs haven't changed over the whole
300 period we need to catch up with. */
301
302 {
303 int key_ons = this->r.g.key_ons;
304 int key_offs = this->r.g.key_offs;
305 /* keying on a voice resets that bit in ENDX */
306 this->r.g.wave_ended &= ~key_ons;
307 /* key_off bits prevent key_on from being acknowledged */
308 this->r.g.key_ons = key_ons & key_offs;
309
310 /* process key events outside loop, since they won't re-occur */
311 struct voice_t* voice = this->voice_state + 8;
312 int vbit = 0x80;
313 do
314 {
315 --voice;
316 if ( key_offs & vbit )
317 {
318 voice->env_mode = state_release;
319 voice->key_on_delay = 0;
320 }
321 else if ( key_ons & vbit )
322 {
323 voice->key_on_delay = 8;
324 }
325 }
326 while ( (vbit >>= 1) != 0 );
327 }
328
329 struct src_dir const* const sd =
330 &ram.sd[this->r.g.wave_page * 0x100/sizeof(struct src_dir)];
331
332 #ifdef ROCKBOX_BIG_ENDIAN
333 /* Convert endiannesses before entering loops - these
334 get used alot */
335 const uint32_t rates[VOICE_COUNT] =
336 {
337 GET_LE16A( this->r.voice[0].rate ) & 0x3FFF,
338 GET_LE16A( this->r.voice[1].rate ) & 0x3FFF,
339 GET_LE16A( this->r.voice[2].rate ) & 0x3FFF,
340 GET_LE16A( this->r.voice[3].rate ) & 0x3FFF,
341 GET_LE16A( this->r.voice[4].rate ) & 0x3FFF,
342 GET_LE16A( this->r.voice[5].rate ) & 0x3FFF,
343 GET_LE16A( this->r.voice[6].rate ) & 0x3FFF,
344 GET_LE16A( this->r.voice[7].rate ) & 0x3FFF,
345 };
346 #define VOICE_RATE(x) *(x)
347 #define IF_RBE(...) __VA_ARGS__
348 #ifdef CPU_COLDFIRE
349 /* Initialize mask register with the buffer address mask */
350 asm volatile ("move.l %[m], %%mask" : : [m]"i"(FIR_BUF_MASK));
351 const int echo_wrap = (this->r.g.echo_delay & 15) * 0x800;
352 const int echo_start = this->r.g.echo_page * 0x100;
353 #endif /* CPU_COLDFIRE */
354 #else
355 #define VOICE_RATE(x) (GET_LE16(raw_voice->rate) & 0x3FFF)
356 #define IF_RBE(...)
357 #endif /* ROCKBOX_BIG_ENDIAN */
358
359#if !SPC_NOINTERP
360 int const slow_gaussian = (this->r.g.pitch_mods >> 1) |
361 this->r.g.noise_enables;
362#endif
363 /* (g.flags & 0x40) ? 30 : 14 */
364 int const global_muting = ((this->r.g.flags & 0x40) >> 2) + 14 - 8;
365 int const global_vol_0 = this->r.g.volume_0;
366 int const global_vol_1 = this->r.g.volume_1;
367
368 /* each rate divides exactly into 0x7800 without remainder */
369 int const env_rate_init = 0x7800;
370 static unsigned short const env_rates [0x20] ICONST_ATTR_SPC =
371 {
372 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
373 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
374 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
375 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
376 };
377
378 do /* one pair of output samples per iteration */
379 {
380 /* Noise */
381 if ( this->r.g.noise_enables )
382 {
383 if ( (this->noise_count -=
384 env_rates [this->r.g.flags & 0x1F]) <= 0 )
385 {
386 this->noise_count = env_rate_init;
387 int feedback = (this->noise << 13) ^ (this->noise << 14);
388 this->noise = (feedback & 0x8000) ^ (this->noise >> 1 & ~1);
389 }
390 }
391
392#if !SPC_NOECHO
393 int echo_0 = 0;
394 int echo_1 = 0;
395#endif
396 long prev_outx = 0; /* TODO: correct value for first channel? */
397 int chans_0 = 0;
398 int chans_1 = 0;
399 /* TODO: put raw_voice pointer in voice_t? */
400 struct raw_voice_t * raw_voice = this->r.voice;
401 struct voice_t* voice = this->voice_state;
402 int vbit = 1;
403 IF_RBE( const uint32_t* vr = rates; )
404 for ( ; vbit < 0x100; vbit <<= 1, ++voice, ++raw_voice IF_RBE( , ++vr ) )
405 {
406 /* pregen involves checking keyon, etc */
407#if 0
408 ENTER_TIMER(dsp_pregen);
409#endif
410
411 /* Key on events are delayed */
412 int key_on_delay = voice->key_on_delay;
413
414 if ( UNLIKELY ( --key_on_delay >= 0 ) ) /* <1% of the time */
415 {
416 key_on(this,voice,sd,raw_voice,key_on_delay,vbit);
417 }
418
419 if ( !(this->keys_down & vbit) ) /* Silent channel */
420 {
421 silent_chan:
422 raw_voice->envx = 0;
423 raw_voice->outx = 0;
424 prev_outx = 0;
425 continue;
426 }
427
428 /* Envelope */
429 {
430 int const ENV_RANGE = 0x800;
431 int env_mode = voice->env_mode;
432 int adsr0 = raw_voice->adsr [0];
433 int env_timer;
434 if ( LIKELY ( env_mode != state_release ) ) /* 99% of the time */
435 {
436 env_timer = voice->env_timer;
437 if ( LIKELY ( adsr0 & 0x80 ) ) /* 79% of the time */
438 {
439 int adsr1 = raw_voice->adsr [1];
440 if ( LIKELY ( env_mode == state_sustain ) ) /* 74% of the time */
441 {
442 if ( (env_timer -= env_rates [adsr1 & 0x1F]) > 0 )
443 goto write_env_timer;
444
445 int envx = voice->envx;
446 envx--; /* envx *= 255 / 256 */
447 envx -= envx >> 8;
448 voice->envx = envx;
449 /* TODO: should this be 8? */
450 raw_voice->envx = envx >> 4;
451 goto init_env_timer;
452 }
453 else if ( env_mode < 0 ) /* 25% state_decay */
454 {
455 int envx = voice->envx;
456 if ( (env_timer -=
457 env_rates [(adsr0 >> 3 & 0x0E) + 0x10]) <= 0 )
458 {
459 envx--; /* envx *= 255 / 256 */
460 envx -= envx >> 8;
461 voice->envx = envx;
462 /* TODO: should this be 8? */
463 raw_voice->envx = envx >> 4;
464 env_timer = env_rate_init;
465 }
466
467 int sustain_level = adsr1 >> 5;
468 if ( envx <= (sustain_level + 1) * 0x100 )
469 voice->env_mode = state_sustain;
470
471 goto write_env_timer;
472 }
473 else /* state_attack */
474 {
475 int t = adsr0 & 0x0F;
476 if ( (env_timer -= env_rates [t * 2 + 1]) > 0 )
477 goto write_env_timer;
478
479 int envx = voice->envx;
480
481 int const step = ENV_RANGE / 64;
482 envx += step;
483 if ( t == 15 )
484 envx += ENV_RANGE / 2 - step;
485
486 if ( envx >= ENV_RANGE )
487 {
488 envx = ENV_RANGE - 1;
489 voice->env_mode = state_decay;
490 }
491 voice->envx = envx;
492 /* TODO: should this be 8? */
493 raw_voice->envx = envx >> 4;
494 goto init_env_timer;
495 }
496 }
497 else /* gain mode */
498 {
499 int t = raw_voice->gain;
500 if ( t < 0x80 )
501 {
502 raw_voice->envx = t;
503 voice->envx = t << 4;
504 goto env_end;
505 }
506 else
507 {
508 if ( (env_timer -= env_rates [t & 0x1F]) > 0 )
509 goto write_env_timer;
510
511 int envx = voice->envx;
512 int mode = t >> 5;
513 if ( mode <= 5 ) /* decay */
514 {
515 int step = ENV_RANGE / 64;
516 if ( mode == 5 ) /* exponential */
517 {
518 envx--; /* envx *= 255 / 256 */
519 step = envx >> 8;
520 }
521 if ( (envx -= step) < 0 )
522 {
523 envx = 0;
524 if ( voice->env_mode == state_attack )
525 voice->env_mode = state_decay;
526 }
527 }
528 else /* attack */
529 {
530 int const step = ENV_RANGE / 64;
531 envx += step;
532 if ( mode == 7 &&
533 envx >= ENV_RANGE * 3 / 4 + step )
534 envx += ENV_RANGE / 256 - step;
535
536 if ( envx >= ENV_RANGE )
537 envx = ENV_RANGE - 1;
538 }
539 voice->envx = envx;
540 /* TODO: should this be 8? */
541 raw_voice->envx = envx >> 4;
542 goto init_env_timer;
543 }
544 }
545 }
546 else /* state_release */
547 {
548 int envx = voice->envx;
549 if ( (envx -= ENV_RANGE / 256) > 0 )
550 {
551 voice->envx = envx;
552 raw_voice->envx = envx >> 8;
553 goto env_end;
554 }
555 else
556 {
557 /* bit was set, so this clears it */
558 this->keys_down ^= vbit;
559 voice->envx = 0;
560 goto silent_chan;
561 }
562 }
563 init_env_timer:
564 env_timer = env_rate_init;
565 write_env_timer:
566 voice->env_timer = env_timer;
567 env_end:;
568 }
569#if 0
570 EXIT_TIMER(dsp_pregen);
571
572 ENTER_TIMER(dsp_gen);
573#endif
574 #if !SPC_BRRCACHE
575 /* Decode BRR block */
576 if ( voice->position >= BRR_BLOCK_SIZE * 0x1000 )
577 {
578 voice->position -= BRR_BLOCK_SIZE * 0x1000;
579
580 uint8_t const* addr = voice->addr;
581 if ( addr >= RAM + 0x10000 )
582 addr -= 0x10000;
583
584 /* action based on previous block's header */
585 if ( voice->block_header & 1 )
586 {
587 addr = RAM + letoh16(sd[raw_voice->waveform].loop);
588 this->r.g.wave_ended |= vbit;
589 if ( !(voice->block_header & 2) ) /* 1% of the time */
590 {
591 /* first block was end block;
592 don't play anything (verified) */
593 /* bit was set, so this clears it */
594 this->keys_down ^= vbit;
595
596 /* since voice->envx is 0,
597 samples and position don't matter */
598 raw_voice->envx = 0;
599 voice->envx = 0;
600 goto skip_decode;
601 }
602 }
603
604 /* header */
605 int const block_header = *addr;
606 addr += 9;
607 voice->addr = addr;
608 voice->block_header = block_header;
609
610 /* previous samples */
611 int smp2 = voice->samples [BRR_BLOCK_SIZE + 1];
612 int smp1 = voice->samples [BRR_BLOCK_SIZE + 2];
613 voice->samples [0] = voice->samples [BRR_BLOCK_SIZE];
614
615 /* output position */
616 short* out = voice->samples + (1 + BRR_BLOCK_SIZE);
617 int offset = -BRR_BLOCK_SIZE << 2;
618
619 /* if next block has end flag set,
620 this block ends early (verified) */
621 if ( (block_header & 3) != 3 && (*addr & 3) == 1 )
622 {
623 /* arrange for last 9 samples to be skipped */
624 int const skip = 9;
625 out += (skip & 1);
626 voice->samples [skip] = voice->samples [BRR_BLOCK_SIZE];
627 voice->position += skip * 0x1000;
628 offset = (-BRR_BLOCK_SIZE + (skip & ~1)) << 2;
629 addr -= skip / 2;
630 /* force sample to end on next decode */
631 voice->block_header = 1;
632 }
633
634 int const filter = block_header & 0x0c;
635 int const scale = block_header >> 4;
636
637 if ( filter == 0x08 ) /* filter 2 (30-90% of the time) */
638 {
639 /* y[n] = x[n] + 61/32 * y[n-1] - 15/16 * y[n-2] */
640 do /* decode and filter 16 samples */
641 {
642 /* Get nybble, sign-extend, then scale
643 get byte, select which nybble, sign-extend, then shift
644 based on scaling. */
645 int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
646 delta = (delta << scale) >> 1;
647
648 if (scale > 0xc)
649 delta = (delta >> 17) << 11;
650
651 out [offset >> 2] = smp2;
652
653 delta -= smp2 >> 1;
654 delta += smp2 >> 5;
655 delta += smp1;
656 delta += (-smp1 - (smp1 >> 1)) >> 5;
657
658 delta = CLAMP16( delta );
659 smp2 = smp1;
660 smp1 = (int16_t) (delta * 2); /* sign-extend */
661 }
662 while ( (offset += 4) != 0 );
663 }
664 else if ( filter == 0x04 ) /* filter 1 */
665 {
666 /* y[n] = x[n] + 15/16 * y[n-1] */
667 do /* decode and filter 16 samples */
668 {
669 /* Get nybble, sign-extend, then scale
670 get byte, select which nybble, sign-extend, then shift
671 based on scaling. */
672 int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
673 delta = (delta << scale) >> 1;
674
675 if (scale > 0xc)
676 delta = (delta >> 17) << 11;
677
678 out [offset >> 2] = smp2;
679
680 delta += smp1 >> 1;
681 delta += (-smp1) >> 5;
682
683 delta = CLAMP16( delta );
684 smp2 = smp1;
685 smp1 = (int16_t) (delta * 2); /* sign-extend */
686 }
687 while ( (offset += 4) != 0 );
688 }
689 else if ( filter == 0x0c ) /* filter 3 */
690 {
691 /* y[n] = x[n] + 115/64 * y[n-1] - 13/16 * y[n-2] */
692 do /* decode and filter 16 samples */
693 {
694 /* Get nybble, sign-extend, then scale
695 get byte, select which nybble, sign-extend, then shift
696 based on scaling. */
697 int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
698 delta = (delta << scale) >> 1;
699
700 if (scale > 0xc)
701 delta = (delta >> 17) << 11;
702
703 out [offset >> 2] = smp2;
704
705 delta -= smp2 >> 1;
706 delta += (smp2 + (smp2 >> 1)) >> 4;
707 delta += smp1;
708 delta += (-smp1 * 13) >> 7;
709
710 delta = CLAMP16( delta );
711 smp2 = smp1;
712 smp1 = (int16_t) (delta * 2); /* sign-extend */
713 }
714 while ( (offset += 4) != 0 );
715 }
716 else /* filter 0 */
717 {
718 /* y[n] = x[n] */
719 do /* decode and filter 16 samples */
720 {
721 /* Get nybble, sign-extend, then scale
722 get byte, select which nybble, sign-extend, then shift
723 based on scaling. */
724 int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
725 delta = (delta << scale) >> 1;
726
727 if (scale > 0xc)
728 delta = (delta >> 17) << 11;
729
730 out [offset >> 2] = smp2;
731
732 smp2 = smp1;
733 smp1 = delta * 2;
734 }
735 while ( (offset += 4) != 0 );
736 }
737
738 out [0] = smp2;
739 out [1] = smp1;
740
741 skip_decode:;
742 }
743 #endif /* !SPC_BRRCACHE */
744 /* Get rate (with possible modulation) */
745 int rate = VOICE_RATE(vr);
746 if ( this->r.g.pitch_mods & vbit )
747 rate = (rate * (prev_outx + 32768)) >> 15;
748
749 #if !SPC_NOINTERP
750 /* Interleved gauss table (to improve cache coherency). */
751 /* gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] */
752 static short const gauss [512] ICONST_ATTR_SPC MEM_ALIGN_ATTR =
753 {
754370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
755339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
756311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
757283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
758257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
759233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
760210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
761188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
762168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
763150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
764132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
765117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
766102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,
767 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,
768 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,
769 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,
770 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,
771 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,
772 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,
773 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,
774 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,
775 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,
776 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,
777 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,
778 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,
779 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,
780 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,
781 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,
782 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,
783 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,
784 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,
785 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,
786 };
787 /* Gaussian interpolation using most recent 4 samples */
788 long position = voice->position;
789 voice->position += rate;
790 short const* interp = voice->samples + (position >> 12);
791 int offset = position >> 4 & 0xFF;
792
793 /* Only left half of gaussian kernel is in table, so we must mirror
794 for right half */
795 short const* fwd = gauss + offset * 2;
796 short const* rev = gauss + 510 - offset * 2;
797
798 /* Use faster gaussian interpolation when exact result isn't needed
799 by pitch modulator of next channel */
800 int amp_0, amp_1; /* Also serve as temps _0, and _1 */
801 if ( LIKELY ( !(slow_gaussian & vbit) ) ) /* 99% of the time */
802 {
803 /* Main optimization is lack of clamping. Not a problem since
804 output never goes more than +/- 16 outside 16-bit range and
805 things are clamped later anyway. Other optimization is to
806 preserve fractional accuracy, eliminating several masks. */
807 #if defined (CPU_ARM)
808 int output;
809 int _2, _3; /* All-purpose temps */
810 /* Multiple ASM blocks keep regs free and reduce result
811 * latency issues. */
812 #if ARM_ARCH >= 6
813 /* Interpolate */
814 asm volatile (
815 "ldr %[_0], [%[interp]] \r\n" /* _0=i0i1 */
816 "ldr %[_2], [%[fwd]] \r\n" /* _2=f0f1 */
817 "ldr %[_1], [%[interp], #4] \r\n" /* _1=i2i3 */
818 "ldr %[_3], [%[rev]] \r\n" /* _3=r0r1 */
819 "smuad %[out], %[_0], %[_2] \r\n" /* out=f0*i0 + f1*i1 */
820 "smladx %[out], %[_1], %[_3], %[out] \r\n" /* out+=r1*i2 + r0*i3 */
821 : [out]"=r"(output),
822 [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
823 [_2]"=&r"(_2), [_3]"=r"(_3)
824 : [fwd]"r"(fwd), [rev]"r"(rev),
825 [interp]"r"(interp));
826 /* Apply voice envelope */
827 asm volatile (
828 "mov %[_2], %[out], asr #(11-5) \r\n" /* To do >> 16 later */
829 "mul %[out], %[_2], %[envx] \r\n" /* and avoid exp. shift */
830 : [out]"+r"(output), [_2]"=&r"(_2)
831 : [envx]"r"((int)voice->envx));
832 /* Apply left and right volume */
833 asm volatile (
834 "smulwb %[amp_0], %[out], %[vvol_0] \r\n" /* (32x16->48)[47:16]->[31:0] */
835 "smulwb %[amp_1], %[out], %[vvol_1] \r\n"
836 : [out]"+r"(output),
837 [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
838 : [vvol_0]"r"(voice->volume[0]),
839 [vvol_1]"r"(voice->volume[1]));
840
841 raw_voice->outx = output >> (8+5); /* 'output' still 5 bits too big */
842 #else /* ARM_ARCH < 6 */
843 /* Perform gaussian interpolation on four samples */
844 asm volatile (
845 "ldrsh %[_0], [%[interp]] \r\n"
846 "ldrsh %[_2], [%[fwd]] \r\n"
847 "ldrsh %[_1], [%[interp], #2] \r\n"
848 "ldrsh %[_3], [%[fwd], #2] \r\n"
849 "mul %[out], %[_0], %[_2] \r\n" /* out= fwd[0]*interp[0] */
850 "ldrsh %[_0], [%[interp], #4] \r\n"
851 "ldrsh %[_2], [%[rev], #2] \r\n"
852 "mla %[out], %[_1], %[_3], %[out] \r\n" /* out+=fwd[1]*interp[1] */
853 "ldrsh %[_1], [%[interp], #6] \r\n"
854 "ldrsh %[_3], [%[rev]] \r\n"
855 "mla %[out], %[_0], %[_2], %[out] \r\n" /* out+=rev[1]*interp[2] */
856 "mla %[out], %[_1], %[_3], %[out] \r\n" /* out+=rev[0]*interp[3] */
857 : [out]"=&r"(output),
858 [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
859 [_2]"=&r"(_2), [_3]"=&r"(_3)
860 : [fwd]"r"(fwd), [rev]"r"(rev),
861 [interp]"r"(interp));
862 /* Apply voice envelope */
863 asm volatile (
864 "mov %[_2], %[out], asr #11 \r\n"
865 "mul %[out], %[_2], %[envx] \r\n"
866 : [out]"+r"(output), [_2]"=&r"(_2)
867 : [envx]"r"((int)voice->envx));
868 /* Reduce and apply left and right volume */
869 asm volatile (
870 "mov %[out], %[out], asr #11 \r\n"
871 "mul %[amp_0], %[out], %[vvol_0] \r\n"
872 "mul %[amp_1], %[out], %[vvol_1] \r\n"
873 : [out]"+r"(output),
874 [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
875 : [vvol_0]"r"((int)voice->volume[0]),
876 [vvol_1]"r"((int)voice->volume[1]));
877
878 raw_voice->outx = output >> 8;
879 #endif /* ARM_ARCH */
880 #else /* Unoptimized CPU */
881 int output = (((fwd [0] * interp [0] +
882 fwd [1] * interp [1] +
883 rev [1] * interp [2] +
884 rev [0] * interp [3] ) >> 11) * voice->envx) >> 11;
885
886 /* duplicated here to give compiler more to run in parallel */
887 amp_0 = voice->volume [0] * output;
888 amp_1 = voice->volume [1] * output;
889
890 raw_voice->outx = output >> 8;
891 #endif /* CPU_* */
892 }
893 else /* slow gaussian */
894 {
895 #if defined(CPU_ARM)
896 #if ARM_ARCH >= 6
897 int output = *(int16_t*) &this->noise;
898
899 if ( !(this->r.g.noise_enables & vbit) )
900 {
901 /* Interpolate */
902 int _2, _3;
903 asm volatile (
904 /* NOTE: often-unaligned accesses */
905 "ldr %[_0], [%[interp]] \r\n" /* _0=i0i1 */
906 "ldr %[_2], [%[fwd]] \r\n" /* _2=f0f1 */
907 "ldr %[_1], [%[interp], #4] \r\n" /* _1=i2i3 */
908 "ldr %[_3], [%[rev]] \r\n" /* _3=f2f3 */
909 "smulbb %[out], %[_0], %[_2] \r\n" /* out=f0*i0 */
910 "smultt %[_0], %[_0], %[_2] \r\n" /* _0=f1*i1 */
911 "smulbt %[_2], %[_1], %[_3] \r\n" /* _2=r1*i2 */
912 "smultb %[_3], %[_1], %[_3] \r\n" /* _3=r0*i3 */
913 : [out]"=r"(output),
914 [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
915 [_2]"=&r"(_2), [_3]"=r"(_3)
916 : [fwd]"r"(fwd), [rev]"r"(rev),
917 [interp]"r"(interp));
918 asm volatile (
919 "mov %[out], %[out], asr#12 \r\n"
920 "add %[_0], %[out], %[_0], asr #12 \r\n"
921 "add %[_2], %[_0], %[_2], asr #12 \r\n"
922 "pkhbt %[_0], %[_2], %[_3], asl #4 \r\n" /* _3[31:16], _2[15:0] */
923 "sadd16 %[_0], %[_0], %[_0] \r\n" /* _3[31:16]*2, _2[15:0]*2 */
924 "qsubaddx %[out], %[_0], %[_0] \r\n" /* out[15:0]=
925 * sat16(_3[31:16]+_2[15:0]) */
926 : [out]"+r"(output),
927 [_0]"+r"(amp_0), [_2]"+r"(_2), [_3]"+r"(_3));
928 }
929 /* Apply voice envelope */
930 asm volatile (
931 "smulbb %[out], %[out], %[envx] \r\n"
932 : [out]"+r"(output)
933 : [envx]"r"(voice->envx));
934 /* Reduce and apply left and right volume */
935 asm volatile (
936 "mov %[out], %[out], asr #11 \r\n"
937 "bic %[out], %[out], #0x1 \r\n"
938 "mul %[amp_0], %[out], %[vvol_0] \r\n"
939 "mul %[amp_1], %[out], %[vvol_1] \r\n"
940 : [out]"+r"(output),
941 [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
942 : [vvol_0]"r"((int)voice->volume[0]),
943 [vvol_1]"r"((int)voice->volume[1]));
944
945 prev_outx = output;
946 raw_voice->outx = output >> 8;
947 #else /* ARM_ARCH < 6 */
948 int output = *(int16_t*) &this->noise;
949
950 if ( !(this->r.g.noise_enables & vbit) )
951 {
952 /* Interpolate */
953 int _2, _3;
954 asm volatile (
955 "ldrsh %[_0], [%[interp]] \r\n"
956 "ldrsh %[_2], [%[fwd]] \r\n"
957 "ldrsh %[_1], [%[interp], #2] \r\n"
958 "ldrsh %[_3], [%[fwd], #2] \r\n"
959 "mul %[out], %[_2], %[_0] \r\n" /* fwd[0]*interp[0] */
960 "ldrsh %[_2], [%[rev], #2] \r\n"
961 "mul %[_0], %[_3], %[_1] \r\n" /* fwd[1]*interp[1] */
962 "ldrsh %[_1], [%[interp], #4] \r\n"
963 "mov %[out], %[out], asr #12 \r\n"
964 "ldrsh %[_3], [%[rev]] \r\n"
965 "mul %[_2], %[_1], %[_2] \r\n" /* rev[1]*interp[2] */
966 "ldrsh %[_1], [%[interp], #6] \r\n"
967 "add %[_0], %[out], %[_0], asr #12 \r\n"
968 "mul %[_3], %[_1], %[_3] \r\n" /* rev[0]*interp[3] */
969 "add %[_2], %[_0], %[_2], asr #12 \r\n"
970 "mov %[_2], %[_2], lsl #17 \r\n"
971 "mov %[_3], %[_3], asr #12 \r\n"
972 "mov %[_3], %[_3], asl #1 \r\n"
973 "add %[out], %[_3], %[_2], asr #16 \r\n"
974 : [out]"=&r"(output),
975 [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
976 [_2]"=&r"(_2), [_3]"=&r"(_3)
977 : [fwd]"r"(fwd), [rev]"r"(rev),
978 [interp]"r"(interp));
979
980 output = CLAMP16(output);
981 }
982 /* Apply voice envelope */
983 asm volatile (
984 "mul %[_0], %[out], %[envx] \r\n"
985 : [_0]"=r"(amp_0)
986 : [out]"r"(output), [envx]"r"((int)voice->envx));
987 /* Reduce and apply left and right volume */
988 asm volatile (
989 "mov %[out], %[amp_0], asr #11 \r\n" /* amp_0 = _0 */
990 "bic %[out], %[out], #0x1 \r\n"
991 "mul %[amp_0], %[out], %[vvol_0] \r\n"
992 "mul %[amp_1], %[out], %[vvol_1] \r\n"
993 : [out]"+r"(output),
994 [amp_0]"+r"(amp_0), [amp_1]"=r"(amp_1)
995 : [vvol_0]"r"((int)voice->volume[0]),
996 [vvol_1]"r"((int)voice->volume[1]));
997
998 prev_outx = output;
999 raw_voice->outx = output >> 8;
1000 #endif /* ARM_ARCH >= 6 */
1001 #else /* Unoptimized CPU */
1002 int output = *(int16_t*) &this->noise;
1003
1004 if ( !(this->r.g.noise_enables & vbit) )
1005 {
1006 output = (fwd [0] * interp [0]) & ~0xFFF;
1007 output = (output + fwd [1] * interp [1]) & ~0xFFF;
1008 output = (output + rev [1] * interp [2]) >> 12;
1009 output = (int16_t) (output * 2);
1010 output += ((rev [0] * interp [3]) >> 12) * 2;
1011 output = CLAMP16( output );
1012 }
1013 output = (output * voice->envx) >> 11 & ~1;
1014
1015 /* duplicated here to give compiler more to run in parallel */
1016 amp_0 = voice->volume [0] * output;
1017 amp_1 = voice->volume [1] * output;
1018
1019 prev_outx = output;
1020 raw_voice->outx = output >> 8;
1021 #endif /* CPU_* */
1022 }
1023 #else /* SPCNOINTERP */
1024 /* two-point linear interpolation */
1025 #ifdef CPU_COLDFIRE
1026 int amp_0 = (int16_t)this->noise;
1027 int amp_1;
1028
1029 if ( (this->r.g.noise_enables & vbit) == 0 )
1030 {
1031 uint32_t f = voice->position;
1032 int32_t y0;
1033
1034 /**
1035 * Formula (fastest found so far of MANY):
1036 * output = y0 + f*y1 - f*y0
1037 */
1038 asm volatile (
1039 /* separate fractional and whole parts */
1040 "move.l %[f], %[y1] \r\n"
1041 "and.l #0xfff, %[f] \r\n"
1042 "lsr.l %[sh], %[y1] \r\n"
1043 /* load samples y0 (upper) & y1 (lower) */
1044 "move.l 2(%[s], %[y1].l*2), %[y1] \r\n"
1045 /* %acc0 = f*y1 */
1046 "mac.w %[f]l, %[y1]l, %%acc0 \r\n"
1047 /* %acc0 -= f*y0 */
1048 "msac.w %[f]l, %[y1]u, %%acc0 \r\n"
1049 /* separate out y0 and sign extend */
1050 "swap %[y1] \r\n"
1051 "movea.w %[y1], %[y0] \r\n"
1052 /* fetch result, scale down and add y0 */
1053 "movclr.l %%acc0, %[y1] \r\n"
1054 /* output = y0 + (result >> 12) */
1055 "asr.l %[sh], %[y1] \r\n"
1056 "add.l %[y0], %[y1] \r\n"
1057 : [f]"+d"(f), [y0]"=&a"(y0), [y1]"=&d"(amp_0)
1058 : [s]"a"(voice->samples), [sh]"d"(12));
1059 }
1060
1061 /* apply voice envelope to output */
1062 asm volatile (
1063 "mac.w %[out]l, %[envx]l, %%acc0 \r\n"
1064 :
1065 : [out]"r"(amp_0), [envx]"r"(voice->envx));
1066
1067 /* advance voice position */
1068 voice->position += rate;
1069
1070 /* fetch output, scale and apply left and right
1071 voice volume */
1072 asm volatile (
1073 "movclr.l %%acc0, %[out] \r\n"
1074 "asr.l %[sh], %[out] \r\n"
1075 "mac.l %[vvol_0], %[out], %%acc0 \r\n"
1076 "mac.l %[vvol_1], %[out], %%acc1 \r\n"
1077 : [out]"=&d"(amp_0)
1078 : [vvol_0]"r"((int)voice->volume[0]),
1079 [vvol_1]"r"((int)voice->volume[1]),
1080 [sh]"d"(11));
1081
1082 /* save this output into previous, scale and save in
1083 output register */
1084 prev_outx = amp_0;
1085 raw_voice->outx = amp_0 >> 8;
1086
1087 /* fetch final voice output */
1088 asm volatile (
1089 "movclr.l %%acc0, %[amp_0] \r\n"
1090 "movclr.l %%acc1, %[amp_1] \r\n"
1091 : [amp_0]"=r"(amp_0), [amp_1]"=r"(amp_1));
1092 #elif defined (CPU_ARM)
1093 int amp_0, amp_1;
1094
1095 if ( (this->r.g.noise_enables & vbit) != 0 )
1096 {
1097 amp_0 = *(int16_t *)&this->noise;
1098 }
1099 else
1100 {
1101 uint32_t f = voice->position;
1102 amp_0 = (uint32_t)voice->samples;
1103
1104 asm volatile(
1105 "mov %[y1], %[f], lsr #12 \r\n"
1106 "eor %[f], %[f], %[y1], lsl #12 \r\n"
1107 "add %[y1], %[y0], %[y1], lsl #1 \r\n"
1108 "ldrsh %[y0], [%[y1], #2] \r\n"
1109 "ldrsh %[y1], [%[y1], #4] \r\n"
1110 "sub %[y1], %[y1], %[y0] \r\n"
1111 "mul %[f], %[y1], %[f] \r\n"
1112 "add %[y0], %[y0], %[f], asr #12 \r\n"
1113 : [f]"+r"(f), [y0]"+r"(amp_0), [y1]"=&r"(amp_1));
1114 }
1115
1116 voice->position += rate;
1117
1118 asm volatile(
1119 "mul %[amp_1], %[amp_0], %[envx] \r\n"
1120 "mov %[amp_0], %[amp_1], asr #11 \r\n"
1121 "mov %[amp_1], %[amp_0], asr #8 \r\n"
1122 : [amp_0]"+r"(amp_0), [amp_1]"=r"(amp_1)
1123 : [envx]"r"(voice->envx));
1124
1125 prev_outx = amp_0;
1126 raw_voice->outx = (int8_t)amp_1;
1127
1128 asm volatile(
1129 "mul %[amp_1], %[amp_0], %[vol_1] \r\n"
1130 "mul %[amp_0], %[vol_0], %[amp_0] \r\n"
1131 : [amp_0]"+r"(amp_0), [amp_1]"=&r"(amp_1)
1132 : [vol_0]"r"((int)voice->volume[0]),
1133 [vol_1]"r"((int)voice->volume[1]));
1134 #else /* Unoptimized CPU */
1135 int output;
1136
1137 if ( (this->r.g.noise_enables & vbit) == 0 )
1138 {
1139 int const fraction = voice->position & 0xfff;
1140 short const* const pos = (voice->samples + (voice->position >> 12)) + 1;
1141 output = pos[0] + ((fraction * (pos[1] - pos[0])) >> 12);
1142 } else {
1143 output = *(int16_t *)&this->noise;
1144 }
1145
1146 voice->position += rate;
1147
1148 output = (output * voice->envx) >> 11;
1149
1150 /* duplicated here to give compiler more to run in parallel */
1151 int amp_0 = voice->volume [0] * output;
1152 int amp_1 = voice->volume [1] * output;
1153
1154 prev_outx = output;
1155 raw_voice->outx = (int8_t) (output >> 8);
1156 #endif /* CPU_* */
1157 #endif /* SPCNOINTERP */
1158
1159 #if SPC_BRRCACHE
1160 if ( voice->position >= voice->wave_end )
1161 {
1162 long loop_len = voice->wave_loop << 12;
1163 voice->position -= loop_len;
1164 this->r.g.wave_ended |= vbit;
1165 if ( !loop_len )
1166 {
1167 this->keys_down ^= vbit;
1168 raw_voice->envx = 0;
1169 voice->envx = 0;
1170 }
1171 }
1172 #endif
1173#if 0
1174 EXIT_TIMER(dsp_gen);
1175
1176 ENTER_TIMER(dsp_mix);
1177#endif
1178 chans_0 += amp_0;
1179 chans_1 += amp_1;
1180 #if !SPC_NOECHO
1181 if ( this->r.g.echo_ons & vbit )
1182 {
1183 echo_0 += amp_0;
1184 echo_1 += amp_1;
1185 }
1186 #endif
1187#if 0
1188 EXIT_TIMER(dsp_mix);
1189#endif
1190 }
1191 /* end of voice loop */
1192
1193 #if !SPC_NOECHO
1194 #ifdef CPU_COLDFIRE
1195 /* Read feedback from echo buffer */
1196 int echo_pos = this->echo_pos;
1197 uint8_t* const echo_ptr = RAM + ((echo_start + echo_pos) & 0xFFFF);
1198 echo_pos += 4;
1199 if ( echo_pos >= echo_wrap )
1200 echo_pos = 0;
1201 this->echo_pos = echo_pos;
1202 int fb = swap_odd_even32(*(int32_t *)echo_ptr);
1203 int out_0, out_1;
1204
1205 /* Keep last 8 samples */
1206 *this->last_fir_ptr = fb;
1207 this->last_fir_ptr = this->fir_ptr;
1208
1209 /* Apply echo FIR filter to output samples read from echo buffer -
1210 circular buffer is hardware incremented and masked; FIR
1211 coefficients and buffer history are loaded in parallel with
1212 multiply accumulate operations. Shift left by one here and once
1213 again when calculating feedback to have sample values justified
1214 to bit 31 in the output to ease endian swap, interleaving and
1215 clamping before placing result in the program's echo buffer. */
1216 int _0, _1, _2;
1217 asm volatile (
1218 "move.l (%[fir_c]) , %[_2] \r\n"
1219 "mac.w %[fb]u, %[_2]u, <<, (%[fir_p])+&, %[_0], %%acc0 \r\n"
1220 "mac.w %[fb]l, %[_2]u, <<, (%[fir_p])& , %[_1], %%acc1 \r\n"
1221 "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
1222 "mac.w %[_0]l, %[_2]l, <<, 4(%[fir_c]) , %[_2], %%acc1 \r\n"
1223 "mac.w %[_1]u, %[_2]u, <<, 4(%[fir_p])& , %[_0], %%acc0 \r\n"
1224 "mac.w %[_1]l, %[_2]u, <<, 8(%[fir_p])& , %[_1], %%acc1 \r\n"
1225 "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
1226 "mac.w %[_0]l, %[_2]l, <<, 8(%[fir_c]) , %[_2], %%acc1 \r\n"
1227 "mac.w %[_1]u, %[_2]u, <<, 12(%[fir_p])& , %[_0], %%acc0 \r\n"
1228 "mac.w %[_1]l, %[_2]u, <<, 16(%[fir_p])& , %[_1], %%acc1 \r\n"
1229 "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
1230 "mac.w %[_0]l, %[_2]l, <<, 12(%[fir_c]) , %[_2], %%acc1 \r\n"
1231 "mac.w %[_1]u, %[_2]u, <<, 20(%[fir_p])& , %[_0], %%acc0 \r\n"
1232 "mac.w %[_1]l, %[_2]u, << , %%acc1 \r\n"
1233 "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
1234 "mac.w %[_0]l, %[_2]l, << , %%acc1 \r\n"
1235 : [_0]"=&r"(_0), [_1]"=&r"(_1), [_2]"=&r"(_2),
1236 [fir_p]"+a"(this->fir_ptr)
1237 : [fir_c]"a"(this->fir_coeff), [fb]"r"(fb)
1238 );
1239
1240 /* Generate output */
1241 asm volatile (
1242 /* fetch filter results _after_ gcc loads asm
1243 block parameters to eliminate emac stalls */
1244 "movclr.l %%acc0, %[out_0] \r\n"
1245 "movclr.l %%acc1, %[out_1] \r\n"
1246 /* apply global volume */
1247 "mac.l %[chans_0], %[gv_0] , %%acc2 \r\n"
1248 "mac.l %[chans_1], %[gv_1] , %%acc3 \r\n"
1249 /* apply echo volume and add to final output */
1250 "mac.l %[ev_0], %[out_0], >>, %%acc2 \r\n"
1251 "mac.l %[ev_1], %[out_1], >>, %%acc3 \r\n"
1252 : [out_0]"=&r"(out_0), [out_1]"=&r"(out_1)
1253 : [chans_0]"r"(chans_0), [gv_0]"r"(global_vol_0),
1254 [ev_0]"r"((int)this->r.g.echo_volume_0),
1255 [chans_1]"r"(chans_1), [gv_1]"r"(global_vol_1),
1256 [ev_1]"r"((int)this->r.g.echo_volume_1)
1257 );
1258
1259 /* Feedback into echo buffer */
1260 if ( !(this->r.g.flags & 0x20) )
1261 {
1262 int sh = 1 << 9;
1263
1264 asm volatile (
1265 /* scale echo voices; saturate if overflow */
1266 "mac.l %[sh], %[e1] , %%acc1 \r\n"
1267 "mac.l %[sh], %[e0] , %%acc0 \r\n"
1268 /* add scaled output from FIR filter */
1269 "mac.l %[out_1], %[ef], <<, %%acc1 \r\n"
1270 "mac.l %[out_0], %[ef], <<, %%acc0 \r\n"
1271 /* swap and fetch feedback results - simply
1272 swap_odd_even32 mixed in between macs and
1273 movclrs to mitigate stall issues */
1274 "move.l #0x00ff00ff, %[sh] \r\n"
1275 "movclr.l %%acc1, %[e1] \r\n"
1276 "swap %[e1] \r\n"
1277 "movclr.l %%acc0, %[e0] \r\n"
1278 "move.w %[e1], %[e0] \r\n"
1279 "and.l %[e0], %[sh] \r\n"
1280 "eor.l %[sh], %[e0] \r\n"
1281 "lsl.l #8, %[sh] \r\n"
1282 "lsr.l #8, %[e0] \r\n"
1283 "or.l %[sh], %[e0] \r\n"
1284 /* save final feedback into echo buffer */
1285 "move.l %[e0], (%[echo_ptr]) \r\n"
1286 : [e0]"+d"(echo_0), [e1]"+d"(echo_1), [sh]"+d"(sh)
1287 : [out_0]"r"(out_0), [out_1]"r"(out_1),
1288 [ef]"r"((int)this->r.g.echo_feedback),
1289 [echo_ptr]"a"((int32_t *)echo_ptr)
1290 );
1291 }
1292
1293 /* Output final samples */
1294 asm volatile (
1295 /* fetch output saved in %acc2 and %acc3 */
1296 "movclr.l %%acc2, %[out_0] \r\n"
1297 "movclr.l %%acc3, %[out_1] \r\n"
1298 /* scale right by global_muting shift */
1299 "asr.l %[gm], %[out_0] \r\n"
1300 "asr.l %[gm], %[out_1] \r\n"
1301 : [out_0]"=&d"(out_0), [out_1]"=&d"(out_1)
1302 : [gm]"d"(global_muting)
1303 );
1304
1305 out_buf [ 0] = out_0;
1306 out_buf [WAV_CHUNK_SIZE] = out_1;
1307 out_buf ++;
1308 #elif defined (CPU_ARM)
1309 /* Read feedback from echo buffer */
1310 int echo_pos = this->echo_pos;
1311 uint8_t* const echo_ptr = RAM +
1312 ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF);
1313 echo_pos += 4;
1314 if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 )
1315 echo_pos = 0;
1316 this->echo_pos = echo_pos;
1317
1318 #if ARM_ARCH >= 6
1319 int32_t *fir_ptr, *fir_coeff;
1320 int fb_0, fb_1;
1321
1322 /* Apply FIR */
1323
1324 /* Keep last 8 samples */
1325 asm volatile (
1326 "ldr %[fb_0], [%[echo_p]] \r\n"
1327 "add %[fir_p], %[t_fir_p], #4 \r\n"
1328 "bic %[t_fir_p], %[fir_p], %[mask] \r\n"
1329 "str %[fb_0], [%[fir_p], #-4] \r\n"
1330 /* duplicate at +8 eliminates wrap checking below */
1331 "str %[fb_0], [%[fir_p], #28] \r\n"
1332 : [fir_p]"=&r"(fir_ptr), [t_fir_p]"+r"(this->fir_ptr),
1333 [fb_0]"=&r"(fb_0)
1334 : [echo_p]"r"(echo_ptr), [mask]"i"(~FIR_BUF_MASK));
1335
1336 fir_coeff = (int32_t *)this->fir_coeff;
1337
1338 /* Fugly, but the best version found. */
1339 int _0;
1340 asm volatile ( /* L0R0 = acc0 */
1341 "ldmia %[fir_p]!, { r2-r5 } \r\n" /* L1R1-L4R4 = r2-r5 */
1342 "ldmia %[fir_c]!, { r0-r1 } \r\n" /* C0C1-C2C3 = r0-r1 */
1343 "pkhbt %[_0], %[acc0], r2, asl #16 \r\n" /* L0R0,L1R1->L0L1,R0R1 */
1344 "pkhtb r2, r2, %[acc0], asr #16 \r\n"
1345 "smuad %[acc0], %[_0], r0 \r\n" /* acc0=L0*C0+L1*C1 */
1346 "smuad %[acc1], r2, r0 \r\n" /* acc1=R0*C0+R1*C1 */
1347 "pkhbt %[_0], r3, r4, asl #16 \r\n" /* L2R2,L3R3->L2L3,R2R3 */
1348 "pkhtb r4, r4, r3, asr #16 \r\n"
1349 "smlad %[acc0], %[_0], r1, %[acc0] \r\n" /* acc0+=L2*C2+L3*C3 */
1350 "smlad %[acc1], r4, r1, %[acc1] \r\n" /* acc1+=R2*C2+R3*C3 */
1351 "ldmia %[fir_p], { r2-r4 } \r\n" /* L5R5-L7R7 = r2-r4 */
1352 "ldmia %[fir_c], { r0-r1 } \r\n" /* C4C5-C6C7 = r0-r1 */
1353 "pkhbt %[_0], r5, r2, asl #16 \r\n" /* L4R4,L5R5->L4L5,R4R5 */
1354 "pkhtb r2, r2, r5, asr #16 \r\n"
1355 "smlad %[acc0], %[_0], r0, %[acc0] \r\n" /* acc0+=L4*C4+L5*C5 */
1356 "smlad %[acc1], r2, r0, %[acc1] \r\n" /* acc1+=R4*C4+R5*C5 */
1357 "pkhbt %[_0], r3, r4, asl #16 \r\n" /* L6R6,L7R7->L6L7,R6R7 */
1358 "pkhtb r4, r4, r3, asr #16 \r\n"
1359 "smlad %[acc0], %[_0], r1, %[acc0] \r\n" /* acc0+=L6*C6+L7*C7 */
1360 "smlad %[acc1], r4, r1, %[acc1] \r\n" /* acc1+=R6*C6+R7*C7 */
1361 : [acc0]"+r"(fb_0), [acc1]"=&r"(fb_1), [_0]"=&r"(_0),
1362 [fir_p]"+r"(fir_ptr), [fir_c]"+r"(fir_coeff)
1363 :
1364 : "r0", "r1", "r2", "r3", "r4", "r5");
1365
1366 /* Generate output */
1367 int amp_0, amp_1;
1368
1369 asm volatile (
1370 "mul %[amp_0], %[gvol_0], %[chans_0] \r\n"
1371 "mul %[amp_1], %[gvol_1], %[chans_1] \r\n"
1372 : [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
1373 : [gvol_0]"r"(global_vol_0), [gvol_1]"r"(global_vol_1),
1374 [chans_0]"r"(chans_0), [chans_1]"r"(chans_1));
1375 asm volatile (
1376 "mla %[amp_0], %[fb_0], %[ev_0], %[amp_0] \r\n"
1377 "mla %[amp_1], %[fb_1], %[ev_1], %[amp_1] \r\n"
1378 : [amp_0]"+r"(amp_0), [amp_1]"+r"(amp_1)
1379 : [fb_0]"r"(fb_0), [fb_1]"r"(fb_1),
1380 [ev_0]"r"((int)this->r.g.echo_volume_0),
1381 [ev_1]"r"((int)this->r.g.echo_volume_1));
1382
1383 out_buf [ 0] = amp_0 >> global_muting;
1384 out_buf [WAV_CHUNK_SIZE] = amp_1 >> global_muting;
1385 out_buf ++;
1386
1387 if ( !(this->r.g.flags & 0x20) )
1388 {
1389 /* Feedback into echo buffer */
1390 int e0, e1;
1391
1392 asm volatile (
1393 "mov %[e0], %[echo_0], asl #7 \r\n"
1394 "mov %[e1], %[echo_1], asl #7 \r\n"
1395 "mla %[e0], %[fb_0], %[efb], %[e0] \r\n"
1396 "mla %[e1], %[fb_1], %[efb], %[e1] \r\n"
1397 : [e0]"=&r"(e0), [e1]"=&r"(e1)
1398 : [echo_0]"r"(echo_0), [echo_1]"r"(echo_1),
1399 [fb_0]"r"(fb_0), [fb_1]"r"(fb_1),
1400 [efb]"r"((int)this->r.g.echo_feedback));
1401 asm volatile (
1402 "ssat %[e0], #16, %[e0], asr #14 \r\n"
1403 "ssat %[e1], #16, %[e1], asr #14 \r\n"
1404 "pkhbt %[e0], %[e0], %[e1], lsl #16 \r\n"
1405 "str %[e0], [%[echo_p]] \r\n"
1406 : [e0]"+r"(e0), [e1]"+r"(e1)
1407 : [echo_p]"r"(echo_ptr));
1408 }
1409 #else /* ARM_ARCH < 6 */
1410 int fb_0 = GET_LE16SA( echo_ptr );
1411 int fb_1 = GET_LE16SA( echo_ptr + 2 );
1412 int32_t *fir_ptr, *fir_coeff;
1413
1414 /* Keep last 8 samples */
1415
1416 /* Apply FIR */
1417 asm volatile (
1418 "add %[fir_p], %[t_fir_p], #8 \r\n"
1419 "bic %[t_fir_p], %[fir_p], %[mask] \r\n"
1420 "str %[fb_0], [%[fir_p], #-8] \r\n"
1421 "str %[fb_1], [%[fir_p], #-4] \r\n"
1422 /* duplicate at +8 eliminates wrap checking below */
1423 "str %[fb_0], [%[fir_p], #56] \r\n"
1424 "str %[fb_1], [%[fir_p], #60] \r\n"
1425 : [fir_p]"=&r"(fir_ptr), [t_fir_p]"+r"(this->fir_ptr)
1426 : [fb_0]"r"(fb_0), [fb_1]"r"(fb_1), [mask]"i"(~FIR_BUF_MASK));
1427
1428 fir_coeff = this->fir_coeff;
1429
1430 asm volatile (
1431 "ldmia %[fir_c]!, { r0-r1 } \r\n"
1432 "ldmia %[fir_p]!, { r4-r5 } \r\n"
1433 "mul %[fb_0], r0, %[fb_0] \r\n"
1434 "mul %[fb_1], r0, %[fb_1] \r\n"
1435 "mla %[fb_0], r4, r1, %[fb_0] \r\n"
1436 "mla %[fb_1], r5, r1, %[fb_1] \r\n"
1437 "ldmia %[fir_c]!, { r0-r1 } \r\n"
1438 "ldmia %[fir_p]!, { r2-r5 } \r\n"
1439 "mla %[fb_0], r2, r0, %[fb_0] \r\n"
1440 "mla %[fb_1], r3, r0, %[fb_1] \r\n"
1441 "mla %[fb_0], r4, r1, %[fb_0] \r\n"
1442 "mla %[fb_1], r5, r1, %[fb_1] \r\n"
1443 "ldmia %[fir_c]!, { r0-r1 } \r\n"
1444 "ldmia %[fir_p]!, { r2-r5 } \r\n"
1445 "mla %[fb_0], r2, r0, %[fb_0] \r\n"
1446 "mla %[fb_1], r3, r0, %[fb_1] \r\n"
1447 "mla %[fb_0], r4, r1, %[fb_0] \r\n"
1448 "mla %[fb_1], r5, r1, %[fb_1] \r\n"
1449 "ldmia %[fir_c]!, { r0-r1 } \r\n"
1450 "ldmia %[fir_p]!, { r2-r5 } \r\n"
1451 "mla %[fb_0], r2, r0, %[fb_0] \r\n"
1452 "mla %[fb_1], r3, r0, %[fb_1] \r\n"
1453 "mla %[fb_0], r4, r1, %[fb_0] \r\n"
1454 "mla %[fb_1], r5, r1, %[fb_1] \r\n"
1455 : [fb_0]"+r"(fb_0), [fb_1]"+r"(fb_1),
1456 [fir_p]"+r"(fir_ptr), [fir_c]"+r"(fir_coeff)
1457 :
1458 : "r0", "r1", "r2", "r3", "r4", "r5");
1459
1460 /* Generate output */
1461 int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0)
1462 >> global_muting;
1463 int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1)
1464 >> global_muting;
1465
1466 out_buf [ 0] = amp_0;
1467 out_buf [WAV_CHUNK_SIZE] = amp_1;
1468 out_buf ++;
1469
1470 if ( !(this->r.g.flags & 0x20) )
1471 {
1472 /* Feedback into echo buffer */
1473 int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14);
1474 int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14);
1475 e0 = CLAMP16( e0 );
1476 SET_LE16A( echo_ptr , e0 );
1477 e1 = CLAMP16( e1 );
1478 SET_LE16A( echo_ptr + 2, e1 );
1479 }
1480 #endif /* ARM_ARCH */
1481 #else /* Unoptimized CPU */
1482 /* Read feedback from echo buffer */
1483 int echo_pos = this->echo_pos;
1484 uint8_t* const echo_ptr = RAM +
1485 ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF);
1486 echo_pos += 4;
1487 if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 )
1488 echo_pos = 0;
1489 this->echo_pos = echo_pos;
1490 int fb_0 = GET_LE16SA( echo_ptr );
1491 int fb_1 = GET_LE16SA( echo_ptr + 2 );
1492
1493 /* Keep last 8 samples */
1494 int (* const fir_ptr) [2] = this->fir_buf + this->fir_pos;
1495 this->fir_pos = (this->fir_pos + 1) & (FIR_BUF_HALF - 1);
1496 fir_ptr [ 0] [0] = fb_0;
1497 fir_ptr [ 0] [1] = fb_1;
1498 /* duplicate at +8 eliminates wrap checking below */
1499 fir_ptr [FIR_BUF_HALF] [0] = fb_0;
1500 fir_ptr [FIR_BUF_HALF] [1] = fb_1;
1501
1502 /* Apply FIR */
1503 fb_0 *= this->fir_coeff [0];
1504 fb_1 *= this->fir_coeff [0];
1505
1506 #define DO_PT( i )\
1507 fb_0 += fir_ptr [i] [0] * this->fir_coeff [i];\
1508 fb_1 += fir_ptr [i] [1] * this->fir_coeff [i];
1509
1510 DO_PT( 1 )
1511 DO_PT( 2 )
1512 DO_PT( 3 )
1513 DO_PT( 4 )
1514 DO_PT( 5 )
1515 DO_PT( 6 )
1516 DO_PT( 7 )
1517
1518 /* Generate output */
1519 int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0)
1520 >> global_muting;
1521 int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1)
1522 >> global_muting;
1523 out_buf [ 0] = amp_0;
1524 out_buf [WAV_CHUNK_SIZE] = amp_1;
1525 out_buf ++;
1526
1527 if ( !(this->r.g.flags & 0x20) )
1528 {
1529 /* Feedback into echo buffer */
1530 int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14);
1531 int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14);
1532 e0 = CLAMP16( e0 );
1533 SET_LE16A( echo_ptr , e0 );
1534 e1 = CLAMP16( e1 );
1535 SET_LE16A( echo_ptr + 2, e1 );
1536 }
1537 #endif /* CPU_* */
1538 #else /* SPCNOECHO == 1*/
1539 /* Generate output */
1540 int amp_0 = (chans_0 * global_vol_0) >> global_muting;
1541 int amp_1 = (chans_1 * global_vol_1) >> global_muting;
1542 out_buf [ 0] = amp_0;
1543 out_buf [WAV_CHUNK_SIZE] = amp_1;
1544 out_buf ++;
1545 #endif /* SPCNOECHO */
1546 }
1547 while ( --count );
1548#if 0
1549 EXIT_TIMER(dsp);
1550 ENTER_TIMER(cpu);
1551#endif
1552}
1553
1554void DSP_reset( struct Spc_Dsp* this )
1555{
1556 this->keys_down = 0;
1557 this->echo_pos = 0;
1558 this->noise_count = 0;
1559 this->noise = 2;
1560
1561 this->r.g.flags = 0xE0; /* reset, mute, echo off */
1562 this->r.g.key_ons = 0;
1563
1564 ci->memset( this->voice_state, 0, sizeof this->voice_state );
1565
1566 int i;
1567 for ( i = VOICE_COUNT; --i >= 0; )
1568 {
1569 struct voice_t* v = this->voice_state + i;
1570 v->env_mode = state_release;
1571 v->addr = ram.ram;
1572 }
1573
1574 #if SPC_BRRCACHE
1575 this->oldsize = 0;
1576 for ( i = 0; i < 256; i++ )
1577 this->wave_entry [i].start_addr = -1;
1578 #endif
1579
1580#if defined(CPU_COLDFIRE)
1581 this->fir_ptr = fir_buf;
1582 this->last_fir_ptr = &fir_buf [7];
1583 ci->memset( fir_buf, 0, sizeof fir_buf );
1584#elif defined (CPU_ARM)
1585 this->fir_ptr = fir_buf;
1586 ci->memset( fir_buf, 0, sizeof fir_buf );
1587#else
1588 this->fir_pos = 0;
1589 ci->memset( this->fir_buf, 0, sizeof this->fir_buf );
1590#endif
1591
1592 assert( offsetof (struct globals_t,unused9 [2]) == REGISTER_COUNT );
1593 assert( sizeof (this->r.voice) == REGISTER_COUNT );
1594}
diff --git a/lib/rbcodec/codecs/libspc/spc_emu.c b/lib/rbcodec/codecs/libspc/spc_emu.c
new file mode 100644
index 0000000000..5ea5b0cdeb
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/spc_emu.c
@@ -0,0 +1,397 @@
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 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "codeclib.h"
24#include "spc_codec.h"
25#include "spc_profiler.h"
26
27/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
28/* DSP Based on Brad Martin's OpenSPC DSP emulator */
29/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
30
31struct cpu_ram_t ram IBSS_ATTR_SPC_LARGE_IRAM CACHEALIGN_ATTR;
32
33/**************** Timers ****************/
34
35static void Timer_run_( struct Timer* t, long time ) ICODE_ATTR_SPC;
36static void Timer_run_( struct Timer* t, long time )
37{
38 /* when disabled, next_tick should always be in the future */
39 assert( t->enabled );
40
41 int elapsed = ((time - t->next_tick) >> t->shift) + 1;
42 t->next_tick += elapsed << t->shift;
43
44 elapsed += t->count;
45 if ( elapsed >= t->period ) /* avoid unnecessary division */
46 {
47 int n = elapsed / t->period;
48 elapsed -= n * t->period;
49 t->counter = (t->counter + n) & 15;
50 }
51 t->count = elapsed;
52}
53
54static inline void Timer_run( struct Timer* t, long time )
55{
56 if ( time >= t->next_tick )
57 Timer_run_( t, time );
58}
59
60/**************** SPC emulator ****************/
61/* 1.024 MHz clock / 32000 samples per second */
62
63static void SPC_enable_rom( THIS, int enable )
64{
65 if ( this->rom_enabled != enable )
66 {
67 this->rom_enabled = enable;
68 ci->memcpy( RAM + ROM_ADDR, (enable ? this->boot_rom : this->extra_ram), ROM_SIZE );
69 /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
70 }
71}
72
73void SPC_Init( THIS )
74{
75 this->timer [0].shift = 4 + 3; /* 8 kHz */
76 this->timer [1].shift = 4 + 3; /* 8 kHz */
77 this->timer [2].shift = 4; /* 8 kHz */
78
79 /* Put STOP instruction around memory to catch PC underflow/overflow. */
80 ci->memset( ram.padding1, 0xFF, sizeof ram.padding1 );
81 ci->memset( ram.padding2, 0xFF, sizeof ram.padding2 );
82
83 /* A few tracks read from the last four bytes of IPL ROM */
84 this->boot_rom [sizeof this->boot_rom - 2] = 0xC0;
85 this->boot_rom [sizeof this->boot_rom - 1] = 0xFF;
86 ci->memset( this->boot_rom, 0, sizeof this->boot_rom - 2 );
87
88 /* Have DSP in a defined state in case EMU is run and hasn't loaded
89 * a program yet */
90 DSP_reset(&this->dsp);
91}
92
93static void SPC_load_state( THIS, struct cpu_regs_t const* cpu_state,
94 const void* new_ram, const void* dsp_state )
95{
96 ci->memcpy(&(this->r),cpu_state,sizeof this->r);
97
98 /* ram */
99 ci->memcpy( RAM, new_ram, sizeof RAM );
100 ci->memcpy( this->extra_ram, RAM + ROM_ADDR, sizeof this->extra_ram );
101
102 /* boot rom (have to force enable_rom() to update it) */
103 this->rom_enabled = !(RAM [0xF1] & 0x80);
104 SPC_enable_rom( this, !this->rom_enabled );
105
106 /* dsp */
107 /* some SPCs rely on DSP immediately generating one sample */
108 this->extra_cycles = 32;
109 DSP_reset( &this->dsp );
110 int i;
111 for ( i = 0; i < REGISTER_COUNT; i++ )
112 DSP_write( &this->dsp, i, ((uint8_t const*) dsp_state) [i] );
113
114 /* timers */
115 for ( i = 0; i < TIMER_COUNT; i++ )
116 {
117 struct Timer* t = &this->timer [i];
118
119 t->next_tick = -EXTRA_CLOCKS;
120 t->enabled = (RAM [0xF1] >> i) & 1;
121 if ( !t->enabled )
122 t->next_tick = TIMER_DISABLED_TIME;
123 t->count = 0;
124 t->counter = RAM [0xFD + i] & 15;
125
126 int p = RAM [0xFA + i];
127 if ( !p )
128 p = 0x100;
129 t->period = p;
130 }
131
132 /* Handle registers which already give 0 when read by
133 setting RAM and not changing it.
134 Put STOP instruction in registers which can be read,
135 to catch attempted execution. */
136 RAM [0xF0] = 0;
137 RAM [0xF1] = 0;
138 RAM [0xF3] = 0xFF;
139 RAM [0xFA] = 0;
140 RAM [0xFB] = 0;
141 RAM [0xFC] = 0;
142 RAM [0xFD] = 0xFF;
143 RAM [0xFE] = 0xFF;
144 RAM [0xFF] = 0xFF;
145}
146
147static void clear_echo( THIS )
148{
149 if ( !(DSP_read( &this->dsp, 0x6C ) & 0x20) )
150 {
151 unsigned addr = 0x100 * DSP_read( &this->dsp, 0x6D );
152 size_t size = 0x800 * DSP_read( &this->dsp, 0x7D );
153 size_t max_size = sizeof RAM - addr;
154 if ( size > max_size )
155 size = sizeof RAM - addr;
156 ci->memset( RAM + addr, 0xFF, size );
157 }
158}
159
160int SPC_load_spc( THIS, const void* data, long size )
161{
162 struct spc_file_t const* spc = (struct spc_file_t const*) data;
163 struct cpu_regs_t regs;
164
165 if ( size < SPC_FILE_SIZE )
166 return -1;
167
168 if ( ci->memcmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
169 return -1;
170
171 regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
172 regs.a = spc->a;
173 regs.x = spc->x;
174 regs.y = spc->y;
175 regs.status = spc->status;
176 regs.sp = spc->sp;
177
178 if ( (unsigned long) size >= sizeof *spc )
179 ci->memcpy( this->boot_rom, spc->ipl_rom, sizeof this->boot_rom );
180
181 SPC_load_state( this, &regs, spc->ram, spc->dsp );
182
183 clear_echo(this);
184
185 return 0;
186}
187
188/**************** DSP interaction ****************/
189static void SPC_run_dsp_( THIS, long time ) ICODE_ATTR_SPC;
190static void SPC_run_dsp_( THIS, long time )
191{
192 /* divide by CLOCKS_PER_SAMPLE */
193 int count = ((time - this->next_dsp) >> 5) + 1;
194 int32_t* buf = this->sample_buf;
195 this->sample_buf = buf + count;
196 this->next_dsp += count * CLOCKS_PER_SAMPLE;
197 DSP_run( &this->dsp, count, buf );
198}
199
200static inline void SPC_run_dsp( THIS, long time )
201{
202 if ( time >= this->next_dsp )
203 SPC_run_dsp_( this, time );
204}
205
206int SPC_read( THIS, unsigned addr, long const time )
207{
208 int result = RAM [addr];
209
210 if ( ((unsigned) (addr - 0xF0)) < 0x10 )
211 {
212 assert( 0xF0 <= addr && addr <= 0xFF );
213
214 /* counters */
215 int i = addr - 0xFD;
216 if ( i >= 0 )
217 {
218 struct Timer* t = &this->timer [i];
219 Timer_run( t, time );
220 result = t->counter;
221 t->counter = 0;
222 }
223 /* dsp */
224 else if ( addr == 0xF3 )
225 {
226 SPC_run_dsp( this, time );
227 result = DSP_read( &this->dsp, RAM [0xF2] & 0x7F );
228 }
229 }
230 return result;
231}
232
233void SPC_write( THIS, unsigned addr, int data, long const time )
234{
235 /* first page is very common */
236 if ( addr < 0xF0 )
237 {
238 RAM [addr] = (uint8_t) data;
239 }
240 else switch ( addr )
241 {
242 /* RAM */
243 default:
244 if ( addr < ROM_ADDR )
245 {
246 RAM [addr] = (uint8_t) data;
247 }
248 else
249 {
250 this->extra_ram [addr - ROM_ADDR] = (uint8_t) data;
251 if ( !this->rom_enabled )
252 RAM [addr] = (uint8_t) data;
253 }
254 break;
255
256 /* DSP */
257 /*case 0xF2:*/ /* mapped to RAM */
258 case 0xF3: {
259 SPC_run_dsp( this, time );
260 int reg = RAM [0xF2];
261 if ( reg < REGISTER_COUNT ) {
262 DSP_write( &this->dsp, reg, data );
263 }
264 else {
265 /*dprintf( "DSP write to $%02X\n", (int) reg ); */
266 }
267 break;
268 }
269
270 case 0xF0: /* Test register */
271 /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
272 break;
273
274 /* Config */
275 case 0xF1:
276 {
277 int i;
278 /* timers */
279 for ( i = 0; i < TIMER_COUNT; i++ )
280 {
281 struct Timer * t = this->timer+i;
282 if ( !(data & (1 << i)) )
283 {
284 t->enabled = 0;
285 t->next_tick = TIMER_DISABLED_TIME;
286 }
287 else if ( !t->enabled )
288 {
289 /* just enabled */
290 t->enabled = 1;
291 t->counter = 0;
292 t->count = 0;
293 t->next_tick = time;
294 }
295 }
296
297 /* port clears */
298 if ( data & 0x10 )
299 {
300 RAM [0xF4] = 0;
301 RAM [0xF5] = 0;
302 }
303 if ( data & 0x20 )
304 {
305 RAM [0xF6] = 0;
306 RAM [0xF7] = 0;
307 }
308
309 SPC_enable_rom( this, (data & 0x80) != 0 );
310 break;
311 }
312
313 /* Ports */
314 case 0xF4:
315 case 0xF5:
316 case 0xF6:
317 case 0xF7:
318 /* to do: handle output ports */
319 break;
320
321 /* verified on SNES that these are read/write (RAM) */
322 /*case 0xF8: */
323 /*case 0xF9: */
324
325 /* Timers */
326 case 0xFA:
327 case 0xFB:
328 case 0xFC: {
329 int i = addr - 0xFA;
330 struct Timer* t = &this->timer [i];
331 if ( (t->period & 0xFF) != data )
332 {
333 Timer_run( t, time );
334 this->timer[i].period = data ? data : 0x100;
335 }
336 break;
337 }
338
339 /* Counters (cleared on write) */
340 case 0xFD:
341 case 0xFE:
342 case 0xFF:
343 /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
344 this->timer [addr - 0xFD].counter = 0;
345 break;
346 }
347}
348
349/**************** Sample generation ****************/
350int SPC_play( THIS, long count, int32_t* out )
351{
352 int i;
353 assert( count % 2 == 0 ); /* output is always in pairs of samples */
354
355 long start_time = -(count >> 1) * CLOCKS_PER_SAMPLE - EXTRA_CLOCKS;
356
357 /* DSP output is made on-the-fly when DSP registers are read or written */
358 this->sample_buf = out;
359 this->next_dsp = start_time + CLOCKS_PER_SAMPLE;
360
361 /* Localize timer next_tick times and run them to the present to prevent
362 a running but ignored timer's next_tick from getting too far behind
363 and overflowing. */
364 for ( i = 0; i < TIMER_COUNT; i++ )
365 {
366 struct Timer* t = &this->timer [i];
367 if ( t->enabled )
368 {
369 t->next_tick += start_time + EXTRA_CLOCKS;
370 Timer_run( t, start_time );
371 }
372 }
373
374 /* Run from start_time to 0, pre-advancing by extra cycles from last run */
375 this->extra_cycles = CPU_run( this, start_time + this->extra_cycles ) +
376 EXTRA_CLOCKS;
377 if ( this->extra_cycles < 0 )
378 {
379 /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
380 (int) CPU_read( r.pc ), (unsigned) r.pc ); */
381
382 return -1;
383 }
384
385 /* Catch DSP up to present */
386#if 0
387 ENTER_TIMER(cpu);
388#endif
389 SPC_run_dsp( this, -EXTRA_CLOCKS );
390#if 0
391 EXIT_TIMER(cpu);
392#endif
393 assert( this->next_dsp == CLOCKS_PER_SAMPLE - EXTRA_CLOCKS );
394 assert( this->sample_buf - out == count );
395
396 return 0;
397}
diff --git a/lib/rbcodec/codecs/libspc/spc_profiler.c b/lib/rbcodec/codecs/libspc/spc_profiler.c
new file mode 100644
index 0000000000..0ced8b5bd3
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/spc_profiler.c
@@ -0,0 +1,66 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
23/* DSP Based on Brad Martin's OpenSPC DSP emulator */
24/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
25
26#if defined(SPC_PROFILE) && defined(USEC_TIMER)
27
28#include "codeclib.h"
29#include "spc_codec.h"
30#define SPC_DEFINE_PROFILER_TIMERS
31#include "spc_profiler.h"
32
33void reset_profile_timers(void)
34{
35 RESET_TIMER(total);
36 RESET_TIMER(render);
37#if 0
38 RESET_TIMER(cpu);
39 RESET_TIMER(dsp);
40 RESET_TIMER(dsp_pregen);
41 RESET_TIMER(dsp_gen);
42 RESET_TIMER(dsp_mix);
43#endif
44}
45
46void print_timers(char * path)
47{
48 int logfd = ci->open("/spclog.txt",O_WRONLY|O_CREAT|O_APPEND, 0666);
49 ci->fdprintf(logfd,"%s:\t",path);
50 ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total));
51 PRINT_TIMER_PCT(render,total,"render");
52#if 0
53 PRINT_TIMER_PCT(cpu,total,"CPU");
54 PRINT_TIMER_PCT(dsp,total,"DSP");
55 ci->fdprintf(logfd,"(");
56 PRINT_TIMER_PCT(dsp_pregen,dsp,"pregen");
57 PRINT_TIMER_PCT(dsp_gen,dsp,"gen");
58 PRINT_TIMER_PCT(dsp_mix,dsp,"mix");
59#endif
60 ci->fdprintf(logfd,"\n");
61
62 ci->close(logfd);
63 logfd=-1;
64}
65
66#endif /* #if defined(SPC_PROFILE) && defined(USEC_TIMER) */
diff --git a/lib/rbcodec/codecs/libspc/spc_profiler.h b/lib/rbcodec/codecs/libspc/spc_profiler.h
new file mode 100644
index 0000000000..405ee43ef9
--- /dev/null
+++ b/lib/rbcodec/codecs/libspc/spc_profiler.h
@@ -0,0 +1,72 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* a fun simple elapsed time profiler */
23#ifndef _SPC_PROFILER_H_
24#define _SPC_PROFILER_H_
25
26#if defined(SPC_PROFILE) && defined(USEC_TIMER)
27
28#ifdef SPC_DEFINE_PROFILER_TIMERS
29#define CREATE_TIMER(name) uint32_t spc_timer_##name##_start,\
30 spc_timer_##name##_total
31#else
32#define CREATE_TIMER(name) extern uint32_t spc_timer_##name##_start,\
33 spc_timer_##name##_total
34#endif
35
36#define ENTER_TIMER(name) spc_timer_##name##_start=USEC_TIMER
37#define EXIT_TIMER(name) spc_timer_##name##_total+=\
38 (USEC_TIMER-spc_timer_##name##_start)
39#define READ_TIMER(name) (spc_timer_##name##_total)
40#define RESET_TIMER(name) spc_timer_##name##_total=0
41
42#define PRINT_TIMER_PCT(bname,tname,nstr) ci->fdprintf( \
43 logfd,"%10ld ",READ_TIMER(bname));\
44 ci->fdprintf(logfd,"(%3d%%) " nstr "\t",\
45 ((uint64_t)READ_TIMER(bname))*100/READ_TIMER(tname))
46
47CREATE_TIMER(total);
48CREATE_TIMER(render);
49#if 0
50CREATE_TIMER(cpu);
51CREATE_TIMER(dsp);
52CREATE_TIMER(dsp_pregen);
53CREATE_TIMER(dsp_gen);
54CREATE_TIMER(dsp_mix);
55#endif
56
57void reset_profile_timers(void);
58void print_timers(char * path);
59
60#else
61
62#define CREATE_TIMER(name)
63#define ENTER_TIMER(name)
64#define EXIT_TIMER(name)
65#define READ_TIMER(name)
66#define RESET_TIMER(name)
67#define print_timers(path)
68#define reset_profile_timers()
69
70#endif
71
72#endif /* _SPC_PROFILER_H_ */