summaryrefslogtreecommitdiffstats
path: root/lib/rbcodec/codecs/libspc
diff options
context:
space:
mode:
authorSean Bartell <wingedtachikoma@gmail.com>2011-06-25 21:32:25 -0400
committerNils Wallménius <nils@rockbox.org>2012-04-25 22:13:20 +0200
commitf40bfc9267b13b54e6379dfe7539447662879d24 (patch)
tree9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libspc
parenta0009907de7a0107d49040d8a180f140e2eff299 (diff)
downloadrockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz
rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97 Reviewed-on: http://gerrit.rockbox.org/137 Reviewed-by: Nils Wallménius <nils@rockbox.org> Tested-by: Nils Wallménius <nils@rockbox.org>
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 @@
+spc_cpu.c
+spc_dsp.c
+spc_emu.c
+spc_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 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+# libspc
+SPCLIB := $(CODECDIR)/libspc.a
+SPCLIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libspc/SOURCES)
+SPCLIB_OBJ := $(call c2obj, $(SPCLIB_SRC))
+OTHER_SRC += $(SPCLIB_SRC)
+
+$(SPCLIB): $(SPCLIB_OBJ)
+ $(SILENT)$(shell rm -f $@)
+ $(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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS)
+ * Copyright (C) 2006-2007 Adam Gashlin (hcs)
+ * Copyright (C) 2004-2007 Shay Green (blargg)
+ * Copyright (C) 2002 Brad Martin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+/* DSP Based on Brad Martin's OpenSPC DSP emulator */
+/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
+
+#ifndef _SPC_CODEC_H_
+#define _SPC_CODEC_H_
+
+/* rather than comment out asserts, just define NDEBUG */
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+#include <assert.h>
+
+/** Basic configuration options **/
+
+#ifndef ARM_ARCH
+#define ARM_ARCH 0
+#endif
+
+#define SPC_DUAL_CORE 1
+
+#if !defined(SPC_DUAL_CORE) || NUM_CORES == 1
+#undef SPC_DUAL_CORE
+#define SPC_DUAL_CORE 0
+#endif
+
+/* Only some targets are fast enough for gaussian and realtime BRR decode */
+#if CONFIG_CPU == S3C2440 || CONFIG_CPU == IMX31L || \
+ CONFIG_CPU == AS3525 || CONFIG_CPU == AS3525v2 || \
+ defined(CPU_S5L870X) || \
+ (CONFIG_PLATFORM & PLATFORM_HOSTED) || MEMORYSIZE <= 2
+ /* Don't cache BRR waves */
+ #define SPC_BRRCACHE 0
+
+ /* Allow gaussian interpolation */
+ #define SPC_NOINTERP 0
+
+ /* Allow echo processing */
+ #define SPC_NOECHO 0
+#elif defined(CPU_COLDFIRE)
+ /* Cache BRR waves */
+ #define SPC_BRRCACHE 1
+
+ /* Disable gaussian interpolation */
+ #define SPC_NOINTERP 1
+
+ /* Allow echo processing */
+ #define SPC_NOECHO 0
+#elif defined (CPU_PP) && SPC_DUAL_CORE
+ /* Cache BRR waves */
+ #define SPC_BRRCACHE 1
+
+ /* Disable gaussian interpolation */
+ #define SPC_NOINTERP 1
+
+ /* Allow echo processing */
+ #define SPC_NOECHO 0
+#else
+ /* Cache BRR waves */
+ #define SPC_BRRCACHE 1
+
+ /* Disable gaussian interpolation */
+ #define SPC_NOINTERP 1
+
+ /* Disable echo processing */
+ #define SPC_NOECHO 1
+#endif
+
+#if (CONFIG_CPU == MCF5250)
+#define IBSS_ATTR_SPC IBSS_ATTR
+#define ICODE_ATTR_SPC ICODE_ATTR
+#define ICONST_ATTR_SPC ICONST_ATTR
+/* Not enough IRAM available to move further data to it. */
+#define IBSS_ATTR_SPC_LARGE_IRAM
+
+#elif (CONFIG_CPU == PP5020)
+/* spc is slower on PP5020 when moving data to IRAM. */
+#define IBSS_ATTR_SPC
+#define ICODE_ATTR_SPC
+#define ICONST_ATTR_SPC
+/* Not enough IRAM available to move further data to it. */
+#define IBSS_ATTR_SPC_LARGE_IRAM
+
+#elif (CONFIG_CPU == PP5022) || (CONFIG_CPU == PP5024)
+#define IBSS_ATTR_SPC IBSS_ATTR
+#define ICODE_ATTR_SPC ICODE_ATTR
+#define ICONST_ATTR_SPC ICONST_ATTR
+/* Not enough IRAM available to move further data to it. */
+#define IBSS_ATTR_SPC_LARGE_IRAM
+
+#elif defined(CPU_S5L870X)
+#define IBSS_ATTR_SPC IBSS_ATTR
+#define ICODE_ATTR_SPC ICODE_ATTR
+#define ICONST_ATTR_SPC ICONST_ATTR
+/* Very large IRAM. Move even more data to it. */
+#define IBSS_ATTR_SPC_LARGE_IRAM IBSS_ATTR
+
+#else
+#define IBSS_ATTR_SPC IBSS_ATTR
+#define ICODE_ATTR_SPC ICODE_ATTR
+#define ICONST_ATTR_SPC ICONST_ATTR
+/* Not enough IRAM available to move further data to it. */
+#define IBSS_ATTR_SPC_LARGE_IRAM
+#endif
+
+#if SPC_DUAL_CORE
+ #undef SHAREDBSS_ATTR
+ #define SHAREDBSS_ATTR __attribute__ ((section(".ibss")))
+ #undef SHAREDDATA_ATTR
+ #define SHAREDDATA_ATTR __attribute__((section(".idata")))
+#endif
+
+/* Samples per channel per iteration */
+#if defined(CPU_PP) && NUM_CORES == 1
+#define WAV_CHUNK_SIZE 2048
+#else
+#define WAV_CHUNK_SIZE 1024
+#endif
+
+/**************** Little-endian handling ****************/
+
+static inline unsigned get_le16( void const* p )
+{
+ return ((unsigned char const*) p) [1] * 0x100u +
+ ((unsigned char const*) p) [0];
+}
+
+static inline int get_le16s( void const* p )
+{
+ return ((signed char const*) p) [1] * 0x100 +
+ ((unsigned char const*) p) [0];
+}
+
+static inline void set_le16( void* p, unsigned n )
+{
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [0] = (unsigned char) n;
+}
+
+#define GET_LE16( addr ) get_le16( addr )
+#define GET_LE16A( addr ) get_le16( addr )
+#define SET_LE16( addr, data ) set_le16( addr, data )
+#define INT16A( addr ) (*(uint16_t*) (addr))
+#define INT16SA( addr ) (*(int16_t*) (addr))
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+ #define GET_LE16SA( addr ) (*( int16_t*) (addr))
+ #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data))
+#else
+ #define GET_LE16SA( addr ) get_le16s( addr )
+ #define SET_LE16A( addr, data ) set_le16 ( addr, data )
+#endif
+
+struct Spc_Emu;
+#define THIS struct Spc_Emu* const this
+
+/* The CPU portion (shock!) */
+
+struct cpu_regs_t
+{
+ long pc; /* more than 16 bits to allow overflow detection */
+ uint8_t a;
+ uint8_t x;
+ uint8_t y;
+ uint8_t status;
+ uint8_t sp;
+};
+
+struct src_dir
+{
+ uint16_t start;
+ uint16_t loop;
+};
+
+struct cpu_ram_t
+{
+ union {
+ uint8_t padding1 [0x100];
+ uint16_t align;
+ } padding1 [1];
+ union {
+ uint8_t ram [0x10000];
+ struct src_dir sd [0x10000/sizeof(struct src_dir)];
+ };
+ uint8_t padding2 [0x100];
+};
+
+#undef RAM
+#define RAM ram.ram
+extern struct cpu_ram_t ram;
+
+long CPU_run( THIS, long start_time ) ICODE_ATTR_SPC;
+void CPU_Init( THIS );
+
+/* The DSP portion (awe!) */
+enum { VOICE_COUNT = 8 };
+enum { REGISTER_COUNT = 128 };
+
+struct raw_voice_t
+{
+ int8_t volume [2];
+ uint8_t rate [2];
+ uint8_t waveform;
+ uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */
+ uint8_t gain; /* envelope gain (if not using ADSR) */
+ int8_t envx; /* current envelope level */
+ int8_t outx; /* current sample */
+ int8_t unused [6];
+};
+
+struct globals_t
+{
+ int8_t unused1 [12];
+ int8_t volume_0; /* 0C Main Volume Left (-.7) */
+ int8_t echo_feedback; /* 0D Echo Feedback (-.7) */
+ int8_t unused2 [14];
+ int8_t volume_1; /* 1C Main Volume Right (-.7) */
+ int8_t unused3 [15];
+ int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */
+ uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */
+ int8_t unused4 [14];
+ int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */
+ uint8_t noise_enables; /* 3D Noise output on/off for each voice */
+ int8_t unused5 [14];
+ uint8_t key_ons; /* 4C Key On for each voice */
+ uint8_t echo_ons; /* 4D Echo on/off for each voice */
+ int8_t unused6 [14];
+ uint8_t key_offs; /* 5C key off for each voice
+ (instantiates release mode) */
+ uint8_t wave_page; /* 5D source directory (wave table offsets) */
+ int8_t unused7 [14];
+ uint8_t flags; /* 6C flags and noise freq */
+ uint8_t echo_page; /* 6D */
+ int8_t unused8 [14];
+ uint8_t wave_ended; /* 7C */
+ uint8_t echo_delay; /* 7D ms >> 4 */
+ char unused9 [2];
+};
+
+enum state_t
+{ /* -1, 0, +1 allows more efficient if statements */
+ state_decay = -1,
+ state_sustain = 0,
+ state_attack = +1,
+ state_release = 2
+};
+
+struct cache_entry_t
+{
+ int16_t const* samples;
+ unsigned end; /* past-the-end position */
+ unsigned loop; /* number of samples in loop */
+ unsigned start_addr;
+};
+
+enum { BRR_BLOCK_SIZE = 16 };
+enum { BRR_CACHE_SIZE = 0x20000 + 32} ;
+
+struct voice_t
+{
+#if SPC_BRRCACHE
+ int16_t const* samples;
+ long wave_end;
+ int wave_loop;
+#else
+ int16_t samples [3 + BRR_BLOCK_SIZE + 1];
+ int block_header; /* header byte from current block */
+#endif
+ uint8_t const* addr;
+ short volume [2];
+ long position;/* position in samples buffer, with 12-bit fraction */
+ short envx;
+ short env_mode;
+ short env_timer;
+ short key_on_delay;
+};
+
+#if SPC_BRRCACHE
+/* a little extra for samples that go past end */
+extern int16_t BRRcache [BRR_CACHE_SIZE];
+#endif
+
+enum { FIR_BUF_HALF = 8 };
+
+#if defined(CPU_COLDFIRE)
+/* global because of the large aligment requirement for hardware masking -
+ * L-R interleaved 16-bit samples for easy loading and mac.w use.
+ */
+enum
+{
+ FIR_BUF_CNT = FIR_BUF_HALF,
+ FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ),
+ FIR_BUF_ALIGN = FIR_BUF_SIZE * 2,
+ FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1))
+};
+#elif defined (CPU_ARM)
+#if ARM_ARCH >= 6
+enum
+{
+ FIR_BUF_CNT = FIR_BUF_HALF * 2,
+ FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ),
+ FIR_BUF_ALIGN = FIR_BUF_SIZE,
+ FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1))
+};
+#else
+enum
+{
+ FIR_BUF_CNT = FIR_BUF_HALF * 2 * 2,
+ FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ),
+ FIR_BUF_ALIGN = FIR_BUF_SIZE,
+ FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) * 2 - 1))
+};
+#endif /* ARM_ARCH */
+#endif /* CPU_* */
+
+struct Spc_Dsp
+{
+ union
+ {
+ struct raw_voice_t voice [VOICE_COUNT];
+ uint8_t reg [REGISTER_COUNT];
+ struct globals_t g;
+ int16_t align;
+ } r;
+
+ unsigned echo_pos;
+ int keys_down;
+ int noise_count;
+ uint16_t noise; /* also read as int16_t */
+
+#if defined(CPU_COLDFIRE)
+ /* FIR history is interleaved. Hardware handles wrapping by mask.
+ * |LR|LR|LR|LR|LR|LR|LR|LR| */
+ int32_t *fir_ptr;
+ /* wrapped address just behind current position -
+ allows mac.w to increment and mask fir_ptr */
+ int32_t *last_fir_ptr;
+ /* copy of echo FIR constants as int16_t for use with mac.w */
+ int16_t fir_coeff [VOICE_COUNT];
+#elif defined (CPU_ARM)
+ /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */
+ int32_t *fir_ptr;
+#if ARM_ARCH >= 6
+ /* FIR history is interleaved with guard to eliminate wrap checking
+ * when convolving.
+ * |LR|LR|LR|LR|LR|LR|LR|LR|--|--|--|--|--|--|--|--| */
+ /* copy of echo FIR constants as int16_t, loaded as int32 for
+ * halfword, packed multiples */
+ int16_t fir_coeff [VOICE_COUNT];
+#else
+ /* FIR history is interleaved with guard to eliminate wrap checking
+ * when convolving.
+ * |LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|...
+ * |--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--| */
+ /* copy of echo FIR constants as int32_t, for faster access */
+ int32_t fir_coeff [VOICE_COUNT];
+#endif /* ARM_ARCH */
+#else /* Unoptimized CPU */
+ /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */
+ int fir_pos; /* (0 to 7) */
+ int fir_buf [FIR_BUF_HALF * 2] [2];
+ /* copy of echo FIR constants as int, for faster access */
+ int fir_coeff [VOICE_COUNT];
+#endif
+
+ struct voice_t voice_state [VOICE_COUNT];
+
+#if SPC_BRRCACHE
+ uint8_t oldsize;
+ struct cache_entry_t wave_entry [256];
+ struct cache_entry_t wave_entry_old [256];
+#endif
+};
+
+void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) ICODE_ATTR_SPC;
+void DSP_reset( struct Spc_Dsp* this );
+
+static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out )
+{
+ /* Should we just fill the buffer with silence? Flags won't be cleared */
+ /* during this run so it seems it should keep resetting every sample. */
+ if ( this->r.g.flags & 0x80 )
+ DSP_reset( this );
+
+ DSP_run_( this, count, out );
+}
+
+/**************** SPC emulator ****************/
+/* 1.024 MHz clock / 32000 samples per second */
+enum { CLOCKS_PER_SAMPLE = 32 };
+
+enum { EXTRA_CLOCKS = CLOCKS_PER_SAMPLE / 2 };
+
+/* using this disables timer (since this will always be in the future) */
+enum { TIMER_DISABLED_TIME = 127 };
+
+enum { ROM_SIZE = 64 };
+enum { ROM_ADDR = 0xFFC0 };
+
+enum { TIMER_COUNT = 3 };
+
+struct Timer
+{
+ long next_tick;
+ int period;
+ int count;
+ int shift;
+ int enabled;
+ int counter;
+};
+
+struct Spc_Emu
+{
+ uint8_t cycle_table [0x100];
+ struct cpu_regs_t r;
+
+ int32_t* sample_buf;
+ long next_dsp;
+ int rom_enabled;
+ int extra_cycles;
+
+ struct Timer timer [TIMER_COUNT];
+
+ /* large objects at end */
+ struct Spc_Dsp dsp;
+ uint8_t extra_ram [ROM_SIZE];
+ uint8_t boot_rom [ROM_SIZE];
+};
+
+enum { SPC_FILE_SIZE = 0x10180 };
+
+struct spc_file_t
+{
+ char signature [27];
+ char unused [10];
+ uint8_t pc [2];
+ uint8_t a;
+ uint8_t x;
+ uint8_t y;
+ uint8_t status;
+ uint8_t sp;
+ char unused2 [212];
+ uint8_t ram [0x10000];
+ uint8_t dsp [128];
+ uint8_t ipl_rom [128];
+};
+
+void SPC_Init( THIS );
+
+int SPC_load_spc( THIS, const void* data, long size );
+
+/**************** DSP interaction ****************/
+void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR_SPC;
+
+static inline int DSP_read( struct Spc_Dsp* this, int i )
+{
+ assert( (unsigned) i < REGISTER_COUNT );
+ return this->r.reg [i];
+}
+
+int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR_SPC;
+void SPC_write( THIS, unsigned addr, int data, long const time ) ICODE_ATTR_SPC;
+
+/**************** Sample generation ****************/
+int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR_SPC;
+
+#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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Adam Gashlin (hcs)
+ * Copyright (C) 2004-2007 Shay Green (blargg)
+ * Copyright (C) 2002 Brad Martin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* The CPU portion (shock!) */
+#include "codeclib.h"
+#include "spc_codec.h"
+#include "spc_profiler.h"
+
+#undef check
+#define check assert
+
+#define READ( addr ) (SPC_read( this, addr, spc_time_ ))
+#define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ ))
+
+#define READ_DP( addr ) READ( (addr) + dp )
+#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
+
+#define READ_PROG16( addr ) GET_LE16( RAM + (addr) )
+
+#define READ_PC( pc ) (*(pc))
+#define READ_PC16( pc ) GET_LE16( pc )
+
+#define SET_PC( n ) (pc = RAM + (n))
+#define GET_PC() (pc - RAM)
+
+static unsigned char const cycle_table [0x100] = {
+ 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, /* 0 */
+ 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, /* 1 */
+ 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, /* 2 */
+ 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, /* 3 */
+ 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, /* 4 */
+ 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, /* 5 */
+ 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, /* 6 */
+ 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, /* 7 */
+ 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, /* 8 */
+ 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,/* 9 */
+ 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, /* A */
+ 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, /* B */
+ 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, /* C */
+ 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, /* D */
+ 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, /* E */
+ 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 /* F */
+};
+
+#define MEM_BIT() CPU_mem_bit( this, pc, spc_time_ )
+
+static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
+ ICODE_ATTR_SPC;
+
+static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ )
+{
+ unsigned addr = READ_PC16( pc );
+ unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13);
+ return (t << 8) & 0x100;
+}
+
+/* status flags */
+enum { st_n = 0x80 };
+enum { st_v = 0x40 };
+enum { st_p = 0x20 };
+enum { st_b = 0x10 };
+enum { st_h = 0x08 };
+enum { st_i = 0x04 };
+enum { st_z = 0x02 };
+enum { st_c = 0x01 };
+
+#define IS_NEG (nz & 0x880)
+
+#define CALC_STATUS( out )\
+{\
+ out = status & ~(st_n | st_z | st_c);\
+ out |= (c >> 8) & st_c;\
+ out |= (dp >> 3) & st_p;\
+ if ( IS_NEG ) out |= st_n;\
+ if ( !(nz & 0xFF) ) out |= st_z;\
+}
+
+#define SET_STATUS( in )\
+{\
+ status = in & ~(st_n | st_z | st_c | st_p);\
+ c = in << 8;\
+ nz = ((in << 4) & 0x800) | (~in & st_z);\
+ dp = (in << 3) & 0x100;\
+}
+
+
+/* stack */
+#define PUSH( v ) (*--sp = (uint8_t) (v))
+#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
+#define POP() (*sp++)
+#define SET_SP( v ) (sp = RAM + 0x101 + (v))
+#define GET_SP() (sp - 0x101 - RAM)
+
+long CPU_run( THIS, long start_time )
+{
+#if 0
+ ENTER_TIMER(cpu);
+#endif
+
+ register long spc_time_ = start_time;
+
+#ifdef CPU_ARM
+ uint8_t* const ram_ = ram.ram;
+ #undef RAM
+ #define RAM ram_
+#endif
+
+ int a = this->r.a;
+ int x = this->r.x;
+ int y = this->r.y;
+
+ uint8_t const* pc;
+ SET_PC( this->r.pc );
+
+ uint8_t* sp;
+ SET_SP( this->r.sp );
+
+ int status;
+ int c;
+ int nz;
+ unsigned dp;
+ {
+ int temp = this->r.status;
+ SET_STATUS( temp );
+ }
+
+ goto loop;
+
+ /* main loop */
+cbranch_taken_loop:
+ pc += *(int8_t const*) pc;
+ spc_time_ += 2;
+inc_pc_loop:
+ pc++;
+loop:
+ check( (unsigned) GET_PC() < 0x10000 );
+ check( (unsigned) GET_SP() < 0x100 );
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ unsigned opcode = *pc;
+ int cycles = this->cycle_table [opcode];
+ unsigned data = *++pc;
+ if ( (spc_time_ += cycles) > 0 )
+ goto out_of_time;
+ switch ( opcode )
+ {
+
+/* Common instructions */
+
+#define BRANCH( cond )\
+{\
+ pc++;\
+ int offset = (int8_t) data;\
+ if ( cond ) {\
+ pc += offset;\
+ spc_time_ += 2;\
+ }\
+ goto loop;\
+}
+
+ case 0xF0: /* BEQ (most common) */
+ BRANCH( !(uint8_t) nz )
+
+ case 0xD0: /* BNE */
+ BRANCH( (uint8_t) nz )
+
+ case 0x3F: /* CALL */
+ PUSH16( GET_PC() + 2 );
+ SET_PC( READ_PC16( pc ) );
+ goto loop;
+
+ case 0x6F: /* RET */
+ SET_PC( POP() );
+ pc += POP() * 0x100;
+ goto loop;
+
+#define CASE( n ) case n:
+
+/* Define common address modes based on opcode for immediate mode. Execution
+ ends with data set to the address of the operand. */
+#define ADDR_MODES( op )\
+ CASE( op - 0x02 ) /* (X) */\
+ data = x + dp;\
+ pc--;\
+ goto end_##op;\
+ CASE( op + 0x0F ) /* (dp)+Y */\
+ data = READ_PROG16( data + dp ) + y;\
+ goto end_##op;\
+ CASE( op - 0x01 ) /* (dp+X) */\
+ data = READ_PROG16( ((uint8_t) (data + x)) + dp );\
+ goto end_##op;\
+ CASE( op + 0x0E ) /* abs+Y */\
+ data += y;\
+ goto abs_##op;\
+ CASE( op + 0x0D ) /* abs+X */\
+ data += x;\
+ CASE( op - 0x03 ) /* abs */\
+ abs_##op:\
+ data += 0x100 * READ_PC( ++pc );\
+ goto end_##op;\
+ CASE( op + 0x0C ) /* dp+X */\
+ data = (uint8_t) (data + x);\
+ CASE( op - 0x04 ) /* dp */\
+ data += dp;\
+ end_##op:
+
+/* 1. 8-bit Data Transmission Commands. Group I */
+
+ ADDR_MODES( 0xE8 ) /* MOV A,addr */
+ /*case 0xE4:*/ /* MOV a,dp (most common) */
+ mov_a_addr:
+ a = nz = READ( data );
+ goto inc_pc_loop;
+ case 0xBF: /* MOV A,(X)+ */
+ data = x + dp;
+ x = (uint8_t) (x + 1);
+ pc--;
+ goto mov_a_addr;
+
+ case 0xE8: /* MOV A,imm */
+ a = data;
+ nz = data;
+ goto inc_pc_loop;
+
+ case 0xF9: /* MOV X,dp+Y */
+ data = (uint8_t) (data + y);
+ case 0xF8: /* MOV X,dp */
+ data += dp;
+ goto mov_x_addr;
+ case 0xE9: /* MOV X,abs */
+ data = READ_PC16( pc );
+ pc++;
+ mov_x_addr:
+ data = READ( data );
+ case 0xCD: /* MOV X,imm */
+ x = data;
+ nz = data;
+ goto inc_pc_loop;
+
+ case 0xFB: /* MOV Y,dp+X */
+ data = (uint8_t) (data + x);
+ case 0xEB: /* MOV Y,dp */
+ data += dp;
+ goto mov_y_addr;
+ case 0xEC: /* MOV Y,abs */
+ data = READ_PC16( pc );
+ pc++;
+ mov_y_addr:
+ data = READ( data );
+ case 0x8D: /* MOV Y,imm */
+ y = data;
+ nz = data;
+ goto inc_pc_loop;
+
+/* 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 */
+
+ ADDR_MODES( 0xC8 ) /* MOV addr,A */
+ WRITE( data, a );
+ goto inc_pc_loop;
+
+ {
+ int temp;
+ case 0xCC: /* MOV abs,Y */
+ temp = y;
+ goto mov_abs_temp;
+ case 0xC9: /* MOV abs,X */
+ temp = x;
+ mov_abs_temp:
+ WRITE( READ_PC16( pc ), temp );
+ pc += 2;
+ goto loop;
+ }
+
+ case 0xD9: /* MOV dp+Y,X */
+ data = (uint8_t) (data + y);
+ case 0xD8: /* MOV dp,X */
+ WRITE( data + dp, x );
+ goto inc_pc_loop;
+
+ case 0xDB: /* MOV dp+X,Y */
+ data = (uint8_t) (data + x);
+ case 0xCB: /* MOV dp,Y */
+ WRITE( data + dp, y );
+ goto inc_pc_loop;
+
+ case 0xFA: /* MOV dp,dp */
+ data = READ( data + dp );
+ case 0x8F: /* MOV dp,#imm */
+ WRITE_DP( READ_PC( ++pc ), data );
+ goto inc_pc_loop;
+
+/* 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. */
+
+ case 0x7D: /* MOV A,X */
+ a = x;
+ nz = x;
+ goto loop;
+
+ case 0xDD: /* MOV A,Y */
+ a = y;
+ nz = y;
+ goto loop;
+
+ case 0x5D: /* MOV X,A */
+ x = a;
+ nz = a;
+ goto loop;
+
+ case 0xFD: /* MOV Y,A */
+ y = a;
+ nz = a;
+ goto loop;
+
+ case 0x9D: /* MOV X,SP */
+ x = nz = GET_SP();
+ goto loop;
+
+ case 0xBD: /* MOV SP,X */
+ SET_SP( x );
+ goto loop;
+
+ /*case 0xC6:*/ /* MOV (X),A (handled by MOV addr,A in group 2) */
+
+ case 0xAF: /* MOV (X)+,A */
+ WRITE_DP( x, a );
+ x++;
+ goto loop;
+
+/* 5. 8-BIT LOGIC OPERATION COMMANDS */
+
+#define LOGICAL_OP( op, func )\
+ ADDR_MODES( op ) /* addr */\
+ data = READ( data );\
+ case op: /* imm */\
+ nz = a func##= data;\
+ goto inc_pc_loop;\
+ { unsigned addr;\
+ case op + 0x11: /* X,Y */\
+ data = READ_DP( y );\
+ addr = x + dp;\
+ pc--;\
+ goto addr_##op;\
+ case op + 0x01: /* dp,dp */\
+ data = READ_DP( data );\
+ case op + 0x10: /*dp,imm*/\
+ addr = READ_PC( ++pc ) + dp;\
+ addr_##op:\
+ nz = data func READ( addr );\
+ WRITE( addr, nz );\
+ goto inc_pc_loop;\
+ }
+
+ LOGICAL_OP( 0x28, & ); /* AND */
+
+ LOGICAL_OP( 0x08, | ); /* OR */
+
+ LOGICAL_OP( 0x48, ^ ); /* EOR */
+
+/* 4. 8-BIT ARITHMETIC OPERATION COMMANDS */
+
+ ADDR_MODES( 0x68 ) /* CMP addr */
+ data = READ( data );
+ case 0x68: /* CMP imm */
+ nz = a - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x79: /* CMP (X),(Y) */
+ data = READ_DP( x );
+ nz = data - READ_DP( y );
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+ case 0x69: /* CMP (dp),(dp) */
+ data = READ_DP( data );
+ case 0x78: /* CMP dp,imm */
+ nz = READ_DP( READ_PC( ++pc ) ) - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x3E: /* CMP X,dp */
+ data += dp;
+ goto cmp_x_addr;
+ case 0x1E: /* CMP X,abs */
+ data = READ_PC16( pc );
+ pc++;
+ cmp_x_addr:
+ data = READ( data );
+ case 0xC8: /* CMP X,imm */
+ nz = x - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x7E: /* CMP Y,dp */
+ data += dp;
+ goto cmp_y_addr;
+ case 0x5E: /* CMP Y,abs */
+ data = READ_PC16( pc );
+ pc++;
+ cmp_y_addr:
+ data = READ( data );
+ case 0xAD: /* CMP Y,imm */
+ nz = y - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ {
+ int addr;
+ case 0xB9: /* SBC (x),(y) */
+ case 0x99: /* ADC (x),(y) */
+ pc--; /* compensate for inc later */
+ data = READ_DP( x );
+ addr = y + dp;
+ goto adc_addr;
+ case 0xA9: /* SBC dp,dp */
+ case 0x89: /* ADC dp,dp */
+ data = READ_DP( data );
+ case 0xB8: /* SBC dp,imm */
+ case 0x98: /* ADC dp,imm */
+ addr = READ_PC( ++pc ) + dp;
+ adc_addr:
+ nz = READ( addr );
+ goto adc_data;
+
+/* catch ADC and SBC together, then decode later based on operand */
+#undef CASE
+#define CASE( n ) case n: case (n) + 0x20:
+ ADDR_MODES( 0x88 ) /* ADC/SBC addr */
+ data = READ( data );
+ case 0xA8: /* SBC imm */
+ case 0x88: /* ADC imm */
+ addr = -1; /* A */
+ nz = a;
+ adc_data: {
+ if ( opcode & 0x20 )
+ data ^= 0xFF; /* SBC */
+ int carry = (c >> 8) & 1;
+ int ov = (nz ^ 0x80) + carry + (int8_t) data; /* sign-extend */
+ int hc = (nz & 15) + carry;
+ c = nz += data + carry;
+ hc = (nz & 15) - hc;
+ status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) |
+ ((hc >> 1) & st_h);
+ if ( addr < 0 ) {
+ a = (uint8_t) nz;
+ goto inc_pc_loop;
+ }
+ WRITE( addr, (uint8_t) nz );
+ goto inc_pc_loop;
+ }
+
+ }
+
+/* 6. ADDITION & SUBTRACTION COMMANDS */
+
+#define INC_DEC_REG( reg, n )\
+ nz = reg + n;\
+ reg = (uint8_t) nz;\
+ goto loop;
+
+ case 0xBC: INC_DEC_REG( a, 1 ) /* INC A */
+ case 0x3D: INC_DEC_REG( x, 1 ) /* INC X */
+ case 0xFC: INC_DEC_REG( y, 1 ) /* INC Y */
+
+ case 0x9C: INC_DEC_REG( a, -1 ) /* DEC A */
+ case 0x1D: INC_DEC_REG( x, -1 ) /* DEC X */
+ case 0xDC: INC_DEC_REG( y, -1 ) /* DEC Y */
+
+ case 0x9B: /* DEC dp+X */
+ case 0xBB: /* INC dp+X */
+ data = (uint8_t) (data + x);
+ case 0x8B: /* DEC dp */
+ case 0xAB: /* INC dp */
+ data += dp;
+ goto inc_abs;
+ case 0x8C: /* DEC abs */
+ case 0xAC: /* INC abs */
+ data = READ_PC16( pc );
+ pc++;
+ inc_abs:
+ nz = ((opcode >> 4) & 2) - 1;
+ nz += READ( data );
+ WRITE( data, (uint8_t) nz );
+ goto inc_pc_loop;
+
+/* 7. SHIFT, ROTATION COMMANDS */
+
+ case 0x5C: /* LSR A */
+ c = 0;
+ case 0x7C:{/* ROR A */
+ nz = ((c >> 1) & 0x80) | (a >> 1);
+ c = a << 8;
+ a = nz;
+ goto loop;
+ }
+
+ case 0x1C: /* ASL A */
+ c = 0;
+ case 0x3C:{/* ROL A */
+ int temp = (c >> 8) & 1;
+ c = a << 1;
+ nz = c | temp;
+ a = (uint8_t) nz;
+ goto loop;
+ }
+
+ case 0x0B: /* ASL dp */
+ c = 0;
+ data += dp;
+ goto rol_mem;
+ case 0x1B: /* ASL dp+X */
+ c = 0;
+ case 0x3B: /* ROL dp+X */
+ data = (uint8_t) (data + x);
+ case 0x2B: /* ROL dp */
+ data += dp;
+ goto rol_mem;
+ case 0x0C: /* ASL abs */
+ c = 0;
+ case 0x2C: /* ROL abs */
+ data = READ_PC16( pc );
+ pc++;
+ rol_mem:
+ nz = (c >> 8) & 1;
+ nz |= (c = READ( data ) << 1);
+ WRITE( data, (uint8_t) nz );
+ goto inc_pc_loop;
+
+ case 0x4B: /* LSR dp */
+ c = 0;
+ data += dp;
+ goto ror_mem;
+ case 0x5B: /* LSR dp+X */
+ c = 0;
+ case 0x7B: /* ROR dp+X */
+ data = (uint8_t) (data + x);
+ case 0x6B: /* ROR dp */
+ data += dp;
+ goto ror_mem;
+ case 0x4C: /* LSR abs */
+ c = 0;
+ case 0x6C: /* ROR abs */
+ data = READ_PC16( pc );
+ pc++;
+ ror_mem: {
+ int temp = READ( data );
+ nz = ((c >> 1) & 0x80) | (temp >> 1);
+ c = temp << 8;
+ WRITE( data, nz );
+ goto inc_pc_loop;
+ }
+
+ case 0x9F: /* XCN */
+ nz = a = (a >> 4) | (uint8_t) (a << 4);
+ goto loop;
+
+/* 8. 16-BIT TRANSMISION COMMANDS */
+
+ case 0xBA: /* MOVW YA,dp */
+ a = READ_DP( data );
+ nz = (a & 0x7F) | (a >> 1);
+ y = READ_DP( (uint8_t) (data + 1) );
+ nz |= y;
+ goto inc_pc_loop;
+
+ case 0xDA: /* MOVW dp,YA */
+ WRITE_DP( data, a );
+ WRITE_DP( (uint8_t) (data + 1), y );
+ goto inc_pc_loop;
+
+/* 9. 16-BIT OPERATION COMMANDS */
+
+ case 0x3A: /* INCW dp */
+ case 0x1A:{/* DECW dp */
+ data += dp;
+
+ /* low byte */
+ int temp = READ( data );
+ temp += ((opcode >> 4) & 2) - 1; /* +1 for INCW, -1 for DECW */
+ nz = ((temp >> 1) | temp) & 0x7F;
+ WRITE( data, (uint8_t) temp );
+
+ /* high byte */
+ data = ((uint8_t) (data + 1)) + dp;
+ temp >>= 8;
+ temp = (uint8_t) (temp + READ( data ));
+ nz |= temp;
+ WRITE( data, temp );
+
+ goto inc_pc_loop;
+ }
+
+ case 0x9A: /* SUBW YA,dp */
+ case 0x7A: /* ADDW YA,dp */
+ {
+ /* read 16-bit addend */
+ int temp = READ_DP( data );
+ int sign = READ_DP( (uint8_t) (data + 1) );
+ temp += 0x100 * sign;
+ status &= ~(st_v | st_h);
+
+ /* to do: fix half-carry for SUBW (it's probably wrong) */
+
+ /* for SUBW, negate and truncate to 16 bits */
+ if ( opcode & 0x80 ) {
+ temp = (temp ^ 0xFFFF) + 1;
+ sign = temp >> 8;
+ }
+
+ /* add low byte (A) */
+ temp += a;
+ a = (uint8_t) temp;
+ nz = (temp | (temp >> 1)) & 0x7F;
+
+ /* add high byte (Y) */
+ temp >>= 8;
+ c = y + temp;
+ nz = (nz | c) & 0xFF;
+
+ /* half-carry (temporary avoids CodeWarrior optimizer bug) */
+ unsigned hc = (c & 15) - (y & 15);
+ status |= (hc >> 4) & st_h;
+
+ /* overflow if sign of YA changed when previous sign
+ and addend sign were same */
+ status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v;
+
+ y = (uint8_t) c;
+
+ goto inc_pc_loop;
+ }
+
+ case 0x5A: { /* CMPW YA,dp */
+ int temp = a - READ_DP( data );
+ nz = ((temp >> 1) | temp) & 0x7F;
+ temp = y + (temp >> 8);
+ temp -= READ_DP( (uint8_t) (data + 1) );
+ nz |= temp;
+ c = ~temp;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+ }
+
+/* 10. MULTIPLICATION & DIVISON COMMANDS */
+
+ case 0xCF: { /* MUL YA */
+ unsigned temp = y * a;
+ a = (uint8_t) temp;
+ nz = ((temp >> 1) | temp) & 0x7F;
+ y = temp >> 8;
+ nz |= y;
+ goto loop;
+ }
+
+ case 0x9E: /* DIV YA,X */
+ {
+ /* behavior based on SPC CPU tests */
+
+ status &= ~(st_h | st_v);
+
+ if ( (y & 15) >= (x & 15) )
+ status |= st_h;
+
+ if ( y >= x )
+ status |= st_v;
+
+ unsigned ya = y * 0x100 + a;
+ if ( y < x * 2 )
+ {
+ a = ya / x;
+ y = ya - a * x;
+ }
+ else
+ {
+ a = 255 - (ya - x * 0x200) / (256 - x);
+ y = x + (ya - x * 0x200) % (256 - x);
+ }
+
+ nz = (uint8_t) a;
+ a = (uint8_t) a;
+
+ goto loop;
+ }
+
+/* 11. DECIMAL COMPENSATION COMMANDS */
+
+ /* seem unused */
+ /* case 0xDF: */ /* DAA */
+ /* case 0xBE: */ /* DAS */
+
+/* 12. BRANCHING COMMANDS */
+
+ case 0x2F: /* BRA rel */
+ pc += (int8_t) data;
+ goto inc_pc_loop;
+
+ case 0x30: /* BMI */
+ BRANCH( IS_NEG )
+
+ case 0x10: /* BPL */
+ BRANCH( !IS_NEG )
+
+ case 0xB0: /* BCS */
+ BRANCH( c & 0x100 )
+
+ case 0x90: /* BCC */
+ BRANCH( !(c & 0x100) )
+
+ case 0x70: /* BVS */
+ BRANCH( status & st_v )
+
+ case 0x50: /* BVC */
+ BRANCH( !(status & st_v) )
+
+ case 0x03: /* BBS dp.bit,rel */
+ case 0x23:
+ case 0x43:
+ case 0x63:
+ case 0x83:
+ case 0xA3:
+ case 0xC3:
+ case 0xE3:
+ pc++;
+ if ( (READ_DP( data ) >> (opcode >> 5)) & 1 )
+ goto cbranch_taken_loop;
+ goto inc_pc_loop;
+
+ case 0x13: /* BBC dp.bit,rel */
+ case 0x33:
+ case 0x53:
+ case 0x73:
+ case 0x93:
+ case 0xB3:
+ case 0xD3:
+ case 0xF3:
+ pc++;
+ if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) )
+ goto cbranch_taken_loop;
+ goto inc_pc_loop;
+
+ case 0xDE: /* CBNE dp+X,rel */
+ data = (uint8_t) (data + x);
+ /* fall through */
+ case 0x2E: /* CBNE dp,rel */
+ pc++;
+ if ( READ_DP( data ) != a )
+ goto cbranch_taken_loop;
+ goto inc_pc_loop;
+
+ case 0xFE: /* DBNZ Y,rel */
+ y = (uint8_t) (y - 1);
+ BRANCH( y )
+
+ case 0x6E: { /* DBNZ dp,rel */
+ pc++;
+ unsigned temp = READ_DP( data ) - 1;
+ WRITE_DP( (uint8_t) data, (uint8_t) temp );
+ if ( temp )
+ goto cbranch_taken_loop;
+ goto inc_pc_loop;
+ }
+
+ case 0x1F: /* JMP (abs+X) */
+ SET_PC( READ_PC16( pc ) + x );
+ /* fall through */
+ case 0x5F: /* JMP abs */
+ SET_PC( READ_PC16( pc ) );
+ goto loop;
+
+/* 13. SUB-ROUTINE CALL RETURN COMMANDS */
+
+ case 0x0F:{/* BRK */
+ check( 0 ); /* untested */
+ PUSH16( GET_PC() + 1 );
+ SET_PC( READ_PROG16( 0xFFDE ) ); /* vector address verified */
+ int temp;
+ CALC_STATUS( temp );
+ PUSH( temp );
+ status = (status | st_b) & ~st_i;
+ goto loop;
+ }
+
+ case 0x4F: /* PCALL offset */
+ PUSH16( GET_PC() + 1 );
+ SET_PC( 0xFF00 + data );
+ goto loop;
+
+ case 0x01: /* TCALL n */
+ case 0x11:
+ case 0x21:
+ case 0x31:
+ case 0x41:
+ case 0x51:
+ case 0x61:
+ case 0x71:
+ case 0x81:
+ case 0x91:
+ case 0xA1:
+ case 0xB1:
+ case 0xC1:
+ case 0xD1:
+ case 0xE1:
+ case 0xF1:
+ PUSH16( GET_PC() );
+ SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
+ goto loop;
+
+/* 14. STACK OPERATION COMMANDS */
+
+ {
+ int temp;
+ case 0x7F: /* RET1 */
+ temp = POP();
+ SET_PC( POP() );
+ pc += POP() << 8;
+ goto set_status;
+ case 0x8E: /* POP PSW */
+ temp = POP();
+ set_status:
+ SET_STATUS( temp );
+ goto loop;
+ }
+
+ case 0x0D: { /* PUSH PSW */
+ int temp;
+ CALC_STATUS( temp );
+ PUSH( temp );
+ goto loop;
+ }
+
+ case 0x2D: /* PUSH A */
+ PUSH( a );
+ goto loop;
+
+ case 0x4D: /* PUSH X */
+ PUSH( x );
+ goto loop;
+
+ case 0x6D: /* PUSH Y */
+ PUSH( y );
+ goto loop;
+
+ case 0xAE: /* POP A */
+ a = POP();
+ goto loop;
+
+ case 0xCE: /* POP X */
+ x = POP();
+ goto loop;
+
+ case 0xEE: /* POP Y */
+ y = POP();
+ goto loop;
+
+/* 15. BIT OPERATION COMMANDS */
+
+ case 0x02: /* SET1 */
+ case 0x22:
+ case 0x42:
+ case 0x62:
+ case 0x82:
+ case 0xA2:
+ case 0xC2:
+ case 0xE2:
+ case 0x12: /* CLR1 */
+ case 0x32:
+ case 0x52:
+ case 0x72:
+ case 0x92:
+ case 0xB2:
+ case 0xD2:
+ case 0xF2: {
+ data += dp;
+ int bit = 1 << (opcode >> 5);
+ int mask = ~bit;
+ if ( opcode & 0x10 )
+ bit = 0;
+ WRITE( data, (READ( data ) & mask) | bit );
+ goto inc_pc_loop;
+ }
+
+ case 0x0E: /* TSET1 abs */
+ case 0x4E:{/* TCLR1 abs */
+ data = READ_PC16( pc );
+ pc += 2;
+ unsigned temp = READ( data );
+ nz = temp & a;
+ temp &= ~a;
+ if ( !(opcode & 0x40) )
+ temp |= a;
+ WRITE( data, temp );
+ goto loop;
+ }
+
+ case 0x4A: /* AND1 C,mem.bit */
+ c &= MEM_BIT();
+ pc += 2;
+ goto loop;
+
+ case 0x6A: /* AND1 C,/mem.bit */
+ check( 0 ); /* untested */
+ c &= ~MEM_BIT();
+ pc += 2;
+ goto loop;
+
+ case 0x0A: /* OR1 C,mem.bit */
+ check( 0 ); /* untested */
+ c |= MEM_BIT();
+ pc += 2;
+ goto loop;
+
+ case 0x2A: /* OR1 C,/mem.bit */
+ check( 0 ); /* untested */
+ c |= ~MEM_BIT();
+ pc += 2;
+ goto loop;
+
+ case 0x8A: /* EOR1 C,mem.bit */
+ c ^= MEM_BIT();
+ pc += 2;
+ goto loop;
+
+ case 0xEA: { /* NOT1 mem.bit */
+ data = READ_PC16( pc );
+ pc += 2;
+ unsigned temp = READ( data & 0x1FFF );
+ temp ^= 1 << (data >> 13);
+ WRITE( data & 0x1FFF, temp );
+ goto loop;
+ }
+
+ case 0xCA: { /* MOV1 mem.bit,C */
+ data = READ_PC16( pc );
+ pc += 2;
+ unsigned temp = READ( data & 0x1FFF );
+ unsigned bit = data >> 13;
+ temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit);
+ WRITE( data & 0x1FFF, temp );
+ goto loop;
+ }
+
+ case 0xAA: /* MOV1 C,mem.bit */
+ c = MEM_BIT();
+ pc += 2;
+ goto loop;
+
+/* 16. PROGRAM STATUS FLAG OPERATION COMMANDS */
+
+ case 0x60: /* CLRC */
+ c = 0;
+ goto loop;
+
+ case 0x80: /* SETC */
+ c = ~0;
+ goto loop;
+
+ case 0xED: /* NOTC */
+ c ^= 0x100;
+ goto loop;
+
+ case 0xE0: /* CLRV */
+ status &= ~(st_v | st_h);
+ goto loop;
+
+ case 0x20: /* CLRP */
+ dp = 0;
+ goto loop;
+
+ case 0x40: /* SETP */
+ dp = 0x100;
+ goto loop;
+
+ case 0xA0: /* EI */
+ check( 0 ); /* untested */
+ status |= st_i;
+ goto loop;
+
+ case 0xC0: /* DI */
+ check( 0 ); /* untested */
+ status &= ~st_i;
+ goto loop;
+
+/* 17. OTHER COMMANDS */
+
+ case 0x00: /* NOP */
+ goto loop;
+
+ /*case 0xEF:*/ /* SLEEP */
+ /*case 0xFF:*/ /* STOP */
+ case 0xFF:
+ c |= 1; /* force switch table to have 256 entries,
+ hopefully helping optimizer */
+ } /* switch */
+
+ /* unhandled instructions fall out of switch so emulator can catch them */
+
+out_of_time:
+ /* undo partial execution of opcode */
+ spc_time_ -= this->cycle_table [*--pc];
+ {
+ int temp;
+ CALC_STATUS( temp );
+ this->r.status = (uint8_t) temp;
+ }
+
+ this->r.pc = GET_PC();
+ this->r.sp = (uint8_t) GET_SP();
+ this->r.a = (uint8_t) a;
+ this->r.x = (uint8_t) x;
+ this->r.y = (uint8_t) y;
+
+#if 0
+ EXIT_TIMER(cpu);
+#endif
+ return spc_time_;
+}
+
+void CPU_Init( THIS )
+{
+ ci->memcpy( this->cycle_table, cycle_table, sizeof cycle_table );
+}
+
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS)
+ * Copyright (C) 2006-2007 Adam Gashlin (hcs)
+ * Copyright (C) 2004-2007 Shay Green (blargg)
+ * Copyright (C) 2002 Brad Martin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* The DSP portion (awe!) */
+#include "codeclib.h"
+#include "spc_codec.h"
+#include "spc_profiler.h"
+
+#if defined(CPU_COLDFIRE) || defined (CPU_ARM)
+int32_t fir_buf[FIR_BUF_CNT] IBSS_ATTR_SPC
+ __attribute__((aligned(FIR_BUF_ALIGN*1)));
+#endif
+#if SPC_BRRCACHE
+/* a little extra for samples that go past end */
+int16_t BRRcache [BRR_CACHE_SIZE] CACHEALIGN_ATTR;
+#endif
+
+void DSP_write( struct Spc_Dsp* this, int i, int data )
+{
+ assert( (unsigned) i < REGISTER_COUNT );
+
+ this->r.reg [i] = data;
+ int high = i >> 4;
+ int low = i & 0x0F;
+ if ( low < 2 ) /* voice volumes */
+ {
+ int left = *(int8_t const*) &this->r.reg [i & ~1];
+ int right = *(int8_t const*) &this->r.reg [i | 1];
+ struct voice_t* v = this->voice_state + high;
+ v->volume [0] = left;
+ v->volume [1] = right;
+ }
+ else if ( low == 0x0F ) /* fir coefficients */
+ {
+ this->fir_coeff [7 - high] = (int8_t) data; /* sign-extend */
+ }
+}
+
+#define CLAMP16( n ) clip_sample_16( n )
+
+#if SPC_BRRCACHE
+static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
+ struct voice_t* voice,
+ struct raw_voice_t const* const raw_voice ) ICODE_ATTR_SPC;
+static void decode_brr( struct Spc_Dsp* this, unsigned start_addr,
+ struct voice_t* voice,
+ struct raw_voice_t const* const raw_voice )
+{
+ /* setup same variables as where decode_brr() is called from */
+ #undef RAM
+ #define RAM ram.ram
+
+ struct src_dir const* const sd =
+ &ram.sd[this->r.g.wave_page * 0x100/sizeof(struct src_dir)];
+ struct cache_entry_t* const wave_entry =
+ &this->wave_entry [raw_voice->waveform];
+
+ /* the following block can be put in place of the call to
+ decode_brr() below
+ */
+ {
+ DEBUGF( "decode at %08x (wave #%d)\n",
+ start_addr, raw_voice->waveform );
+
+ /* see if in cache */
+ int i;
+ for ( i = 0; i < this->oldsize; i++ )
+ {
+ struct cache_entry_t* e = &this->wave_entry_old [i];
+ if ( e->start_addr == start_addr )
+ {
+ DEBUGF( "found in wave_entry_old (oldsize=%d)\n",
+ this->oldsize );
+ *wave_entry = *e;
+ goto wave_in_cache;
+ }
+ }
+
+ wave_entry->start_addr = start_addr;
+
+ uint8_t const* const loop_ptr =
+ RAM + letoh16(sd[raw_voice->waveform].loop);
+ short* loop_start = 0;
+
+ short* out = BRRcache + start_addr * 2;
+ wave_entry->samples = out;
+ *out++ = 0;
+ int smp1 = 0;
+ int smp2 = 0;
+
+ uint8_t const* addr = RAM + start_addr;
+ int block_header;
+ do
+ {
+ if ( addr == loop_ptr )
+ {
+ loop_start = out;
+ DEBUGF( "loop at %08lx (wave #%d)\n",
+ (unsigned long)(addr - RAM), raw_voice->waveform );
+ }
+
+ /* header */
+ block_header = *addr;
+ addr += 9;
+ voice->addr = addr;
+ int const filter = (block_header & 0x0C) - 0x08;
+
+ /* scaling
+ (invalid scaling gives -4096 for neg nybble, 0 for pos) */
+ static unsigned char const right_shifts [16] = {
+ 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 29,
+ };
+ static unsigned char const left_shifts [16] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11
+ };
+ int const scale = block_header >> 4;
+ int const right_shift = right_shifts [scale];
+ int const left_shift = left_shifts [scale];
+
+ /* output position */
+ out += BRR_BLOCK_SIZE;
+ int offset = -BRR_BLOCK_SIZE << 2;
+
+ do /* decode and filter 16 samples */
+ {
+ /* Get nybble, sign-extend, then scale
+ get byte, select which nybble, sign-extend, then shift based
+ on scaling. also handles invalid scaling values. */
+ int delta = (int) (int8_t) (addr [offset >> 3] << (offset & 4))
+ >> right_shift << left_shift;
+
+ out [offset >> 2] = smp2;
+
+ if ( filter == 0 ) /* mode 0x08 (30-90% of the time) */
+ {
+ delta -= smp2 >> 1;
+ delta += smp2 >> 5;
+ smp2 = smp1;
+ delta += smp1;
+ delta += (-smp1 - (smp1 >> 1)) >> 5;
+ }
+ else
+ {
+ if ( filter == -4 ) /* mode 0x04 */
+ {
+ delta += smp1 >> 1;
+ delta += (-smp1) >> 5;
+ }
+ else if ( filter > -4 ) /* mode 0x0C */
+ {
+ delta -= smp2 >> 1;
+ delta += (smp2 + (smp2 >> 1)) >> 4;
+ delta += smp1;
+ delta += (-smp1 * 13) >> 7;
+ }
+ smp2 = smp1;
+ }
+
+ delta = CLAMP16( delta );
+ smp1 = (int16_t) (delta * 2); /* sign-extend */
+ }
+ while ( (offset += 4) != 0 );
+
+ /* if next block has end flag set, this block ends early */
+ /* (verified) */
+ if ( (block_header & 3) != 3 && (*addr & 3) == 1 )
+ {
+ /* skip last 9 samples */
+ out -= 9;
+ goto early_end;
+ }
+ }
+ while ( !(block_header & 1) && addr < RAM + 0x10000 );
+
+ out [0] = smp2;
+ out [1] = smp1;
+
+ early_end:
+ wave_entry->end = (out - 1 - wave_entry->samples) << 12;
+
+ wave_entry->loop = 0;
+ if ( (block_header & 2) )
+ {
+ if ( loop_start )
+ {
+ int loop = out - loop_start;
+ wave_entry->loop = loop;
+ wave_entry->end += 0x3000;
+ out [2] = loop_start [2];
+ out [3] = loop_start [3];
+ out [4] = loop_start [4];
+ }
+ else
+ {
+ DEBUGF( "loop point outside initial wave\n" );
+ }
+ }
+
+ DEBUGF( "end at %08lx (wave #%d)\n",
+ (unsigned long)(addr - RAM), raw_voice->waveform );
+
+ /* add to cache */
+ this->wave_entry_old [this->oldsize++] = *wave_entry;
+wave_in_cache:;
+ }
+}
+#endif
+
+static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
+ struct src_dir const* const sd,
+ struct raw_voice_t const* const raw_voice,
+ const int key_on_delay, const int vbit) ICODE_ATTR_SPC;
+static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice,
+ struct src_dir const* const sd,
+ struct raw_voice_t const* const raw_voice,
+ const int key_on_delay, const int vbit) {
+ #undef RAM
+ #define RAM ram.ram
+ int const env_rate_init = 0x7800;
+ voice->key_on_delay = key_on_delay;
+ if ( key_on_delay == 0 )
+ {
+ this->keys_down |= vbit;
+ voice->envx = 0;
+ voice->env_mode = state_attack;
+ voice->env_timer = env_rate_init; /* TODO: inaccurate? */
+ unsigned start_addr = letoh16(sd[raw_voice->waveform].start);
+ #if !SPC_BRRCACHE
+ {
+ voice->addr = RAM + start_addr;
+ /* BRR filter uses previous samples */
+ voice->samples [BRR_BLOCK_SIZE + 1] = 0;
+ voice->samples [BRR_BLOCK_SIZE + 2] = 0;
+ /* decode three samples immediately */
+ voice->position = (BRR_BLOCK_SIZE + 3) * 0x1000 - 1;
+ voice->block_header = 0; /* "previous" BRR header */
+ }
+ #else
+ {
+ voice->position = 3 * 0x1000 - 1;
+ struct cache_entry_t* const wave_entry =
+ &this->wave_entry [raw_voice->waveform];
+
+ /* predecode BRR if not already */
+ if ( wave_entry->start_addr != start_addr )
+ {
+ /* the following line can be replaced by the indicated block
+ in decode_brr() */
+ decode_brr( this, start_addr, voice, raw_voice );
+ }
+
+ voice->samples = wave_entry->samples;
+ voice->wave_end = wave_entry->end;
+ voice->wave_loop = wave_entry->loop;
+ }
+ #endif
+ }
+}
+
+void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf )
+{
+ #undef RAM
+#if defined(CPU_ARM) && !SPC_BRRCACHE
+ uint8_t* const ram_ = ram.ram;
+ #define RAM ram_
+#else
+ #define RAM ram.ram
+#endif
+#if 0
+ EXIT_TIMER(cpu);
+ ENTER_TIMER(dsp);
+#endif
+
+ /* Here we check for keys on/off. Docs say that successive writes
+ to KON/KOF must be separated by at least 2 Ts periods or risk
+ being neglected. Therefore DSP only looks at these during an
+ update, and not at the time of the write. Only need to do this
+ once however, since the regs haven't changed over the whole
+ period we need to catch up with. */
+
+ {
+ int key_ons = this->r.g.key_ons;
+ int key_offs = this->r.g.key_offs;
+ /* keying on a voice resets that bit in ENDX */
+ this->r.g.wave_ended &= ~key_ons;
+ /* key_off bits prevent key_on from being acknowledged */
+ this->r.g.key_ons = key_ons & key_offs;
+
+ /* process key events outside loop, since they won't re-occur */
+ struct voice_t* voice = this->voice_state + 8;
+ int vbit = 0x80;
+ do
+ {
+ --voice;
+ if ( key_offs & vbit )
+ {
+ voice->env_mode = state_release;
+ voice->key_on_delay = 0;
+ }
+ else if ( key_ons & vbit )
+ {
+ voice->key_on_delay = 8;
+ }
+ }
+ while ( (vbit >>= 1) != 0 );
+ }
+
+ struct src_dir const* const sd =
+ &ram.sd[this->r.g.wave_page * 0x100/sizeof(struct src_dir)];
+
+ #ifdef ROCKBOX_BIG_ENDIAN
+ /* Convert endiannesses before entering loops - these
+ get used alot */
+ const uint32_t rates[VOICE_COUNT] =
+ {
+ GET_LE16A( this->r.voice[0].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[1].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[2].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[3].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[4].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[5].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[6].rate ) & 0x3FFF,
+ GET_LE16A( this->r.voice[7].rate ) & 0x3FFF,
+ };
+ #define VOICE_RATE(x) *(x)
+ #define IF_RBE(...) __VA_ARGS__
+ #ifdef CPU_COLDFIRE
+ /* Initialize mask register with the buffer address mask */
+ asm volatile ("move.l %[m], %%mask" : : [m]"i"(FIR_BUF_MASK));
+ const int echo_wrap = (this->r.g.echo_delay & 15) * 0x800;
+ const int echo_start = this->r.g.echo_page * 0x100;
+ #endif /* CPU_COLDFIRE */
+ #else
+ #define VOICE_RATE(x) (GET_LE16(raw_voice->rate) & 0x3FFF)
+ #define IF_RBE(...)
+ #endif /* ROCKBOX_BIG_ENDIAN */
+
+#if !SPC_NOINTERP
+ int const slow_gaussian = (this->r.g.pitch_mods >> 1) |
+ this->r.g.noise_enables;
+#endif
+ /* (g.flags & 0x40) ? 30 : 14 */
+ int const global_muting = ((this->r.g.flags & 0x40) >> 2) + 14 - 8;
+ int const global_vol_0 = this->r.g.volume_0;
+ int const global_vol_1 = this->r.g.volume_1;
+
+ /* each rate divides exactly into 0x7800 without remainder */
+ int const env_rate_init = 0x7800;
+ static unsigned short const env_rates [0x20] ICONST_ATTR_SPC =
+ {
+ 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
+ 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
+ 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
+ 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
+ };
+
+ do /* one pair of output samples per iteration */
+ {
+ /* Noise */
+ if ( this->r.g.noise_enables )
+ {
+ if ( (this->noise_count -=
+ env_rates [this->r.g.flags & 0x1F]) <= 0 )
+ {
+ this->noise_count = env_rate_init;
+ int feedback = (this->noise << 13) ^ (this->noise << 14);
+ this->noise = (feedback & 0x8000) ^ (this->noise >> 1 & ~1);
+ }
+ }
+
+#if !SPC_NOECHO
+ int echo_0 = 0;
+ int echo_1 = 0;
+#endif
+ long prev_outx = 0; /* TODO: correct value for first channel? */
+ int chans_0 = 0;
+ int chans_1 = 0;
+ /* TODO: put raw_voice pointer in voice_t? */
+ struct raw_voice_t * raw_voice = this->r.voice;
+ struct voice_t* voice = this->voice_state;
+ int vbit = 1;
+ IF_RBE( const uint32_t* vr = rates; )
+ for ( ; vbit < 0x100; vbit <<= 1, ++voice, ++raw_voice IF_RBE( , ++vr ) )
+ {
+ /* pregen involves checking keyon, etc */
+#if 0
+ ENTER_TIMER(dsp_pregen);
+#endif
+
+ /* Key on events are delayed */
+ int key_on_delay = voice->key_on_delay;
+
+ if ( UNLIKELY ( --key_on_delay >= 0 ) ) /* <1% of the time */
+ {
+ key_on(this,voice,sd,raw_voice,key_on_delay,vbit);
+ }
+
+ if ( !(this->keys_down & vbit) ) /* Silent channel */
+ {
+ silent_chan:
+ raw_voice->envx = 0;
+ raw_voice->outx = 0;
+ prev_outx = 0;
+ continue;
+ }
+
+ /* Envelope */
+ {
+ int const ENV_RANGE = 0x800;
+ int env_mode = voice->env_mode;
+ int adsr0 = raw_voice->adsr [0];
+ int env_timer;
+ if ( LIKELY ( env_mode != state_release ) ) /* 99% of the time */
+ {
+ env_timer = voice->env_timer;
+ if ( LIKELY ( adsr0 & 0x80 ) ) /* 79% of the time */
+ {
+ int adsr1 = raw_voice->adsr [1];
+ if ( LIKELY ( env_mode == state_sustain ) ) /* 74% of the time */
+ {
+ if ( (env_timer -= env_rates [adsr1 & 0x1F]) > 0 )
+ goto write_env_timer;
+
+ int envx = voice->envx;
+ envx--; /* envx *= 255 / 256 */
+ envx -= envx >> 8;
+ voice->envx = envx;
+ /* TODO: should this be 8? */
+ raw_voice->envx = envx >> 4;
+ goto init_env_timer;
+ }
+ else if ( env_mode < 0 ) /* 25% state_decay */
+ {
+ int envx = voice->envx;
+ if ( (env_timer -=
+ env_rates [(adsr0 >> 3 & 0x0E) + 0x10]) <= 0 )
+ {
+ envx--; /* envx *= 255 / 256 */
+ envx -= envx >> 8;
+ voice->envx = envx;
+ /* TODO: should this be 8? */
+ raw_voice->envx = envx >> 4;
+ env_timer = env_rate_init;
+ }
+
+ int sustain_level = adsr1 >> 5;
+ if ( envx <= (sustain_level + 1) * 0x100 )
+ voice->env_mode = state_sustain;
+
+ goto write_env_timer;
+ }
+ else /* state_attack */
+ {
+ int t = adsr0 & 0x0F;
+ if ( (env_timer -= env_rates [t * 2 + 1]) > 0 )
+ goto write_env_timer;
+
+ int envx = voice->envx;
+
+ int const step = ENV_RANGE / 64;
+ envx += step;
+ if ( t == 15 )
+ envx += ENV_RANGE / 2 - step;
+
+ if ( envx >= ENV_RANGE )
+ {
+ envx = ENV_RANGE - 1;
+ voice->env_mode = state_decay;
+ }
+ voice->envx = envx;
+ /* TODO: should this be 8? */
+ raw_voice->envx = envx >> 4;
+ goto init_env_timer;
+ }
+ }
+ else /* gain mode */
+ {
+ int t = raw_voice->gain;
+ if ( t < 0x80 )
+ {
+ raw_voice->envx = t;
+ voice->envx = t << 4;
+ goto env_end;
+ }
+ else
+ {
+ if ( (env_timer -= env_rates [t & 0x1F]) > 0 )
+ goto write_env_timer;
+
+ int envx = voice->envx;
+ int mode = t >> 5;
+ if ( mode <= 5 ) /* decay */
+ {
+ int step = ENV_RANGE / 64;
+ if ( mode == 5 ) /* exponential */
+ {
+ envx--; /* envx *= 255 / 256 */
+ step = envx >> 8;
+ }
+ if ( (envx -= step) < 0 )
+ {
+ envx = 0;
+ if ( voice->env_mode == state_attack )
+ voice->env_mode = state_decay;
+ }
+ }
+ else /* attack */
+ {
+ int const step = ENV_RANGE / 64;
+ envx += step;
+ if ( mode == 7 &&
+ envx >= ENV_RANGE * 3 / 4 + step )
+ envx += ENV_RANGE / 256 - step;
+
+ if ( envx >= ENV_RANGE )
+ envx = ENV_RANGE - 1;
+ }
+ voice->envx = envx;
+ /* TODO: should this be 8? */
+ raw_voice->envx = envx >> 4;
+ goto init_env_timer;
+ }
+ }
+ }
+ else /* state_release */
+ {
+ int envx = voice->envx;
+ if ( (envx -= ENV_RANGE / 256) > 0 )
+ {
+ voice->envx = envx;
+ raw_voice->envx = envx >> 8;
+ goto env_end;
+ }
+ else
+ {
+ /* bit was set, so this clears it */
+ this->keys_down ^= vbit;
+ voice->envx = 0;
+ goto silent_chan;
+ }
+ }
+ init_env_timer:
+ env_timer = env_rate_init;
+ write_env_timer:
+ voice->env_timer = env_timer;
+ env_end:;
+ }
+#if 0
+ EXIT_TIMER(dsp_pregen);
+
+ ENTER_TIMER(dsp_gen);
+#endif
+ #if !SPC_BRRCACHE
+ /* Decode BRR block */
+ if ( voice->position >= BRR_BLOCK_SIZE * 0x1000 )
+ {
+ voice->position -= BRR_BLOCK_SIZE * 0x1000;
+
+ uint8_t const* addr = voice->addr;
+ if ( addr >= RAM + 0x10000 )
+ addr -= 0x10000;
+
+ /* action based on previous block's header */
+ if ( voice->block_header & 1 )
+ {
+ addr = RAM + letoh16(sd[raw_voice->waveform].loop);
+ this->r.g.wave_ended |= vbit;
+ if ( !(voice->block_header & 2) ) /* 1% of the time */
+ {
+ /* first block was end block;
+ don't play anything (verified) */
+ /* bit was set, so this clears it */
+ this->keys_down ^= vbit;
+
+ /* since voice->envx is 0,
+ samples and position don't matter */
+ raw_voice->envx = 0;
+ voice->envx = 0;
+ goto skip_decode;
+ }
+ }
+
+ /* header */
+ int const block_header = *addr;
+ addr += 9;
+ voice->addr = addr;
+ voice->block_header = block_header;
+
+ /* previous samples */
+ int smp2 = voice->samples [BRR_BLOCK_SIZE + 1];
+ int smp1 = voice->samples [BRR_BLOCK_SIZE + 2];
+ voice->samples [0] = voice->samples [BRR_BLOCK_SIZE];
+
+ /* output position */
+ short* out = voice->samples + (1 + BRR_BLOCK_SIZE);
+ int offset = -BRR_BLOCK_SIZE << 2;
+
+ /* if next block has end flag set,
+ this block ends early (verified) */
+ if ( (block_header & 3) != 3 && (*addr & 3) == 1 )
+ {
+ /* arrange for last 9 samples to be skipped */
+ int const skip = 9;
+ out += (skip & 1);
+ voice->samples [skip] = voice->samples [BRR_BLOCK_SIZE];
+ voice->position += skip * 0x1000;
+ offset = (-BRR_BLOCK_SIZE + (skip & ~1)) << 2;
+ addr -= skip / 2;
+ /* force sample to end on next decode */
+ voice->block_header = 1;
+ }
+
+ int const filter = block_header & 0x0c;
+ int const scale = block_header >> 4;
+
+ if ( filter == 0x08 ) /* filter 2 (30-90% of the time) */
+ {
+ /* y[n] = x[n] + 61/32 * y[n-1] - 15/16 * y[n-2] */
+ do /* decode and filter 16 samples */
+ {
+ /* Get nybble, sign-extend, then scale
+ get byte, select which nybble, sign-extend, then shift
+ based on scaling. */
+ int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
+ delta = (delta << scale) >> 1;
+
+ if (scale > 0xc)
+ delta = (delta >> 17) << 11;
+
+ out [offset >> 2] = smp2;
+
+ delta -= smp2 >> 1;
+ delta += smp2 >> 5;
+ delta += smp1;
+ delta += (-smp1 - (smp1 >> 1)) >> 5;
+
+ delta = CLAMP16( delta );
+ smp2 = smp1;
+ smp1 = (int16_t) (delta * 2); /* sign-extend */
+ }
+ while ( (offset += 4) != 0 );
+ }
+ else if ( filter == 0x04 ) /* filter 1 */
+ {
+ /* y[n] = x[n] + 15/16 * y[n-1] */
+ do /* decode and filter 16 samples */
+ {
+ /* Get nybble, sign-extend, then scale
+ get byte, select which nybble, sign-extend, then shift
+ based on scaling. */
+ int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
+ delta = (delta << scale) >> 1;
+
+ if (scale > 0xc)
+ delta = (delta >> 17) << 11;
+
+ out [offset >> 2] = smp2;
+
+ delta += smp1 >> 1;
+ delta += (-smp1) >> 5;
+
+ delta = CLAMP16( delta );
+ smp2 = smp1;
+ smp1 = (int16_t) (delta * 2); /* sign-extend */
+ }
+ while ( (offset += 4) != 0 );
+ }
+ else if ( filter == 0x0c ) /* filter 3 */
+ {
+ /* y[n] = x[n] + 115/64 * y[n-1] - 13/16 * y[n-2] */
+ do /* decode and filter 16 samples */
+ {
+ /* Get nybble, sign-extend, then scale
+ get byte, select which nybble, sign-extend, then shift
+ based on scaling. */
+ int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
+ delta = (delta << scale) >> 1;
+
+ if (scale > 0xc)
+ delta = (delta >> 17) << 11;
+
+ out [offset >> 2] = smp2;
+
+ delta -= smp2 >> 1;
+ delta += (smp2 + (smp2 >> 1)) >> 4;
+ delta += smp1;
+ delta += (-smp1 * 13) >> 7;
+
+ delta = CLAMP16( delta );
+ smp2 = smp1;
+ smp1 = (int16_t) (delta * 2); /* sign-extend */
+ }
+ while ( (offset += 4) != 0 );
+ }
+ else /* filter 0 */
+ {
+ /* y[n] = x[n] */
+ do /* decode and filter 16 samples */
+ {
+ /* Get nybble, sign-extend, then scale
+ get byte, select which nybble, sign-extend, then shift
+ based on scaling. */
+ int delta = (int8_t)(addr [offset >> 3] << (offset & 4)) >> 4;
+ delta = (delta << scale) >> 1;
+
+ if (scale > 0xc)
+ delta = (delta >> 17) << 11;
+
+ out [offset >> 2] = smp2;
+
+ smp2 = smp1;
+ smp1 = delta * 2;
+ }
+ while ( (offset += 4) != 0 );
+ }
+
+ out [0] = smp2;
+ out [1] = smp1;
+
+ skip_decode:;
+ }
+ #endif /* !SPC_BRRCACHE */
+ /* Get rate (with possible modulation) */
+ int rate = VOICE_RATE(vr);
+ if ( this->r.g.pitch_mods & vbit )
+ rate = (rate * (prev_outx + 32768)) >> 15;
+
+ #if !SPC_NOINTERP
+ /* Interleved gauss table (to improve cache coherency). */
+ /* gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] */
+ static short const gauss [512] ICONST_ATTR_SPC MEM_ALIGN_ATTR =
+ {
+370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
+339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
+311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
+283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
+257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
+233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
+210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
+188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
+168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
+150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
+132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
+117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
+102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074,
+ 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040,
+ 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005,
+ 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969,
+ 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932,
+ 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894,
+ 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855,
+ 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816,
+ 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777,
+ 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737,
+ 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698,
+ 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659,
+ 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620,
+ 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582,
+ 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545,
+ 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508,
+ 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473,
+ 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439,
+ 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405,
+ 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374,
+ };
+ /* Gaussian interpolation using most recent 4 samples */
+ long position = voice->position;
+ voice->position += rate;
+ short const* interp = voice->samples + (position >> 12);
+ int offset = position >> 4 & 0xFF;
+
+ /* Only left half of gaussian kernel is in table, so we must mirror
+ for right half */
+ short const* fwd = gauss + offset * 2;
+ short const* rev = gauss + 510 - offset * 2;
+
+ /* Use faster gaussian interpolation when exact result isn't needed
+ by pitch modulator of next channel */
+ int amp_0, amp_1; /* Also serve as temps _0, and _1 */
+ if ( LIKELY ( !(slow_gaussian & vbit) ) ) /* 99% of the time */
+ {
+ /* Main optimization is lack of clamping. Not a problem since
+ output never goes more than +/- 16 outside 16-bit range and
+ things are clamped later anyway. Other optimization is to
+ preserve fractional accuracy, eliminating several masks. */
+ #if defined (CPU_ARM)
+ int output;
+ int _2, _3; /* All-purpose temps */
+ /* Multiple ASM blocks keep regs free and reduce result
+ * latency issues. */
+ #if ARM_ARCH >= 6
+ /* Interpolate */
+ asm volatile (
+ "ldr %[_0], [%[interp]] \r\n" /* _0=i0i1 */
+ "ldr %[_2], [%[fwd]] \r\n" /* _2=f0f1 */
+ "ldr %[_1], [%[interp], #4] \r\n" /* _1=i2i3 */
+ "ldr %[_3], [%[rev]] \r\n" /* _3=r0r1 */
+ "smuad %[out], %[_0], %[_2] \r\n" /* out=f0*i0 + f1*i1 */
+ "smladx %[out], %[_1], %[_3], %[out] \r\n" /* out+=r1*i2 + r0*i3 */
+ : [out]"=r"(output),
+ [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
+ [_2]"=&r"(_2), [_3]"=r"(_3)
+ : [fwd]"r"(fwd), [rev]"r"(rev),
+ [interp]"r"(interp));
+ /* Apply voice envelope */
+ asm volatile (
+ "mov %[_2], %[out], asr #(11-5) \r\n" /* To do >> 16 later */
+ "mul %[out], %[_2], %[envx] \r\n" /* and avoid exp. shift */
+ : [out]"+r"(output), [_2]"=&r"(_2)
+ : [envx]"r"((int)voice->envx));
+ /* Apply left and right volume */
+ asm volatile (
+ "smulwb %[amp_0], %[out], %[vvol_0] \r\n" /* (32x16->48)[47:16]->[31:0] */
+ "smulwb %[amp_1], %[out], %[vvol_1] \r\n"
+ : [out]"+r"(output),
+ [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
+ : [vvol_0]"r"(voice->volume[0]),
+ [vvol_1]"r"(voice->volume[1]));
+
+ raw_voice->outx = output >> (8+5); /* 'output' still 5 bits too big */
+ #else /* ARM_ARCH < 6 */
+ /* Perform gaussian interpolation on four samples */
+ asm volatile (
+ "ldrsh %[_0], [%[interp]] \r\n"
+ "ldrsh %[_2], [%[fwd]] \r\n"
+ "ldrsh %[_1], [%[interp], #2] \r\n"
+ "ldrsh %[_3], [%[fwd], #2] \r\n"
+ "mul %[out], %[_0], %[_2] \r\n" /* out= fwd[0]*interp[0] */
+ "ldrsh %[_0], [%[interp], #4] \r\n"
+ "ldrsh %[_2], [%[rev], #2] \r\n"
+ "mla %[out], %[_1], %[_3], %[out] \r\n" /* out+=fwd[1]*interp[1] */
+ "ldrsh %[_1], [%[interp], #6] \r\n"
+ "ldrsh %[_3], [%[rev]] \r\n"
+ "mla %[out], %[_0], %[_2], %[out] \r\n" /* out+=rev[1]*interp[2] */
+ "mla %[out], %[_1], %[_3], %[out] \r\n" /* out+=rev[0]*interp[3] */
+ : [out]"=&r"(output),
+ [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
+ [_2]"=&r"(_2), [_3]"=&r"(_3)
+ : [fwd]"r"(fwd), [rev]"r"(rev),
+ [interp]"r"(interp));
+ /* Apply voice envelope */
+ asm volatile (
+ "mov %[_2], %[out], asr #11 \r\n"
+ "mul %[out], %[_2], %[envx] \r\n"
+ : [out]"+r"(output), [_2]"=&r"(_2)
+ : [envx]"r"((int)voice->envx));
+ /* Reduce and apply left and right volume */
+ asm volatile (
+ "mov %[out], %[out], asr #11 \r\n"
+ "mul %[amp_0], %[out], %[vvol_0] \r\n"
+ "mul %[amp_1], %[out], %[vvol_1] \r\n"
+ : [out]"+r"(output),
+ [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
+ : [vvol_0]"r"((int)voice->volume[0]),
+ [vvol_1]"r"((int)voice->volume[1]));
+
+ raw_voice->outx = output >> 8;
+ #endif /* ARM_ARCH */
+ #else /* Unoptimized CPU */
+ int output = (((fwd [0] * interp [0] +
+ fwd [1] * interp [1] +
+ rev [1] * interp [2] +
+ rev [0] * interp [3] ) >> 11) * voice->envx) >> 11;
+
+ /* duplicated here to give compiler more to run in parallel */
+ amp_0 = voice->volume [0] * output;
+ amp_1 = voice->volume [1] * output;
+
+ raw_voice->outx = output >> 8;
+ #endif /* CPU_* */
+ }
+ else /* slow gaussian */
+ {
+ #if defined(CPU_ARM)
+ #if ARM_ARCH >= 6
+ int output = *(int16_t*) &this->noise;
+
+ if ( !(this->r.g.noise_enables & vbit) )
+ {
+ /* Interpolate */
+ int _2, _3;
+ asm volatile (
+ /* NOTE: often-unaligned accesses */
+ "ldr %[_0], [%[interp]] \r\n" /* _0=i0i1 */
+ "ldr %[_2], [%[fwd]] \r\n" /* _2=f0f1 */
+ "ldr %[_1], [%[interp], #4] \r\n" /* _1=i2i3 */
+ "ldr %[_3], [%[rev]] \r\n" /* _3=f2f3 */
+ "smulbb %[out], %[_0], %[_2] \r\n" /* out=f0*i0 */
+ "smultt %[_0], %[_0], %[_2] \r\n" /* _0=f1*i1 */
+ "smulbt %[_2], %[_1], %[_3] \r\n" /* _2=r1*i2 */
+ "smultb %[_3], %[_1], %[_3] \r\n" /* _3=r0*i3 */
+ : [out]"=r"(output),
+ [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
+ [_2]"=&r"(_2), [_3]"=r"(_3)
+ : [fwd]"r"(fwd), [rev]"r"(rev),
+ [interp]"r"(interp));
+ asm volatile (
+ "mov %[out], %[out], asr#12 \r\n"
+ "add %[_0], %[out], %[_0], asr #12 \r\n"
+ "add %[_2], %[_0], %[_2], asr #12 \r\n"
+ "pkhbt %[_0], %[_2], %[_3], asl #4 \r\n" /* _3[31:16], _2[15:0] */
+ "sadd16 %[_0], %[_0], %[_0] \r\n" /* _3[31:16]*2, _2[15:0]*2 */
+ "qsubaddx %[out], %[_0], %[_0] \r\n" /* out[15:0]=
+ * sat16(_3[31:16]+_2[15:0]) */
+ : [out]"+r"(output),
+ [_0]"+r"(amp_0), [_2]"+r"(_2), [_3]"+r"(_3));
+ }
+ /* Apply voice envelope */
+ asm volatile (
+ "smulbb %[out], %[out], %[envx] \r\n"
+ : [out]"+r"(output)
+ : [envx]"r"(voice->envx));
+ /* Reduce and apply left and right volume */
+ asm volatile (
+ "mov %[out], %[out], asr #11 \r\n"
+ "bic %[out], %[out], #0x1 \r\n"
+ "mul %[amp_0], %[out], %[vvol_0] \r\n"
+ "mul %[amp_1], %[out], %[vvol_1] \r\n"
+ : [out]"+r"(output),
+ [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
+ : [vvol_0]"r"((int)voice->volume[0]),
+ [vvol_1]"r"((int)voice->volume[1]));
+
+ prev_outx = output;
+ raw_voice->outx = output >> 8;
+ #else /* ARM_ARCH < 6 */
+ int output = *(int16_t*) &this->noise;
+
+ if ( !(this->r.g.noise_enables & vbit) )
+ {
+ /* Interpolate */
+ int _2, _3;
+ asm volatile (
+ "ldrsh %[_0], [%[interp]] \r\n"
+ "ldrsh %[_2], [%[fwd]] \r\n"
+ "ldrsh %[_1], [%[interp], #2] \r\n"
+ "ldrsh %[_3], [%[fwd], #2] \r\n"
+ "mul %[out], %[_2], %[_0] \r\n" /* fwd[0]*interp[0] */
+ "ldrsh %[_2], [%[rev], #2] \r\n"
+ "mul %[_0], %[_3], %[_1] \r\n" /* fwd[1]*interp[1] */
+ "ldrsh %[_1], [%[interp], #4] \r\n"
+ "mov %[out], %[out], asr #12 \r\n"
+ "ldrsh %[_3], [%[rev]] \r\n"
+ "mul %[_2], %[_1], %[_2] \r\n" /* rev[1]*interp[2] */
+ "ldrsh %[_1], [%[interp], #6] \r\n"
+ "add %[_0], %[out], %[_0], asr #12 \r\n"
+ "mul %[_3], %[_1], %[_3] \r\n" /* rev[0]*interp[3] */
+ "add %[_2], %[_0], %[_2], asr #12 \r\n"
+ "mov %[_2], %[_2], lsl #17 \r\n"
+ "mov %[_3], %[_3], asr #12 \r\n"
+ "mov %[_3], %[_3], asl #1 \r\n"
+ "add %[out], %[_3], %[_2], asr #16 \r\n"
+ : [out]"=&r"(output),
+ [_0]"=&r"(amp_0), [_1]"=&r"(amp_1),
+ [_2]"=&r"(_2), [_3]"=&r"(_3)
+ : [fwd]"r"(fwd), [rev]"r"(rev),
+ [interp]"r"(interp));
+
+ output = CLAMP16(output);
+ }
+ /* Apply voice envelope */
+ asm volatile (
+ "mul %[_0], %[out], %[envx] \r\n"
+ : [_0]"=r"(amp_0)
+ : [out]"r"(output), [envx]"r"((int)voice->envx));
+ /* Reduce and apply left and right volume */
+ asm volatile (
+ "mov %[out], %[amp_0], asr #11 \r\n" /* amp_0 = _0 */
+ "bic %[out], %[out], #0x1 \r\n"
+ "mul %[amp_0], %[out], %[vvol_0] \r\n"
+ "mul %[amp_1], %[out], %[vvol_1] \r\n"
+ : [out]"+r"(output),
+ [amp_0]"+r"(amp_0), [amp_1]"=r"(amp_1)
+ : [vvol_0]"r"((int)voice->volume[0]),
+ [vvol_1]"r"((int)voice->volume[1]));
+
+ prev_outx = output;
+ raw_voice->outx = output >> 8;
+ #endif /* ARM_ARCH >= 6 */
+ #else /* Unoptimized CPU */
+ int output = *(int16_t*) &this->noise;
+
+ if ( !(this->r.g.noise_enables & vbit) )
+ {
+ output = (fwd [0] * interp [0]) & ~0xFFF;
+ output = (output + fwd [1] * interp [1]) & ~0xFFF;
+ output = (output + rev [1] * interp [2]) >> 12;
+ output = (int16_t) (output * 2);
+ output += ((rev [0] * interp [3]) >> 12) * 2;
+ output = CLAMP16( output );
+ }
+ output = (output * voice->envx) >> 11 & ~1;
+
+ /* duplicated here to give compiler more to run in parallel */
+ amp_0 = voice->volume [0] * output;
+ amp_1 = voice->volume [1] * output;
+
+ prev_outx = output;
+ raw_voice->outx = output >> 8;
+ #endif /* CPU_* */
+ }
+ #else /* SPCNOINTERP */
+ /* two-point linear interpolation */
+ #ifdef CPU_COLDFIRE
+ int amp_0 = (int16_t)this->noise;
+ int amp_1;
+
+ if ( (this->r.g.noise_enables & vbit) == 0 )
+ {
+ uint32_t f = voice->position;
+ int32_t y0;
+
+ /**
+ * Formula (fastest found so far of MANY):
+ * output = y0 + f*y1 - f*y0
+ */
+ asm volatile (
+ /* separate fractional and whole parts */
+ "move.l %[f], %[y1] \r\n"
+ "and.l #0xfff, %[f] \r\n"
+ "lsr.l %[sh], %[y1] \r\n"
+ /* load samples y0 (upper) & y1 (lower) */
+ "move.l 2(%[s], %[y1].l*2), %[y1] \r\n"
+ /* %acc0 = f*y1 */
+ "mac.w %[f]l, %[y1]l, %%acc0 \r\n"
+ /* %acc0 -= f*y0 */
+ "msac.w %[f]l, %[y1]u, %%acc0 \r\n"
+ /* separate out y0 and sign extend */
+ "swap %[y1] \r\n"
+ "movea.w %[y1], %[y0] \r\n"
+ /* fetch result, scale down and add y0 */
+ "movclr.l %%acc0, %[y1] \r\n"
+ /* output = y0 + (result >> 12) */
+ "asr.l %[sh], %[y1] \r\n"
+ "add.l %[y0], %[y1] \r\n"
+ : [f]"+d"(f), [y0]"=&a"(y0), [y1]"=&d"(amp_0)
+ : [s]"a"(voice->samples), [sh]"d"(12));
+ }
+
+ /* apply voice envelope to output */
+ asm volatile (
+ "mac.w %[out]l, %[envx]l, %%acc0 \r\n"
+ :
+ : [out]"r"(amp_0), [envx]"r"(voice->envx));
+
+ /* advance voice position */
+ voice->position += rate;
+
+ /* fetch output, scale and apply left and right
+ voice volume */
+ asm volatile (
+ "movclr.l %%acc0, %[out] \r\n"
+ "asr.l %[sh], %[out] \r\n"
+ "mac.l %[vvol_0], %[out], %%acc0 \r\n"
+ "mac.l %[vvol_1], %[out], %%acc1 \r\n"
+ : [out]"=&d"(amp_0)
+ : [vvol_0]"r"((int)voice->volume[0]),
+ [vvol_1]"r"((int)voice->volume[1]),
+ [sh]"d"(11));
+
+ /* save this output into previous, scale and save in
+ output register */
+ prev_outx = amp_0;
+ raw_voice->outx = amp_0 >> 8;
+
+ /* fetch final voice output */
+ asm volatile (
+ "movclr.l %%acc0, %[amp_0] \r\n"
+ "movclr.l %%acc1, %[amp_1] \r\n"
+ : [amp_0]"=r"(amp_0), [amp_1]"=r"(amp_1));
+ #elif defined (CPU_ARM)
+ int amp_0, amp_1;
+
+ if ( (this->r.g.noise_enables & vbit) != 0 )
+ {
+ amp_0 = *(int16_t *)&this->noise;
+ }
+ else
+ {
+ uint32_t f = voice->position;
+ amp_0 = (uint32_t)voice->samples;
+
+ asm volatile(
+ "mov %[y1], %[f], lsr #12 \r\n"
+ "eor %[f], %[f], %[y1], lsl #12 \r\n"
+ "add %[y1], %[y0], %[y1], lsl #1 \r\n"
+ "ldrsh %[y0], [%[y1], #2] \r\n"
+ "ldrsh %[y1], [%[y1], #4] \r\n"
+ "sub %[y1], %[y1], %[y0] \r\n"
+ "mul %[f], %[y1], %[f] \r\n"
+ "add %[y0], %[y0], %[f], asr #12 \r\n"
+ : [f]"+r"(f), [y0]"+r"(amp_0), [y1]"=&r"(amp_1));
+ }
+
+ voice->position += rate;
+
+ asm volatile(
+ "mul %[amp_1], %[amp_0], %[envx] \r\n"
+ "mov %[amp_0], %[amp_1], asr #11 \r\n"
+ "mov %[amp_1], %[amp_0], asr #8 \r\n"
+ : [amp_0]"+r"(amp_0), [amp_1]"=r"(amp_1)
+ : [envx]"r"(voice->envx));
+
+ prev_outx = amp_0;
+ raw_voice->outx = (int8_t)amp_1;
+
+ asm volatile(
+ "mul %[amp_1], %[amp_0], %[vol_1] \r\n"
+ "mul %[amp_0], %[vol_0], %[amp_0] \r\n"
+ : [amp_0]"+r"(amp_0), [amp_1]"=&r"(amp_1)
+ : [vol_0]"r"((int)voice->volume[0]),
+ [vol_1]"r"((int)voice->volume[1]));
+ #else /* Unoptimized CPU */
+ int output;
+
+ if ( (this->r.g.noise_enables & vbit) == 0 )
+ {
+ int const fraction = voice->position & 0xfff;
+ short const* const pos = (voice->samples + (voice->position >> 12)) + 1;
+ output = pos[0] + ((fraction * (pos[1] - pos[0])) >> 12);
+ } else {
+ output = *(int16_t *)&this->noise;
+ }
+
+ voice->position += rate;
+
+ output = (output * voice->envx) >> 11;
+
+ /* duplicated here to give compiler more to run in parallel */
+ int amp_0 = voice->volume [0] * output;
+ int amp_1 = voice->volume [1] * output;
+
+ prev_outx = output;
+ raw_voice->outx = (int8_t) (output >> 8);
+ #endif /* CPU_* */
+ #endif /* SPCNOINTERP */
+
+ #if SPC_BRRCACHE
+ if ( voice->position >= voice->wave_end )
+ {
+ long loop_len = voice->wave_loop << 12;
+ voice->position -= loop_len;
+ this->r.g.wave_ended |= vbit;
+ if ( !loop_len )
+ {
+ this->keys_down ^= vbit;
+ raw_voice->envx = 0;
+ voice->envx = 0;
+ }
+ }
+ #endif
+#if 0
+ EXIT_TIMER(dsp_gen);
+
+ ENTER_TIMER(dsp_mix);
+#endif
+ chans_0 += amp_0;
+ chans_1 += amp_1;
+ #if !SPC_NOECHO
+ if ( this->r.g.echo_ons & vbit )
+ {
+ echo_0 += amp_0;
+ echo_1 += amp_1;
+ }
+ #endif
+#if 0
+ EXIT_TIMER(dsp_mix);
+#endif
+ }
+ /* end of voice loop */
+
+ #if !SPC_NOECHO
+ #ifdef CPU_COLDFIRE
+ /* Read feedback from echo buffer */
+ int echo_pos = this->echo_pos;
+ uint8_t* const echo_ptr = RAM + ((echo_start + echo_pos) & 0xFFFF);
+ echo_pos += 4;
+ if ( echo_pos >= echo_wrap )
+ echo_pos = 0;
+ this->echo_pos = echo_pos;
+ int fb = swap_odd_even32(*(int32_t *)echo_ptr);
+ int out_0, out_1;
+
+ /* Keep last 8 samples */
+ *this->last_fir_ptr = fb;
+ this->last_fir_ptr = this->fir_ptr;
+
+ /* Apply echo FIR filter to output samples read from echo buffer -
+ circular buffer is hardware incremented and masked; FIR
+ coefficients and buffer history are loaded in parallel with
+ multiply accumulate operations. Shift left by one here and once
+ again when calculating feedback to have sample values justified
+ to bit 31 in the output to ease endian swap, interleaving and
+ clamping before placing result in the program's echo buffer. */
+ int _0, _1, _2;
+ asm volatile (
+ "move.l (%[fir_c]) , %[_2] \r\n"
+ "mac.w %[fb]u, %[_2]u, <<, (%[fir_p])+&, %[_0], %%acc0 \r\n"
+ "mac.w %[fb]l, %[_2]u, <<, (%[fir_p])& , %[_1], %%acc1 \r\n"
+ "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
+ "mac.w %[_0]l, %[_2]l, <<, 4(%[fir_c]) , %[_2], %%acc1 \r\n"
+ "mac.w %[_1]u, %[_2]u, <<, 4(%[fir_p])& , %[_0], %%acc0 \r\n"
+ "mac.w %[_1]l, %[_2]u, <<, 8(%[fir_p])& , %[_1], %%acc1 \r\n"
+ "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
+ "mac.w %[_0]l, %[_2]l, <<, 8(%[fir_c]) , %[_2], %%acc1 \r\n"
+ "mac.w %[_1]u, %[_2]u, <<, 12(%[fir_p])& , %[_0], %%acc0 \r\n"
+ "mac.w %[_1]l, %[_2]u, <<, 16(%[fir_p])& , %[_1], %%acc1 \r\n"
+ "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
+ "mac.w %[_0]l, %[_2]l, <<, 12(%[fir_c]) , %[_2], %%acc1 \r\n"
+ "mac.w %[_1]u, %[_2]u, <<, 20(%[fir_p])& , %[_0], %%acc0 \r\n"
+ "mac.w %[_1]l, %[_2]u, << , %%acc1 \r\n"
+ "mac.w %[_0]u, %[_2]l, << , %%acc0 \r\n"
+ "mac.w %[_0]l, %[_2]l, << , %%acc1 \r\n"
+ : [_0]"=&r"(_0), [_1]"=&r"(_1), [_2]"=&r"(_2),
+ [fir_p]"+a"(this->fir_ptr)
+ : [fir_c]"a"(this->fir_coeff), [fb]"r"(fb)
+ );
+
+ /* Generate output */
+ asm volatile (
+ /* fetch filter results _after_ gcc loads asm
+ block parameters to eliminate emac stalls */
+ "movclr.l %%acc0, %[out_0] \r\n"
+ "movclr.l %%acc1, %[out_1] \r\n"
+ /* apply global volume */
+ "mac.l %[chans_0], %[gv_0] , %%acc2 \r\n"
+ "mac.l %[chans_1], %[gv_1] , %%acc3 \r\n"
+ /* apply echo volume and add to final output */
+ "mac.l %[ev_0], %[out_0], >>, %%acc2 \r\n"
+ "mac.l %[ev_1], %[out_1], >>, %%acc3 \r\n"
+ : [out_0]"=&r"(out_0), [out_1]"=&r"(out_1)
+ : [chans_0]"r"(chans_0), [gv_0]"r"(global_vol_0),
+ [ev_0]"r"((int)this->r.g.echo_volume_0),
+ [chans_1]"r"(chans_1), [gv_1]"r"(global_vol_1),
+ [ev_1]"r"((int)this->r.g.echo_volume_1)
+ );
+
+ /* Feedback into echo buffer */
+ if ( !(this->r.g.flags & 0x20) )
+ {
+ int sh = 1 << 9;
+
+ asm volatile (
+ /* scale echo voices; saturate if overflow */
+ "mac.l %[sh], %[e1] , %%acc1 \r\n"
+ "mac.l %[sh], %[e0] , %%acc0 \r\n"
+ /* add scaled output from FIR filter */
+ "mac.l %[out_1], %[ef], <<, %%acc1 \r\n"
+ "mac.l %[out_0], %[ef], <<, %%acc0 \r\n"
+ /* swap and fetch feedback results - simply
+ swap_odd_even32 mixed in between macs and
+ movclrs to mitigate stall issues */
+ "move.l #0x00ff00ff, %[sh] \r\n"
+ "movclr.l %%acc1, %[e1] \r\n"
+ "swap %[e1] \r\n"
+ "movclr.l %%acc0, %[e0] \r\n"
+ "move.w %[e1], %[e0] \r\n"
+ "and.l %[e0], %[sh] \r\n"
+ "eor.l %[sh], %[e0] \r\n"
+ "lsl.l #8, %[sh] \r\n"
+ "lsr.l #8, %[e0] \r\n"
+ "or.l %[sh], %[e0] \r\n"
+ /* save final feedback into echo buffer */
+ "move.l %[e0], (%[echo_ptr]) \r\n"
+ : [e0]"+d"(echo_0), [e1]"+d"(echo_1), [sh]"+d"(sh)
+ : [out_0]"r"(out_0), [out_1]"r"(out_1),
+ [ef]"r"((int)this->r.g.echo_feedback),
+ [echo_ptr]"a"((int32_t *)echo_ptr)
+ );
+ }
+
+ /* Output final samples */
+ asm volatile (
+ /* fetch output saved in %acc2 and %acc3 */
+ "movclr.l %%acc2, %[out_0] \r\n"
+ "movclr.l %%acc3, %[out_1] \r\n"
+ /* scale right by global_muting shift */
+ "asr.l %[gm], %[out_0] \r\n"
+ "asr.l %[gm], %[out_1] \r\n"
+ : [out_0]"=&d"(out_0), [out_1]"=&d"(out_1)
+ : [gm]"d"(global_muting)
+ );
+
+ out_buf [ 0] = out_0;
+ out_buf [WAV_CHUNK_SIZE] = out_1;
+ out_buf ++;
+ #elif defined (CPU_ARM)
+ /* Read feedback from echo buffer */
+ int echo_pos = this->echo_pos;
+ uint8_t* const echo_ptr = RAM +
+ ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF);
+ echo_pos += 4;
+ if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 )
+ echo_pos = 0;
+ this->echo_pos = echo_pos;
+
+ #if ARM_ARCH >= 6
+ int32_t *fir_ptr, *fir_coeff;
+ int fb_0, fb_1;
+
+ /* Apply FIR */
+
+ /* Keep last 8 samples */
+ asm volatile (
+ "ldr %[fb_0], [%[echo_p]] \r\n"
+ "add %[fir_p], %[t_fir_p], #4 \r\n"
+ "bic %[t_fir_p], %[fir_p], %[mask] \r\n"
+ "str %[fb_0], [%[fir_p], #-4] \r\n"
+ /* duplicate at +8 eliminates wrap checking below */
+ "str %[fb_0], [%[fir_p], #28] \r\n"
+ : [fir_p]"=&r"(fir_ptr), [t_fir_p]"+r"(this->fir_ptr),
+ [fb_0]"=&r"(fb_0)
+ : [echo_p]"r"(echo_ptr), [mask]"i"(~FIR_BUF_MASK));
+
+ fir_coeff = (int32_t *)this->fir_coeff;
+
+ /* Fugly, but the best version found. */
+ int _0;
+ asm volatile ( /* L0R0 = acc0 */
+ "ldmia %[fir_p]!, { r2-r5 } \r\n" /* L1R1-L4R4 = r2-r5 */
+ "ldmia %[fir_c]!, { r0-r1 } \r\n" /* C0C1-C2C3 = r0-r1 */
+ "pkhbt %[_0], %[acc0], r2, asl #16 \r\n" /* L0R0,L1R1->L0L1,R0R1 */
+ "pkhtb r2, r2, %[acc0], asr #16 \r\n"
+ "smuad %[acc0], %[_0], r0 \r\n" /* acc0=L0*C0+L1*C1 */
+ "smuad %[acc1], r2, r0 \r\n" /* acc1=R0*C0+R1*C1 */
+ "pkhbt %[_0], r3, r4, asl #16 \r\n" /* L2R2,L3R3->L2L3,R2R3 */
+ "pkhtb r4, r4, r3, asr #16 \r\n"
+ "smlad %[acc0], %[_0], r1, %[acc0] \r\n" /* acc0+=L2*C2+L3*C3 */
+ "smlad %[acc1], r4, r1, %[acc1] \r\n" /* acc1+=R2*C2+R3*C3 */
+ "ldmia %[fir_p], { r2-r4 } \r\n" /* L5R5-L7R7 = r2-r4 */
+ "ldmia %[fir_c], { r0-r1 } \r\n" /* C4C5-C6C7 = r0-r1 */
+ "pkhbt %[_0], r5, r2, asl #16 \r\n" /* L4R4,L5R5->L4L5,R4R5 */
+ "pkhtb r2, r2, r5, asr #16 \r\n"
+ "smlad %[acc0], %[_0], r0, %[acc0] \r\n" /* acc0+=L4*C4+L5*C5 */
+ "smlad %[acc1], r2, r0, %[acc1] \r\n" /* acc1+=R4*C4+R5*C5 */
+ "pkhbt %[_0], r3, r4, asl #16 \r\n" /* L6R6,L7R7->L6L7,R6R7 */
+ "pkhtb r4, r4, r3, asr #16 \r\n"
+ "smlad %[acc0], %[_0], r1, %[acc0] \r\n" /* acc0+=L6*C6+L7*C7 */
+ "smlad %[acc1], r4, r1, %[acc1] \r\n" /* acc1+=R6*C6+R7*C7 */
+ : [acc0]"+r"(fb_0), [acc1]"=&r"(fb_1), [_0]"=&r"(_0),
+ [fir_p]"+r"(fir_ptr), [fir_c]"+r"(fir_coeff)
+ :
+ : "r0", "r1", "r2", "r3", "r4", "r5");
+
+ /* Generate output */
+ int amp_0, amp_1;
+
+ asm volatile (
+ "mul %[amp_0], %[gvol_0], %[chans_0] \r\n"
+ "mul %[amp_1], %[gvol_1], %[chans_1] \r\n"
+ : [amp_0]"=&r"(amp_0), [amp_1]"=r"(amp_1)
+ : [gvol_0]"r"(global_vol_0), [gvol_1]"r"(global_vol_1),
+ [chans_0]"r"(chans_0), [chans_1]"r"(chans_1));
+ asm volatile (
+ "mla %[amp_0], %[fb_0], %[ev_0], %[amp_0] \r\n"
+ "mla %[amp_1], %[fb_1], %[ev_1], %[amp_1] \r\n"
+ : [amp_0]"+r"(amp_0), [amp_1]"+r"(amp_1)
+ : [fb_0]"r"(fb_0), [fb_1]"r"(fb_1),
+ [ev_0]"r"((int)this->r.g.echo_volume_0),
+ [ev_1]"r"((int)this->r.g.echo_volume_1));
+
+ out_buf [ 0] = amp_0 >> global_muting;
+ out_buf [WAV_CHUNK_SIZE] = amp_1 >> global_muting;
+ out_buf ++;
+
+ if ( !(this->r.g.flags & 0x20) )
+ {
+ /* Feedback into echo buffer */
+ int e0, e1;
+
+ asm volatile (
+ "mov %[e0], %[echo_0], asl #7 \r\n"
+ "mov %[e1], %[echo_1], asl #7 \r\n"
+ "mla %[e0], %[fb_0], %[efb], %[e0] \r\n"
+ "mla %[e1], %[fb_1], %[efb], %[e1] \r\n"
+ : [e0]"=&r"(e0), [e1]"=&r"(e1)
+ : [echo_0]"r"(echo_0), [echo_1]"r"(echo_1),
+ [fb_0]"r"(fb_0), [fb_1]"r"(fb_1),
+ [efb]"r"((int)this->r.g.echo_feedback));
+ asm volatile (
+ "ssat %[e0], #16, %[e0], asr #14 \r\n"
+ "ssat %[e1], #16, %[e1], asr #14 \r\n"
+ "pkhbt %[e0], %[e0], %[e1], lsl #16 \r\n"
+ "str %[e0], [%[echo_p]] \r\n"
+ : [e0]"+r"(e0), [e1]"+r"(e1)
+ : [echo_p]"r"(echo_ptr));
+ }
+ #else /* ARM_ARCH < 6 */
+ int fb_0 = GET_LE16SA( echo_ptr );
+ int fb_1 = GET_LE16SA( echo_ptr + 2 );
+ int32_t *fir_ptr, *fir_coeff;
+
+ /* Keep last 8 samples */
+
+ /* Apply FIR */
+ asm volatile (
+ "add %[fir_p], %[t_fir_p], #8 \r\n"
+ "bic %[t_fir_p], %[fir_p], %[mask] \r\n"
+ "str %[fb_0], [%[fir_p], #-8] \r\n"
+ "str %[fb_1], [%[fir_p], #-4] \r\n"
+ /* duplicate at +8 eliminates wrap checking below */
+ "str %[fb_0], [%[fir_p], #56] \r\n"
+ "str %[fb_1], [%[fir_p], #60] \r\n"
+ : [fir_p]"=&r"(fir_ptr), [t_fir_p]"+r"(this->fir_ptr)
+ : [fb_0]"r"(fb_0), [fb_1]"r"(fb_1), [mask]"i"(~FIR_BUF_MASK));
+
+ fir_coeff = this->fir_coeff;
+
+ asm volatile (
+ "ldmia %[fir_c]!, { r0-r1 } \r\n"
+ "ldmia %[fir_p]!, { r4-r5 } \r\n"
+ "mul %[fb_0], r0, %[fb_0] \r\n"
+ "mul %[fb_1], r0, %[fb_1] \r\n"
+ "mla %[fb_0], r4, r1, %[fb_0] \r\n"
+ "mla %[fb_1], r5, r1, %[fb_1] \r\n"
+ "ldmia %[fir_c]!, { r0-r1 } \r\n"
+ "ldmia %[fir_p]!, { r2-r5 } \r\n"
+ "mla %[fb_0], r2, r0, %[fb_0] \r\n"
+ "mla %[fb_1], r3, r0, %[fb_1] \r\n"
+ "mla %[fb_0], r4, r1, %[fb_0] \r\n"
+ "mla %[fb_1], r5, r1, %[fb_1] \r\n"
+ "ldmia %[fir_c]!, { r0-r1 } \r\n"
+ "ldmia %[fir_p]!, { r2-r5 } \r\n"
+ "mla %[fb_0], r2, r0, %[fb_0] \r\n"
+ "mla %[fb_1], r3, r0, %[fb_1] \r\n"
+ "mla %[fb_0], r4, r1, %[fb_0] \r\n"
+ "mla %[fb_1], r5, r1, %[fb_1] \r\n"
+ "ldmia %[fir_c]!, { r0-r1 } \r\n"
+ "ldmia %[fir_p]!, { r2-r5 } \r\n"
+ "mla %[fb_0], r2, r0, %[fb_0] \r\n"
+ "mla %[fb_1], r3, r0, %[fb_1] \r\n"
+ "mla %[fb_0], r4, r1, %[fb_0] \r\n"
+ "mla %[fb_1], r5, r1, %[fb_1] \r\n"
+ : [fb_0]"+r"(fb_0), [fb_1]"+r"(fb_1),
+ [fir_p]"+r"(fir_ptr), [fir_c]"+r"(fir_coeff)
+ :
+ : "r0", "r1", "r2", "r3", "r4", "r5");
+
+ /* Generate output */
+ int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0)
+ >> global_muting;
+ int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1)
+ >> global_muting;
+
+ out_buf [ 0] = amp_0;
+ out_buf [WAV_CHUNK_SIZE] = amp_1;
+ out_buf ++;
+
+ if ( !(this->r.g.flags & 0x20) )
+ {
+ /* Feedback into echo buffer */
+ int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14);
+ int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14);
+ e0 = CLAMP16( e0 );
+ SET_LE16A( echo_ptr , e0 );
+ e1 = CLAMP16( e1 );
+ SET_LE16A( echo_ptr + 2, e1 );
+ }
+ #endif /* ARM_ARCH */
+ #else /* Unoptimized CPU */
+ /* Read feedback from echo buffer */
+ int echo_pos = this->echo_pos;
+ uint8_t* const echo_ptr = RAM +
+ ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF);
+ echo_pos += 4;
+ if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 )
+ echo_pos = 0;
+ this->echo_pos = echo_pos;
+ int fb_0 = GET_LE16SA( echo_ptr );
+ int fb_1 = GET_LE16SA( echo_ptr + 2 );
+
+ /* Keep last 8 samples */
+ int (* const fir_ptr) [2] = this->fir_buf + this->fir_pos;
+ this->fir_pos = (this->fir_pos + 1) & (FIR_BUF_HALF - 1);
+ fir_ptr [ 0] [0] = fb_0;
+ fir_ptr [ 0] [1] = fb_1;
+ /* duplicate at +8 eliminates wrap checking below */
+ fir_ptr [FIR_BUF_HALF] [0] = fb_0;
+ fir_ptr [FIR_BUF_HALF] [1] = fb_1;
+
+ /* Apply FIR */
+ fb_0 *= this->fir_coeff [0];
+ fb_1 *= this->fir_coeff [0];
+
+ #define DO_PT( i )\
+ fb_0 += fir_ptr [i] [0] * this->fir_coeff [i];\
+ fb_1 += fir_ptr [i] [1] * this->fir_coeff [i];
+
+ DO_PT( 1 )
+ DO_PT( 2 )
+ DO_PT( 3 )
+ DO_PT( 4 )
+ DO_PT( 5 )
+ DO_PT( 6 )
+ DO_PT( 7 )
+
+ /* Generate output */
+ int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0)
+ >> global_muting;
+ int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1)
+ >> global_muting;
+ out_buf [ 0] = amp_0;
+ out_buf [WAV_CHUNK_SIZE] = amp_1;
+ out_buf ++;
+
+ if ( !(this->r.g.flags & 0x20) )
+ {
+ /* Feedback into echo buffer */
+ int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14);
+ int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14);
+ e0 = CLAMP16( e0 );
+ SET_LE16A( echo_ptr , e0 );
+ e1 = CLAMP16( e1 );
+ SET_LE16A( echo_ptr + 2, e1 );
+ }
+ #endif /* CPU_* */
+ #else /* SPCNOECHO == 1*/
+ /* Generate output */
+ int amp_0 = (chans_0 * global_vol_0) >> global_muting;
+ int amp_1 = (chans_1 * global_vol_1) >> global_muting;
+ out_buf [ 0] = amp_0;
+ out_buf [WAV_CHUNK_SIZE] = amp_1;
+ out_buf ++;
+ #endif /* SPCNOECHO */
+ }
+ while ( --count );
+#if 0
+ EXIT_TIMER(dsp);
+ ENTER_TIMER(cpu);
+#endif
+}
+
+void DSP_reset( struct Spc_Dsp* this )
+{
+ this->keys_down = 0;
+ this->echo_pos = 0;
+ this->noise_count = 0;
+ this->noise = 2;
+
+ this->r.g.flags = 0xE0; /* reset, mute, echo off */
+ this->r.g.key_ons = 0;
+
+ ci->memset( this->voice_state, 0, sizeof this->voice_state );
+
+ int i;
+ for ( i = VOICE_COUNT; --i >= 0; )
+ {
+ struct voice_t* v = this->voice_state + i;
+ v->env_mode = state_release;
+ v->addr = ram.ram;
+ }
+
+ #if SPC_BRRCACHE
+ this->oldsize = 0;
+ for ( i = 0; i < 256; i++ )
+ this->wave_entry [i].start_addr = -1;
+ #endif
+
+#if defined(CPU_COLDFIRE)
+ this->fir_ptr = fir_buf;
+ this->last_fir_ptr = &fir_buf [7];
+ ci->memset( fir_buf, 0, sizeof fir_buf );
+#elif defined (CPU_ARM)
+ this->fir_ptr = fir_buf;
+ ci->memset( fir_buf, 0, sizeof fir_buf );
+#else
+ this->fir_pos = 0;
+ ci->memset( this->fir_buf, 0, sizeof this->fir_buf );
+#endif
+
+ assert( offsetof (struct globals_t,unused9 [2]) == REGISTER_COUNT );
+ assert( sizeof (this->r.voice) == REGISTER_COUNT );
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Adam Gashlin (hcs)
+ * Copyright (C) 2004-2007 Shay Green (blargg)
+ * Copyright (C) 2002 Brad Martin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "codeclib.h"
+#include "spc_codec.h"
+#include "spc_profiler.h"
+
+/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+/* DSP Based on Brad Martin's OpenSPC DSP emulator */
+/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
+
+struct cpu_ram_t ram IBSS_ATTR_SPC_LARGE_IRAM CACHEALIGN_ATTR;
+
+/**************** Timers ****************/
+
+static void Timer_run_( struct Timer* t, long time ) ICODE_ATTR_SPC;
+static void Timer_run_( struct Timer* t, long time )
+{
+ /* when disabled, next_tick should always be in the future */
+ assert( t->enabled );
+
+ int elapsed = ((time - t->next_tick) >> t->shift) + 1;
+ t->next_tick += elapsed << t->shift;
+
+ elapsed += t->count;
+ if ( elapsed >= t->period ) /* avoid unnecessary division */
+ {
+ int n = elapsed / t->period;
+ elapsed -= n * t->period;
+ t->counter = (t->counter + n) & 15;
+ }
+ t->count = elapsed;
+}
+
+static inline void Timer_run( struct Timer* t, long time )
+{
+ if ( time >= t->next_tick )
+ Timer_run_( t, time );
+}
+
+/**************** SPC emulator ****************/
+/* 1.024 MHz clock / 32000 samples per second */
+
+static void SPC_enable_rom( THIS, int enable )
+{
+ if ( this->rom_enabled != enable )
+ {
+ this->rom_enabled = enable;
+ ci->memcpy( RAM + ROM_ADDR, (enable ? this->boot_rom : this->extra_ram), ROM_SIZE );
+ /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
+ }
+}
+
+void SPC_Init( THIS )
+{
+ this->timer [0].shift = 4 + 3; /* 8 kHz */
+ this->timer [1].shift = 4 + 3; /* 8 kHz */
+ this->timer [2].shift = 4; /* 8 kHz */
+
+ /* Put STOP instruction around memory to catch PC underflow/overflow. */
+ ci->memset( ram.padding1, 0xFF, sizeof ram.padding1 );
+ ci->memset( ram.padding2, 0xFF, sizeof ram.padding2 );
+
+ /* A few tracks read from the last four bytes of IPL ROM */
+ this->boot_rom [sizeof this->boot_rom - 2] = 0xC0;
+ this->boot_rom [sizeof this->boot_rom - 1] = 0xFF;
+ ci->memset( this->boot_rom, 0, sizeof this->boot_rom - 2 );
+
+ /* Have DSP in a defined state in case EMU is run and hasn't loaded
+ * a program yet */
+ DSP_reset(&this->dsp);
+}
+
+static void SPC_load_state( THIS, struct cpu_regs_t const* cpu_state,
+ const void* new_ram, const void* dsp_state )
+{
+ ci->memcpy(&(this->r),cpu_state,sizeof this->r);
+
+ /* ram */
+ ci->memcpy( RAM, new_ram, sizeof RAM );
+ ci->memcpy( this->extra_ram, RAM + ROM_ADDR, sizeof this->extra_ram );
+
+ /* boot rom (have to force enable_rom() to update it) */
+ this->rom_enabled = !(RAM [0xF1] & 0x80);
+ SPC_enable_rom( this, !this->rom_enabled );
+
+ /* dsp */
+ /* some SPCs rely on DSP immediately generating one sample */
+ this->extra_cycles = 32;
+ DSP_reset( &this->dsp );
+ int i;
+ for ( i = 0; i < REGISTER_COUNT; i++ )
+ DSP_write( &this->dsp, i, ((uint8_t const*) dsp_state) [i] );
+
+ /* timers */
+ for ( i = 0; i < TIMER_COUNT; i++ )
+ {
+ struct Timer* t = &this->timer [i];
+
+ t->next_tick = -EXTRA_CLOCKS;
+ t->enabled = (RAM [0xF1] >> i) & 1;
+ if ( !t->enabled )
+ t->next_tick = TIMER_DISABLED_TIME;
+ t->count = 0;
+ t->counter = RAM [0xFD + i] & 15;
+
+ int p = RAM [0xFA + i];
+ if ( !p )
+ p = 0x100;
+ t->period = p;
+ }
+
+ /* Handle registers which already give 0 when read by
+ setting RAM and not changing it.
+ Put STOP instruction in registers which can be read,
+ to catch attempted execution. */
+ RAM [0xF0] = 0;
+ RAM [0xF1] = 0;
+ RAM [0xF3] = 0xFF;
+ RAM [0xFA] = 0;
+ RAM [0xFB] = 0;
+ RAM [0xFC] = 0;
+ RAM [0xFD] = 0xFF;
+ RAM [0xFE] = 0xFF;
+ RAM [0xFF] = 0xFF;
+}
+
+static void clear_echo( THIS )
+{
+ if ( !(DSP_read( &this->dsp, 0x6C ) & 0x20) )
+ {
+ unsigned addr = 0x100 * DSP_read( &this->dsp, 0x6D );
+ size_t size = 0x800 * DSP_read( &this->dsp, 0x7D );
+ size_t max_size = sizeof RAM - addr;
+ if ( size > max_size )
+ size = sizeof RAM - addr;
+ ci->memset( RAM + addr, 0xFF, size );
+ }
+}
+
+int SPC_load_spc( THIS, const void* data, long size )
+{
+ struct spc_file_t const* spc = (struct spc_file_t const*) data;
+ struct cpu_regs_t regs;
+
+ if ( size < SPC_FILE_SIZE )
+ return -1;
+
+ if ( ci->memcmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
+ return -1;
+
+ regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
+ regs.a = spc->a;
+ regs.x = spc->x;
+ regs.y = spc->y;
+ regs.status = spc->status;
+ regs.sp = spc->sp;
+
+ if ( (unsigned long) size >= sizeof *spc )
+ ci->memcpy( this->boot_rom, spc->ipl_rom, sizeof this->boot_rom );
+
+ SPC_load_state( this, &regs, spc->ram, spc->dsp );
+
+ clear_echo(this);
+
+ return 0;
+}
+
+/**************** DSP interaction ****************/
+static void SPC_run_dsp_( THIS, long time ) ICODE_ATTR_SPC;
+static void SPC_run_dsp_( THIS, long time )
+{
+ /* divide by CLOCKS_PER_SAMPLE */
+ int count = ((time - this->next_dsp) >> 5) + 1;
+ int32_t* buf = this->sample_buf;
+ this->sample_buf = buf + count;
+ this->next_dsp += count * CLOCKS_PER_SAMPLE;
+ DSP_run( &this->dsp, count, buf );
+}
+
+static inline void SPC_run_dsp( THIS, long time )
+{
+ if ( time >= this->next_dsp )
+ SPC_run_dsp_( this, time );
+}
+
+int SPC_read( THIS, unsigned addr, long const time )
+{
+ int result = RAM [addr];
+
+ if ( ((unsigned) (addr - 0xF0)) < 0x10 )
+ {
+ assert( 0xF0 <= addr && addr <= 0xFF );
+
+ /* counters */
+ int i = addr - 0xFD;
+ if ( i >= 0 )
+ {
+ struct Timer* t = &this->timer [i];
+ Timer_run( t, time );
+ result = t->counter;
+ t->counter = 0;
+ }
+ /* dsp */
+ else if ( addr == 0xF3 )
+ {
+ SPC_run_dsp( this, time );
+ result = DSP_read( &this->dsp, RAM [0xF2] & 0x7F );
+ }
+ }
+ return result;
+}
+
+void SPC_write( THIS, unsigned addr, int data, long const time )
+{
+ /* first page is very common */
+ if ( addr < 0xF0 )
+ {
+ RAM [addr] = (uint8_t) data;
+ }
+ else switch ( addr )
+ {
+ /* RAM */
+ default:
+ if ( addr < ROM_ADDR )
+ {
+ RAM [addr] = (uint8_t) data;
+ }
+ else
+ {
+ this->extra_ram [addr - ROM_ADDR] = (uint8_t) data;
+ if ( !this->rom_enabled )
+ RAM [addr] = (uint8_t) data;
+ }
+ break;
+
+ /* DSP */
+ /*case 0xF2:*/ /* mapped to RAM */
+ case 0xF3: {
+ SPC_run_dsp( this, time );
+ int reg = RAM [0xF2];
+ if ( reg < REGISTER_COUNT ) {
+ DSP_write( &this->dsp, reg, data );
+ }
+ else {
+ /*dprintf( "DSP write to $%02X\n", (int) reg ); */
+ }
+ break;
+ }
+
+ case 0xF0: /* Test register */
+ /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
+ break;
+
+ /* Config */
+ case 0xF1:
+ {
+ int i;
+ /* timers */
+ for ( i = 0; i < TIMER_COUNT; i++ )
+ {
+ struct Timer * t = this->timer+i;
+ if ( !(data & (1 << i)) )
+ {
+ t->enabled = 0;
+ t->next_tick = TIMER_DISABLED_TIME;
+ }
+ else if ( !t->enabled )
+ {
+ /* just enabled */
+ t->enabled = 1;
+ t->counter = 0;
+ t->count = 0;
+ t->next_tick = time;
+ }
+ }
+
+ /* port clears */
+ if ( data & 0x10 )
+ {
+ RAM [0xF4] = 0;
+ RAM [0xF5] = 0;
+ }
+ if ( data & 0x20 )
+ {
+ RAM [0xF6] = 0;
+ RAM [0xF7] = 0;
+ }
+
+ SPC_enable_rom( this, (data & 0x80) != 0 );
+ break;
+ }
+
+ /* Ports */
+ case 0xF4:
+ case 0xF5:
+ case 0xF6:
+ case 0xF7:
+ /* to do: handle output ports */
+ break;
+
+ /* verified on SNES that these are read/write (RAM) */
+ /*case 0xF8: */
+ /*case 0xF9: */
+
+ /* Timers */
+ case 0xFA:
+ case 0xFB:
+ case 0xFC: {
+ int i = addr - 0xFA;
+ struct Timer* t = &this->timer [i];
+ if ( (t->period & 0xFF) != data )
+ {
+ Timer_run( t, time );
+ this->timer[i].period = data ? data : 0x100;
+ }
+ break;
+ }
+
+ /* Counters (cleared on write) */
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
+ this->timer [addr - 0xFD].counter = 0;
+ break;
+ }
+}
+
+/**************** Sample generation ****************/
+int SPC_play( THIS, long count, int32_t* out )
+{
+ int i;
+ assert( count % 2 == 0 ); /* output is always in pairs of samples */
+
+ long start_time = -(count >> 1) * CLOCKS_PER_SAMPLE - EXTRA_CLOCKS;
+
+ /* DSP output is made on-the-fly when DSP registers are read or written */
+ this->sample_buf = out;
+ this->next_dsp = start_time + CLOCKS_PER_SAMPLE;
+
+ /* Localize timer next_tick times and run them to the present to prevent
+ a running but ignored timer's next_tick from getting too far behind
+ and overflowing. */
+ for ( i = 0; i < TIMER_COUNT; i++ )
+ {
+ struct Timer* t = &this->timer [i];
+ if ( t->enabled )
+ {
+ t->next_tick += start_time + EXTRA_CLOCKS;
+ Timer_run( t, start_time );
+ }
+ }
+
+ /* Run from start_time to 0, pre-advancing by extra cycles from last run */
+ this->extra_cycles = CPU_run( this, start_time + this->extra_cycles ) +
+ EXTRA_CLOCKS;
+ if ( this->extra_cycles < 0 )
+ {
+ /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
+ (int) CPU_read( r.pc ), (unsigned) r.pc ); */
+
+ return -1;
+ }
+
+ /* Catch DSP up to present */
+#if 0
+ ENTER_TIMER(cpu);
+#endif
+ SPC_run_dsp( this, -EXTRA_CLOCKS );
+#if 0
+ EXIT_TIMER(cpu);
+#endif
+ assert( this->next_dsp == CLOCKS_PER_SAMPLE - EXTRA_CLOCKS );
+ assert( this->sample_buf - out == count );
+
+ return 0;
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Adam Gashlin (hcs)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+/* DSP Based on Brad Martin's OpenSPC DSP emulator */
+/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
+
+#if defined(SPC_PROFILE) && defined(USEC_TIMER)
+
+#include "codeclib.h"
+#include "spc_codec.h"
+#define SPC_DEFINE_PROFILER_TIMERS
+#include "spc_profiler.h"
+
+void reset_profile_timers(void)
+{
+ RESET_TIMER(total);
+ RESET_TIMER(render);
+#if 0
+ RESET_TIMER(cpu);
+ RESET_TIMER(dsp);
+ RESET_TIMER(dsp_pregen);
+ RESET_TIMER(dsp_gen);
+ RESET_TIMER(dsp_mix);
+#endif
+}
+
+void print_timers(char * path)
+{
+ int logfd = ci->open("/spclog.txt",O_WRONLY|O_CREAT|O_APPEND, 0666);
+ ci->fdprintf(logfd,"%s:\t",path);
+ ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total));
+ PRINT_TIMER_PCT(render,total,"render");
+#if 0
+ PRINT_TIMER_PCT(cpu,total,"CPU");
+ PRINT_TIMER_PCT(dsp,total,"DSP");
+ ci->fdprintf(logfd,"(");
+ PRINT_TIMER_PCT(dsp_pregen,dsp,"pregen");
+ PRINT_TIMER_PCT(dsp_gen,dsp,"gen");
+ PRINT_TIMER_PCT(dsp_mix,dsp,"mix");
+#endif
+ ci->fdprintf(logfd,"\n");
+
+ ci->close(logfd);
+ logfd=-1;
+}
+
+#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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Adam Gashlin (hcs)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* a fun simple elapsed time profiler */
+#ifndef _SPC_PROFILER_H_
+#define _SPC_PROFILER_H_
+
+#if defined(SPC_PROFILE) && defined(USEC_TIMER)
+
+#ifdef SPC_DEFINE_PROFILER_TIMERS
+#define CREATE_TIMER(name) uint32_t spc_timer_##name##_start,\
+ spc_timer_##name##_total
+#else
+#define CREATE_TIMER(name) extern uint32_t spc_timer_##name##_start,\
+ spc_timer_##name##_total
+#endif
+
+#define ENTER_TIMER(name) spc_timer_##name##_start=USEC_TIMER
+#define EXIT_TIMER(name) spc_timer_##name##_total+=\
+ (USEC_TIMER-spc_timer_##name##_start)
+#define READ_TIMER(name) (spc_timer_##name##_total)
+#define RESET_TIMER(name) spc_timer_##name##_total=0
+
+#define PRINT_TIMER_PCT(bname,tname,nstr) ci->fdprintf( \
+ logfd,"%10ld ",READ_TIMER(bname));\
+ ci->fdprintf(logfd,"(%3d%%) " nstr "\t",\
+ ((uint64_t)READ_TIMER(bname))*100/READ_TIMER(tname))
+
+CREATE_TIMER(total);
+CREATE_TIMER(render);
+#if 0
+CREATE_TIMER(cpu);
+CREATE_TIMER(dsp);
+CREATE_TIMER(dsp_pregen);
+CREATE_TIMER(dsp_gen);
+CREATE_TIMER(dsp_mix);
+#endif
+
+void reset_profile_timers(void);
+void print_timers(char * path);
+
+#else
+
+#define CREATE_TIMER(name)
+#define ENTER_TIMER(name)
+#define EXIT_TIMER(name)
+#define READ_TIMER(name)
+#define RESET_TIMER(name)
+#define print_timers(path)
+#define reset_profile_timers()
+
+#endif
+
+#endif /* _SPC_PROFILER_H_ */