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 | |
parent | 76fa0f7e30398e01d405b4391c30b99dca4c9288 (diff) |
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')
-rw-r--r-- | apps/codecs/Makefile | 15 | ||||
-rw-r--r-- | apps/codecs/spc.c | 567 | ||||
-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 |
9 files changed, 609 insertions, 814 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile index 44caacf780..2c0a18295e 100644 --- a/apps/codecs/Makefile +++ b/apps/codecs/Makefile @@ -17,7 +17,7 @@ ifdef APPEXTRA endif ifdef SOFTWARECODECS - CODECLIBS = -lmad -la52 -lffmpegFLAC -lTremor -lwavpack -lmusepack -lalac -lfaad -lm4a -lspeex -ldemac -lwma + CODECLIBS = -lspc -lmad -la52 -lffmpegFLAC -lTremor -lwavpack -lmusepack -lalac -lfaad -lm4a -lspeex -ldemac -lwma endif # we "borrow" the plugin LDS file @@ -39,7 +39,7 @@ DIRS = . CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a -.PHONY: libmad liba52 libffmpegFLAC libTremor libspeex libwavpack libmusepack libalac libfaad libm4a libdemac libwma +.PHONY: libspc libmad liba52 libffmpegFLAC libTremor libspeex libwavpack libmusepack libalac libfaad libm4a libdemac libwma OUTPUT = $(SOFTWARECODECS) @@ -52,7 +52,7 @@ $(OBJDIR)/wav.elf : $(OBJDIR)/wav.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/sid.elf : $(OBJDIR)/sid.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/nsf.elf : $(OBJDIR)/nsf.o $(OBJDIR)/codec_crt0.o -$(OBJDIR)/spc.elf : $(OBJDIR)/spc.o $(OBJDIR)/codec_crt0.o +$(OBJDIR)/spc.elf : $(OBJDIR)/spc.o $(BUILDDIR)/libspc.a $(OBJDIR)/codec_crt0.o $(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a $(OBJDIR)/codec_crt0.o $(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a $(OBJDIR)/codec_crt0.o @@ -140,6 +140,12 @@ $(BUILDDIR)/libcodec.a: $(LINKCODEC): $(LDS) $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) -DCODEC $(INCLUDES) $(TARGET) $(DEFINES) -E -P - >$@ +$(BUILDDIR)/libspc.a: libspc + +libspc: + $(SILENT)mkdir -p $(OBJDIR)/spc + $(call PRINTS,MAKE in spc)$(MAKE) -C spc OBJDIR=$(OBJDIR)/spc OUTPUT=$(BUILDDIR)/libspc.a + $(BUILDDIR)/libmad.a: libmad libmad: @@ -213,7 +219,8 @@ libdemac: $(call PRINTS,MAKE in libdemac)$(MAKE) -C demac/libdemac OBJDIR=$(OBJDIR)/libdemac OUTPUT=$(BUILDDIR)/libdemac.a clean: - $(call PRINTS,cleaning codecs)rm -fr $(OBJDIR)/libmad $(BUILDDIR)/libmad.a $(OBJDIR)/liba52 $(BUILDDIR)/liba52.a $(OBJDIR)/libffmpegFLAC $(BUILDDIR)/libffmpegFLAC.a $(OBJDIR)/Tremor $(BUILDDIR)/libTremor.a $(OBJDIR)/libspeex $(BUILDDIR)/libSpeex.a $(OBJDIR)/libwavpack $(BUILDDIR)/libwavpack.a $(OBJDIR)/libmusepack $(BUILDDIR)/libmusepack.a $(OBJDIR)/libalac $(BUILDDIR)/libalac.a $(OBJDIR)/libfaad $(BUILDDIR)/libfaad.a $(OBJDIR)/libm4a $(BUILDDIR)/libm4a.a $(OBJDIR)/libdemac $(BUILDDIR)/libdemac.a $(OBJDIR)/libwma $(BUILDDIR)/libwma.a + $(call PRINTS,cleaning codecs)rm -fr $(OBJDIR)/spc $(BUILDDIR)/libspc.a $(OBJDIR)/libmad $(BUILDDIR)/libmad.a $(OBJDIR)/liba52 $(BUILDDIR)/liba52.a $(OBJDIR)/libffmpegFLAC $(BUILDDIR)/libffmpegFLAC.a $(OBJDIR)/Tremor $(BUILDDIR)/libTremor.a $(OBJDIR)/libspeex $(BUILDDIR)/libSpeex.a $(OBJDIR)/libwavpack $(BUILDDIR)/libwavpack.a $(OBJDIR)/libmusepack $(BUILDDIR)/libmusepack.a $(OBJDIR)/libalac $(BUILDDIR)/libalac.a $(OBJDIR)/libfaad $(BUILDDIR)/libfaad.a $(OBJDIR)/libm4a $(BUILDDIR)/libm4a.a $(OBJDIR)/libdemac $(BUILDDIR)/libdemac.a $(OBJDIR)/libwma $(BUILDDIR)/libwma.a + $(SILENT)$(MAKE) -C spc clean OBJDIR=$(OBJDIR)/spc $(SILENT)$(MAKE) -C libmad clean OBJDIR=$(OBJDIR)/libmad $(SILENT)$(MAKE) -C liba52 clean OBJDIR=$(OBJDIR)/liba52 $(SILENT)$(MAKE) -C libffmpegFLAC clean OBJDIR=$(OBJDIR)/libffmpegFLAC diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c index 6c791b46a1..f2890cd4a4 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 */ diff --git a/apps/codecs/spc/Makefile b/apps/codecs/spc/Makefile new file mode 100644 index 0000000000..8929149ce2 --- /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 0000000000..901232a6eb --- /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 b931ca2a3b..3b7c129ad8 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 d670b20c52..6a8aaebc7f 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 0000000000..30aaf5d64b --- /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 0000000000..60e0ef7f82 --- /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 99d3fdf16b..330d95bca7 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_ */ |