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 | |
| 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')
| -rw-r--r-- | apps/codecs/spc/Makefile | 44 | ||||
| -rw-r--r-- | apps/codecs/spc/SOURCES | 4 | ||||
| -rw-r--r-- | apps/codecs/spc/spc_cpu.c (renamed from apps/codecs/spc/Spc_Cpu.h) | 21 | ||||
| -rw-r--r-- | apps/codecs/spc/spc_dsp.c (renamed from apps/codecs/spc/Spc_Dsp.h) | 283 | ||||
| -rw-r--r-- | apps/codecs/spc/spc_emu.c | 378 | ||||
| -rw-r--r-- | apps/codecs/spc/spc_profiler.c | 64 | ||||
| -rw-r--r-- | apps/codecs/spc/spc_profiler.h | 47 |
7 files changed, 586 insertions, 255 deletions
diff --git a/apps/codecs/spc/Makefile b/apps/codecs/spc/Makefile new file mode 100644 index 0000000..8929149 --- /dev/null +++ b/apps/codecs/spc/Makefile @@ -0,0 +1,44 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +INCLUDES=-I$(APPSDIR) -I.. -I. -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ + -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(BUILDDIR) + +ifdef APPEXTRA + INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) +endif + +SPCOPTS = -O -DROCKBOX + +CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET_INC) $(SPCOPTS) $(TARGET) \ +$(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} $(PROFILE_OPTS) -DCODEC=1 + +# This sets up 'SRC' based on the files mentioned in SOURCES +include $(TOOLSDIR)/makesrc.inc + +SOURCES = $(SRC) +OBJS2 := $(SRC:%.c=$(OBJDIR)/%.o) +OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2)) +DEPFILE = $(OBJDIR)/dep-spc +DIRS = + +all: $(OUTPUT) + +$(OUTPUT): $(OBJS) + $(call PRINTS,AR+RANLIB $(@F))$(AR) ruv $@ $+ >/dev/null 2>&1 + $(SILENT)$(RANLIB) $@ + +include $(TOOLSDIR)/make.inc + +clean: + $(call PRINTS,cleaning spc)rm -f $(OBJS) $(OUTPUT) $(DEPFILE) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPFILE) +endif diff --git a/apps/codecs/spc/SOURCES b/apps/codecs/spc/SOURCES new file mode 100644 index 0000000..901232a --- /dev/null +++ b/apps/codecs/spc/SOURCES @@ -0,0 +1,4 @@ +spc_cpu.c +spc_dsp.c +spc_emu.c +spc_profiler.c diff --git a/apps/codecs/spc/Spc_Cpu.h b/apps/codecs/spc/spc_cpu.c index b931ca2..3b7c129 100644 --- a/apps/codecs/spc/Spc_Cpu.h +++ b/apps/codecs/spc/spc_cpu.c @@ -5,6 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ + * $Id$ * * Copyright (C) 2006-2007 Adam Gashlin (hcs) * Copyright (C) 2004-2007 Shay Green (blargg) @@ -17,9 +18,15 @@ * KIND, either express or implied. * ****************************************************************************/ - - + /* The CPU portion (shock!) */ +#include "codec.h" +#include "codecs.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_ )) @@ -103,9 +110,7 @@ enum { st_c = 0x01 }; #define SET_SP( v ) (sp = RAM + 0x101 + (v)) #define GET_SP() (sp - 0x101 - RAM) -static long CPU_run( THIS, long start_time ) ICODE_ATTR; - -static long CPU_run( THIS, long start_time ) +long CPU_run( THIS, long start_time ) { #if 0 ENTER_TIMER(cpu); @@ -1035,3 +1040,9 @@ out_of_time: #endif return spc_time_; } + +void CPU_Init( THIS ) +{ + ci->memcpy( this->cycle_table, cycle_table, sizeof cycle_table ); +} + diff --git a/apps/codecs/spc/Spc_Dsp.h b/apps/codecs/spc/spc_dsp.c index d670b20..6a8aaeb 100644 --- a/apps/codecs/spc/Spc_Dsp.h +++ b/apps/codecs/spc/spc_dsp.c @@ -20,196 +20,24 @@ ****************************************************************************/ /* The DSP portion (awe!) */ +#include "codec.h" +#include "codecs.h" +#include "spc_codec.h" +#include "spc_profiler.h" -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 }; - -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 */ +#ifdef CPU_COLDFIRE +static int32_t fir_buf[FIR_BUF_HALF] + __attribute__ ((aligned (FIR_BUF_SIZE*2))) IBSS_ATTR; #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 */ -static int16_t BRRcache [0x20000 + 32]; -#endif - -enum { fir_buf_half = 8 }; - -#ifdef 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_size = fir_buf_half * sizeof ( int32_t ), - fir_buf_mask = ~fir_buf_size -}; -int32_t fir_buf[fir_buf_half] - __attribute__ ((aligned (fir_buf_size*2))) IBSS_ATTR; -#endif /* CPU_COLDFIRE */ - -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 */ - -#ifdef CPU_COLDFIRE - /* circularly hardware masked address */ - 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]; -#else - /* 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 -}; - -struct src_dir -{ - char start [2]; - char loop [2]; -}; - -static 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; - - 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 - -#ifdef CPU_COLDFIRE - this->fir_ptr = fir_buf; - this->last_fir_ptr = &fir_buf [7]; - memset( fir_buf, 0, sizeof fir_buf ); -#else - this->fir_pos = 0; - memset( this->fir_buf, 0, sizeof this->fir_buf ); +int16_t BRRcache [0x20000 + 32]; #endif - assert( offsetof (struct globals_t,unused9 [2]) == register_count ); - assert( sizeof (this->r.voice) == register_count ); -} - -static void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR; -static void DSP_write( struct Spc_Dsp* this, int i, int data ) +void DSP_write( struct Spc_Dsp* this, int i, int data ) { - assert( (unsigned) i < register_count ); + assert( (unsigned) i < REGISTER_COUNT ); this->r.reg [i] = data; int high = i >> 4; @@ -228,12 +56,6 @@ static void DSP_write( struct Spc_Dsp* this, int i, int data ) } } -static inline int DSP_read( struct Spc_Dsp* this, int i ) -{ - assert( (unsigned) i < register_count ); - return this->r.reg [i]; -} - /* if ( n < -32768 ) out = -32768; */ /* if ( n > 32767 ) out = 32767; */ #define CLAMP16( n, out )\ @@ -321,8 +143,8 @@ static void decode_brr( struct Spc_Dsp* this, unsigned start_addr, int const left_shift = left_shifts [scale]; /* output position */ - out += brr_block_size; - int offset = -brr_block_size << 2; + out += BRR_BLOCK_SIZE; + int offset = -BRR_BLOCK_SIZE << 2; do /* decode and filter 16 samples */ { @@ -432,10 +254,10 @@ static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice, { voice->addr = RAM + start_addr; /* BRR filter uses previous samples */ - voice->samples [brr_block_size + 1] = 0; - voice->samples [brr_block_size + 2] = 0; + 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->position = (BRR_BLOCK_SIZE + 3) * 0x1000 - 1; voice->block_header = 0; /* "previous" BRR header */ } #else @@ -460,9 +282,7 @@ static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice, } } -static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) - ICODE_ATTR; -static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) +void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) { #undef RAM #ifdef CPU_ARM @@ -516,7 +336,7 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) #ifdef ROCKBOX_BIG_ENDIAN /* Convert endiannesses before entering loops - these get used alot */ - const uint32_t rates[voice_count] = + const uint32_t rates[VOICE_COUNT] = { GET_LE16A( this->r.voice[0].rate ) & 0x3FFF, GET_LE16A( this->r.voice[1].rate ) & 0x3FFF, @@ -531,7 +351,7 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) #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)); + 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 */ @@ -757,9 +577,9 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) #endif #if !SPC_BRRCACHE /* Decode BRR block */ - if ( voice->position >= brr_block_size * 0x1000 ) + if ( voice->position >= BRR_BLOCK_SIZE * 0x1000 ) { - voice->position -= brr_block_size * 0x1000; + voice->position -= BRR_BLOCK_SIZE * 0x1000; uint8_t const* addr = voice->addr; if ( addr >= RAM + 0x10000 ) @@ -805,13 +625,13 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) int const left_shift = left_shifts [scale]; /* 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]; + 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; + 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) */ @@ -820,9 +640,9 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) /* arrange for last 9 samples to be skipped */ int const skip = 9; out += (skip & 1); - voice->samples [skip] = voice->samples [brr_block_size]; + voice->samples [skip] = voice->samples [BRR_BLOCK_SIZE]; voice->position += skip * 0x1000; - offset = (-brr_block_size + (skip & ~1)) << 2; + offset = (-BRR_BLOCK_SIZE + (skip & ~1)) << 2; addr -= skip / 2; /* force sample to end on next decode */ voice->block_header = 1; @@ -1026,7 +846,7 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) "asr.l %[sh], %[output] \r\n" "mac.l %[vvol_0], %[output], %%acc0 \r\n" "mac.l %[vvol_1], %[output], %%acc1 \r\n" - : [output]"=&r"(amp_0) + : [output]"=&d"(amp_0) : [vvol_0]"r"((int)voice->volume[0]), [vvol_1]"r"((int)voice->volume[1]), [sh]"d"(11) @@ -1252,12 +1072,12 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) /* 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); + 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; + fir_ptr [FIR_BUF_HALF] [0] = fb_0; + fir_ptr [FIR_BUF_HALF] [1] = fb_1; /* Apply FIR */ fb_0 *= this->fir_coeff [0]; @@ -1311,12 +1131,41 @@ static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) #endif } -static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out ) +void DSP_reset( struct Spc_Dsp* this ) { - /* 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 ); + this->keys_down = 0; + this->echo_pos = 0; + this->noise_count = 0; + this->noise = 2; - DSP_run_( this, count, out ); + 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 + +#ifdef CPU_COLDFIRE + this->fir_ptr = fir_buf; + this->last_fir_ptr = &fir_buf [7]; + 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/apps/codecs/spc/spc_emu.c b/apps/codecs/spc/spc_emu.c new file mode 100644 index 0000000..30aaf5d --- /dev/null +++ b/apps/codecs/spc/spc_emu.c @@ -0,0 +1,378 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Adam Gashlin (hcs) + * Copyright (C) 2004-2007 Shay Green (blargg) + * Copyright (C) 2002 Brad Martin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "codec.h" +#include "codecs.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; + +/**************** Timers ****************/ + +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; +} + +/**************** 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 ); +} + +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, ®s, spc->ram, spc->dsp ); + + clear_echo(this); + + return 0; +} + +/**************** DSP interaction ****************/ +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 ); +} + +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/apps/codecs/spc/spc_profiler.c b/apps/codecs/spc/spc_profiler.c new file mode 100644 index 0000000..60e0ef7 --- /dev/null +++ b/apps/codecs/spc/spc_profiler.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Adam Gashlin (hcs) + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * 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 "codec.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); + 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/apps/codecs/spc/spc_profiler.h b/apps/codecs/spc/spc_profiler.h index 99d3fdf..330d95b 100644 --- a/apps/codecs/spc/spc_profiler.h +++ b/apps/codecs/spc/spc_profiler.h @@ -5,6 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ + * $Id$ * * Copyright (C) 2006-2007 Adam Gashlin (hcs) * @@ -17,11 +18,19 @@ ****************************************************************************/ /* a fun simple elapsed time profiler */ +#ifndef _SPC_PROFILER_H_ +#define _SPC_PROFILER_H_ #if defined(SPC_PROFILE) && defined(USEC_TIMER) -#define CREATE_TIMER(name) static uint32_t spc_timer_##name##_start,\ +#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) @@ -43,38 +52,8 @@ CREATE_TIMER(dsp_gen); CREATE_TIMER(dsp_mix); #endif -static 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 -} - -static int logfd=-1; - -static void print_timers(char * path) { - logfd = ci->open("/spclog.txt",O_WRONLY|O_CREAT|O_APPEND); - 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; -} +void reset_profile_timers(void); +void print_timers(char * path); #else @@ -87,3 +66,5 @@ static void print_timers(char * path) { #define reset_profile_timers() #endif + +#endif /* _SPC_PROFILER_H_ */ |