summaryrefslogtreecommitdiff
path: root/apps/codecs/spc.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-07-16 21:03:25 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-07-16 21:03:25 +0000
commit8552eff9e46ede8968ea13cc172e1c61856bee18 (patch)
tree7649c84039f0ee4801d56b7b9b38966c3731f79a /apps/codecs/spc.c
parent76fa0f7e30398e01d405b4391c30b99dca4c9288 (diff)
downloadrockbox-8552eff9e46ede8968ea13cc172e1c61856bee18.zip
rockbox-8552eff9e46ede8968ea13cc172e1c61856bee18.tar.gz
rockbox-8552eff9e46ede8968ea13cc172e1c61856bee18.tar.bz2
rockbox-8552eff9e46ede8968ea13cc172e1c61856bee18.tar.xz
Make the SPC codec run like it used to on Coldfire before -Os crushed it. Build as a lib using the old -O option. Should not impact ARM targets.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13919 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/spc.c')
-rw-r--r--apps/codecs/spc.c567
1 files changed, 12 insertions, 555 deletions
diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c
index 6c791b4..f2890cd 100644
--- a/apps/codecs/spc.c
+++ b/apps/codecs/spc.c
@@ -5,6 +5,7 @@
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
+ * $Id$
*
* Copyright (C) 2006-2007 Adam Gashlin (hcs)
* Copyright (C) 2004-2007 Shay Green (blargg)
@@ -21,555 +22,11 @@
/* 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 */
-
#include "codeclib.h"
-#include "inttypes.h"
-#include "system.h"
-
-/* rather than comment out asserts, just define NDEBUG */
-#define NDEBUG
-#include <assert.h>
-#undef check
-#define check assert
-
-CODEC_HEADER
-
-#ifdef CPU_ARM
- #undef ICODE_ATTR
- #define ICODE_ATTR
-
- #undef IDATA_ATTR
- #define IDATA_ATTR
-#endif
-
-/* TGB is the only target fast enough for gaussian and realtime BRR decode */
-/* echo is almost fast enough but not quite */
-#ifndef TOSHIBA_GIGABEAT_F
- /* Cache BRR waves */
- #define SPC_BRRCACHE 1
-
- /* Disable gaussian interpolation */
- #define SPC_NOINTERP 1
-
-#ifndef CPU_COLDFIRE
- /* Disable echo processing */
- #define SPC_NOECHO 1
-#else
- /* Enable echo processing */
- #define SPC_NOECHO 0
-#endif
-#else
- /* Don't cache BRR waves */
- #define SPC_BRRCACHE 0
-
- /* Allow gaussian interpolation */
- #define SPC_NOINTERP 0
-
- /* Allow echo processing */
- #define SPC_NOECHO 0
-#endif
-
-/* Samples per channel per iteration */
-#ifdef CPU_COLDFIRE
-#define WAV_CHUNK_SIZE 1024
-#else
-#define WAV_CHUNK_SIZE 2048
-#endif
-
-/* simple profiling with USEC_TIMER */
-/*#define SPC_PROFILE*/
-
+#include "spc/spc_codec.h"
#include "spc/spc_profiler.h"
-#define THIS struct Spc_Emu* const this
-
-/**************** 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 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_LE16A( addr ) (*(uint16_t*) (addr))
- #define GET_LE16SA( addr ) (*( int16_t*) (addr))
- #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data))
-#else
- #define GET_LE16A( addr ) get_le16 ( addr )
- #define GET_LE16SA( addr ) get_le16s( addr )
- #define SET_LE16A( addr, data ) set_le16 ( addr, data )
-#endif
-
-static struct
-{
- union {
- uint8_t padding1 [0x100];
- uint16_t align;
- } padding1 [1];
- uint8_t ram [0x10000];
- uint8_t padding2 [0x100];
-} ram;
-
-#include "spc/Spc_Dsp.h"
-
-#undef RAM
-#define RAM ram.ram
-
-/**************** Timers ****************/
-
-enum { timer_count = 3 };
-
-struct Timer
-{
- long next_tick;
- int period;
- int count;
- int shift;
- int enabled;
- int counter;
-};
-
-static void Timer_run_( struct Timer* t, long time ) ICODE_ATTR;
-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 */
-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 };
-
-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 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];
-};
-
-static void SPC_enable_rom( THIS, int enable )
-{
- if ( this->rom_enabled != enable )
- {
- this->rom_enabled = enable;
- 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 */
- }
-}
-
-static 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. */
- memset( ram.padding1, 0xFF, sizeof ram.padding1 );
- 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;
- memset( this->boot_rom, 0, sizeof this->boot_rom - 2 );
-}
-
-static void SPC_load_state( THIS, struct cpu_regs_t const* cpu_state,
- const void* new_ram, const void* dsp_state )
-{
- memcpy(&(this->r),cpu_state,sizeof this->r);
-
- /* ram */
- memcpy( RAM, new_ram, sizeof RAM );
- 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;
- memset( RAM + addr, 0xFF, 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];
-};
-
-static 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 ( 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 )
- 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;
-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 );
-}
-
-static int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR;
-static 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;
-}
-
-static void SPC_write( THIS, unsigned addr, int data, long const time )
- ICODE_ATTR;
-static 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;
- }
-}
-
-#include "spc/Spc_Cpu.h"
-
-/**************** Sample generation ****************/
-
-static int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR;
-static 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;
-}
+CODEC_HEADER
/**************** ID666 parsing ****************/
@@ -733,15 +190,15 @@ static int32_t samples[WAV_CHUNK_SIZE*2] IBSS_ATTR;
static struct Spc_Emu spc_emu IDATA_ATTR;
-enum {sample_rate = 32000};
+enum {SAMPLE_RATE = 32000};
/* The main decoder loop */
static int play_track( void )
{
int sampleswritten=0;
- unsigned long fadestartsample = ID666.length*(long long) sample_rate/1000;
- unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) sample_rate/1000;
+ unsigned long fadestartsample = ID666.length*(long long) SAMPLE_RATE/1000;
+ unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000;
int fadedec = 0;
int fadevol = 0x7fffffffl;
@@ -758,7 +215,7 @@ static int play_track( void )
}
if (ci->seek_time) {
- int curtime = sampleswritten*1000LL/sample_rate;
+ int curtime = sampleswritten*1000LL/SAMPLE_RATE;
DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime);
if (ci->seek_time < curtime) {
DEBUGF("seek backwards = reset\n");
@@ -778,7 +235,7 @@ static int play_track( void )
/* is track timed? */
if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) {
- unsigned long curtime = sampleswritten*1000LL/sample_rate;
+ unsigned long curtime = sampleswritten*1000LL/SAMPLE_RATE;
unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE);
/* fade? */
@@ -811,7 +268,7 @@ static int play_track( void )
ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
if (ci->global_settings->repeat_mode!=REPEAT_ONE)
- ci->set_elapsed(sampleswritten*1000LL/sample_rate);
+ ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE);
else
ci->set_elapsed(0);
}
@@ -824,11 +281,11 @@ static int play_track( void )
/* this is the codec entry point */
enum codec_status codec_main(void)
{
- memcpy( spc_emu.cycle_table, cycle_table, sizeof cycle_table );
-
#ifdef CPU_COLDFIRE
+ /* signed integer mode with saturation */
coldfire_set_macsr(EMAC_SATURATE);
#endif
+ CPU_Init(&spc_emu);
do
{
@@ -839,7 +296,7 @@ enum codec_status codec_main(void)
DEBUGF("SPC: after init\n");
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
- ci->configure(DSP_SET_FREQUENCY, sample_rate);
+ ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
/* wait for track info to load */