diff options
| author | Michael Sevakis <jethead71@rockbox.org> | 2007-07-16 21:03:25 +0000 |
|---|---|---|
| committer | Michael Sevakis <jethead71@rockbox.org> | 2007-07-16 21:03:25 +0000 |
| commit | 8552eff9e46ede8968ea13cc172e1c61856bee18 (patch) | |
| tree | 7649c84039f0ee4801d56b7b9b38966c3731f79a /apps/codecs/spc.c | |
| parent | 76fa0f7e30398e01d405b4391c30b99dca4c9288 (diff) | |
| download | rockbox-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.c | 567 |
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, ®s, 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 */ |