Mercurial > spc_convert
changeset 0:e38dacceb958
initial import
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Makefile Fri Oct 21 05:53:11 2011 -0700 1.3 @@ -0,0 +1,11 @@ 1.4 +spc_record: 1.5 + 1.6 + 1.7 +whatever : snes_spc/SNES_SPC.cpp 1.8 + g++ -c snes_spc/*.cpp 1.9 + 1.10 + 1.11 + 1.12 +clean: 1.13 + find . \( -name "*.o" -print0 \) -o \ 1.14 + \( -name "spc" -print0 \) | xargs -t -0 rm 1.15 \ No newline at end of file
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/changes.txt Fri Oct 21 05:53:11 2011 -0700 2.3 @@ -0,0 +1,107 @@ 2.4 +snes_spc Change Log 2.5 +------------------- 2.6 + 2.7 +snes_spc 0.9.0 2.8 +-------------- 2.9 +- Improved documentation 2.10 + 2.11 +- SPC: Added spc_skip() function for quickly seeking in an SPC music 2.12 +file. Runs 3-4x faster than normal playback using the fast DSP (or about 2.13 +43-60X real-time on my 400 MHz Mac). 2.14 + 2.15 +- SPC: Added spc_set_tempo() to change tempo of SPC music playback. 2.16 + 2.17 +- SPC: Sample generation is now corrected to generate exactly one pair 2.18 +of samples every 32 clocks without exception. Before it could generate a 2.19 +few samples more or less depending on how far ahead or behind DSP was at 2.20 +the moment. 2.21 + 2.22 +- SPC: Changed spc_reset() and spc_soft_reset() to also reset output 2.23 +buffer (see spc.h). 2.24 + 2.25 +- SPC: Fixed minor timer counting bug. 2.26 + 2.27 +- SPC: Stack pointer wrap-around is now emulated (and without any 2.28 +noticeable performance hit). 2.29 + 2.30 +- SPC: Runs about 5% faster due to various optimizations. 2.31 + 2.32 +- SPC: Found way to make fast DSP register accesses cycle-accurate in 2.33 +most cases, without reducing performance. Allows fast DSP to pass most 2.34 +of my validation tests. 2.35 + 2.36 +- DSP: Added surround disable support to fast DSP again. 2.37 + 2.38 +- DSP: Improved voice un-muting to take effect immediately on fast DSP. 2.39 + 2.40 +- DSP: Noise shift register now starts at 0x4000 instead of 0x4001 as it 2.41 +incorrectly did before. 2.42 + 2.43 +- Converted library to C++ code internally. A C interface is still 2.44 +included in spc.h and dsp.h. Note that these are different than the 2.45 +previous interface, so your code will require minor changes: 2.46 + 2.47 + Old SPC code New SPC code 2.48 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.49 + #include "spc/spc.h" #include "snes_spc/spc.h" 2.50 + 2.51 + snes_spc_t* spc; SNES_SPC* spc; 2.52 + spc = malloc( sizeof (snes_spc_t) ); spc = spc_new(); 2.53 + spc_init( spc ); 2.54 + 2.55 + spc_end_frame( time ); spc_end_frame( spc, time ); 2.56 + /* etc. */ 2.57 + 2.58 + /* done using SPC */ spc_delete( spc ); 2.59 + 2.60 + 2.61 + Old DSP code New DSP code 2.62 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.63 + #include "spc/spc_dsp.h" #include "snes_spc/dsp.h" 2.64 + 2.65 + spc_dsp_init( ram ); SPC_DSP* dsp; 2.66 + dsp = spc_dsp_new(); 2.67 + spc_dsp_init( dsp, ram ); 2.68 + 2.69 + spc_dsp_run( count ); spc_dsp_run( dsp, count ); 2.70 + /* etc. */ 2.71 + 2.72 + /* done using DSP */ spc_dsp_delete( dsp ); 2.73 + 2.74 + 2.75 +snes_spc 0.8.0 2.76 +-------------- 2.77 +- Added several demos 2.78 + 2.79 +- Added high-pass/low-pass filter to better match SNES sound 2.80 + 2.81 +- Added save state functionality for SPC and accurate DSP (but not fast 2.82 +DSP) 2.83 + 2.84 +- Added emulation of reset switch on NES (soft reset) 2.85 + 2.86 +- Made source more compatible with pre-C99 compilers by eliminating 2.87 +mid-block declarations 2.88 + 2.89 +- SPC: Many S-SMP accuracy improvements, mostly in memory access times 2.90 + 2.91 +- SPC: S-SMP speed improvements 2.92 + 2.93 +- SPC: Added SPC load/save functions and KON checking to help trim 2.94 +silence from beginning 2.95 + 2.96 +- SPC: Changed spc_init() to have you allocate most of the memory used 2.97 +by the library so you have more control over it 2.98 + 2.99 +- DSP: New highly accurate DSP and faster version derived from same code 2.100 + 2.101 +- DSP: Changed prefix from dsp_ to spc_dsp_. Your DSP code will require 2.102 +changes. 2.103 + 2.104 +- DSP: Removed surround disable and gain. Gain can now be done with the 2.105 +dsp_filter module, and surround disable will probably only be 2.106 +implemented in the fast DSP at some point. 2.107 + 2.108 +- DSP: Changed interface to work in clocks rather than samples, 2.109 +necessary for the new accurate DSP. Sample output is now done with 2.110 +separate functions. Your DSP code will require changes.
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/demo/benchmark.c Fri Oct 21 05:53:11 2011 -0700 3.3 @@ -0,0 +1,58 @@ 3.4 +/* Measures performance of SPC emulator. Takes about 4 seconds. 3.5 +NOTE: This assumes that the program is getting all processor time; you might need to 3.6 +arrange for this or else the performance will be reported lower than it really is. 3.7 + 3.8 +Usage: benchmark [test.spc] 3.9 +*/ 3.10 + 3.11 +#include "snes_spc/spc.h" 3.12 + 3.13 +#include "demo_util.h" /* error(), load_file() */ 3.14 +#include <time.h> 3.15 + 3.16 +clock_t start_timing( int seconds ); 3.17 + 3.18 +int main( int argc, char** argv ) 3.19 +{ 3.20 + /* Load SPC */ 3.21 + long spc_size; 3.22 + void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size ); 3.23 + SNES_SPC* snes_spc = spc_new(); 3.24 + if ( !snes_spc ) error( "Out of memory" ); 3.25 + spc_load_spc( snes_spc, spc, spc_size ); 3.26 + free( spc ); 3.27 + 3.28 + { 3.29 + /* Repeatedly fill buffer for 4 seconds */ 3.30 + int const seconds = 4; 3.31 + #define BUF_SIZE 4096 3.32 + clock_t end = start_timing( seconds ); 3.33 + int count = 0; 3.34 + while ( clock() < end ) 3.35 + { 3.36 + static short buf [BUF_SIZE]; 3.37 + error( spc_play( snes_spc, BUF_SIZE, buf ) ); 3.38 + count++; 3.39 + } 3.40 + 3.41 + /* Report performance based on how many buffer fills were possible */ 3.42 + { 3.43 + double rate = (double) count * BUF_SIZE / (spc_sample_rate * 2 * seconds); 3.44 + printf( "Performance: %.3fx real-time, or %.0f%% CPU for normal rate\n", 3.45 + rate, 100.0 / rate ); 3.46 + } 3.47 + } 3.48 + 3.49 + return 0; 3.50 +} 3.51 + 3.52 +/* Synchronizes with host clock and returns clock() time that is duration seconds from now */ 3.53 +clock_t start_timing( int duration ) 3.54 +{ 3.55 + clock_t clock_dur = duration * CLOCKS_PER_SEC; 3.56 + clock_t time = clock(); 3.57 + while ( clock() == time ) { } 3.58 + if ( clock() - time > clock_dur ) 3.59 + error( "Insufficient clock() time resolution" ); 3.60 + return clock() + clock_dur; 3.61 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/demo/comm.c Fri Oct 21 05:53:11 2011 -0700 4.3 @@ -0,0 +1,70 @@ 4.4 +/* Communicates with SPC the way the SNES would. 4.5 + 4.6 +Note: You'll need an "spc_rom.h" file that contains the 64-byte IPL ROM contents */ 4.7 + 4.8 +#include "snes_spc/spc.h" 4.9 + 4.10 +#include "demo_util.h" 4.11 +#include <string.h> 4.12 +#include <stdio.h> 4.13 + 4.14 +static SNES_SPC* snes_spc; 4.15 + 4.16 +/* Make port access more convenient. Fakes time by simply incrementing it each call. */ 4.17 +static spc_time_t stime; 4.18 +static int pread ( int port ) { return spc_read_port( snes_spc, stime++, port ); } 4.19 +static void pwrite( int port, int data ) { spc_write_port( snes_spc, stime++, port, data ); } 4.20 + 4.21 +static unsigned char const spc_rom [spc_rom_size] = { 4.22 + /* ROM data not provided with emulator */ 4.23 + #include "spc_rom.h" 4.24 +}; 4.25 + 4.26 +int main() 4.27 +{ 4.28 + int i; 4.29 + 4.30 + /* Data to upload */ 4.31 + static unsigned char const data [4] = "\xFA\xDE\xD1"; 4.32 + unsigned const data_addr = 0xF5; /* second I/O port */ 4.33 + 4.34 + snes_spc = spc_new(); 4.35 + if ( !snes_spc ) error( "Out of memory" ); 4.36 + spc_init_rom( snes_spc, spc_rom ); 4.37 + spc_reset( snes_spc ); 4.38 + 4.39 + /* Simulate reads and writes that SNES code would do */ 4.40 + 4.41 + /* Wait for SPC to be ready */ 4.42 + while ( pread( 0 ) != 0xAA || pread( 1 ) != 0xBB ) { } 4.43 + 4.44 + /* Start address */ 4.45 + pwrite( 2, data_addr & 0xFF ); 4.46 + pwrite( 3, data_addr >> 8 ); 4.47 + 4.48 + /* Tell SPC to start transfer and wait for acknowledgement */ 4.49 + pwrite( 0, 0xCC ); 4.50 + pwrite( 1, 0x01 ); 4.51 + while ( pread( 0 ) != 0xCC ) { } 4.52 + 4.53 + /* Send each byte and wait for acknowledgement */ 4.54 + for ( i = 0; i < 4; i++ ) 4.55 + { 4.56 + printf( "%02X ", data [i] ); 4.57 + pwrite( 1, data [i] ); 4.58 + pwrite( 0, i ); 4.59 + while ( pread( 0 ) != i ) { } 4.60 + } 4.61 + printf( "\n" ); 4.62 + 4.63 + /* Verify that data was transferred properly */ 4.64 + for ( i = 0; i < 3; i++ ) 4.65 + printf( "%02X ", pread( i + 1 ) ); 4.66 + printf( "\n" ); 4.67 + 4.68 + printf( "Cycles: %ld\n", (long) stime ); 4.69 + 4.70 + spc_delete( snes_spc ); 4.71 + 4.72 + return 0; 4.73 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/demo/demo_util.c Fri Oct 21 05:53:11 2011 -0700 5.3 @@ -0,0 +1,57 @@ 5.4 +#include "demo_util.h" 5.5 + 5.6 +#include <assert.h> 5.7 +#include <string.h> 5.8 +#include <stdlib.h> 5.9 +#include <stdio.h> 5.10 + 5.11 +/* Copyright (C) 2007 Shay Green. This module is free software; you 5.12 +can redistribute it and/or modify it under the terms of the GNU Lesser 5.13 +General Public License as published by the Free Software Foundation; either 5.14 +version 2.1 of the License, or (at your option) any later version. This 5.15 +module is distributed in the hope that it will be useful, but WITHOUT ANY 5.16 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 5.17 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 5.18 +details. You should have received a copy of the GNU Lesser General Public 5.19 +License along with this module; if not, write to the Free Software Foundation, 5.20 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 5.21 + 5.22 +unsigned char* load_file( const char* path, long* size_out ) 5.23 +{ 5.24 + size_t size; 5.25 + unsigned char* data; 5.26 + 5.27 + FILE* in = fopen( path, "rb" ); 5.28 + if ( !in ) error( "Couldn't open file" ); 5.29 + 5.30 + fseek( in, 0, SEEK_END ); 5.31 + size = ftell( in ); 5.32 + if ( size_out ) 5.33 + *size_out = size; 5.34 + rewind( in ); 5.35 + 5.36 + data = (unsigned char*) malloc( size ); 5.37 + if ( !data ) error( "Out of memory" ); 5.38 + 5.39 + if ( fread( data, 1, size, in ) < size ) error( "Couldn't read file" ); 5.40 + fclose( in ); 5.41 + 5.42 + return data; 5.43 +} 5.44 + 5.45 +void write_file( const char* path, void const* in, long size ) 5.46 +{ 5.47 + FILE* out = fopen( path, "wb" ); 5.48 + if ( !out ) error( "Couldn't create file" ); 5.49 + if ( (long) fwrite( in, 1, size, out ) < size ) error( "Couldn't write file" ); 5.50 + fclose( out ); 5.51 +} 5.52 + 5.53 +void error( const char* str ) 5.54 +{ 5.55 + if ( str ) 5.56 + { 5.57 + fprintf( stderr, "Error: %s\n", str ); 5.58 + exit( EXIT_FAILURE ); 5.59 + } 5.60 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/demo/demo_util.h Fri Oct 21 05:53:11 2011 -0700 6.3 @@ -0,0 +1,31 @@ 6.4 +/* General-purpose utilities used by demos */ 6.5 + 6.6 +/* snes_spc 0.9.0 */ 6.7 +#ifndef DEMO_UTIL_H 6.8 +#define DEMO_UTIL_H 6.9 + 6.10 +/* commonly used headers */ 6.11 +#include <assert.h> 6.12 +#include <stdlib.h> 6.13 +#include <string.h> 6.14 +#include <stdio.h> 6.15 + 6.16 +#ifdef __cplusplus 6.17 + extern "C" { 6.18 +#endif 6.19 + 6.20 +/* If str is not NULL, prints it and exits program, otherwise returns */ 6.21 +void error( const char* str ); 6.22 + 6.23 +/* Loads file and returns pointer to data in memory, allocated with malloc(). 6.24 +If size_out != NULL, sets *size_out to size of data. */ 6.25 +unsigned char* load_file( const char* path, long* size_out ); 6.26 + 6.27 +/* Writes data to file */ 6.28 +void write_file( const char* path, void const* in, long size ); 6.29 + 6.30 +#ifdef __cplusplus 6.31 + } 6.32 +#endif 6.33 + 6.34 +#endif
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/demo/play_spc.c Fri Oct 21 05:53:11 2011 -0700 7.3 @@ -0,0 +1,57 @@ 7.4 +/* Records SPC into wave file. Uses dsp_filter to give more authentic sound. 7.5 + 7.6 +Usage: play_spc [test.spc] 7.7 +*/ 7.8 + 7.9 +#include "snes_spc/spc.h" 7.10 + 7.11 +#include "wave_writer.h" 7.12 +#include "demo_util.h" /* error(), load_file() */ 7.13 + 7.14 +int main( int argc, char** argv ) 7.15 +{ 7.16 + /* Create emulator and filter */ 7.17 + SNES_SPC* snes_spc = spc_new(); 7.18 + SPC_Filter* filter = spc_filter_new(); 7.19 + if ( !snes_spc || !filter ) error( "Out of memory" ); 7.20 + 7.21 + /* Load SPC */ 7.22 + { 7.23 + /* Load file into memory */ 7.24 + long spc_size; 7.25 + void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size ); 7.26 + 7.27 + /* Load SPC data into emulator */ 7.28 + error( spc_load_spc( snes_spc, spc, spc_size ) ); 7.29 + free( spc ); /* emulator makes copy of data */ 7.30 + 7.31 + /* Most SPC files have garbage data in the echo buffer, so clear that */ 7.32 + spc_clear_echo( snes_spc ); 7.33 + 7.34 + /* Clear filter before playing */ 7.35 + spc_filter_clear( filter ); 7.36 + } 7.37 + 7.38 + /* Record 20 seconds to wave file */ 7.39 + wave_open( spc_sample_rate, "out.wav" ); 7.40 + wave_enable_stereo(); 7.41 + while ( wave_sample_count() < 20 * spc_sample_rate * 2 ) 7.42 + { 7.43 + /* Play into buffer */ 7.44 + #define BUF_SIZE 2048 7.45 + short buf [BUF_SIZE]; 7.46 + error( spc_play( snes_spc, BUF_SIZE, buf ) ); 7.47 + 7.48 + /* Filter samples */ 7.49 + spc_filter_run( filter, buf, BUF_SIZE ); 7.50 + 7.51 + wave_write( buf, BUF_SIZE ); 7.52 + } 7.53 + 7.54 + /* Cleanup */ 7.55 + spc_filter_delete( filter ); 7.56 + spc_delete( snes_spc ); 7.57 + wave_close(); 7.58 + 7.59 + return 0; 7.60 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/demo/save_state.c Fri Oct 21 05:53:11 2011 -0700 8.3 @@ -0,0 +1,107 @@ 8.4 +/* Loads "test.spc", skips 5 seconds, saves exact emulator state to "state.bin", 8.5 +hen records 5 seconds to "first.wav". When run again, loads this state back into 8.6 +emulator and records 5 seconds to "second.wav". These two wave files should 8.7 +be identical. 8.8 + 8.9 +Usage: save_state [test.spc] 8.10 +*/ 8.11 + 8.12 +#include "snes_spc/spc.h" 8.13 + 8.14 +#include "wave_writer.h" 8.15 +#include "demo_util.h" /* error(), load_file() */ 8.16 + 8.17 +static SNES_SPC* snes_spc; 8.18 + 8.19 +void record_wav( const char* path, int secs ) 8.20 +{ 8.21 + /* Start writing wave file */ 8.22 + wave_open( spc_sample_rate, path ); 8.23 + wave_enable_stereo(); 8.24 + while ( wave_sample_count() < secs * spc_sample_rate * 2 ) 8.25 + { 8.26 + /* Play into buffer */ 8.27 + #define BUF_SIZE 2048 8.28 + short buf [BUF_SIZE]; 8.29 + error( spc_play( snes_spc, BUF_SIZE, buf ) ); 8.30 + 8.31 + wave_write( buf, BUF_SIZE ); 8.32 + } 8.33 + wave_close(); 8.34 +} 8.35 + 8.36 +void state_save( unsigned char** out, void* in, size_t size ) 8.37 +{ 8.38 + memcpy( *out, in, size ); 8.39 + *out += size; 8.40 +} 8.41 + 8.42 +void make_save_state( const char* path ) 8.43 +{ 8.44 + /* Load SPC */ 8.45 + long spc_size; 8.46 + void* spc = load_file( path, &spc_size ); 8.47 + error( spc_load_spc( snes_spc, spc, spc_size ) ); 8.48 + free( spc ); 8.49 + spc_clear_echo( snes_spc ); 8.50 + 8.51 + /* Skip several seconds */ 8.52 + error( spc_play( snes_spc, 5 * spc_sample_rate * 2, 0 ) ); 8.53 + 8.54 + /* Save state to file */ 8.55 + { 8.56 + static unsigned char state [spc_state_size]; /* buffer large enough for data */ 8.57 + unsigned char* out = state; 8.58 + spc_copy_state( snes_spc, &out, state_save ); 8.59 + write_file( "state.bin", state, out - state ); 8.60 + } 8.61 + 8.62 + record_wav( "first.wav", 5 ); 8.63 +} 8.64 + 8.65 +void state_load( unsigned char** in, void* out, size_t size ) 8.66 +{ 8.67 + memcpy( out, *in, size ); 8.68 + *in += size; 8.69 +} 8.70 + 8.71 +void use_save_state() 8.72 +{ 8.73 + /* Load state into memory */ 8.74 + long state_size; 8.75 + unsigned char* state = load_file( "state.bin", &state_size ); 8.76 + 8.77 + /* Load into emulator */ 8.78 + unsigned char* in = state; 8.79 + spc_copy_state( snes_spc, &in, state_load ); 8.80 + assert( in - state <= state_size ); /* be sure it didn't read past end */ 8.81 + 8.82 + record_wav( "second.wav", 5 ); 8.83 +} 8.84 + 8.85 +int file_exists( const char* path ) 8.86 +{ 8.87 + FILE* file = fopen( path, "rb" ); 8.88 + if ( !file ) 8.89 + return 0; 8.90 + 8.91 + fclose( file ); 8.92 + return 1; 8.93 +} 8.94 + 8.95 +int main( int argc, char** argv ) 8.96 +{ 8.97 + snes_spc = spc_new(); 8.98 + if ( !snes_spc ) error( "Out of memory" ); 8.99 + 8.100 + /* Make new state if it doesn't exist, otherwise load it and 8.101 + record to wave file */ 8.102 + if ( !file_exists( "state.bin" ) ) 8.103 + make_save_state( (argc > 1) ? argv [1] : "test.spc" ); 8.104 + else 8.105 + use_save_state(); 8.106 + 8.107 + spc_delete( snes_spc ); 8.108 + 8.109 + return 0; 8.110 +}
9.1 Binary file demo/spc_record has changed
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/demo/trim_spc.c Fri Oct 21 05:53:11 2011 -0700 10.3 @@ -0,0 +1,83 @@ 10.4 +/* Trims silence off beginning of SPC file. 10.5 +Requires the accurate DSP; won't compile with the fast DSP. 10.6 +Please note that the algorithm could be improved; this is just 10.7 +a simple example showing the idea. 10.8 + 10.9 +Usage: trim_spc [test.spc [trimmed.spc]] 10.10 +*/ 10.11 + 10.12 +#include "snes_spc/spc.h" 10.13 + 10.14 +#include "demo_util.h" /* error(), load_file() */ 10.15 + 10.16 +/* Change to 1 to have it trim to next key on event rather than trim silence */ 10.17 +enum { use_kon_check = 0 }; 10.18 + 10.19 +/* True if all count samples from in are silent (or very close to it) */ 10.20 +int is_silent( short const* in, int count ); 10.21 + 10.22 +int main( int argc, char** argv ) 10.23 +{ 10.24 + /* Load file into memory */ 10.25 + long spc_size; 10.26 + void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size ); 10.27 + 10.28 + /* Load into emulator */ 10.29 + SNES_SPC* snes_spc = spc_new(); 10.30 + if ( !snes_spc ) error( "Out of memory" ); 10.31 + error( spc_load_spc( snes_spc, spc, spc_size ) ); 10.32 + 10.33 + /* Expand SPC data so there's enough room for emulator to save to. 10.34 + We simply overwrite the emulator data in the old SPC file rather 10.35 + than creating new SPC data. This preserves the text tags from 10.36 + the old file. */ 10.37 + if ( spc_size < spc_file_size ) 10.38 + { 10.39 + spc_size = spc_file_size; 10.40 + spc = realloc( spc, spc_size ); /* leaks memory if allocation fails */ 10.41 + if ( !spc ) error( "Out of memory" ); 10.42 + } 10.43 + 10.44 + /* Keep saving SPC, then playing a little more. Once SPC becomes non-silent, 10.45 + write the SPC data saved just before this. */ 10.46 + { 10.47 + long samples_trimmed = 0; 10.48 + while ( 1 ) 10.49 + { 10.50 + #define BUF_SIZE 1024 10.51 + short buf [BUF_SIZE]; 10.52 + 10.53 + if ( samples_trimmed > 10 * spc_sample_rate * 2 ) 10.54 + error( "Excess silence found" ); 10.55 + 10.56 + spc_clear_echo( snes_spc ); 10.57 + spc_save_spc( snes_spc, spc ); 10.58 + 10.59 + /* Fill buffer */ 10.60 + error( spc_play( snes_spc, BUF_SIZE, buf ) ); 10.61 + samples_trimmed += BUF_SIZE; 10.62 + 10.63 + /* See if SPC became non-silent */ 10.64 + if ( use_kon_check ? spc_check_kon( snes_spc ) : !is_silent( buf, BUF_SIZE ) ) 10.65 + break; 10.66 + } 10.67 + 10.68 + printf( "Trimmed %.1f seconds\n", (double) samples_trimmed / spc_sample_rate / 2 ); 10.69 + } 10.70 + 10.71 + spc_delete( snes_spc ); 10.72 + write_file( (argc > 2) ? argv [2] : "trimmed.spc", spc, spc_size ); 10.73 + 10.74 + return 0; 10.75 +} 10.76 + 10.77 +int is_silent( short const* in, int count ) 10.78 +{ 10.79 + unsigned const threshold = 0x10; 10.80 + while ( count-- ) 10.81 + { 10.82 + if ( (unsigned) (*in++ + threshold / 2) > threshold ) 10.83 + return 0; 10.84 + } 10.85 + return 1; 10.86 +}
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/demo/wave_writer.c Fri Oct 21 05:53:11 2011 -0700 11.3 @@ -0,0 +1,153 @@ 11.4 +/* snes_spc 0.9.0. http://www.slack.net/~ant/ */ 11.5 + 11.6 +#include "wave_writer.h" 11.7 + 11.8 +#include <assert.h> 11.9 +#include <stdlib.h> 11.10 +#include <stdio.h> 11.11 + 11.12 +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you 11.13 +can redistribute it and/or modify it under the terms of the GNU Lesser 11.14 +General Public License as published by the Free Software Foundation; either 11.15 +version 2.1 of the License, or (at your option) any later version. This 11.16 +module is distributed in the hope that it will be useful, but WITHOUT ANY 11.17 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11.18 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 11.19 +details. You should have received a copy of the GNU Lesser General Public 11.20 +License along with this module; if not, write to the Free Software Foundation, 11.21 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 11.22 + 11.23 +enum { buf_size = 32768 * 2 }; 11.24 +enum { header_size = 0x2C }; 11.25 + 11.26 +typedef short sample_t; 11.27 + 11.28 +static unsigned char* buf; 11.29 +static FILE* file; 11.30 +static long sample_count_; 11.31 +static long sample_rate_; 11.32 +static long buf_pos; 11.33 +static int chan_count; 11.34 + 11.35 +static void exit_with_error( const char* str ) 11.36 +{ 11.37 + printf( "Error: %s\n", str ); getchar(); 11.38 + exit( EXIT_FAILURE ); 11.39 +} 11.40 + 11.41 +void wave_open( long sample_rate, const char* filename ) 11.42 +{ 11.43 + sample_count_ = 0; 11.44 + sample_rate_ = sample_rate; 11.45 + buf_pos = header_size; 11.46 + chan_count = 1; 11.47 + 11.48 + buf = (unsigned char*) malloc( buf_size * sizeof *buf ); 11.49 + if ( !buf ) 11.50 + exit_with_error( "Out of memory" ); 11.51 + 11.52 + file = fopen( filename, "wb" ); 11.53 + if ( !file ) 11.54 + exit_with_error( "Couldn't open WAVE file for writing" ); 11.55 + 11.56 + setvbuf( file, 0, _IOFBF, 32 * 1024L ); 11.57 +} 11.58 + 11.59 +void wave_enable_stereo( void ) 11.60 +{ 11.61 + chan_count = 2; 11.62 +} 11.63 + 11.64 +static void flush_() 11.65 +{ 11.66 + if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) ) 11.67 + exit_with_error( "Couldn't write WAVE data" ); 11.68 + buf_pos = 0; 11.69 +} 11.70 + 11.71 +void wave_write( short const* in, long remain ) 11.72 +{ 11.73 + sample_count_ += remain; 11.74 + while ( remain ) 11.75 + { 11.76 + if ( buf_pos >= buf_size ) 11.77 + flush_(); 11.78 + 11.79 + { 11.80 + unsigned char* p = &buf [buf_pos]; 11.81 + long n = (buf_size - buf_pos) / sizeof (sample_t); 11.82 + if ( n > remain ) 11.83 + n = remain; 11.84 + remain -= n; 11.85 + 11.86 + /* convert to LSB first format */ 11.87 + while ( n-- ) 11.88 + { 11.89 + int s = *in++; 11.90 + *p++ = (unsigned char) s; 11.91 + *p++ = (unsigned char) (s >> 8); 11.92 + } 11.93 + 11.94 + buf_pos = p - buf; 11.95 + assert( buf_pos <= buf_size ); 11.96 + } 11.97 + } 11.98 +} 11.99 + 11.100 +long wave_sample_count( void ) 11.101 +{ 11.102 + return sample_count_; 11.103 +} 11.104 + 11.105 +static void set_le32( void* p, unsigned long n ) 11.106 +{ 11.107 + ((unsigned char*) p) [0] = (unsigned char) n; 11.108 + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); 11.109 + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); 11.110 + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); 11.111 +} 11.112 + 11.113 +void wave_close( void ) 11.114 +{ 11.115 + if ( file ) 11.116 + { 11.117 + /* generate header */ 11.118 + unsigned char h [header_size] = 11.119 + { 11.120 + 'R','I','F','F', 11.121 + 0,0,0,0, /* length of rest of file */ 11.122 + 'W','A','V','E', 11.123 + 'f','m','t',' ', 11.124 + 0x10,0,0,0, /* size of fmt chunk */ 11.125 + 1,0, /* uncompressed format */ 11.126 + 0,0, /* channel count */ 11.127 + 0,0,0,0, /* sample rate */ 11.128 + 0,0,0,0, /* bytes per second */ 11.129 + 0,0, /* bytes per sample frame */ 11.130 + 16,0, /* bits per sample */ 11.131 + 'd','a','t','a', 11.132 + 0,0,0,0, /* size of sample data */ 11.133 + /* ... */ /* sample data */ 11.134 + }; 11.135 + long ds = sample_count_ * sizeof (sample_t); 11.136 + int frame_size = chan_count * sizeof (sample_t); 11.137 + 11.138 + set_le32( h + 0x04, header_size - 8 + ds ); 11.139 + h [0x16] = chan_count; 11.140 + set_le32( h + 0x18, sample_rate_ ); 11.141 + set_le32( h + 0x1C, sample_rate_ * frame_size ); 11.142 + h [0x20] = frame_size; 11.143 + set_le32( h + 0x28, ds ); 11.144 + 11.145 + flush_(); 11.146 + 11.147 + /* write header */ 11.148 + fseek( file, 0, SEEK_SET ); 11.149 + fwrite( h, sizeof h, 1, file ); 11.150 + 11.151 + fclose( file ); 11.152 + file = 0; 11.153 + free( buf ); 11.154 + buf = 0; 11.155 + } 11.156 +}
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/demo/wave_writer.h Fri Oct 21 05:53:11 2011 -0700 12.3 @@ -0,0 +1,20 @@ 12.4 +/* WAVE sound file writer for recording 16-bit output during program development */ 12.5 + 12.6 +#ifndef WAVE_WRITER_H 12.7 +#define WAVE_WRITER_H 12.8 + 12.9 +#ifdef __cplusplus 12.10 + extern "C" { 12.11 +#endif 12.12 + 12.13 +void wave_open( long sample_rate, const char* filename ); 12.14 +void wave_enable_stereo( void ); 12.15 +void wave_write( short const* in, long count ); 12.16 +long wave_sample_count( void ); 12.17 +void wave_close( void ); 12.18 + 12.19 +#ifdef __cplusplus 12.20 + } 12.21 +#endif 12.22 + 12.23 +#endif
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/fast_dsp/SPC_DSP.cpp Fri Oct 21 05:53:11 2011 -0700 13.3 @@ -0,0 +1,703 @@ 13.4 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 13.5 + 13.6 +#include "SPC_DSP.h" 13.7 + 13.8 +#include "blargg_endian.h" 13.9 +#include <string.h> 13.10 + 13.11 +/* Copyright (C) 2007 Shay Green. This module is free software; you 13.12 +can redistribute it and/or modify it under the terms of the GNU Lesser 13.13 +General Public License as published by the Free Software Foundation; either 13.14 +version 2.1 of the License, or (at your option) any later version. This 13.15 +module is distributed in the hope that it will be useful, but WITHOUT ANY 13.16 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13.17 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13.18 +details. You should have received a copy of the GNU Lesser General Public 13.19 +License along with this module; if not, write to the Free Software Foundation, 13.20 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 13.21 + 13.22 +#include "blargg_source.h" 13.23 + 13.24 +#ifdef BLARGG_ENABLE_OPTIMIZER 13.25 + #include BLARGG_ENABLE_OPTIMIZER 13.26 +#endif 13.27 + 13.28 +#if INT_MAX < 0x7FFFFFFF 13.29 + #error "Requires that int type have at least 32 bits" 13.30 +#endif 13.31 + 13.32 + 13.33 +// TODO: add to blargg_endian.h 13.34 +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) 13.35 +#define GET_LE16A( addr ) GET_LE16( addr ) 13.36 +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) 13.37 + 13.38 +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = 13.39 +{ 13.40 + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, 13.41 + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, 13.42 + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, 13.43 + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, 13.44 + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, 13.45 + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, 13.46 + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, 13.47 + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF 13.48 +}; 13.49 + 13.50 +// if ( io < -32768 ) io = -32768; 13.51 +// if ( io > 32767 ) io = 32767; 13.52 +#define CLAMP16( io )\ 13.53 +{\ 13.54 + if ( (int16_t) io != io )\ 13.55 + io = (io >> 31) ^ 0x7FFF;\ 13.56 +} 13.57 + 13.58 +// Access global DSP register 13.59 +#define REG(n) m.regs [r_##n] 13.60 + 13.61 +// Access voice DSP register 13.62 +#define VREG(r,n) r [v_##n] 13.63 + 13.64 +#define WRITE_SAMPLES( l, r, out ) \ 13.65 +{\ 13.66 + out [0] = l;\ 13.67 + out [1] = r;\ 13.68 + out += 2;\ 13.69 + if ( out >= m.out_end )\ 13.70 + {\ 13.71 + check( out == m.out_end );\ 13.72 + check( m.out_end != &m.extra [extra_size] || \ 13.73 + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ 13.74 + out = m.extra;\ 13.75 + m.out_end = &m.extra [extra_size];\ 13.76 + }\ 13.77 +}\ 13.78 + 13.79 +void SPC_DSP::set_output( sample_t* out, int size ) 13.80 +{ 13.81 + require( (size & 1) == 0 ); // must be even 13.82 + if ( !out ) 13.83 + { 13.84 + out = m.extra; 13.85 + size = extra_size; 13.86 + } 13.87 + m.out_begin = out; 13.88 + m.out = out; 13.89 + m.out_end = out + size; 13.90 +} 13.91 + 13.92 +// Volume registers and efb are signed! Easy to forget int8_t cast. 13.93 +// Prefixes are to avoid accidental use of locals with same names. 13.94 + 13.95 +// Interleved gauss table (to improve cache coherency) 13.96 +// interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)] 13.97 +static short const interleved_gauss [512] = 13.98 +{ 13.99 + 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, 13.100 + 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, 13.101 + 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, 13.102 + 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, 13.103 + 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, 13.104 + 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, 13.105 + 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, 13.106 + 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, 13.107 + 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, 13.108 + 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, 13.109 + 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, 13.110 + 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, 13.111 + 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, 13.112 + 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, 13.113 + 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, 13.114 + 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, 13.115 + 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, 13.116 + 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, 13.117 + 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, 13.118 + 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, 13.119 + 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, 13.120 + 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, 13.121 + 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, 13.122 + 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, 13.123 + 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, 13.124 + 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, 13.125 + 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, 13.126 + 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, 13.127 + 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, 13.128 + 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, 13.129 + 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, 13.130 + 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, 13.131 +}; 13.132 + 13.133 + 13.134 +//// Counters 13.135 + 13.136 +#define RATE( rate, div )\ 13.137 + (rate >= div ? rate / div * 8 - 1 : rate - 1) 13.138 + 13.139 +static unsigned const counter_mask [32] = 13.140 +{ 13.141 + RATE( 2,2), RATE(2048,4), RATE(1536,3), 13.142 + RATE(1280,5), RATE(1024,4), RATE( 768,3), 13.143 + RATE( 640,5), RATE( 512,4), RATE( 384,3), 13.144 + RATE( 320,5), RATE( 256,4), RATE( 192,3), 13.145 + RATE( 160,5), RATE( 128,4), RATE( 96,3), 13.146 + RATE( 80,5), RATE( 64,4), RATE( 48,3), 13.147 + RATE( 40,5), RATE( 32,4), RATE( 24,3), 13.148 + RATE( 20,5), RATE( 16,4), RATE( 12,3), 13.149 + RATE( 10,5), RATE( 8,4), RATE( 6,3), 13.150 + RATE( 5,5), RATE( 4,4), RATE( 3,3), 13.151 + RATE( 2,4), 13.152 + RATE( 1,4) 13.153 +}; 13.154 +#undef RATE 13.155 + 13.156 +inline void SPC_DSP::init_counter() 13.157 +{ 13.158 + // counters start out with this synchronization 13.159 + m.counters [0] = 1; 13.160 + m.counters [1] = 0; 13.161 + m.counters [2] = -0x20u; 13.162 + m.counters [3] = 0x0B; 13.163 + 13.164 + int n = 2; 13.165 + for ( int i = 1; i < 32; i++ ) 13.166 + { 13.167 + m.counter_select [i] = &m.counters [n]; 13.168 + if ( !--n ) 13.169 + n = 3; 13.170 + } 13.171 + m.counter_select [ 0] = &m.counters [0]; 13.172 + m.counter_select [30] = &m.counters [2]; 13.173 +} 13.174 + 13.175 +inline void SPC_DSP::run_counter( int i ) 13.176 +{ 13.177 + int n = m.counters [i]; 13.178 + if ( !(n-- & 7) ) 13.179 + n -= 6 - i; 13.180 + m.counters [i] = n; 13.181 +} 13.182 + 13.183 +#define READ_COUNTER( rate )\ 13.184 + (*m.counter_select [rate] & counter_mask [rate]) 13.185 + 13.186 + 13.187 +//// Emulation 13.188 + 13.189 +void SPC_DSP::run( int clock_count ) 13.190 +{ 13.191 + int new_phase = m.phase + clock_count; 13.192 + int count = new_phase >> 5; 13.193 + m.phase = new_phase & 31; 13.194 + if ( !count ) 13.195 + return; 13.196 + 13.197 + uint8_t* const ram = m.ram; 13.198 + uint8_t const* const dir = &ram [REG(dir) * 0x100]; 13.199 + int const slow_gaussian = (REG(pmon) >> 1) | REG(non); 13.200 + int const noise_rate = REG(flg) & 0x1F; 13.201 + 13.202 + // Global volume 13.203 + int mvoll = (int8_t) REG(mvoll); 13.204 + int mvolr = (int8_t) REG(mvolr); 13.205 + if ( mvoll * mvolr < m.surround_threshold ) 13.206 + mvoll = -mvoll; // eliminate surround 13.207 + 13.208 + do 13.209 + { 13.210 + // KON/KOFF reading 13.211 + if ( (m.every_other_sample ^= 1) != 0 ) 13.212 + { 13.213 + m.new_kon &= ~m.kon; 13.214 + m.kon = m.new_kon; 13.215 + m.t_koff = REG(koff); 13.216 + } 13.217 + 13.218 + run_counter( 1 ); 13.219 + run_counter( 2 ); 13.220 + run_counter( 3 ); 13.221 + 13.222 + // Noise 13.223 + if ( !READ_COUNTER( noise_rate ) ) 13.224 + { 13.225 + int feedback = (m.noise << 13) ^ (m.noise << 14); 13.226 + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); 13.227 + } 13.228 + 13.229 + // Voices 13.230 + int pmon_input = 0; 13.231 + int main_out_l = 0; 13.232 + int main_out_r = 0; 13.233 + int echo_out_l = 0; 13.234 + int echo_out_r = 0; 13.235 + voice_t* v = m.voices; 13.236 + uint8_t* v_regs = m.regs; 13.237 + int vbit = 1; 13.238 + do 13.239 + { 13.240 + #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] ) 13.241 + 13.242 + int brr_header = ram [v->brr_addr]; 13.243 + int kon_delay = v->kon_delay; 13.244 + 13.245 + // Pitch 13.246 + int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF; 13.247 + if ( REG(pmon) & vbit ) 13.248 + pitch += ((pmon_input >> 5) * pitch) >> 10; 13.249 + 13.250 + // KON phases 13.251 + if ( --kon_delay >= 0 ) 13.252 + { 13.253 + v->kon_delay = kon_delay; 13.254 + 13.255 + // Get ready to start BRR decoding on next sample 13.256 + if ( kon_delay == 4 ) 13.257 + { 13.258 + v->brr_addr = SAMPLE_PTR( 0 ); 13.259 + v->brr_offset = 1; 13.260 + v->buf_pos = v->buf; 13.261 + brr_header = 0; // header is ignored on this sample 13.262 + } 13.263 + 13.264 + // Envelope is never run during KON 13.265 + v->env = 0; 13.266 + v->hidden_env = 0; 13.267 + 13.268 + // Disable BRR decoding until last three samples 13.269 + v->interp_pos = (kon_delay & 3 ? 0x4000 : 0); 13.270 + 13.271 + // Pitch is never added during KON 13.272 + pitch = 0; 13.273 + } 13.274 + 13.275 + int env = v->env; 13.276 + 13.277 + // Gaussian interpolation 13.278 + { 13.279 + int output = 0; 13.280 + VREG(v_regs,envx) = (uint8_t) (env >> 4); 13.281 + if ( env ) 13.282 + { 13.283 + // Make pointers into gaussian based on fractional position between samples 13.284 + int offset = (unsigned) v->interp_pos >> 3 & 0x1FE; 13.285 + short const* fwd = interleved_gauss + offset; 13.286 + short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian 13.287 + 13.288 + int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12]; 13.289 + 13.290 + if ( !(slow_gaussian & vbit) ) // 99% 13.291 + { 13.292 + // Faster approximation when exact sample value isn't necessary for pitch mod 13.293 + output = (fwd [0] * in [0] + 13.294 + fwd [1] * in [1] + 13.295 + rev [1] * in [2] + 13.296 + rev [0] * in [3]) >> 11; 13.297 + output = (output * env) >> 11; 13.298 + } 13.299 + else 13.300 + { 13.301 + output = (int16_t) (m.noise * 2); 13.302 + if ( !(REG(non) & vbit) ) 13.303 + { 13.304 + output = (fwd [0] * in [0]) >> 11; 13.305 + output += (fwd [1] * in [1]) >> 11; 13.306 + output += (rev [1] * in [2]) >> 11; 13.307 + output = (int16_t) output; 13.308 + output += (rev [0] * in [3]) >> 11; 13.309 + 13.310 + CLAMP16( output ); 13.311 + output &= ~1; 13.312 + } 13.313 + output = (output * env) >> 11 & ~1; 13.314 + } 13.315 + 13.316 + // Output 13.317 + int l = output * v->volume [0]; 13.318 + int r = output * v->volume [1]; 13.319 + 13.320 + main_out_l += l; 13.321 + main_out_r += r; 13.322 + 13.323 + if ( REG(eon) & vbit ) 13.324 + { 13.325 + echo_out_l += l; 13.326 + echo_out_r += r; 13.327 + } 13.328 + } 13.329 + 13.330 + pmon_input = output; 13.331 + VREG(v_regs,outx) = (uint8_t) (output >> 8); 13.332 + } 13.333 + 13.334 + // Soft reset or end of sample 13.335 + if ( REG(flg) & 0x80 || (brr_header & 3) == 1 ) 13.336 + { 13.337 + v->env_mode = env_release; 13.338 + env = 0; 13.339 + } 13.340 + 13.341 + if ( m.every_other_sample ) 13.342 + { 13.343 + // KOFF 13.344 + if ( m.t_koff & vbit ) 13.345 + v->env_mode = env_release; 13.346 + 13.347 + // KON 13.348 + if ( m.kon & vbit ) 13.349 + { 13.350 + v->kon_delay = 5; 13.351 + v->env_mode = env_attack; 13.352 + REG(endx) &= ~vbit; 13.353 + } 13.354 + } 13.355 + 13.356 + // Envelope 13.357 + if ( !v->kon_delay ) 13.358 + { 13.359 + if ( v->env_mode == env_release ) // 97% 13.360 + { 13.361 + env -= 0x8; 13.362 + v->env = env; 13.363 + if ( env <= 0 ) 13.364 + { 13.365 + v->env = 0; 13.366 + goto skip_brr; // no BRR decoding for you! 13.367 + } 13.368 + } 13.369 + else // 3% 13.370 + { 13.371 + int rate; 13.372 + int const adsr0 = VREG(v_regs,adsr0); 13.373 + int env_data = VREG(v_regs,adsr1); 13.374 + if ( adsr0 >= 0x80 ) // 97% ADSR 13.375 + { 13.376 + if ( v->env_mode > env_decay ) // 89% 13.377 + { 13.378 + env--; 13.379 + env -= env >> 8; 13.380 + rate = env_data & 0x1F; 13.381 + 13.382 + // optimized handling 13.383 + v->hidden_env = env; 13.384 + if ( READ_COUNTER( rate ) ) 13.385 + goto exit_env; 13.386 + v->env = env; 13.387 + goto exit_env; 13.388 + } 13.389 + else if ( v->env_mode == env_decay ) 13.390 + { 13.391 + env--; 13.392 + env -= env >> 8; 13.393 + rate = (adsr0 >> 3 & 0x0E) + 0x10; 13.394 + } 13.395 + else // env_attack 13.396 + { 13.397 + rate = (adsr0 & 0x0F) * 2 + 1; 13.398 + env += rate < 31 ? 0x20 : 0x400; 13.399 + } 13.400 + } 13.401 + else // GAIN 13.402 + { 13.403 + int mode; 13.404 + env_data = VREG(v_regs,gain); 13.405 + mode = env_data >> 5; 13.406 + if ( mode < 4 ) // direct 13.407 + { 13.408 + env = env_data * 0x10; 13.409 + rate = 31; 13.410 + } 13.411 + else 13.412 + { 13.413 + rate = env_data & 0x1F; 13.414 + if ( mode == 4 ) // 4: linear decrease 13.415 + { 13.416 + env -= 0x20; 13.417 + } 13.418 + else if ( mode < 6 ) // 5: exponential decrease 13.419 + { 13.420 + env--; 13.421 + env -= env >> 8; 13.422 + } 13.423 + else // 6,7: linear increase 13.424 + { 13.425 + env += 0x20; 13.426 + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) 13.427 + env += 0x8 - 0x20; // 7: two-slope linear increase 13.428 + } 13.429 + } 13.430 + } 13.431 + 13.432 + // Sustain level 13.433 + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) 13.434 + v->env_mode = env_sustain; 13.435 + 13.436 + v->hidden_env = env; 13.437 + 13.438 + // unsigned cast because linear decrease going negative also triggers this 13.439 + if ( (unsigned) env > 0x7FF ) 13.440 + { 13.441 + env = (env < 0 ? 0 : 0x7FF); 13.442 + if ( v->env_mode == env_attack ) 13.443 + v->env_mode = env_decay; 13.444 + } 13.445 + 13.446 + if ( !READ_COUNTER( rate ) ) 13.447 + v->env = env; // nothing else is controlled by the counter 13.448 + } 13.449 + } 13.450 + exit_env: 13.451 + 13.452 + { 13.453 + // Apply pitch 13.454 + int old_pos = v->interp_pos; 13.455 + int interp_pos = (old_pos & 0x3FFF) + pitch; 13.456 + if ( interp_pos > 0x7FFF ) 13.457 + interp_pos = 0x7FFF; 13.458 + v->interp_pos = interp_pos; 13.459 + 13.460 + // BRR decode if necessary 13.461 + if ( old_pos >= 0x4000 ) 13.462 + { 13.463 + // Arrange the four input nybbles in 0xABCD order for easy decoding 13.464 + int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 + 13.465 + ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; 13.466 + 13.467 + // Advance read position 13.468 + int const brr_block_size = 9; 13.469 + int brr_offset = v->brr_offset; 13.470 + if ( (brr_offset += 2) >= brr_block_size ) 13.471 + { 13.472 + // Next BRR block 13.473 + int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; 13.474 + assert( brr_offset == brr_block_size ); 13.475 + if ( brr_header & 1 ) 13.476 + { 13.477 + brr_addr = SAMPLE_PTR( 1 ); 13.478 + if ( !v->kon_delay ) 13.479 + REG(endx) |= vbit; 13.480 + } 13.481 + v->brr_addr = brr_addr; 13.482 + brr_offset = 1; 13.483 + } 13.484 + v->brr_offset = brr_offset; 13.485 + 13.486 + // Decode 13.487 + 13.488 + // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 13.489 + static unsigned char const shifts [16 * 2] = { 13.490 + 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, 13.491 + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 13.492 + }; 13.493 + int const scale = brr_header >> 4; 13.494 + int const right_shift = shifts [scale]; 13.495 + int const left_shift = shifts [scale + 16]; 13.496 + 13.497 + // Write to next four samples in circular buffer 13.498 + int* pos = v->buf_pos; 13.499 + int* end; 13.500 + 13.501 + // Decode four samples 13.502 + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) 13.503 + { 13.504 + // Extract upper nybble and scale appropriately 13.505 + int s = ((int16_t) nybbles >> right_shift) << left_shift; 13.506 + 13.507 + // Apply IIR filter (8 is the most commonly used) 13.508 + int const filter = brr_header & 0x0C; 13.509 + int const p1 = pos [brr_buf_size - 1]; 13.510 + int const p2 = pos [brr_buf_size - 2] >> 1; 13.511 + if ( filter >= 8 ) 13.512 + { 13.513 + s += p1; 13.514 + s -= p2; 13.515 + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 13.516 + { 13.517 + s += p2 >> 4; 13.518 + s += (p1 * -3) >> 6; 13.519 + } 13.520 + else // s += p1 * 0.8984375 - p2 * 0.40625 13.521 + { 13.522 + s += (p1 * -13) >> 7; 13.523 + s += (p2 * 3) >> 4; 13.524 + } 13.525 + } 13.526 + else if ( filter ) // s += p1 * 0.46875 13.527 + { 13.528 + s += p1 >> 1; 13.529 + s += (-p1) >> 5; 13.530 + } 13.531 + 13.532 + // Adjust and write sample 13.533 + CLAMP16( s ); 13.534 + s = (int16_t) (s * 2); 13.535 + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around 13.536 + } 13.537 + 13.538 + if ( pos >= &v->buf [brr_buf_size] ) 13.539 + pos = v->buf; 13.540 + v->buf_pos = pos; 13.541 + } 13.542 + } 13.543 +skip_brr: 13.544 + // Next voice 13.545 + vbit <<= 1; 13.546 + v_regs += 0x10; 13.547 + v++; 13.548 + } 13.549 + while ( vbit < 0x100 ); 13.550 + 13.551 + // Echo position 13.552 + int echo_offset = m.echo_offset; 13.553 + uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF]; 13.554 + if ( !echo_offset ) 13.555 + m.echo_length = (REG(edl) & 0x0F) * 0x800; 13.556 + echo_offset += 4; 13.557 + if ( echo_offset >= m.echo_length ) 13.558 + echo_offset = 0; 13.559 + m.echo_offset = echo_offset; 13.560 + 13.561 + // FIR 13.562 + int echo_in_l = GET_LE16SA( echo_ptr + 0 ); 13.563 + int echo_in_r = GET_LE16SA( echo_ptr + 2 ); 13.564 + 13.565 + int (*echo_hist_pos) [2] = m.echo_hist_pos; 13.566 + if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] ) 13.567 + echo_hist_pos = m.echo_hist; 13.568 + m.echo_hist_pos = echo_hist_pos; 13.569 + 13.570 + echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l; 13.571 + echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r; 13.572 + 13.573 + #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10)) 13.574 + echo_in_l = CALC_FIR_( 7, echo_in_l ); 13.575 + echo_in_r = CALC_FIR_( 7, echo_in_r ); 13.576 + 13.577 + #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] ) 13.578 + #define DO_FIR( i )\ 13.579 + echo_in_l += CALC_FIR( i, 0 );\ 13.580 + echo_in_r += CALC_FIR( i, 1 ); 13.581 + DO_FIR( 0 ); 13.582 + DO_FIR( 1 ); 13.583 + DO_FIR( 2 ); 13.584 + #if defined (__MWERKS__) && __MWERKS__ < 0x3200 13.585 + __eieio(); // keeps compiler from stupidly "caching" things in memory 13.586 + #endif 13.587 + DO_FIR( 3 ); 13.588 + DO_FIR( 4 ); 13.589 + DO_FIR( 5 ); 13.590 + DO_FIR( 6 ); 13.591 + 13.592 + // Echo out 13.593 + if ( !(REG(flg) & 0x20) ) 13.594 + { 13.595 + int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14); 13.596 + int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14); 13.597 + 13.598 + // just to help pass more validation tests 13.599 + #if SPC_MORE_ACCURACY 13.600 + l &= ~1; 13.601 + r &= ~1; 13.602 + #endif 13.603 + 13.604 + CLAMP16( l ); 13.605 + CLAMP16( r ); 13.606 + 13.607 + SET_LE16A( echo_ptr + 0, l ); 13.608 + SET_LE16A( echo_ptr + 2, r ); 13.609 + } 13.610 + 13.611 + // Sound out 13.612 + int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14; 13.613 + int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14; 13.614 + 13.615 + CLAMP16( l ); 13.616 + CLAMP16( r ); 13.617 + 13.618 + if ( (REG(flg) & 0x40) ) 13.619 + { 13.620 + l = 0; 13.621 + r = 0; 13.622 + } 13.623 + 13.624 + sample_t* out = m.out; 13.625 + WRITE_SAMPLES( l, r, out ); 13.626 + m.out = out; 13.627 + } 13.628 + while ( --count ); 13.629 +} 13.630 + 13.631 + 13.632 +//// Setup 13.633 + 13.634 +void SPC_DSP::mute_voices( int mask ) 13.635 +{ 13.636 + m.mute_mask = mask; 13.637 + for ( int i = 0; i < voice_count; i++ ) 13.638 + { 13.639 + m.voices [i].enabled = (mask >> i & 1) - 1; 13.640 + update_voice_vol( i * 0x10 ); 13.641 + } 13.642 +} 13.643 + 13.644 +void SPC_DSP::init( void* ram_64k ) 13.645 +{ 13.646 + m.ram = (uint8_t*) ram_64k; 13.647 + mute_voices( 0 ); 13.648 + disable_surround( false ); 13.649 + set_output( 0, 0 ); 13.650 + reset(); 13.651 + 13.652 + #ifndef NDEBUG 13.653 + // be sure this sign-extends 13.654 + assert( (int16_t) 0x8000 == -0x8000 ); 13.655 + 13.656 + // be sure right shift preserves sign 13.657 + assert( (-1 >> 1) == -1 ); 13.658 + 13.659 + // check clamp macro 13.660 + int i; 13.661 + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); 13.662 + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); 13.663 + 13.664 + blargg_verify_byte_order(); 13.665 + #endif 13.666 +} 13.667 + 13.668 +void SPC_DSP::soft_reset_common() 13.669 +{ 13.670 + require( m.ram ); // init() must have been called already 13.671 + 13.672 + m.noise = 0x4000; 13.673 + m.echo_hist_pos = m.echo_hist; 13.674 + m.every_other_sample = 1; 13.675 + m.echo_offset = 0; 13.676 + m.phase = 0; 13.677 + 13.678 + init_counter(); 13.679 +} 13.680 + 13.681 +void SPC_DSP::soft_reset() 13.682 +{ 13.683 + REG(flg) = 0xE0; 13.684 + soft_reset_common(); 13.685 +} 13.686 + 13.687 +void SPC_DSP::load( uint8_t const regs [register_count] ) 13.688 +{ 13.689 + memcpy( m.regs, regs, sizeof m.regs ); 13.690 + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); 13.691 + 13.692 + // Internal state 13.693 + int i; 13.694 + for ( i = voice_count; --i >= 0; ) 13.695 + { 13.696 + voice_t& v = m.voices [i]; 13.697 + v.brr_offset = 1; 13.698 + v.buf_pos = v.buf; 13.699 + } 13.700 + m.new_kon = REG(kon); 13.701 + 13.702 + mute_voices( m.mute_mask ); 13.703 + soft_reset_common(); 13.704 +} 13.705 + 13.706 +void SPC_DSP::reset() { load( initial_regs ); }
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/fast_dsp/SPC_DSP.h Fri Oct 21 05:53:11 2011 -0700 14.3 @@ -0,0 +1,212 @@ 14.4 +// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) 14.5 + 14.6 +// snes_spc 0.9.0 14.7 +#ifndef SPC_DSP_H 14.8 +#define SPC_DSP_H 14.9 + 14.10 +#include "blargg_common.h" 14.11 + 14.12 +class SPC_DSP { 14.13 +public: 14.14 + typedef BOOST::uint8_t uint8_t; 14.15 + 14.16 +// Setup 14.17 + 14.18 + // Initializes DSP and has it use the 64K RAM provided 14.19 + void init( void* ram_64k ); 14.20 + 14.21 + // Sets destination for output samples. If out is NULL or out_size is 0, 14.22 + // doesn't generate any. 14.23 + typedef short sample_t; 14.24 + void set_output( sample_t* out, int out_size ); 14.25 + 14.26 + // Number of samples written to output since it was last set, always 14.27 + // a multiple of 2. Undefined if more samples were generated than 14.28 + // output buffer could hold. 14.29 + int sample_count() const; 14.30 + 14.31 +// Emulation 14.32 + 14.33 + // Resets DSP to power-on state 14.34 + void reset(); 14.35 + 14.36 + // Emulates pressing reset switch on SNES 14.37 + void soft_reset(); 14.38 + 14.39 + // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() 14.40 + // to catch the DSP up to present. 14.41 + int read ( int addr ) const; 14.42 + void write( int addr, int data ); 14.43 + 14.44 + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks 14.45 + // a pair of samples is be generated. 14.46 + void run( int clock_count ); 14.47 + 14.48 +// Sound control 14.49 + 14.50 + // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0). 14.51 + // Reduces emulation accuracy. 14.52 + enum { voice_count = 8 }; 14.53 + void mute_voices( int mask ); 14.54 + 14.55 + // If true, prevents channels and global volumes from being phase-negated 14.56 + void disable_surround( bool disable = true ); 14.57 + 14.58 +// State 14.59 + 14.60 + // Resets DSP and uses supplied values to initialize registers 14.61 + enum { register_count = 128 }; 14.62 + void load( uint8_t const regs [register_count] ); 14.63 + 14.64 +// DSP register addresses 14.65 + 14.66 + // Global registers 14.67 + enum { 14.68 + r_mvoll = 0x0C, r_mvolr = 0x1C, 14.69 + r_evoll = 0x2C, r_evolr = 0x3C, 14.70 + r_kon = 0x4C, r_koff = 0x5C, 14.71 + r_flg = 0x6C, r_endx = 0x7C, 14.72 + r_efb = 0x0D, r_pmon = 0x2D, 14.73 + r_non = 0x3D, r_eon = 0x4D, 14.74 + r_dir = 0x5D, r_esa = 0x6D, 14.75 + r_edl = 0x7D, 14.76 + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F 14.77 + }; 14.78 + 14.79 + // Voice registers 14.80 + enum { 14.81 + v_voll = 0x00, v_volr = 0x01, 14.82 + v_pitchl = 0x02, v_pitchh = 0x03, 14.83 + v_srcn = 0x04, v_adsr0 = 0x05, 14.84 + v_adsr1 = 0x06, v_gain = 0x07, 14.85 + v_envx = 0x08, v_outx = 0x09 14.86 + }; 14.87 + 14.88 +public: 14.89 + enum { extra_size = 16 }; 14.90 + sample_t* extra() { return m.extra; } 14.91 + sample_t const* out_pos() const { return m.out; } 14.92 +public: 14.93 + BLARGG_DISABLE_NOTHROW 14.94 + 14.95 + typedef BOOST::int8_t int8_t; 14.96 + typedef BOOST::int16_t int16_t; 14.97 + 14.98 + enum { echo_hist_size = 8 }; 14.99 + 14.100 + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; 14.101 + enum { brr_buf_size = 12 }; 14.102 + struct voice_t 14.103 + { 14.104 + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) 14.105 + int* buf_pos; // place in buffer where next samples will be decoded 14.106 + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) 14.107 + int brr_addr; // address of current BRR block 14.108 + int brr_offset; // current decoding offset in BRR block 14.109 + int kon_delay; // KON delay/current setup phase 14.110 + env_mode_t env_mode; 14.111 + int env; // current envelope level 14.112 + int hidden_env; // used by GAIN mode 7, very obscure quirk 14.113 + int volume [2]; // copy of volume from DSP registers, with surround disabled 14.114 + int enabled; // -1 if enabled, 0 if muted 14.115 + }; 14.116 +private: 14.117 + struct state_t 14.118 + { 14.119 + uint8_t regs [register_count]; 14.120 + 14.121 + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) 14.122 + int echo_hist [echo_hist_size * 2] [2]; 14.123 + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] 14.124 + 14.125 + int every_other_sample; // toggles every sample 14.126 + int kon; // KON value when last checked 14.127 + int noise; 14.128 + int echo_offset; // offset from ESA in echo buffer 14.129 + int echo_length; // number of bytes that echo_offset will stop at 14.130 + int phase; // next clock cycle to run (0-31) 14.131 + unsigned counters [4]; 14.132 + 14.133 + int new_kon; 14.134 + int t_koff; 14.135 + 14.136 + voice_t voices [voice_count]; 14.137 + 14.138 + unsigned* counter_select [32]; 14.139 + 14.140 + // non-emulation state 14.141 + uint8_t* ram; // 64K shared RAM between DSP and SMP 14.142 + int mute_mask; 14.143 + int surround_threshold; 14.144 + sample_t* out; 14.145 + sample_t* out_end; 14.146 + sample_t* out_begin; 14.147 + sample_t extra [extra_size]; 14.148 + }; 14.149 + state_t m; 14.150 + 14.151 + void init_counter(); 14.152 + void run_counter( int ); 14.153 + void soft_reset_common(); 14.154 + void write_outline( int addr, int data ); 14.155 + void update_voice_vol( int addr ); 14.156 +}; 14.157 + 14.158 +#include <assert.h> 14.159 + 14.160 +inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } 14.161 + 14.162 +inline int SPC_DSP::read( int addr ) const 14.163 +{ 14.164 + assert( (unsigned) addr < register_count ); 14.165 + return m.regs [addr]; 14.166 +} 14.167 + 14.168 +inline void SPC_DSP::update_voice_vol( int addr ) 14.169 +{ 14.170 + int l = (int8_t) m.regs [addr + v_voll]; 14.171 + int r = (int8_t) m.regs [addr + v_volr]; 14.172 + 14.173 + if ( l * r < m.surround_threshold ) 14.174 + { 14.175 + // signs differ, so negate those that are negative 14.176 + l ^= l >> 7; 14.177 + r ^= r >> 7; 14.178 + } 14.179 + 14.180 + voice_t& v = m.voices [addr >> 4]; 14.181 + int enabled = v.enabled; 14.182 + v.volume [0] = l & enabled; 14.183 + v.volume [1] = r & enabled; 14.184 +} 14.185 + 14.186 +inline void SPC_DSP::write( int addr, int data ) 14.187 +{ 14.188 + assert( (unsigned) addr < register_count ); 14.189 + 14.190 + m.regs [addr] = (uint8_t) data; 14.191 + int low = addr & 0x0F; 14.192 + if ( low < 0x2 ) // voice volumes 14.193 + { 14.194 + update_voice_vol( low ^ addr ); 14.195 + } 14.196 + else if ( low == 0xC ) 14.197 + { 14.198 + if ( addr == r_kon ) 14.199 + m.new_kon = (uint8_t) data; 14.200 + 14.201 + if ( addr == r_endx ) // always cleared, regardless of data written 14.202 + m.regs [r_endx] = 0; 14.203 + } 14.204 +} 14.205 + 14.206 +inline void SPC_DSP::disable_surround( bool disable ) 14.207 +{ 14.208 + m.surround_threshold = disable ? 0 : -0x4000; 14.209 +} 14.210 + 14.211 +#define SPC_NO_COPY_STATE_FUNCS 1 14.212 + 14.213 +#define SPC_LESS_ACCURATE 1 14.214 + 14.215 +#endif
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/license.txt Fri Oct 21 05:53:11 2011 -0700 15.3 @@ -0,0 +1,504 @@ 15.4 + GNU LESSER GENERAL PUBLIC LICENSE 15.5 + Version 2.1, February 1999 15.6 + 15.7 + Copyright (C) 1991, 1999 Free Software Foundation, Inc. 15.8 + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 15.9 + Everyone is permitted to copy and distribute verbatim copies 15.10 + of this license document, but changing it is not allowed. 15.11 + 15.12 +[This is the first released version of the Lesser GPL. It also counts 15.13 + as the successor of the GNU Library Public License, version 2, hence 15.14 + the version number 2.1.] 15.15 + 15.16 + Preamble 15.17 + 15.18 + The licenses for most software are designed to take away your 15.19 +freedom to share and change it. By contrast, the GNU General Public 15.20 +Licenses are intended to guarantee your freedom to share and change 15.21 +free software--to make sure the software is free for all its users. 15.22 + 15.23 + This license, the Lesser General Public License, applies to some 15.24 +specially designated software packages--typically libraries--of the 15.25 +Free Software Foundation and other authors who decide to use it. You 15.26 +can use it too, but we suggest you first think carefully about whether 15.27 +this license or the ordinary General Public License is the better 15.28 +strategy to use in any particular case, based on the explanations below. 15.29 + 15.30 + When we speak of free software, we are referring to freedom of use, 15.31 +not price. Our General Public Licenses are designed to make sure that 15.32 +you have the freedom to distribute copies of free software (and charge 15.33 +for this service if you wish); that you receive source code or can get 15.34 +it if you want it; that you can change the software and use pieces of 15.35 +it in new free programs; and that you are informed that you can do 15.36 +these things. 15.37 + 15.38 + To protect your rights, we need to make restrictions that forbid 15.39 +distributors to deny you these rights or to ask you to surrender these 15.40 +rights. These restrictions translate to certain responsibilities for 15.41 +you if you distribute copies of the library or if you modify it. 15.42 + 15.43 + For example, if you distribute copies of the library, whether gratis 15.44 +or for a fee, you must give the recipients all the rights that we gave 15.45 +you. You must make sure that they, too, receive or can get the source 15.46 +code. If you link other code with the library, you must provide 15.47 +complete object files to the recipients, so that they can relink them 15.48 +with the library after making changes to the library and recompiling 15.49 +it. And you must show them these terms so they know their rights. 15.50 + 15.51 + We protect your rights with a two-step method: (1) we copyright the 15.52 +library, and (2) we offer you this license, which gives you legal 15.53 +permission to copy, distribute and/or modify the library. 15.54 + 15.55 + To protect each distributor, we want to make it very clear that 15.56 +there is no warranty for the free library. Also, if the library is 15.57 +modified by someone else and passed on, the recipients should know 15.58 +that what they have is not the original version, so that the original 15.59 +author's reputation will not be affected by problems that might be 15.60 +introduced by others. 15.61 + 15.62 + Finally, software patents pose a constant threat to the existence of 15.63 +any free program. We wish to make sure that a company cannot 15.64 +effectively restrict the users of a free program by obtaining a 15.65 +restrictive license from a patent holder. Therefore, we insist that 15.66 +any patent license obtained for a version of the library must be 15.67 +consistent with the full freedom of use specified in this license. 15.68 + 15.69 + Most GNU software, including some libraries, is covered by the 15.70 +ordinary GNU General Public License. This license, the GNU Lesser 15.71 +General Public License, applies to certain designated libraries, and 15.72 +is quite different from the ordinary General Public License. We use 15.73 +this license for certain libraries in order to permit linking those 15.74 +libraries into non-free programs. 15.75 + 15.76 + When a program is linked with a library, whether statically or using 15.77 +a shared library, the combination of the two is legally speaking a 15.78 +combined work, a derivative of the original library. The ordinary 15.79 +General Public License therefore permits such linking only if the 15.80 +entire combination fits its criteria of freedom. The Lesser General 15.81 +Public License permits more lax criteria for linking other code with 15.82 +the library. 15.83 + 15.84 + We call this license the "Lesser" General Public License because it 15.85 +does Less to protect the user's freedom than the ordinary General 15.86 +Public License. It also provides other free software developers Less 15.87 +of an advantage over competing non-free programs. These disadvantages 15.88 +are the reason we use the ordinary General Public License for many 15.89 +libraries. However, the Lesser license provides advantages in certain 15.90 +special circumstances. 15.91 + 15.92 + For example, on rare occasions, there may be a special need to 15.93 +encourage the widest possible use of a certain library, so that it becomes 15.94 +a de-facto standard. To achieve this, non-free programs must be 15.95 +allowed to use the library. A more frequent case is that a free 15.96 +library does the same job as widely used non-free libraries. In this 15.97 +case, there is little to gain by limiting the free library to free 15.98 +software only, so we use the Lesser General Public License. 15.99 + 15.100 + In other cases, permission to use a particular library in non-free 15.101 +programs enables a greater number of people to use a large body of 15.102 +free software. For example, permission to use the GNU C Library in 15.103 +non-free programs enables many more people to use the whole GNU 15.104 +operating system, as well as its variant, the GNU/Linux operating 15.105 +system. 15.106 + 15.107 + Although the Lesser General Public License is Less protective of the 15.108 +users' freedom, it does ensure that the user of a program that is 15.109 +linked with the Library has the freedom and the wherewithal to run 15.110 +that program using a modified version of the Library. 15.111 + 15.112 + The precise terms and conditions for copying, distribution and 15.113 +modification follow. Pay close attention to the difference between a 15.114 +"work based on the library" and a "work that uses the library". The 15.115 +former contains code derived from the library, whereas the latter must 15.116 +be combined with the library in order to run. 15.117 + 15.118 + GNU LESSER GENERAL PUBLIC LICENSE 15.119 + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 15.120 + 15.121 + 0. This License Agreement applies to any software library or other 15.122 +program which contains a notice placed by the copyright holder or 15.123 +other authorized party saying it may be distributed under the terms of 15.124 +this Lesser General Public License (also called "this License"). 15.125 +Each licensee is addressed as "you". 15.126 + 15.127 + A "library" means a collection of software functions and/or data 15.128 +prepared so as to be conveniently linked with application programs 15.129 +(which use some of those functions and data) to form executables. 15.130 + 15.131 + The "Library", below, refers to any such software library or work 15.132 +which has been distributed under these terms. A "work based on the 15.133 +Library" means either the Library or any derivative work under 15.134 +copyright law: that is to say, a work containing the Library or a 15.135 +portion of it, either verbatim or with modifications and/or translated 15.136 +straightforwardly into another language. (Hereinafter, translation is 15.137 +included without limitation in the term "modification".) 15.138 + 15.139 + "Source code" for a work means the preferred form of the work for 15.140 +making modifications to it. For a library, complete source code means 15.141 +all the source code for all modules it contains, plus any associated 15.142 +interface definition files, plus the scripts used to control compilation 15.143 +and installation of the library. 15.144 + 15.145 + Activities other than copying, distribution and modification are not 15.146 +covered by this License; they are outside its scope. The act of 15.147 +running a program using the Library is not restricted, and output from 15.148 +such a program is covered only if its contents constitute a work based 15.149 +on the Library (independent of the use of the Library in a tool for 15.150 +writing it). Whether that is true depends on what the Library does 15.151 +and what the program that uses the Library does. 15.152 + 15.153 + 1. You may copy and distribute verbatim copies of the Library's 15.154 +complete source code as you receive it, in any medium, provided that 15.155 +you conspicuously and appropriately publish on each copy an 15.156 +appropriate copyright notice and disclaimer of warranty; keep intact 15.157 +all the notices that refer to this License and to the absence of any 15.158 +warranty; and distribute a copy of this License along with the 15.159 +Library. 15.160 + 15.161 + You may charge a fee for the physical act of transferring a copy, 15.162 +and you may at your option offer warranty protection in exchange for a 15.163 +fee. 15.164 + 15.165 + 2. You may modify your copy or copies of the Library or any portion 15.166 +of it, thus forming a work based on the Library, and copy and 15.167 +distribute such modifications or work under the terms of Section 1 15.168 +above, provided that you also meet all of these conditions: 15.169 + 15.170 + a) The modified work must itself be a software library. 15.171 + 15.172 + b) You must cause the files modified to carry prominent notices 15.173 + stating that you changed the files and the date of any change. 15.174 + 15.175 + c) You must cause the whole of the work to be licensed at no 15.176 + charge to all third parties under the terms of this License. 15.177 + 15.178 + d) If a facility in the modified Library refers to a function or a 15.179 + table of data to be supplied by an application program that uses 15.180 + the facility, other than as an argument passed when the facility 15.181 + is invoked, then you must make a good faith effort to ensure that, 15.182 + in the event an application does not supply such function or 15.183 + table, the facility still operates, and performs whatever part of 15.184 + its purpose remains meaningful. 15.185 + 15.186 + (For example, a function in a library to compute square roots has 15.187 + a purpose that is entirely well-defined independent of the 15.188 + application. Therefore, Subsection 2d requires that any 15.189 + application-supplied function or table used by this function must 15.190 + be optional: if the application does not supply it, the square 15.191 + root function must still compute square roots.) 15.192 + 15.193 +These requirements apply to the modified work as a whole. If 15.194 +identifiable sections of that work are not derived from the Library, 15.195 +and can be reasonably considered independent and separate works in 15.196 +themselves, then this License, and its terms, do not apply to those 15.197 +sections when you distribute them as separate works. But when you 15.198 +distribute the same sections as part of a whole which is a work based 15.199 +on the Library, the distribution of the whole must be on the terms of 15.200 +this License, whose permissions for other licensees extend to the 15.201 +entire whole, and thus to each and every part regardless of who wrote 15.202 +it. 15.203 + 15.204 +Thus, it is not the intent of this section to claim rights or contest 15.205 +your rights to work written entirely by you; rather, the intent is to 15.206 +exercise the right to control the distribution of derivative or 15.207 +collective works based on the Library. 15.208 + 15.209 +In addition, mere aggregation of another work not based on the Library 15.210 +with the Library (or with a work based on the Library) on a volume of 15.211 +a storage or distribution medium does not bring the other work under 15.212 +the scope of this License. 15.213 + 15.214 + 3. You may opt to apply the terms of the ordinary GNU General Public 15.215 +License instead of this License to a given copy of the Library. To do 15.216 +this, you must alter all the notices that refer to this License, so 15.217 +that they refer to the ordinary GNU General Public License, version 2, 15.218 +instead of to this License. (If a newer version than version 2 of the 15.219 +ordinary GNU General Public License has appeared, then you can specify 15.220 +that version instead if you wish.) Do not make any other change in 15.221 +these notices. 15.222 + 15.223 + Once this change is made in a given copy, it is irreversible for 15.224 +that copy, so the ordinary GNU General Public License applies to all 15.225 +subsequent copies and derivative works made from that copy. 15.226 + 15.227 + This option is useful when you wish to copy part of the code of 15.228 +the Library into a program that is not a library. 15.229 + 15.230 + 4. You may copy and distribute the Library (or a portion or 15.231 +derivative of it, under Section 2) in object code or executable form 15.232 +under the terms of Sections 1 and 2 above provided that you accompany 15.233 +it with the complete corresponding machine-readable source code, which 15.234 +must be distributed under the terms of Sections 1 and 2 above on a 15.235 +medium customarily used for software interchange. 15.236 + 15.237 + If distribution of object code is made by offering access to copy 15.238 +from a designated place, then offering equivalent access to copy the 15.239 +source code from the same place satisfies the requirement to 15.240 +distribute the source code, even though third parties are not 15.241 +compelled to copy the source along with the object code. 15.242 + 15.243 + 5. A program that contains no derivative of any portion of the 15.244 +Library, but is designed to work with the Library by being compiled or 15.245 +linked with it, is called a "work that uses the Library". Such a 15.246 +work, in isolation, is not a derivative work of the Library, and 15.247 +therefore falls outside the scope of this License. 15.248 + 15.249 + However, linking a "work that uses the Library" with the Library 15.250 +creates an executable that is a derivative of the Library (because it 15.251 +contains portions of the Library), rather than a "work that uses the 15.252 +library". The executable is therefore covered by this License. 15.253 +Section 6 states terms for distribution of such executables. 15.254 + 15.255 + When a "work that uses the Library" uses material from a header file 15.256 +that is part of the Library, the object code for the work may be a 15.257 +derivative work of the Library even though the source code is not. 15.258 +Whether this is true is especially significant if the work can be 15.259 +linked without the Library, or if the work is itself a library. The 15.260 +threshold for this to be true is not precisely defined by law. 15.261 + 15.262 + If such an object file uses only numerical parameters, data 15.263 +structure layouts and accessors, and small macros and small inline 15.264 +functions (ten lines or less in length), then the use of the object 15.265 +file is unrestricted, regardless of whether it is legally a derivative 15.266 +work. (Executables containing this object code plus portions of the 15.267 +Library will still fall under Section 6.) 15.268 + 15.269 + Otherwise, if the work is a derivative of the Library, you may 15.270 +distribute the object code for the work under the terms of Section 6. 15.271 +Any executables containing that work also fall under Section 6, 15.272 +whether or not they are linked directly with the Library itself. 15.273 + 15.274 + 6. As an exception to the Sections above, you may also combine or 15.275 +link a "work that uses the Library" with the Library to produce a 15.276 +work containing portions of the Library, and distribute that work 15.277 +under terms of your choice, provided that the terms permit 15.278 +modification of the work for the customer's own use and reverse 15.279 +engineering for debugging such modifications. 15.280 + 15.281 + You must give prominent notice with each copy of the work that the 15.282 +Library is used in it and that the Library and its use are covered by 15.283 +this License. You must supply a copy of this License. If the work 15.284 +during execution displays copyright notices, you must include the 15.285 +copyright notice for the Library among them, as well as a reference 15.286 +directing the user to the copy of this License. Also, you must do one 15.287 +of these things: 15.288 + 15.289 + a) Accompany the work with the complete corresponding 15.290 + machine-readable source code for the Library including whatever 15.291 + changes were used in the work (which must be distributed under 15.292 + Sections 1 and 2 above); and, if the work is an executable linked 15.293 + with the Library, with the complete machine-readable "work that 15.294 + uses the Library", as object code and/or source code, so that the 15.295 + user can modify the Library and then relink to produce a modified 15.296 + executable containing the modified Library. (It is understood 15.297 + that the user who changes the contents of definitions files in the 15.298 + Library will not necessarily be able to recompile the application 15.299 + to use the modified definitions.) 15.300 + 15.301 + b) Use a suitable shared library mechanism for linking with the 15.302 + Library. A suitable mechanism is one that (1) uses at run time a 15.303 + copy of the library already present on the user's computer system, 15.304 + rather than copying library functions into the executable, and (2) 15.305 + will operate properly with a modified version of the library, if 15.306 + the user installs one, as long as the modified version is 15.307 + interface-compatible with the version that the work was made with. 15.308 + 15.309 + c) Accompany the work with a written offer, valid for at 15.310 + least three years, to give the same user the materials 15.311 + specified in Subsection 6a, above, for a charge no more 15.312 + than the cost of performing this distribution. 15.313 + 15.314 + d) If distribution of the work is made by offering access to copy 15.315 + from a designated place, offer equivalent access to copy the above 15.316 + specified materials from the same place. 15.317 + 15.318 + e) Verify that the user has already received a copy of these 15.319 + materials or that you have already sent this user a copy. 15.320 + 15.321 + For an executable, the required form of the "work that uses the 15.322 +Library" must include any data and utility programs needed for 15.323 +reproducing the executable from it. However, as a special exception, 15.324 +the materials to be distributed need not include anything that is 15.325 +normally distributed (in either source or binary form) with the major 15.326 +components (compiler, kernel, and so on) of the operating system on 15.327 +which the executable runs, unless that component itself accompanies 15.328 +the executable. 15.329 + 15.330 + It may happen that this requirement contradicts the license 15.331 +restrictions of other proprietary libraries that do not normally 15.332 +accompany the operating system. Such a contradiction means you cannot 15.333 +use both them and the Library together in an executable that you 15.334 +distribute. 15.335 + 15.336 + 7. You may place library facilities that are a work based on the 15.337 +Library side-by-side in a single library together with other library 15.338 +facilities not covered by this License, and distribute such a combined 15.339 +library, provided that the separate distribution of the work based on 15.340 +the Library and of the other library facilities is otherwise 15.341 +permitted, and provided that you do these two things: 15.342 + 15.343 + a) Accompany the combined library with a copy of the same work 15.344 + based on the Library, uncombined with any other library 15.345 + facilities. This must be distributed under the terms of the 15.346 + Sections above. 15.347 + 15.348 + b) Give prominent notice with the combined library of the fact 15.349 + that part of it is a work based on the Library, and explaining 15.350 + where to find the accompanying uncombined form of the same work. 15.351 + 15.352 + 8. You may not copy, modify, sublicense, link with, or distribute 15.353 +the Library except as expressly provided under this License. Any 15.354 +attempt otherwise to copy, modify, sublicense, link with, or 15.355 +distribute the Library is void, and will automatically terminate your 15.356 +rights under this License. However, parties who have received copies, 15.357 +or rights, from you under this License will not have their licenses 15.358 +terminated so long as such parties remain in full compliance. 15.359 + 15.360 + 9. You are not required to accept this License, since you have not 15.361 +signed it. However, nothing else grants you permission to modify or 15.362 +distribute the Library or its derivative works. These actions are 15.363 +prohibited by law if you do not accept this License. Therefore, by 15.364 +modifying or distributing the Library (or any work based on the 15.365 +Library), you indicate your acceptance of this License to do so, and 15.366 +all its terms and conditions for copying, distributing or modifying 15.367 +the Library or works based on it. 15.368 + 15.369 + 10. Each time you redistribute the Library (or any work based on the 15.370 +Library), the recipient automatically receives a license from the 15.371 +original licensor to copy, distribute, link with or modify the Library 15.372 +subject to these terms and conditions. You may not impose any further 15.373 +restrictions on the recipients' exercise of the rights granted herein. 15.374 +You are not responsible for enforcing compliance by third parties with 15.375 +this License. 15.376 + 15.377 + 11. If, as a consequence of a court judgment or allegation of patent 15.378 +infringement or for any other reason (not limited to patent issues), 15.379 +conditions are imposed on you (whether by court order, agreement or 15.380 +otherwise) that contradict the conditions of this License, they do not 15.381 +excuse you from the conditions of this License. If you cannot 15.382 +distribute so as to satisfy simultaneously your obligations under this 15.383 +License and any other pertinent obligations, then as a consequence you 15.384 +may not distribute the Library at all. For example, if a patent 15.385 +license would not permit royalty-free redistribution of the Library by 15.386 +all those who receive copies directly or indirectly through you, then 15.387 +the only way you could satisfy both it and this License would be to 15.388 +refrain entirely from distribution of the Library. 15.389 + 15.390 +If any portion of this section is held invalid or unenforceable under any 15.391 +particular circumstance, the balance of the section is intended to apply, 15.392 +and the section as a whole is intended to apply in other circumstances. 15.393 + 15.394 +It is not the purpose of this section to induce you to infringe any 15.395 +patents or other property right claims or to contest validity of any 15.396 +such claims; this section has the sole purpose of protecting the 15.397 +integrity of the free software distribution system which is 15.398 +implemented by public license practices. Many people have made 15.399 +generous contributions to the wide range of software distributed 15.400 +through that system in reliance on consistent application of that 15.401 +system; it is up to the author/donor to decide if he or she is willing 15.402 +to distribute software through any other system and a licensee cannot 15.403 +impose that choice. 15.404 + 15.405 +This section is intended to make thoroughly clear what is believed to 15.406 +be a consequence of the rest of this License. 15.407 + 15.408 + 12. If the distribution and/or use of the Library is restricted in 15.409 +certain countries either by patents or by copyrighted interfaces, the 15.410 +original copyright holder who places the Library under this License may add 15.411 +an explicit geographical distribution limitation excluding those countries, 15.412 +so that distribution is permitted only in or among countries not thus 15.413 +excluded. In such case, this License incorporates the limitation as if 15.414 +written in the body of this License. 15.415 + 15.416 + 13. The Free Software Foundation may publish revised and/or new 15.417 +versions of the Lesser General Public License from time to time. 15.418 +Such new versions will be similar in spirit to the present version, 15.419 +but may differ in detail to address new problems or concerns. 15.420 + 15.421 +Each version is given a distinguishing version number. If the Library 15.422 +specifies a version number of this License which applies to it and 15.423 +"any later version", you have the option of following the terms and 15.424 +conditions either of that version or of any later version published by 15.425 +the Free Software Foundation. If the Library does not specify a 15.426 +license version number, you may choose any version ever published by 15.427 +the Free Software Foundation. 15.428 + 15.429 + 14. If you wish to incorporate parts of the Library into other free 15.430 +programs whose distribution conditions are incompatible with these, 15.431 +write to the author to ask for permission. For software which is 15.432 +copyrighted by the Free Software Foundation, write to the Free 15.433 +Software Foundation; we sometimes make exceptions for this. Our 15.434 +decision will be guided by the two goals of preserving the free status 15.435 +of all derivatives of our free software and of promoting the sharing 15.436 +and reuse of software generally. 15.437 + 15.438 + NO WARRANTY 15.439 + 15.440 + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 15.441 +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 15.442 +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 15.443 +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 15.444 +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 15.445 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15.446 +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 15.447 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 15.448 +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 15.449 + 15.450 + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 15.451 +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 15.452 +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 15.453 +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 15.454 +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 15.455 +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 15.456 +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 15.457 +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 15.458 +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 15.459 +DAMAGES. 15.460 + 15.461 + END OF TERMS AND CONDITIONS 15.462 + 15.463 + How to Apply These Terms to Your New Libraries 15.464 + 15.465 + If you develop a new library, and you want it to be of the greatest 15.466 +possible use to the public, we recommend making it free software that 15.467 +everyone can redistribute and change. You can do so by permitting 15.468 +redistribution under these terms (or, alternatively, under the terms of the 15.469 +ordinary General Public License). 15.470 + 15.471 + To apply these terms, attach the following notices to the library. It is 15.472 +safest to attach them to the start of each source file to most effectively 15.473 +convey the exclusion of warranty; and each file should have at least the 15.474 +"copyright" line and a pointer to where the full notice is found. 15.475 + 15.476 + <one line to give the library's name and a brief idea of what it does.> 15.477 + Copyright (C) <year> <name of author> 15.478 + 15.479 + This library is free software; you can redistribute it and/or 15.480 + modify it under the terms of the GNU Lesser General Public 15.481 + License as published by the Free Software Foundation; either 15.482 + version 2.1 of the License, or (at your option) any later version. 15.483 + 15.484 + This library is distributed in the hope that it will be useful, 15.485 + but WITHOUT ANY WARRANTY; without even the implied warranty of 15.486 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15.487 + Lesser General Public License for more details. 15.488 + 15.489 + You should have received a copy of the GNU Lesser General Public 15.490 + License along with this library; if not, write to the Free Software 15.491 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 15.492 + 15.493 +Also add information on how to contact you by electronic and paper mail. 15.494 + 15.495 +You should also get your employer (if you work as a programmer) or your 15.496 +school, if any, to sign a "copyright disclaimer" for the library, if 15.497 +necessary. Here is a sample; alter the names: 15.498 + 15.499 + Yoyodyne, Inc., hereby disclaims all copyright interest in the 15.500 + library `Frob' (a library for tweaking knobs) written by James Random Hacker. 15.501 + 15.502 + <signature of Ty Coon>, 1 April 1990 15.503 + Ty Coon, President of Vice 15.504 + 15.505 +That's all there is to it! 15.506 + 15.507 +
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/readme.txt Fri Oct 21 05:53:11 2011 -0700 16.3 @@ -0,0 +1,86 @@ 16.4 +snes_spc 0.9.0: SNES SPC-700 APU Emulator 16.5 +----------------------------------------- 16.6 +This library includes a full SPC emulator and an S-DSP emulator that can 16.7 +be used on its own. Two S-DSP emulators are available: a highly accurate 16.8 +one for use in a SNES emulator, and a 3x faster one for use in an SPC 16.9 +music player or a resource-limited SNES emulator. 16.10 + 16.11 +* Can be used from C and C++ code 16.12 +* Full SPC-700 APU emulator with cycle accuracy in most cases 16.13 +* Loads, plays, and saves SPC music files 16.14 +* Can save and load exact full emulator state 16.15 +* DSP voice muting, surround sound disable, and song tempo adjustment 16.16 +* Uses 7% CPU average on 400 MHz Mac to play an SPC using fast DSP 16.17 + 16.18 +The accurate DSP emulator is based on past research by others and 16.19 +hundreds of hours of recent research by me. It passes over a hundred 16.20 +strenuous timing and behavior validation tests that were also run on the 16.21 +SNES. As far as I know, it's the first DSP emulator with cycle accuracy, 16.22 +properly emulating every DSP register and memory access at the exact SPC 16.23 +cycle it occurs at, whereas previous DSP emulators emulated these only 16.24 +to the nearest sample (which occurs every 32 clocks). 16.25 + 16.26 +Author : Shay Green <gblargg@gmail.com> 16.27 +Website: http://www.slack.net/~ant/ 16.28 +Forum : http://groups.google.com/group/blargg-sound-libs 16.29 +License: GNU Lesser General Public License (LGPL) 16.30 + 16.31 + 16.32 +Getting Started 16.33 +--------------- 16.34 +Build a program consisting of demo/play_spc.c, demo/demo_util.c, 16.35 +demo/wave_writer.c, and all source files in snes_spc/. Put an SPC music 16.36 +file in the same directory and name it "test.spc". Running the program 16.37 +should generate the recording "out.wav". 16.38 + 16.39 +Read snes_spc.txt for more information. Post to the discussion forum for 16.40 +assistance. 16.41 + 16.42 + 16.43 +Files 16.44 +----- 16.45 +snes_spc.txt Documentation 16.46 +changes.txt Change log 16.47 +license.txt GNU LGPL license 16.48 + 16.49 +demo/ 16.50 + play_spc.c Records SPC file to wave sound file 16.51 + benchmark.c Finds how fast emulator runs on your computer 16.52 + trim_spc.c Trims silence off beginning of an SPC file 16.53 + save_state.c Saves/loads exact emulator state to/from file 16.54 + comm.c Communicates with SPC how SNES would 16.55 + demo_util.h General utility functions used by demos 16.56 + demo_util.c 16.57 + wave_writer.h WAVE sound file writer used for demo output 16.58 + wave_writer.c 16.59 + 16.60 +fast_dsp/ Optional standalone fast DSP emulator 16.61 + SPC_DSP.h To use with full SPC emulator, move into 16.62 + SPC_DSP.cpp snes_spc/ and replace original files 16.63 + 16.64 +snes_spc/ Library sources 16.65 + blargg_config.h Configuration (modify as necessary) 16.66 + 16.67 + spc.h C interface to SPC emulator and sound filter 16.68 + spc.cpp 16.69 + 16.70 + SPC_Filter.h Optional filter to make sound more authentic 16.71 + SPC_Filter.cpp 16.72 + 16.73 + SNES_SPC.h Full SPC emulator 16.74 + SNES_SPC.cpp 16.75 + SNES_SPC_misc.cpp 16.76 + SNES_SPC_state.cpp 16.77 + SPC_CPU.h 16.78 + 16.79 + dsp.h C interface to DSP emulator 16.80 + dsp.cpp 16.81 + 16.82 + SPC_DSP.h Standalone accurate DSP emulator 16.83 + SPC_DSP.cpp 16.84 + blargg_common.h 16.85 + blargg_endian.h 16.86 + blargg_source.h 16.87 + 16.88 +-- 16.89 +Shay Green <gblargg@gmail.com>
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/snes_spc.txt Fri Oct 21 05:53:11 2011 -0700 17.3 @@ -0,0 +1,318 @@ 17.4 +snes_spc 0.9.0: SNES SPC-700 APU Emulator 17.5 +----------------------------------------- 17.6 +Author : Shay Green <gblargg@gmail.com> 17.7 +Website: http://www.slack.net/~ant/ 17.8 +Forum : http://groups.google.com/group/blargg-sound-libs 17.9 +License: GNU Lesser General Public License (LGPL) 17.10 + 17.11 + 17.12 +Contents 17.13 +-------- 17.14 +* C and C++ Interfaces 17.15 +* Overview 17.16 +* Full SPC Emulation 17.17 +* DSP Emulation 17.18 +* SPC Music Playback 17.19 +* State Copying 17.20 +* Library Compilation 17.21 +* Error handling 17.22 +* Solving Problems 17.23 +* Accurate S-DSP Limitations 17.24 +* Fast S-DSP Limitations 17.25 +* S-SMP Limitations 17.26 +* To Do 17.27 +* Thanks 17.28 + 17.29 + 17.30 +C and C++ Interfaces 17.31 +-------------------- 17.32 +The library includes a C interface in spc.h and dsp.h, which can be used 17.33 +from C and C++. This C interface is referred to in the following 17.34 +documentation. If you're building this as a shared library (rather than 17.35 +linking statically), you should use the C interface since it will change 17.36 +less in future versions. 17.37 + 17.38 +The native C++ interface is in the header files SNES_SPC.h, SPC_DSP.h, 17.39 +and SPC_Filter.h, and the two interfaces can be freely mixed in C++ 17.40 +code. Conversion between the two interfaces generally follows a pattern: 17.41 + 17.42 + C interface C++ interface 17.43 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 17.44 + SNES_SPC* spc; SNES_SPC* spc; 17.45 + 17.46 + spc = spc_new(); spc = new SNES_SPC; 17.47 + 17.48 + spc_play( spc, count, buf ); spc->play( count, buf ); 17.49 + 17.50 + spc_sample_rate SNES_SPC::sample_rate 17.51 + 17.52 + spc_delete( spc ); delete spc; 17.53 + 17.54 + 17.55 +Overview 17.56 +-------- 17.57 +There are three main roles for this library: 17.58 +* Full SPC emulation in a SNES emulator 17.59 +* DSP emulation in a SNES emulator (where you emulate the SMP CPU) 17.60 +* SPC playback in an SPC music file player 17.61 + 17.62 +Each of these uses are described separately below. 17.63 + 17.64 + 17.65 +Full SPC Emulation 17.66 +------------------ 17.67 +See spc.h for full function reference (SNES_SPC.h if using C++). 17.68 + 17.69 +* Create SPC emulator with spc_new() and check for NULL. 17.70 + 17.71 +* Call spc_init_rom() with a pointer to the 64-byte IPL ROM dump (not 17.72 +included with library). 17.73 + 17.74 +* When your emulated SNES is powered on, call spc_reset(). When the 17.75 +reset switch is pressed, call spc_soft_reset(). 17.76 + 17.77 +* Call spc_set_output() with your output buffer, then do emulation. 17.78 + 17.79 +* When the SNES CPU accesses APU ports, call spc_read_port() and 17.80 +spc_write_port(). 17.81 + 17.82 +* When your emulator's timebase is going back to 0, call 17.83 +spc_end_frame(), usually at the end of a video frame or scanline. 17.84 + 17.85 +* Periodically play samples from your buffer. Use spc_sample_count() to 17.86 +find out how many samples have been written, then spc_set_output() after 17.87 +you've made more space in your buffer. 17.88 + 17.89 +* Save/load full emulator state with spc_copy_state(). 17.90 + 17.91 +* You can save as an SPC music file with spc_save_spc(). 17.92 + 17.93 +* When done, use spc_delete() to free memory. 17.94 + 17.95 + 17.96 +DSP Emulation 17.97 +------------- 17.98 +See dsp.h for full function reference (SPC_DSP.h if using C++). 17.99 + 17.100 +* Create DSP emulator with spc_dsp_new() and check for NULL. 17.101 + 17.102 +* Let the DSP know where your 64K RAM is with spc_dsp_init(). 17.103 + 17.104 +* When your emulated SNES is powered on, call spc_dsp_reset(). When the 17.105 +reset switch is pressed, call spc_dsp_soft_reset(). 17.106 + 17.107 +* Call spc_dsp_set_output() with your output buffer, then do emulation. 17.108 + 17.109 +* Use spc_dsp_run() to run DSP for specified number of clocks (1024000 17.110 +per second). Every 32 clocks a pair of samples is added to your output 17.111 +buffer. 17.112 + 17.113 +* Use spc_dsp_read() and spc_dsp_write() to handle DSP reads/writes from 17.114 +the S-SMP. Before calling these always catch the DSP up to present time 17.115 +with spc_dsp_run(). 17.116 + 17.117 +* Periodically play samples from your buffer. Use spc_dsp_sample_count() 17.118 +to find out how many samples have been written, then 17.119 +spc_dsp_set_output() after you've made more space in your buffer. 17.120 + 17.121 +* Use spc_dsp_copy_state() to save/load full DSP state. 17.122 + 17.123 +* When done, use spc_delete() to free memory. 17.124 + 17.125 + 17.126 +SPC Music Playback 17.127 +------------------ 17.128 +See spc.h for full function reference (SNES_SPC.h if using C++). 17.129 + 17.130 +* Create SPC emulator with spc_new() and check for NULL. 17.131 + 17.132 +* Load SPC with spc_load_spc() and check for error. 17.133 + 17.134 +* Optionally cear echo buffer with spc_clear_echo(). Many SPCs have 17.135 +garbage in echo buffer, which causes noise at the beginning. 17.136 + 17.137 +* Generate samples as needed with spc_play(). 17.138 + 17.139 +* When done, use spc_delete() to free memory. 17.140 + 17.141 +* For a more complete game music playback library, use Game_Music_Emu 17.142 + 17.143 + 17.144 +State Copying 17.145 +------------- 17.146 +The SPC and DSP modules include state save/load functions. They take a 17.147 +pointer to a pointer to a buffer to store state, and a copy function. 17.148 +This copy function can either copy data to the buffer or from it, 17.149 +allowing state save and restore with the same library function. The 17.150 +internal save state format allows for future expansion without making 17.151 +previous save states unusable. 17.152 + 17.153 +The SPC save state format puts the most important parts first to make it 17.154 +easier to manually examine. It's organized as follows: 17.155 + 17.156 +Offset Size Data 17.157 +- - - - - - - - - - - - - - - - - - 17.158 + 0 $10000 SPC RAM 17.159 +$10000 $10 SMP $F0-$FF registers 17.160 +$10010 4 SMP $F4-$F8 output registers 17.161 +$10014 2 PC 17.162 +$10016 1 A 17.163 +$10017 1 X 17.164 +$10018 1 Y 17.165 +$10019 1 PSW 17.166 +$1001A 1 SP 17.167 +$1001B 5 internal 17.168 +$10020 $80 DSP registers 17.169 +$100A0 ... internal 17.170 + 17.171 + 17.172 +Library Compilation 17.173 +------------------- 17.174 +While this library is in C++, it has been written to easily link in a C 17.175 +program *without* needing the standard C++ library. It doesn't use 17.176 +exception handling or run-time type information (RTTI), so you can 17.177 +disable these in your C++ compiler to increase efficiency. 17.178 + 17.179 +If you're building a shared library (DLL), I recommend only exporting 17.180 +the C interfaces in spc.h and dsp.h, as the C++ interfaces expose 17.181 +implementation details that will break link compatibility across 17.182 +versions. 17.183 + 17.184 +If you're using C and compiling with GCC, I recommend the following 17.185 +command-line options when compiling the library source, otherwise GCC 17.186 +will insert calls to the standard C++ library and require that it be 17.187 +linked in: 17.188 + 17.189 + -fno-rtti -fno-exceptions 17.190 + 17.191 +For maximum optimization, see the NDEBUG and BLARGG_NONPORTABLE options 17.192 +in blargg_config. If using GCC, you can enable these by adding the 17.193 +following command-line options when you invoke gcc. If you encounter 17.194 +problems, try without -DBLARGG_NONPORTABLE; if that works, contact me so 17.195 +I can figure out why BLARGG_NONPORTABLE was causing it to fail. 17.196 + 17.197 + -O3 -DNDEBUG -DBLARGG_NONPORTABLE -fno-rtti -fno-exceptions 17.198 + 17.199 + 17.200 + 17.201 +Error handling 17.202 +-------------- 17.203 +Functions which can fail have a return type of spc_err_t (blargg_err_t 17.204 +in the C++ interfaces), which is a pointer to an error string (const 17.205 +char*). If a function is successful it returns NULL. Errors that you can 17.206 +easily avoid are checked with debug assertions; spc_err_t return values 17.207 +are only used for genuine run-time errors that can't be easily predicted 17.208 +in advance (out of memory, I/O errors, incompatible file data). Your 17.209 +code should check all error values. 17.210 + 17.211 +To improve usability for C programmers, C++ programmers unfamiliar with 17.212 +exceptions, and compatibility with older C++ compilers, the library does 17.213 +*not* throw any C++ exceptions and uses malloc() instead of the standard 17.214 +operator new. This means that you *must* check for NULL when creating a 17.215 +library object with the new operator. 17.216 + 17.217 + 17.218 +Solving Problems 17.219 +---------------- 17.220 +If you're having problems, try the following: 17.221 + 17.222 +* If you're getting garbled sound, try this simple siren generator in 17.223 +place of your call to play(). This will quickly tell whether the problem 17.224 +is in the library or in your code. 17.225 + 17.226 + static void play_siren( long count, short* out ) 17.227 + { 17.228 + static double a, a2; 17.229 + while ( count-- ) 17.230 + *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); 17.231 + } 17.232 + 17.233 +* Enable debugging support in your environment. This enables assertions 17.234 +and other run-time checks. 17.235 + 17.236 +* Turn the compiler's optimizer is off. Sometimes an optimizer generates 17.237 +bad code. 17.238 + 17.239 +* If multiple threads are being used, ensure that only one at a time is 17.240 +accessing a given set of objects from the library. This library is not 17.241 +in general thread-safe, though independent objects can be used in 17.242 +separate threads. 17.243 + 17.244 +* If all else fails, see if the demos work. 17.245 + 17.246 + 17.247 +Accurate S-DSP Limitations 17.248 +-------------------------- 17.249 +* Power-up and soft reset behavior might have slight inaccuracies. 17.250 + 17.251 +* Muting (FLG bit 6) behavior when toggling bit very rapidly is not 17.252 +emulated properly. 17.253 + 17.254 +* No other known inaccuracies. Has passed 100+ strenuous tests. 17.255 + 17.256 + 17.257 +Fast S-DSP Limitations 17.258 +---------------------- 17.259 +* Uses faster sample calculations except in cases where exact value is 17.260 +actually important (BRR decoding, and gaussian interpolation combined 17.261 +with pitch modulation). 17.262 + 17.263 +* Stops decoding BRR data when a voice's envelope has released to 17.264 +silence. 17.265 + 17.266 +* Emulates 32 clocks at a time, so DSP register and memory accesses are 17.267 +all done in a bunch rather than spread out. Even though, some clever 17.268 +code makes register accesses separated by 40 or so clocks occur with 17.269 +cycle-accurate timing. 17.270 + 17.271 + 17.272 +S-SMP Limitations 17.273 +----------------- 17.274 +* Opcode fetches and indirect pointers are always read directly from 17.275 +memory, even for the $F0-$FF region, and the DSP is not caught up for 17.276 +these fetches. 17.277 + 17.278 +* Attempts to perversely execute data in registers or an area being 17.279 +modified by echo will not be emulated properly. 17.280 + 17.281 +* Has not been thoroughly tested. 17.282 + 17.283 +* Test register ($F0) is not implemented. 17.284 + 17.285 +* Echo buffer can overwrite IPL ROM area, and does not correctly update 17.286 +extra RAM there. 17.287 + 17.288 + 17.289 +To Do 17.290 +----- 17.291 +* I'd like feedback on the interface and any ways to improve it. In 17.292 +particular, the differing features between the accurate and fast DSP 17.293 +emulators might make it harder to cleanly switch between them without 17.294 +modifying source code. 17.295 + 17.296 +* Finish thorough tests on SMP memory access times. 17.297 + 17.298 +* Finish thorough tests on SMP instruction behavior (flags, registers). 17.299 + 17.300 +* Finish thorough tests on SMP timers. 17.301 + 17.302 +* Finish power-up and reset behavior testing. 17.303 + 17.304 +* Come up with best starting conditions to play an SPC and implement in 17.305 +hardware SNES SPC player for verification. 17.306 + 17.307 + 17.308 +Thanks 17.309 +------ 17.310 +Thanks to Anti-Resonance's SPC2ROM and help getting SPCs playing on my 17.311 +SNES in the first place, then Brad Martin's openspc and Chris Moeller's 17.312 +openspc++ C++ adaptation, giving me a good SPC emulator to start with 17.313 +several years ago. Thanks to Richard Bannister, Mahendra Tallur, Shazz, 17.314 +nenolod, theHobbit, Johan Samuelsson, nes6502, and Micket for helping 17.315 +test my Game_Music_Emu library. Thanks to hcs for help in converting to 17.316 +C for the Rockbox port. Thanks to byuu (bsnes author) and pagefault and 17.317 +Nach (zsnes team) for testing and using my new rewritten DSP in their 17.318 +emulators. Thanks to anomie for his good SNES documentation and 17.319 +discussions with me to keep it up to date with my latest findings. 17.320 +-- 17.321 +Shay Green <gblargg@gmail.com>
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/snes_spc/SNES_SPC.cpp Fri Oct 21 05:53:11 2011 -0700 18.3 @@ -0,0 +1,564 @@ 18.4 +// Core SPC emulation: CPU, timers, SMP registers, memory 18.5 + 18.6 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 18.7 + 18.8 +#include "SNES_SPC.h" 18.9 + 18.10 +#include <string.h> 18.11 + 18.12 +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you 18.13 +can redistribute it and/or modify it under the terms of the GNU Lesser 18.14 +General Public License as published by the Free Software Foundation; either 18.15 +version 2.1 of the License, or (at your option) any later version. This 18.16 +module is distributed in the hope that it will be useful, but WITHOUT ANY 18.17 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18.18 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 18.19 +details. You should have received a copy of the GNU Lesser General Public 18.20 +License along with this module; if not, write to the Free Software Foundation, 18.21 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 18.22 + 18.23 +#include "blargg_source.h" 18.24 + 18.25 +#define RAM (m.ram.ram) 18.26 +#define REGS (m.smp_regs [0]) 18.27 +#define REGS_IN (m.smp_regs [1]) 18.28 + 18.29 +// (n ? n : 256) 18.30 +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) 18.31 + 18.32 +// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which 18.33 +// do crazy echo buffer accesses. 18.34 +#ifndef SPC_MORE_ACCURACY 18.35 + #define SPC_MORE_ACCURACY 0 18.36 +#endif 18.37 + 18.38 +#ifdef BLARGG_ENABLE_OPTIMIZER 18.39 + #include BLARGG_ENABLE_OPTIMIZER 18.40 +#endif 18.41 + 18.42 + 18.43 +//// Timers 18.44 + 18.45 +#if SPC_DISABLE_TEMPO 18.46 + #define TIMER_DIV( t, n ) ((n) >> t->prescaler) 18.47 + #define TIMER_MUL( t, n ) ((n) << t->prescaler) 18.48 +#else 18.49 + #define TIMER_DIV( t, n ) ((n) / t->prescaler) 18.50 + #define TIMER_MUL( t, n ) ((n) * t->prescaler) 18.51 +#endif 18.52 + 18.53 +SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time ) 18.54 +{ 18.55 + int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; 18.56 + t->next_time += TIMER_MUL( t, elapsed ); 18.57 + 18.58 + if ( t->enabled ) 18.59 + { 18.60 + int remain = IF_0_THEN_256( t->period - t->divider ); 18.61 + int divider = t->divider + elapsed; 18.62 + int over = elapsed - remain; 18.63 + if ( over >= 0 ) 18.64 + { 18.65 + int n = over / t->period; 18.66 + t->counter = (t->counter + 1 + n) & 0x0F; 18.67 + divider = over - n * t->period; 18.68 + } 18.69 + t->divider = (uint8_t) divider; 18.70 + } 18.71 + return t; 18.72 +} 18.73 + 18.74 +inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time ) 18.75 +{ 18.76 + if ( time >= t->next_time ) 18.77 + t = run_timer_( t, time ); 18.78 + return t; 18.79 +} 18.80 + 18.81 + 18.82 +//// ROM 18.83 + 18.84 +void SNES_SPC::enable_rom( int enable ) 18.85 +{ 18.86 + if ( m.rom_enabled != enable ) 18.87 + { 18.88 + m.rom_enabled = enable; 18.89 + if ( enable ) 18.90 + memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram ); 18.91 + memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size ); 18.92 + // TODO: ROM can still get overwritten when DSP writes to echo buffer 18.93 + } 18.94 +} 18.95 + 18.96 + 18.97 +//// DSP 18.98 + 18.99 +#if SPC_LESS_ACCURATE 18.100 + int const max_reg_time = 29; 18.101 + 18.102 + signed char const SNES_SPC::reg_times_ [256] = 18.103 + { 18.104 + -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, 18.105 + 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23, 18.106 + 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23, 18.107 + 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24, 18.108 + 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24, 18.109 + 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24, 18.110 + 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25, 18.111 + 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25, 18.112 + 18.113 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.114 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.115 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.116 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.117 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.118 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.119 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.120 + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 18.121 + }; 18.122 + 18.123 + #define RUN_DSP( time, offset ) \ 18.124 + int count = (time) - (offset) - m.dsp_time;\ 18.125 + if ( count >= 0 )\ 18.126 + {\ 18.127 + int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\ 18.128 + m.dsp_time += clock_count;\ 18.129 + dsp.run( clock_count );\ 18.130 + } 18.131 +#else 18.132 + #define RUN_DSP( time, offset ) \ 18.133 + {\ 18.134 + int count = (time) - m.dsp_time;\ 18.135 + if ( !SPC_MORE_ACCURACY || count )\ 18.136 + {\ 18.137 + assert( count > 0 );\ 18.138 + m.dsp_time = (time);\ 18.139 + dsp.run( count );\ 18.140 + }\ 18.141 + } 18.142 +#endif 18.143 + 18.144 +int SNES_SPC::dsp_read( rel_time_t time ) 18.145 +{ 18.146 + RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); 18.147 + 18.148 + int result = dsp.read( REGS [r_dspaddr] & 0x7F ); 18.149 + 18.150 + #ifdef SPC_DSP_READ_HOOK 18.151 + SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); 18.152 + #endif 18.153 + 18.154 + return result; 18.155 +} 18.156 + 18.157 +inline void SNES_SPC::dsp_write( int data, rel_time_t time ) 18.158 +{ 18.159 + RUN_DSP( time, reg_times [REGS [r_dspaddr]] ) 18.160 + #if SPC_LESS_ACCURATE 18.161 + else if ( m.dsp_time == skipping_time ) 18.162 + { 18.163 + int r = REGS [r_dspaddr]; 18.164 + if ( r == SPC_DSP::r_kon ) 18.165 + m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff ); 18.166 + 18.167 + if ( r == SPC_DSP::r_koff ) 18.168 + { 18.169 + m.skipped_koff |= data; 18.170 + m.skipped_kon &= ~data; 18.171 + } 18.172 + } 18.173 + #endif 18.174 + 18.175 + #ifdef SPC_DSP_WRITE_HOOK 18.176 + SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); 18.177 + #endif 18.178 + 18.179 + if ( REGS [r_dspaddr] <= 0x7F ) 18.180 + dsp.write( REGS [r_dspaddr], data ); 18.181 + else if ( !SPC_MORE_ACCURACY ) 18.182 + dprintf( "SPC wrote to DSP register > $7F\n" ); 18.183 +} 18.184 + 18.185 + 18.186 +//// Memory access extras 18.187 + 18.188 +#if SPC_MORE_ACCURACY 18.189 + #define MEM_ACCESS( time, addr ) \ 18.190 + {\ 18.191 + if ( time >= m.dsp_time )\ 18.192 + {\ 18.193 + RUN_DSP( time, max_reg_time );\ 18.194 + }\ 18.195 + } 18.196 +#elif !defined (NDEBUG) 18.197 + // Debug-only check for read/write within echo buffer, since this might result in 18.198 + // inaccurate emulation due to the DSP not being caught up to the present. 18.199 + 18.200 + bool SNES_SPC::check_echo_access( int addr ) 18.201 + { 18.202 + if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) ) 18.203 + { 18.204 + int start = 0x100 * dsp.read( SPC_DSP::r_esa ); 18.205 + int size = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F); 18.206 + int end = start + (size ? size : 4); 18.207 + if ( start <= addr && addr < end ) 18.208 + { 18.209 + if ( !m.echo_accessed ) 18.210 + { 18.211 + m.echo_accessed = 1; 18.212 + return true; 18.213 + } 18.214 + } 18.215 + } 18.216 + return false; 18.217 + } 18.218 + 18.219 + #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); 18.220 +#else 18.221 + #define MEM_ACCESS( time, addr ) 18.222 +#endif 18.223 + 18.224 + 18.225 +//// CPU write 18.226 + 18.227 +#if SPC_MORE_ACCURACY 18.228 +static unsigned char const glitch_probs [3] [256] = 18.229 +{ 18.230 + 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, 18.231 + 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, 18.232 + 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, 18.233 + 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, 18.234 + 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, 18.235 + 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, 18.236 + 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, 18.237 + 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, 18.238 + 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, 18.239 + 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, 18.240 + 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, 18.241 + 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, 18.242 + 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, 18.243 + 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, 18.244 + 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, 18.245 + 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, 18.246 + 18.247 + 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, 18.248 + 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, 18.249 + 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, 18.250 + 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, 18.251 + 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, 18.252 + 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, 18.253 + 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, 18.254 + 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, 18.255 + 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, 18.256 + 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, 18.257 + 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, 18.258 + 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, 18.259 + 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, 18.260 + 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, 18.261 + 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, 18.262 + 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01, 18.263 + 18.264 + 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, 18.265 + 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, 18.266 + 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, 18.267 + 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, 18.268 + 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, 18.269 + 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, 18.270 + 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, 18.271 + 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, 18.272 + 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, 18.273 + 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, 18.274 + 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, 18.275 + 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, 18.276 + 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, 18.277 + 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, 18.278 + 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, 18.279 + 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, 18.280 +}; 18.281 +#endif 18.282 + 18.283 +// divided into multiple functions to keep rarely-used functionality separate 18.284 +// so often-used functionality can be optimized better by compiler 18.285 + 18.286 +// If write isn't preceded by read, data has this added to it 18.287 +int const no_read_before_write = 0x2000; 18.288 + 18.289 +void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) 18.290 +{ 18.291 + switch ( addr ) 18.292 + { 18.293 + case r_t0target: 18.294 + case r_t1target: 18.295 + case r_t2target: { 18.296 + Timer* t = &m.timers [addr - r_t0target]; 18.297 + int period = IF_0_THEN_256( data ); 18.298 + if ( t->period != period ) 18.299 + { 18.300 + t = run_timer( t, time ); 18.301 + #if SPC_MORE_ACCURACY 18.302 + // Insane behavior when target is written just after counter is 18.303 + // clocked and counter matches new period and new period isn't 1, 2, 4, or 8 18.304 + if ( t->divider == (period & 0xFF) && 18.305 + t->next_time == time + TIMER_MUL( t, 1 ) && 18.306 + ((period - 1) | ~0x0F) & period ) 18.307 + { 18.308 + //dprintf( "SPC pathological timer target write\n" ); 18.309 + 18.310 + // If the period is 3, 5, or 9, there's a probability this behavior won't occur, 18.311 + // based on the previous period 18.312 + int prob = 0xFF; 18.313 + int old_period = t->period & 0xFF; 18.314 + if ( period == 3 ) prob = glitch_probs [0] [old_period]; 18.315 + if ( period == 5 ) prob = glitch_probs [1] [old_period]; 18.316 + if ( period == 9 ) prob = glitch_probs [2] [old_period]; 18.317 + 18.318 + // The glitch suppresses incrementing of one of the counter bits, based on 18.319 + // the lowest set bit in the new period 18.320 + int b = 1; 18.321 + while ( !(period & b) ) 18.322 + b <<= 1; 18.323 + 18.324 + if ( (rand() >> 4 & 0xFF) <= prob ) 18.325 + t->divider = (t->divider - b) & 0xFF; 18.326 + } 18.327 + #endif 18.328 + t->period = period; 18.329 + } 18.330 + break; 18.331 + } 18.332 + 18.333 + case r_t0out: 18.334 + case r_t1out: 18.335 + case r_t2out: 18.336 + if ( !SPC_MORE_ACCURACY ) 18.337 + dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); 18.338 + 18.339 + if ( data < no_read_before_write / 2 ) 18.340 + run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; 18.341 + break; 18.342 + 18.343 + // Registers that act like RAM 18.344 + case 0x8: 18.345 + case 0x9: 18.346 + REGS_IN [addr] = (uint8_t) data; 18.347 + break; 18.348 + 18.349 + case r_test: 18.350 + if ( (uint8_t) data != 0x0A ) 18.351 + dprintf( "SPC wrote to test register\n" ); 18.352 + break; 18.353 + 18.354 + case r_control: 18.355 + // port clears 18.356 + if ( data & 0x10 ) 18.357 + { 18.358 + REGS_IN [r_cpuio0] = 0; 18.359 + REGS_IN [r_cpuio1] = 0; 18.360 + } 18.361 + if ( data & 0x20 ) 18.362 + { 18.363 + REGS_IN [r_cpuio2] = 0; 18.364 + REGS_IN [r_cpuio3] = 0; 18.365 + } 18.366 + 18.367 + // timers 18.368 + { 18.369 + for ( int i = 0; i < timer_count; i++ ) 18.370 + { 18.371 + Timer* t = &m.timers [i]; 18.372 + int enabled = data >> i & 1; 18.373 + if ( t->enabled != enabled ) 18.374 + { 18.375 + t = run_timer( t, time ); 18.376 + t->enabled = enabled; 18.377 + if ( enabled ) 18.378 + { 18.379 + t->divider = 0; 18.380 + t->counter = 0; 18.381 + } 18.382 + } 18.383 + } 18.384 + } 18.385 + enable_rom( data & 0x80 ); 18.386 + break; 18.387 + } 18.388 +} 18.389 + 18.390 +void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr ) 18.391 +{ 18.392 + if ( addr == r_dspdata ) // 99% 18.393 + dsp_write( data, time ); 18.394 + else 18.395 + cpu_write_smp_reg_( data, time, addr ); 18.396 +} 18.397 + 18.398 +void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time ) 18.399 +{ 18.400 + if ( i < rom_size ) 18.401 + { 18.402 + m.hi_ram [i] = (uint8_t) data; 18.403 + if ( m.rom_enabled ) 18.404 + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM 18.405 + } 18.406 + else 18.407 + { 18.408 + assert( RAM [i + rom_addr] == (uint8_t) data ); 18.409 + RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding 18.410 + cpu_write( data, i + rom_addr - 0x10000, time ); 18.411 + } 18.412 +} 18.413 + 18.414 +int const bits_in_int = CHAR_BIT * sizeof (int); 18.415 + 18.416 +void SNES_SPC::cpu_write( int data, int addr, rel_time_t time ) 18.417 +{ 18.418 + MEM_ACCESS( time, addr ) 18.419 + 18.420 + // RAM 18.421 + RAM [addr] = (uint8_t) data; 18.422 + int reg = addr - 0xF0; 18.423 + if ( reg >= 0 ) // 64% 18.424 + { 18.425 + // $F0-$FF 18.426 + if ( reg < reg_count ) // 87% 18.427 + { 18.428 + REGS [reg] = (uint8_t) data; 18.429 + 18.430 + // Ports 18.431 + #ifdef SPC_PORT_WRITE_HOOK 18.432 + if ( (unsigned) (reg - r_cpuio0) < port_count ) 18.433 + SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), 18.434 + (uint8_t) data, ®S [r_cpuio0] ); 18.435 + #endif 18.436 + 18.437 + // Registers other than $F2 and $F4-$F7 18.438 + //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 ) 18.439 + // TODO: this is a bit on the fragile side 18.440 + if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% 18.441 + cpu_write_smp_reg( data, time, reg ); 18.442 + } 18.443 + // High mem/address wrap-around 18.444 + else 18.445 + { 18.446 + reg -= rom_addr - 0xF0; 18.447 + if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around 18.448 + cpu_write_high( data, reg, time ); 18.449 + } 18.450 + } 18.451 +} 18.452 + 18.453 + 18.454 +//// CPU read 18.455 + 18.456 +inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time ) 18.457 +{ 18.458 + int result = REGS_IN [reg]; 18.459 + reg -= r_dspaddr; 18.460 + // DSP addr and data 18.461 + if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3 18.462 + { 18.463 + result = REGS [r_dspaddr]; 18.464 + if ( (unsigned) reg == 1 ) 18.465 + result = dsp_read( time ); // 0xF3 18.466 + } 18.467 + return result; 18.468 +} 18.469 + 18.470 +int SNES_SPC::cpu_read( int addr, rel_time_t time ) 18.471 +{ 18.472 + MEM_ACCESS( time, addr ) 18.473 + 18.474 + // RAM 18.475 + int result = RAM [addr]; 18.476 + int reg = addr - 0xF0; 18.477 + if ( reg >= 0 ) // 40% 18.478 + { 18.479 + reg -= 0x10; 18.480 + if ( (unsigned) reg >= 0xFF00 ) // 21% 18.481 + { 18.482 + reg += 0x10 - r_t0out; 18.483 + 18.484 + // Timers 18.485 + if ( (unsigned) reg < timer_count ) // 90% 18.486 + { 18.487 + Timer* t = &m.timers [reg]; 18.488 + if ( time >= t->next_time ) 18.489 + t = run_timer_( t, time ); 18.490 + result = t->counter; 18.491 + t->counter = 0; 18.492 + } 18.493 + // Other registers 18.494 + else if ( reg < 0 ) // 10% 18.495 + { 18.496 + result = cpu_read_smp_reg( reg + r_t0out, time ); 18.497 + } 18.498 + else // 1% 18.499 + { 18.500 + assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 ); 18.501 + result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time ); 18.502 + } 18.503 + } 18.504 + } 18.505 + 18.506 + return result; 18.507 +} 18.508 + 18.509 + 18.510 +//// Run 18.511 + 18.512 +// Prefix and suffix for CPU emulator function 18.513 +#define SPC_CPU_RUN_FUNC \ 18.514 +BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\ 18.515 +{\ 18.516 + rel_time_t rel_time = m.spc_time - end_time;\ 18.517 + assert( rel_time <= 0 );\ 18.518 + m.spc_time = end_time;\ 18.519 + m.dsp_time += rel_time;\ 18.520 + m.timers [0].next_time += rel_time;\ 18.521 + m.timers [1].next_time += rel_time;\ 18.522 + m.timers [2].next_time += rel_time; 18.523 + 18.524 +#define SPC_CPU_RUN_FUNC_END \ 18.525 + m.spc_time += rel_time;\ 18.526 + m.dsp_time -= rel_time;\ 18.527 + m.timers [0].next_time -= rel_time;\ 18.528 + m.timers [1].next_time -= rel_time;\ 18.529 + m.timers [2].next_time -= rel_time;\ 18.530 + assert( m.spc_time <= end_time );\ 18.531 + return ®S [r_cpuio0];\ 18.532 +} 18.533 + 18.534 +int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks 18.535 + 18.536 +void SNES_SPC::end_frame( time_t end_time ) 18.537 +{ 18.538 + // Catch CPU up to as close to end as possible. If final instruction 18.539 + // would exceed end, does NOT execute it and leaves m.spc_time < end. 18.540 + if ( end_time > m.spc_time ) 18.541 + run_until_( end_time ); 18.542 + 18.543 + m.spc_time -= end_time; 18.544 + m.extra_clocks += end_time; 18.545 + 18.546 + // Greatest number of clocks early that emulation can stop early due to 18.547 + // not being able to execute current instruction without going over 18.548 + // allowed time. 18.549 + assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); 18.550 + 18.551 + // Catch timers up to CPU 18.552 + for ( int i = 0; i < timer_count; i++ ) 18.553 + run_timer( &m.timers [i], 0 ); 18.554 + 18.555 + // Catch DSP up to CPU 18.556 + if ( m.dsp_time < 0 ) 18.557 + { 18.558 + RUN_DSP( 0, max_reg_time ); 18.559 + } 18.560 + 18.561 + // Save any extra samples beyond what should be generated 18.562 + if ( m.buf_begin ) 18.563 + save_extra(); 18.564 +} 18.565 + 18.566 +// Inclusion here allows static memory access functions and better optimization 18.567 +#include "SPC_CPU.h"
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/snes_spc/SNES_SPC.h Fri Oct 21 05:53:11 2011 -0700 19.3 @@ -0,0 +1,279 @@ 19.4 +// SNES SPC-700 APU emulator 19.5 + 19.6 +// snes_spc 0.9.0 19.7 +#ifndef SNES_SPC_H 19.8 +#define SNES_SPC_H 19.9 + 19.10 +#include "SPC_DSP.h" 19.11 +#include "blargg_endian.h" 19.12 + 19.13 +struct SNES_SPC { 19.14 +public: 19.15 + typedef BOOST::uint8_t uint8_t; 19.16 + 19.17 + // Must be called once before using 19.18 + blargg_err_t init(); 19.19 + 19.20 + // Sample pairs generated per second 19.21 + enum { sample_rate = 32000 }; 19.22 + 19.23 +// Emulator use 19.24 + 19.25 + // Sets IPL ROM data. Library does not include ROM data. Most SPC music files 19.26 + // don't need ROM, but a full emulator must provide this. 19.27 + enum { rom_size = 0x40 }; 19.28 + void init_rom( uint8_t const rom [rom_size] ); 19.29 + 19.30 + // Sets destination for output samples 19.31 + typedef short sample_t; 19.32 + void set_output( sample_t* out, int out_size ); 19.33 + 19.34 + // Number of samples written to output since last set 19.35 + int sample_count() const; 19.36 + 19.37 + // Resets SPC to power-on state. This resets your output buffer, so you must 19.38 + // call set_output() after this. 19.39 + void reset(); 19.40 + 19.41 + // Emulates pressing reset switch on SNES. This resets your output buffer, so 19.42 + // you must call set_output() after this. 19.43 + void soft_reset(); 19.44 + 19.45 + // 1024000 SPC clocks per second, sample pair every 32 clocks 19.46 + typedef int time_t; 19.47 + enum { clock_rate = 1024000 }; 19.48 + enum { clocks_per_sample = 32 }; 19.49 + 19.50 + // Emulated port read/write at specified time 19.51 + enum { port_count = 4 }; 19.52 + int read_port ( time_t, int port ); 19.53 + void write_port( time_t, int port, int data ); 19.54 + 19.55 + // Runs SPC to end_time and starts a new time frame at 0 19.56 + void end_frame( time_t end_time ); 19.57 + 19.58 +// Sound control 19.59 + 19.60 + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). 19.61 + // Reduces emulation accuracy. 19.62 + enum { voice_count = 8 }; 19.63 + void mute_voices( int mask ); 19.64 + 19.65 + // If true, prevents channels and global volumes from being phase-negated. 19.66 + // Only supported by fast DSP. 19.67 + void disable_surround( bool disable = true ); 19.68 + 19.69 + // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. 19.70 + enum { tempo_unit = 0x100 }; 19.71 + void set_tempo( int ); 19.72 + 19.73 +// SPC music files 19.74 + 19.75 + // Loads SPC data into emulator 19.76 + enum { spc_min_file_size = 0x10180 }; 19.77 + enum { spc_file_size = 0x10200 }; 19.78 + blargg_err_t load_spc( void const* in, long size ); 19.79 + 19.80 + // Clears echo region. Useful after loading an SPC as many have garbage in echo. 19.81 + void clear_echo(); 19.82 + 19.83 + // Plays for count samples and write samples to out. Discards samples if out 19.84 + // is NULL. Count must be a multiple of 2 since output is stereo. 19.85 + blargg_err_t play( int count, sample_t* out ); 19.86 + 19.87 + // Skips count samples. Several times faster than play() when using fast DSP. 19.88 + blargg_err_t skip( int count ); 19.89 + 19.90 +// State save/load (only available with accurate DSP) 19.91 + 19.92 +#if !SPC_NO_COPY_STATE_FUNCS 19.93 + // Saves/loads state 19.94 + enum { state_size = 67 * 1024L }; // maximum space needed when saving 19.95 + typedef SPC_DSP::copy_func_t copy_func_t; 19.96 + void copy_state( unsigned char** io, copy_func_t ); 19.97 + 19.98 + // Writes minimal header to spc_out 19.99 + static void init_header( void* spc_out ); 19.100 + 19.101 + // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. 19.102 + // Does not set up SPC header; use init_header() for that. 19.103 + void save_spc( void* spc_out ); 19.104 + 19.105 + // Returns true if new key-on events occurred since last check. Useful for 19.106 + // trimming silence while saving an SPC. 19.107 + bool check_kon(); 19.108 +#endif 19.109 + 19.110 +public: 19.111 + BLARGG_DISABLE_NOTHROW 19.112 + 19.113 + typedef BOOST::uint16_t uint16_t; 19.114 + 19.115 + // Time relative to m_spc_time. Speeds up code a bit by eliminating need to 19.116 + // constantly add m_spc_time to time from CPU. CPU uses time that ends at 19.117 + // 0 to eliminate reloading end time every instruction. It pays off. 19.118 + typedef int rel_time_t; 19.119 + 19.120 + struct Timer 19.121 + { 19.122 + rel_time_t next_time; // time of next event 19.123 + int prescaler; 19.124 + int period; 19.125 + int divider; 19.126 + int enabled; 19.127 + int counter; 19.128 + }; 19.129 + enum { reg_count = 0x10 }; 19.130 + enum { timer_count = 3 }; 19.131 + enum { extra_size = SPC_DSP::extra_size }; 19.132 + 19.133 + enum { signature_size = 35 }; 19.134 + 19.135 +private: 19.136 + SPC_DSP dsp; 19.137 + 19.138 + #if SPC_LESS_ACCURATE 19.139 + static signed char const reg_times_ [256]; 19.140 + signed char reg_times [256]; 19.141 + #endif 19.142 + 19.143 + struct state_t 19.144 + { 19.145 + Timer timers [timer_count]; 19.146 + 19.147 + uint8_t smp_regs [2] [reg_count]; 19.148 + 19.149 + struct 19.150 + { 19.151 + int pc; 19.152 + int a; 19.153 + int x; 19.154 + int y; 19.155 + int psw; 19.156 + int sp; 19.157 + } cpu_regs; 19.158 + 19.159 + rel_time_t dsp_time; 19.160 + time_t spc_time; 19.161 + bool echo_accessed; 19.162 + 19.163 + int tempo; 19.164 + int skipped_kon; 19.165 + int skipped_koff; 19.166 + const char* cpu_error; 19.167 + 19.168 + int extra_clocks; 19.169 + sample_t* buf_begin; 19.170 + sample_t const* buf_end; 19.171 + sample_t* extra_pos; 19.172 + sample_t extra_buf [extra_size]; 19.173 + 19.174 + int rom_enabled; 19.175 + uint8_t rom [rom_size]; 19.176 + uint8_t hi_ram [rom_size]; 19.177 + 19.178 + unsigned char cycle_table [256]; 19.179 + 19.180 + struct 19.181 + { 19.182 + // padding to neutralize address overflow 19.183 + union { 19.184 + uint8_t padding1 [0x100]; 19.185 + uint16_t align; // makes compiler align data for 16-bit access 19.186 + } padding1 [1]; 19.187 + uint8_t ram [0x10000]; 19.188 + uint8_t padding2 [0x100]; 19.189 + } ram; 19.190 + }; 19.191 + state_t m; 19.192 + 19.193 + enum { rom_addr = 0xFFC0 }; 19.194 + 19.195 + enum { skipping_time = 127 }; 19.196 + 19.197 + // Value that padding should be filled with 19.198 + enum { cpu_pad_fill = 0xFF }; 19.199 + 19.200 + enum { 19.201 + r_test = 0x0, r_control = 0x1, 19.202 + r_dspaddr = 0x2, r_dspdata = 0x3, 19.203 + r_cpuio0 = 0x4, r_cpuio1 = 0x5, 19.204 + r_cpuio2 = 0x6, r_cpuio3 = 0x7, 19.205 + r_f8 = 0x8, r_f9 = 0x9, 19.206 + r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, 19.207 + r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF 19.208 + }; 19.209 + 19.210 + void timers_loaded(); 19.211 + void enable_rom( int enable ); 19.212 + void reset_buf(); 19.213 + void save_extra(); 19.214 + void load_regs( uint8_t const in [reg_count] ); 19.215 + void ram_loaded(); 19.216 + void regs_loaded(); 19.217 + void reset_time_regs(); 19.218 + void reset_common( int timer_counter_init ); 19.219 + 19.220 + Timer* run_timer_ ( Timer* t, rel_time_t ); 19.221 + Timer* run_timer ( Timer* t, rel_time_t ); 19.222 + int dsp_read ( rel_time_t ); 19.223 + void dsp_write ( int data, rel_time_t ); 19.224 + void cpu_write_smp_reg_( int data, rel_time_t, int addr ); 19.225 + void cpu_write_smp_reg ( int data, rel_time_t, int addr ); 19.226 + void cpu_write_high ( int data, int i, rel_time_t ); 19.227 + void cpu_write ( int data, int addr, rel_time_t ); 19.228 + int cpu_read_smp_reg ( int i, rel_time_t ); 19.229 + int cpu_read ( int addr, rel_time_t ); 19.230 + unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); 19.231 + 19.232 + bool check_echo_access ( int addr ); 19.233 + uint8_t* run_until_( time_t end_time ); 19.234 + 19.235 + struct spc_file_t 19.236 + { 19.237 + char signature [signature_size]; 19.238 + uint8_t has_id666; 19.239 + uint8_t version; 19.240 + uint8_t pcl, pch; 19.241 + uint8_t a; 19.242 + uint8_t x; 19.243 + uint8_t y; 19.244 + uint8_t psw; 19.245 + uint8_t sp; 19.246 + char text [212]; 19.247 + uint8_t ram [0x10000]; 19.248 + uint8_t dsp [128]; 19.249 + uint8_t unused [0x40]; 19.250 + uint8_t ipl_rom [0x40]; 19.251 + }; 19.252 + 19.253 + static char const signature [signature_size + 1]; 19.254 + 19.255 + void save_regs( uint8_t out [reg_count] ); 19.256 +}; 19.257 + 19.258 +#include <assert.h> 19.259 + 19.260 +inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; } 19.261 + 19.262 +inline int SNES_SPC::read_port( time_t t, int port ) 19.263 +{ 19.264 + assert( (unsigned) port < port_count ); 19.265 + return run_until_( t ) [port]; 19.266 +} 19.267 + 19.268 +inline void SNES_SPC::write_port( time_t t, int port, int data ) 19.269 +{ 19.270 + assert( (unsigned) port < port_count ); 19.271 + run_until_( t ) [0x10 + port] = data; 19.272 +} 19.273 + 19.274 +inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); } 19.275 + 19.276 +inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); } 19.277 + 19.278 +#if !SPC_NO_COPY_STATE_FUNCS 19.279 +inline bool SNES_SPC::check_kon() { return dsp.check_kon(); } 19.280 +#endif 19.281 + 19.282 +#endif
20.1 Binary file snes_spc/SNES_SPC.h.gch has changed
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/snes_spc/SNES_SPC_misc.cpp Fri Oct 21 05:53:11 2011 -0700 21.3 @@ -0,0 +1,380 @@ 21.4 +// SPC emulation support: init, sample buffering, reset, SPC loading 21.5 + 21.6 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 21.7 + 21.8 +#include "SNES_SPC.h" 21.9 + 21.10 +#include <string.h> 21.11 + 21.12 +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you 21.13 +can redistribute it and/or modify it under the terms of the GNU Lesser 21.14 +General Public License as published by the Free Software Foundation; either 21.15 +version 2.1 of the License, or (at your option) any later version. This 21.16 +module is distributed in the hope that it will be useful, but WITHOUT ANY 21.17 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21.18 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 21.19 +details. You should have received a copy of the GNU Lesser General Public 21.20 +License along with this module; if not, write to the Free Software Foundation, 21.21 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 21.22 + 21.23 +#include "blargg_source.h" 21.24 + 21.25 +#define RAM (m.ram.ram) 21.26 +#define REGS (m.smp_regs [0]) 21.27 +#define REGS_IN (m.smp_regs [1]) 21.28 + 21.29 +// (n ? n : 256) 21.30 +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) 21.31 + 21.32 + 21.33 +//// Init 21.34 + 21.35 +blargg_err_t SNES_SPC::init() 21.36 +{ 21.37 + memset( &m, 0, sizeof m ); 21.38 + dsp.init( RAM ); 21.39 + 21.40 + m.tempo = tempo_unit; 21.41 + 21.42 + // Most SPC music doesn't need ROM, and almost all the rest only rely 21.43 + // on these two bytes 21.44 + m.rom [0x3E] = 0xFF; 21.45 + m.rom [0x3F] = 0xC0; 21.46 + 21.47 + static unsigned char const cycle_table [128] = 21.48 + {// 01 23 45 67 89 AB CD EF 21.49 + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 21.50 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1 21.51 + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2 21.52 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3 21.53 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4 21.54 + 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5 21.55 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6 21.56 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7 21.57 + 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8 21.58 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9 21.59 + 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A 21.60 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B 21.61 + 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C 21.62 + 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D 21.63 + 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E 21.64 + 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F 21.65 + }; 21.66 + 21.67 + // unpack cycle table 21.68 + for ( int i = 0; i < 128; i++ ) 21.69 + { 21.70 + int n = cycle_table [i]; 21.71 + m.cycle_table [i * 2 + 0] = n >> 4; 21.72 + m.cycle_table [i * 2 + 1] = n & 0x0F; 21.73 + } 21.74 + 21.75 + #if SPC_LESS_ACCURATE 21.76 + memcpy( reg_times, reg_times_, sizeof reg_times ); 21.77 + #endif 21.78 + 21.79 + reset(); 21.80 + return 0; 21.81 +} 21.82 + 21.83 +void SNES_SPC::init_rom( uint8_t const in [rom_size] ) 21.84 +{ 21.85 + memcpy( m.rom, in, sizeof m.rom ); 21.86 +} 21.87 + 21.88 +void SNES_SPC::set_tempo( int t ) 21.89 +{ 21.90 + m.tempo = t; 21.91 + int const timer2_shift = 4; // 64 kHz 21.92 + int const other_shift = 3; // 8 kHz 21.93 + 21.94 + #if SPC_DISABLE_TEMPO 21.95 + m.timers [2].prescaler = timer2_shift; 21.96 + m.timers [1].prescaler = timer2_shift + other_shift; 21.97 + m.timers [0].prescaler = timer2_shift + other_shift; 21.98 + #else 21.99 + if ( !t ) 21.100 + t = 1; 21.101 + int const timer2_rate = 1 << timer2_shift; 21.102 + int rate = (timer2_rate * tempo_unit + (t >> 1)) / t; 21.103 + if ( rate < timer2_rate / 4 ) 21.104 + rate = timer2_rate / 4; // max 4x tempo 21.105 + m.timers [2].prescaler = rate; 21.106 + m.timers [1].prescaler = rate << other_shift; 21.107 + m.timers [0].prescaler = rate << other_shift; 21.108 + #endif 21.109 +} 21.110 + 21.111 +// Timer registers have been loaded. Applies these to the timers. Does not 21.112 +// reset timer prescalers or dividers. 21.113 +void SNES_SPC::timers_loaded() 21.114 +{ 21.115 + int i; 21.116 + for ( i = 0; i < timer_count; i++ ) 21.117 + { 21.118 + Timer* t = &m.timers [i]; 21.119 + t->period = IF_0_THEN_256( REGS [r_t0target + i] ); 21.120 + t->enabled = REGS [r_control] >> i & 1; 21.121 + t->counter = REGS_IN [r_t0out + i] & 0x0F; 21.122 + } 21.123 + 21.124 + set_tempo( m.tempo ); 21.125 +} 21.126 + 21.127 +// Loads registers from unified 16-byte format 21.128 +void SNES_SPC::load_regs( uint8_t const in [reg_count] ) 21.129 +{ 21.130 + memcpy( REGS, in, reg_count ); 21.131 + memcpy( REGS_IN, REGS, reg_count ); 21.132 + 21.133 + // These always read back as 0 21.134 + REGS_IN [r_test ] = 0; 21.135 + REGS_IN [r_control ] = 0; 21.136 + REGS_IN [r_t0target] = 0; 21.137 + REGS_IN [r_t1target] = 0; 21.138 + REGS_IN [r_t2target] = 0; 21.139 +} 21.140 + 21.141 +// RAM was just loaded from SPC, with $F0-$FF containing SMP registers 21.142 +// and timer counts. Copies these to proper registers. 21.143 +void SNES_SPC::ram_loaded() 21.144 +{ 21.145 + m.rom_enabled = 0; 21.146 + load_regs( &RAM [0xF0] ); 21.147 + 21.148 + // Put STOP instruction around memory to catch PC underflow/overflow 21.149 + memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); 21.150 + memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 ); 21.151 +} 21.152 + 21.153 +// Registers were just loaded. Applies these new values. 21.154 +void SNES_SPC::regs_loaded() 21.155 +{ 21.156 + enable_rom( REGS [r_control] & 0x80 ); 21.157 + timers_loaded(); 21.158 +} 21.159 + 21.160 +void SNES_SPC::reset_time_regs() 21.161 +{ 21.162 + m.cpu_error = 0; 21.163 + m.echo_accessed = 0; 21.164 + m.spc_time = 0; 21.165 + m.dsp_time = 0; 21.166 + #if SPC_LESS_ACCURATE 21.167 + m.dsp_time = clocks_per_sample + 1; 21.168 + #endif 21.169 + 21.170 + for ( int i = 0; i < timer_count; i++ ) 21.171 + { 21.172 + Timer* t = &m.timers [i]; 21.173 + t->next_time = 1; 21.174 + t->divider = 0; 21.175 + } 21.176 + 21.177 + regs_loaded(); 21.178 + 21.179 + m.extra_clocks = 0; 21.180 + reset_buf(); 21.181 +} 21.182 + 21.183 +void SNES_SPC::reset_common( int timer_counter_init ) 21.184 +{ 21.185 + int i; 21.186 + for ( i = 0; i < timer_count; i++ ) 21.187 + REGS_IN [r_t0out + i] = timer_counter_init; 21.188 + 21.189 + // Run IPL ROM 21.190 + memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); 21.191 + m.cpu_regs.pc = rom_addr; 21.192 + 21.193 + REGS [r_test ] = 0x0A; 21.194 + REGS [r_control] = 0xB0; // ROM enabled, clear ports 21.195 + for ( i = 0; i < port_count; i++ ) 21.196 + REGS_IN [r_cpuio0 + i] = 0; 21.197 + 21.198 + reset_time_regs(); 21.199 +} 21.200 + 21.201 +void SNES_SPC::soft_reset() 21.202 +{ 21.203 + reset_common( 0 ); 21.204 + dsp.soft_reset(); 21.205 +} 21.206 + 21.207 +void SNES_SPC::reset() 21.208 +{ 21.209 + memset( RAM, 0xFF, 0x10000 ); 21.210 + ram_loaded(); 21.211 + reset_common( 0x0F ); 21.212 + dsp.reset(); 21.213 +} 21.214 + 21.215 +char const SNES_SPC::signature [signature_size + 1] = 21.216 + "SNES-SPC700 Sound File Data v0.30\x1A\x1A"; 21.217 + 21.218 +blargg_err_t SNES_SPC::load_spc( void const* data, long size ) 21.219 +{ 21.220 + spc_file_t const* const spc = (spc_file_t const*) data; 21.221 + 21.222 + // be sure compiler didn't insert any padding into fle_t 21.223 + assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 ); 21.224 + 21.225 + // Check signature and file size 21.226 + if ( size < signature_size || memcmp( spc, signature, 27 ) ) 21.227 + return "Not an SPC file"; 21.228 + 21.229 + if ( size < spc_min_file_size ) 21.230 + return "Corrupt SPC file"; 21.231 + 21.232 + // CPU registers 21.233 + m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; 21.234 + m.cpu_regs.a = spc->a; 21.235 + m.cpu_regs.x = spc->x; 21.236 + m.cpu_regs.y = spc->y; 21.237 + m.cpu_regs.psw = spc->psw; 21.238 + m.cpu_regs.sp = spc->sp; 21.239 + 21.240 + // RAM and registers 21.241 + memcpy( RAM, spc->ram, 0x10000 ); 21.242 + ram_loaded(); 21.243 + 21.244 + // DSP registers 21.245 + dsp.load( spc->dsp ); 21.246 + 21.247 + reset_time_regs(); 21.248 + 21.249 + return 0; 21.250 +} 21.251 + 21.252 +void SNES_SPC::clear_echo() 21.253 +{ 21.254 + if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) ) 21.255 + { 21.256 + int addr = 0x100 * dsp.read( SPC_DSP::r_esa ); 21.257 + int end = addr + 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F); 21.258 + if ( end > 0x10000 ) 21.259 + end = 0x10000; 21.260 + memset( &RAM [addr], 0xFF, end - addr ); 21.261 + } 21.262 +} 21.263 + 21.264 + 21.265 +//// Sample output 21.266 + 21.267 +void SNES_SPC::reset_buf() 21.268 +{ 21.269 + // Start with half extra buffer of silence 21.270 + sample_t* out = m.extra_buf; 21.271 + while ( out < &m.extra_buf [extra_size / 2] ) 21.272 + *out++ = 0; 21.273 + 21.274 + m.extra_pos = out; 21.275 + m.buf_begin = 0; 21.276 + 21.277 + dsp.set_output( 0, 0 ); 21.278 +} 21.279 + 21.280 +void SNES_SPC::set_output( sample_t* out, int size ) 21.281 +{ 21.282 + require( (size & 1) == 0 ); // size must be even 21.283 + 21.284 + m.extra_clocks &= clocks_per_sample - 1; 21.285 + if ( out ) 21.286 + { 21.287 + sample_t const* out_end = out + size; 21.288 + m.buf_begin = out; 21.289 + m.buf_end = out_end; 21.290 + 21.291 + // Copy extra to output 21.292 + sample_t const* in = m.extra_buf; 21.293 + while ( in < m.extra_pos && out < out_end ) 21.294 + *out++ = *in++; 21.295 + 21.296 + // Handle output being full already 21.297 + if ( out >= out_end ) 21.298 + { 21.299 + // Have DSP write to remaining extra space 21.300 + out = dsp.extra(); 21.301 + out_end = &dsp.extra() [extra_size]; 21.302 + 21.303 + // Copy any remaining extra samples as if DSP wrote them 21.304 + while ( in < m.extra_pos ) 21.305 + *out++ = *in++; 21.306 + assert( out <= out_end ); 21.307 + } 21.308 + 21.309 + dsp.set_output( out, out_end - out ); 21.310 + } 21.311 + else 21.312 + { 21.313 + reset_buf(); 21.314 + } 21.315 +} 21.316 + 21.317 +void SNES_SPC::save_extra() 21.318 +{ 21.319 + // Get end pointers 21.320 + sample_t const* main_end = m.buf_end; // end of data written to buf 21.321 + sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra() 21.322 + if ( m.buf_begin <= dsp_end && dsp_end <= main_end ) 21.323 + { 21.324 + main_end = dsp_end; 21.325 + dsp_end = dsp.extra(); // nothing in DSP's extra 21.326 + } 21.327 + 21.328 + // Copy any extra samples at these ends into extra_buf 21.329 + sample_t* out = m.extra_buf; 21.330 + sample_t const* in; 21.331 + for ( in = m.buf_begin + sample_count(); in < main_end; in++ ) 21.332 + *out++ = *in; 21.333 + for ( in = dsp.extra(); in < dsp_end ; in++ ) 21.334 + *out++ = *in; 21.335 + 21.336 + m.extra_pos = out; 21.337 + assert( out <= &m.extra_buf [extra_size] ); 21.338 +} 21.339 + 21.340 +blargg_err_t SNES_SPC::play( int count, sample_t* out ) 21.341 +{ 21.342 + require( (count & 1) == 0 ); // must be even 21.343 + if ( count ) 21.344 + { 21.345 + set_output( out, count ); 21.346 + end_frame( count * (clocks_per_sample / 2) ); 21.347 + } 21.348 + 21.349 + const char* err = m.cpu_error; 21.350 + m.cpu_error = 0; 21.351 + return err; 21.352 +} 21.353 + 21.354 +blargg_err_t SNES_SPC::skip( int count ) 21.355 +{ 21.356 + #if SPC_LESS_ACCURATE 21.357 + if ( count > 2 * sample_rate * 2 ) 21.358 + { 21.359 + set_output( 0, 0 ); 21.360 + 21.361 + // Skip a multiple of 4 samples 21.362 + time_t end = count; 21.363 + count = (count & 3) + 1 * sample_rate * 2; 21.364 + end = (end - count) * (clocks_per_sample / 2); 21.365 + 21.366 + m.skipped_kon = 0; 21.367 + m.skipped_koff = 0; 21.368 + 21.369 + // Preserve DSP and timer synchronization 21.370 + // TODO: verify that this really preserves it 21.371 + int old_dsp_time = m.dsp_time + m.spc_time; 21.372 + m.dsp_time = end - m.spc_time + skipping_time; 21.373 + end_frame( end ); 21.374 + m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; 21.375 + 21.376 + dsp.write( SPC_DSP::r_koff, m.skipped_koff & ~m.skipped_kon ); 21.377 + dsp.write( SPC_DSP::r_kon , m.skipped_kon ); 21.378 + clear_echo(); 21.379 + } 21.380 + #endif 21.381 + 21.382 + return play( count, 0 ); 21.383 +}
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/snes_spc/SNES_SPC_state.cpp Fri Oct 21 05:53:11 2011 -0700 22.3 @@ -0,0 +1,129 @@ 22.4 +// SPC emulation state save/load: copy_state(), save_spc() 22.5 +// Separate file to avoid linking in unless needed 22.6 + 22.7 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 22.8 + 22.9 +#include "SNES_SPC.h" 22.10 + 22.11 +#if !SPC_NO_COPY_STATE_FUNCS 22.12 + 22.13 +#include <string.h> 22.14 + 22.15 +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you 22.16 +can redistribute it and/or modify it under the terms of the GNU Lesser 22.17 +General Public License as published by the Free Software Foundation; either 22.18 +version 2.1 of the License, or (at your option) any later version. This 22.19 +module is distributed in the hope that it will be useful, but WITHOUT ANY 22.20 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 22.21 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 22.22 +details. You should have received a copy of the GNU Lesser General Public 22.23 +License along with this module; if not, write to the Free Software Foundation, 22.24 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 22.25 + 22.26 +#include "blargg_source.h" 22.27 + 22.28 +#define RAM (m.ram.ram) 22.29 +#define REGS (m.smp_regs [0]) 22.30 +#define REGS_IN (m.smp_regs [1]) 22.31 + 22.32 +void SNES_SPC::save_regs( uint8_t out [reg_count] ) 22.33 +{ 22.34 + // Use current timer counter values 22.35 + for ( int i = 0; i < timer_count; i++ ) 22.36 + out [r_t0out + i] = m.timers [i].counter; 22.37 + 22.38 + // Last written values 22.39 + memcpy( out, REGS, r_t0out ); 22.40 +} 22.41 + 22.42 +void SNES_SPC::init_header( void* spc_out ) 22.43 +{ 22.44 + spc_file_t* const spc = (spc_file_t*) spc_out; 22.45 + 22.46 + spc->has_id666 = 26; // has none 22.47 + spc->version = 30; 22.48 + memcpy( spc, signature, sizeof spc->signature ); 22.49 + memset( spc->text, 0, sizeof spc->text ); 22.50 +} 22.51 + 22.52 +void SNES_SPC::save_spc( void* spc_out ) 22.53 +{ 22.54 + spc_file_t* const spc = (spc_file_t*) spc_out; 22.55 + 22.56 + // CPU 22.57 + spc->pcl = (uint8_t) (m.cpu_regs.pc >> 0); 22.58 + spc->pch = (uint8_t) (m.cpu_regs.pc >> 8); 22.59 + spc->a = m.cpu_regs.a; 22.60 + spc->x = m.cpu_regs.x; 22.61 + spc->y = m.cpu_regs.y; 22.62 + spc->psw = m.cpu_regs.psw; 22.63 + spc->sp = m.cpu_regs.sp; 22.64 + 22.65 + // RAM, ROM 22.66 + memcpy( spc->ram, RAM, sizeof spc->ram ); 22.67 + if ( m.rom_enabled ) 22.68 + memcpy( spc->ram + rom_addr, m.hi_ram, sizeof m.hi_ram ); 22.69 + memset( spc->unused, 0, sizeof spc->unused ); 22.70 + memcpy( spc->ipl_rom, m.rom, sizeof spc->ipl_rom ); 22.71 + 22.72 + // SMP registers 22.73 + save_regs( &spc->ram [0xF0] ); 22.74 + int i; 22.75 + for ( i = 0; i < port_count; i++ ) 22.76 + spc->ram [0xF0 + r_cpuio0 + i] = REGS_IN [r_cpuio0 + i]; 22.77 + 22.78 + // DSP registers 22.79 + for ( i = 0; i < SPC_DSP::register_count; i++ ) 22.80 + spc->dsp [i] = dsp.read( i ); 22.81 +} 22.82 + 22.83 +void SNES_SPC::copy_state( unsigned char** io, copy_func_t copy ) 22.84 +{ 22.85 + SPC_State_Copier copier( io, copy ); 22.86 + 22.87 + // Make state data more readable by putting 64K RAM, 16 SMP registers, 22.88 + // then DSP (with its 128 registers) first 22.89 + 22.90 + // RAM 22.91 + enable_rom( 0 ); // will get re-enabled if necessary in regs_loaded() below 22.92 + copier.copy( RAM, 0x10000 ); 22.93 + 22.94 + { 22.95 + // SMP registers 22.96 + uint8_t out_ports [port_count]; 22.97 + uint8_t regs [reg_count]; 22.98 + memcpy( out_ports, ®S [r_cpuio0], sizeof out_ports ); 22.99 + save_regs( regs ); 22.100 + copier.copy( regs, sizeof regs ); 22.101 + copier.copy( out_ports, sizeof out_ports ); 22.102 + load_regs( regs ); 22.103 + regs_loaded(); 22.104 + memcpy( ®S [r_cpuio0], out_ports, sizeof out_ports ); 22.105 + } 22.106 + 22.107 + // CPU registers 22.108 + SPC_COPY( uint16_t, m.cpu_regs.pc ); 22.109 + SPC_COPY( uint8_t, m.cpu_regs.a ); 22.110 + SPC_COPY( uint8_t, m.cpu_regs.x ); 22.111 + SPC_COPY( uint8_t, m.cpu_regs.y ); 22.112 + SPC_COPY( uint8_t, m.cpu_regs.psw ); 22.113 + SPC_COPY( uint8_t, m.cpu_regs.sp ); 22.114 + copier.extra(); 22.115 + 22.116 + SPC_COPY( int16_t, m.spc_time ); 22.117 + SPC_COPY( int16_t, m.dsp_time ); 22.118 + 22.119 + // DSP 22.120 + dsp.copy_state( io, copy ); 22.121 + 22.122 + // Timers 22.123 + for ( int i = 0; i < timer_count; i++ ) 22.124 + { 22.125 + Timer* t = &m.timers [i]; 22.126 + SPC_COPY( int16_t, t->next_time ); 22.127 + SPC_COPY( uint8_t, t->divider ); 22.128 + copier.extra(); 22.129 + } 22.130 + copier.extra(); 22.131 +} 22.132 +#endif
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/snes_spc/SPC_CPU.h Fri Oct 21 05:53:11 2011 -0700 23.3 @@ -0,0 +1,1220 @@ 23.4 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 23.5 + 23.6 +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you 23.7 +can redistribute it and/or modify it under the terms of the GNU Lesser 23.8 +General Public License as published by the Free Software Foundation; either 23.9 +version 2.1 of the License, or (at your option) any later version. This 23.10 +module is distributed in the hope that it will be useful, but WITHOUT ANY 23.11 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 23.12 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 23.13 +details. You should have received a copy of the GNU Lesser General Public 23.14 +License along with this module; if not, write to the Free Software Foundation, 23.15 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 23.16 + 23.17 +//// Memory access 23.18 + 23.19 +#if SPC_MORE_ACCURACY 23.20 + #define SUSPICIOUS_OPCODE( name ) ((void) 0) 23.21 +#else 23.22 + #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) 23.23 +#endif 23.24 + 23.25 +#define CPU_READ( time, offset, addr )\ 23.26 + cpu_read( addr, time + offset ) 23.27 + 23.28 +#define CPU_WRITE( time, offset, addr, data )\ 23.29 + cpu_write( data, addr, time + offset ) 23.30 + 23.31 +#if SPC_MORE_ACCURACY 23.32 + #define CPU_READ_TIMER( time, offset, addr, out )\ 23.33 + { out = CPU_READ( time, offset, addr ); } 23.34 + 23.35 +#else 23.36 + // timers are by far the most common thing read from dp 23.37 + #define CPU_READ_TIMER( time, offset, addr_, out )\ 23.38 + {\ 23.39 + rel_time_t adj_time = time + offset;\ 23.40 + int dp_addr = addr_;\ 23.41 + int ti = dp_addr - (r_t0out + 0xF0);\ 23.42 + if ( (unsigned) ti < timer_count )\ 23.43 + {\ 23.44 + Timer* t = &m.timers [ti];\ 23.45 + if ( adj_time >= t->next_time )\ 23.46 + t = run_timer_( t, adj_time );\ 23.47 + out = t->counter;\ 23.48 + t->counter = 0;\ 23.49 + }\ 23.50 + else\ 23.51 + {\ 23.52 + out = ram [dp_addr];\ 23.53 + int i = dp_addr - 0xF0;\ 23.54 + if ( (unsigned) i < 0x10 )\ 23.55 + out = cpu_read_smp_reg( i, adj_time );\ 23.56 + }\ 23.57 + } 23.58 +#endif 23.59 + 23.60 +#define TIME_ADJ( n ) (n) 23.61 + 23.62 +#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) 23.63 +#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) 23.64 +#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) 23.65 + 23.66 +#define DP_ADDR( addr ) (dp + (addr)) 23.67 + 23.68 +#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) 23.69 +#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) 23.70 +#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) 23.71 + 23.72 +#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) 23.73 + 23.74 +#define SET_PC( n ) (pc = ram + (n)) 23.75 +#define GET_PC() (pc - ram) 23.76 +#define READ_PC( pc ) (*(pc)) 23.77 +#define READ_PC16( pc ) GET_LE16( pc ) 23.78 + 23.79 +// TODO: remove non-wrapping versions? 23.80 +#define SPC_NO_SP_WRAPAROUND 0 23.81 + 23.82 +#define SET_SP( v ) (sp = ram + 0x101 + (v)) 23.83 +#define GET_SP() (sp - 0x101 - ram) 23.84 + 23.85 +#if SPC_NO_SP_WRAPAROUND 23.86 +#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) 23.87 +#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) 23.88 +#define POP( out ) (void) ((out) = *sp++) 23.89 + 23.90 +#else 23.91 +#define PUSH16( data )\ 23.92 +{\ 23.93 + int addr = (sp -= 2) - ram;\ 23.94 + if ( addr > 0x100 )\ 23.95 + {\ 23.96 + SET_LE16( sp, data );\ 23.97 + }\ 23.98 + else\ 23.99 + {\ 23.100 + ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ 23.101 + sp [1] = (uint8_t) (data >> 8);\ 23.102 + sp += 0x100;\ 23.103 + }\ 23.104 +} 23.105 + 23.106 +#define PUSH( data )\ 23.107 +{\ 23.108 + *--sp = (uint8_t) (data);\ 23.109 + if ( sp - ram == 0x100 )\ 23.110 + sp += 0x100;\ 23.111 +} 23.112 + 23.113 +#define POP( out )\ 23.114 +{\ 23.115 + out = *sp++;\ 23.116 + if ( sp - ram == 0x201 )\ 23.117 + {\ 23.118 + out = sp [-0x101];\ 23.119 + sp -= 0x100;\ 23.120 + }\ 23.121 +} 23.122 + 23.123 +#endif 23.124 + 23.125 +#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) 23.126 + 23.127 +unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) 23.128 +{ 23.129 + unsigned addr = READ_PC16( pc ); 23.130 + unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); 23.131 + return t << 8 & 0x100; 23.132 +} 23.133 + 23.134 +//// Status flag handling 23.135 + 23.136 +// Hex value in name to clarify code and bit shifting. 23.137 +// Flag stored in indicated variable during emulation 23.138 +int const n80 = 0x80; // nz 23.139 +int const v40 = 0x40; // psw 23.140 +int const p20 = 0x20; // dp 23.141 +int const b10 = 0x10; // psw 23.142 +int const h08 = 0x08; // psw 23.143 +int const i04 = 0x04; // psw 23.144 +int const z02 = 0x02; // nz 23.145 +int const c01 = 0x01; // c 23.146 + 23.147 +int const nz_neg_mask = 0x880; // either bit set indicates N flag set 23.148 + 23.149 +#define GET_PSW( out )\ 23.150 +{\ 23.151 + out = psw & ~(n80 | p20 | z02 | c01);\ 23.152 + out |= c >> 8 & c01;\ 23.153 + out |= dp >> 3 & p20;\ 23.154 + out |= ((nz >> 4) | nz) & n80;\ 23.155 + if ( !(uint8_t) nz ) out |= z02;\ 23.156 +} 23.157 + 23.158 +#define SET_PSW( in )\ 23.159 +{\ 23.160 + psw = in;\ 23.161 + c = in << 8;\ 23.162 + dp = in << 3 & 0x100;\ 23.163 + nz = (in << 4 & 0x800) | (~in & z02);\ 23.164 +} 23.165 + 23.166 +SPC_CPU_RUN_FUNC 23.167 +{ 23.168 + uint8_t* const ram = RAM; 23.169 + int a = m.cpu_regs.a; 23.170 + int x = m.cpu_regs.x; 23.171 + int y = m.cpu_regs.y; 23.172 + uint8_t const* pc; 23.173 + uint8_t* sp; 23.174 + int psw; 23.175 + int c; 23.176 + int nz; 23.177 + int dp; 23.178 + 23.179 + SET_PC( m.cpu_regs.pc ); 23.180 + SET_SP( m.cpu_regs.sp ); 23.181 + SET_PSW( m.cpu_regs.psw ); 23.182 + 23.183 + goto loop; 23.184 + 23.185 + 23.186 + // Main loop 23.187 + 23.188 +cbranch_taken_loop: 23.189 + pc += *(BOOST::int8_t const*) pc; 23.190 +inc_pc_loop: 23.191 + pc++; 23.192 +loop: 23.193 +{ 23.194 + unsigned opcode; 23.195 + unsigned data; 23.196 + 23.197 + check( (unsigned) a < 0x100 ); 23.198 + check( (unsigned) x < 0x100 ); 23.199 + check( (unsigned) y < 0x100 ); 23.200 + 23.201 + opcode = *pc; 23.202 + if ( (rel_time += m.cycle_table [opcode]) > 0 ) 23.203 + goto out_of_time; 23.204 + 23.205 + #ifdef SPC_CPU_OPCODE_HOOK 23.206 + SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); 23.207 + #endif 23.208 + /* 23.209 + //SUB_CASE_COUNTER( 1 ); 23.210 + #define PROFILE_TIMER_LOOP( op, addr, len )\ 23.211 + if ( opcode == op )\ 23.212 + {\ 23.213 + int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ 23.214 + pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ 23.215 + SUB_CASE_COUNTER( op && cond );\ 23.216 + } 23.217 + 23.218 + PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); 23.219 + PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); 23.220 + PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); 23.221 + */ 23.222 + 23.223 + // TODO: if PC is at end of memory, this will get wrong operand (very obscure) 23.224 + data = *++pc; 23.225 + switch ( opcode ) 23.226 + { 23.227 + 23.228 +// Common instructions 23.229 + 23.230 +#define BRANCH( cond )\ 23.231 +{\ 23.232 + pc++;\ 23.233 + pc += (BOOST::int8_t) data;\ 23.234 + if ( cond )\ 23.235 + goto loop;\ 23.236 + pc -= (BOOST::int8_t) data;\ 23.237 + rel_time -= 2;\ 23.238 + goto loop;\ 23.239 +} 23.240 + 23.241 + case 0xF0: // BEQ 23.242 + BRANCH( !(uint8_t) nz ) // 89% taken 23.243 + 23.244 + case 0xD0: // BNE 23.245 + BRANCH( (uint8_t) nz ) 23.246 + 23.247 + case 0x3F:{// CALL 23.248 + int old_addr = GET_PC() + 2; 23.249 + SET_PC( READ_PC16( pc ) ); 23.250 + PUSH16( old_addr ); 23.251 + goto loop; 23.252 + } 23.253 + 23.254 + case 0x6F:// RET 23.255 + #if SPC_NO_SP_WRAPAROUND 23.256 + { 23.257 + SET_PC( GET_LE16( sp ) ); 23.258 + sp += 2; 23.259 + } 23.260 + #else 23.261 + { 23.262 + int addr = sp - ram; 23.263 + SET_PC( GET_LE16( sp ) ); 23.264 + sp += 2; 23.265 + if ( addr < 0x1FF ) 23.266 + goto loop; 23.267 + 23.268 + SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); 23.269 + sp -= 0x100; 23.270 + } 23.271 + #endif 23.272 + goto loop; 23.273 + 23.274 + case 0xE4: // MOV a,dp 23.275 + ++pc; 23.276 + // 80% from timer 23.277 + READ_DP_TIMER( 0, data, a = nz ); 23.278 + goto loop; 23.279 + 23.280 + case 0xFA:{// MOV dp,dp 23.281 + int temp; 23.282 + READ_DP_TIMER( -2, data, temp ); 23.283 + data = temp + no_read_before_write ; 23.284 + } 23.285 + // fall through 23.286 + case 0x8F:{// MOV dp,#imm 23.287 + int temp = READ_PC( pc + 1 ); 23.288 + pc += 2; 23.289 + 23.290 + #if !SPC_MORE_ACCURACY 23.291 + { 23.292 + int i = dp + temp; 23.293 + ram [i] = (uint8_t) data; 23.294 + i -= 0xF0; 23.295 + if ( (unsigned) i < 0x10 ) // 76% 23.296 + { 23.297 + REGS [i] = (uint8_t) data; 23.298 + 23.299 + // Registers other than $F2 and $F4-$F7 23.300 + //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) 23.301 + if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% 23.302 + cpu_write_smp_reg( data, rel_time, i ); 23.303 + } 23.304 + } 23.305 + #else 23.306 + WRITE_DP( 0, temp, data ); 23.307 + #endif 23.308 + goto loop; 23.309 + } 23.310 + 23.311 + case 0xC4: // MOV dp,a 23.312 + ++pc; 23.313 + #if !SPC_MORE_ACCURACY 23.314 + { 23.315 + int i = dp + data; 23.316 + ram [i] = (uint8_t) a; 23.317 + i -= 0xF0; 23.318 + if ( (unsigned) i < 0x10 ) // 39% 23.319 + { 23.320 + unsigned sel = i - 2; 23.321 + REGS [i] = (uint8_t) a; 23.322 + 23.323 + if ( sel == 1 ) // 51% $F3 23.324 + dsp_write( a, rel_time ); 23.325 + else if ( sel > 1 ) // 1% not $F2 or $F3 23.326 + cpu_write_smp_reg_( a, rel_time, i ); 23.327 + } 23.328 + } 23.329 + #else 23.330 + WRITE_DP( 0, data, a ); 23.331 + #endif 23.332 + goto loop; 23.333 + 23.334 +#define CASE( n ) case n: 23.335 + 23.336 +// Define common address modes based on opcode for immediate mode. Execution 23.337 +// ends with data set to the address of the operand. 23.338 +#define ADDR_MODES_( op )\ 23.339 + CASE( op - 0x02 ) /* (X) */\ 23.340 + data = x + dp;\ 23.341 + pc--;\ 23.342 + goto end_##op;\ 23.343 + CASE( op + 0x0F ) /* (dp)+Y */\ 23.344 + data = READ_PROG16( data + dp ) + y;\ 23.345 + goto end_##op;\ 23.346 + CASE( op - 0x01 ) /* (dp+X) */\ 23.347 + data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ 23.348 + goto end_##op;\ 23.349 + CASE( op + 0x0E ) /* abs+Y */\ 23.350 + data += y;\ 23.351 + goto abs_##op;\ 23.352 + CASE( op + 0x0D ) /* abs+X */\ 23.353 + data += x;\ 23.354 + CASE( op - 0x03 ) /* abs */\ 23.355 + abs_##op:\ 23.356 + data += 0x100 * READ_PC( ++pc );\ 23.357 + goto end_##op;\ 23.358 + CASE( op + 0x0C ) /* dp+X */\ 23.359 + data = (uint8_t) (data + x); 23.360 + 23.361 +#define ADDR_MODES_NO_DP( op )\ 23.362 + ADDR_MODES_( op )\ 23.363 + data += dp;\ 23.364 + end_##op: 23.365 + 23.366 +#define ADDR_MODES( op )\ 23.367 + ADDR_MODES_( op )\ 23.368 + CASE( op - 0x04 ) /* dp */\ 23.369 + data += dp;\ 23.370 + end_##op: 23.371 + 23.372 +// 1. 8-bit Data Transmission Commands. Group I 23.373 + 23.374 + ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr 23.375 + a = nz = READ( 0, data ); 23.376 + goto inc_pc_loop; 23.377 + 23.378 + case 0xBF:{// MOV A,(X)+ 23.379 + int temp = x + dp; 23.380 + x = (uint8_t) (x + 1); 23.381 + a = nz = READ( -1, temp ); 23.382 + goto loop; 23.383 + } 23.384 + 23.385 + case 0xE8: // MOV A,imm 23.386 + a = data; 23.387 + nz = data; 23.388 + goto inc_pc_loop; 23.389 + 23.390 + case 0xF9: // MOV X,dp+Y 23.391 + data = (uint8_t) (data + y); 23.392 + case 0xF8: // MOV X,dp 23.393 + READ_DP_TIMER( 0, data, x = nz ); 23.394 + goto inc_pc_loop; 23.395 + 23.396 + case 0xE9: // MOV X,abs 23.397 + data = READ_PC16( pc ); 23.398 + ++pc; 23.399 + data = READ( 0, data ); 23.400 + case 0xCD: // MOV X,imm 23.401 + x = data; 23.402 + nz = data; 23.403 + goto inc_pc_loop; 23.404 + 23.405 + case 0xFB: // MOV Y,dp+X 23.406 + data = (uint8_t) (data + x); 23.407 + case 0xEB: // MOV Y,dp 23.408 + // 70% from timer 23.409 + pc++; 23.410 + READ_DP_TIMER( 0, data, y = nz ); 23.411 + goto loop; 23.412 + 23.413 + case 0xEC:{// MOV Y,abs 23.414 + int temp = READ_PC16( pc ); 23.415 + pc += 2; 23.416 + READ_TIMER( 0, temp, y = nz ); 23.417 + //y = nz = READ( 0, temp ); 23.418 + goto loop; 23.419 + } 23.420 + 23.421 + case 0x8D: // MOV Y,imm 23.422 + y = data; 23.423 + nz = data; 23.424 + goto inc_pc_loop; 23.425 + 23.426 +// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 23.427 + 23.428 + ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A 23.429 + WRITE( 0, data, a ); 23.430 + goto inc_pc_loop; 23.431 + 23.432 + { 23.433 + int temp; 23.434 + case 0xCC: // MOV abs,Y 23.435 + temp = y; 23.436 + goto mov_abs_temp; 23.437 + case 0xC9: // MOV abs,X 23.438 + temp = x; 23.439 + mov_abs_temp: 23.440 + WRITE( 0, READ_PC16( pc ), temp ); 23.441 + pc += 2; 23.442 + goto loop; 23.443 + } 23.444 + 23.445 + case 0xD9: // MOV dp+Y,X 23.446 + data = (uint8_t) (data + y); 23.447 + case 0xD8: // MOV dp,X 23.448 + WRITE( 0, data + dp, x ); 23.449 + goto inc_pc_loop; 23.450 + 23.451 + case 0xDB: // MOV dp+X,Y 23.452 + data = (uint8_t) (data + x); 23.453 + case 0xCB: // MOV dp,Y 23.454 + WRITE( 0, data + dp, y ); 23.455 + goto inc_pc_loop; 23.456 + 23.457 +// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. 23.458 + 23.459 + case 0x7D: // MOV A,X 23.460 + a = x; 23.461 + nz = x; 23.462 + goto loop; 23.463 + 23.464 + case 0xDD: // MOV A,Y 23.465 + a = y; 23.466 + nz = y; 23.467 + goto loop; 23.468 + 23.469 + case 0x5D: // MOV X,A 23.470 + x = a; 23.471 + nz = a; 23.472 + goto loop; 23.473 + 23.474 + case 0xFD: // MOV Y,A 23.475 + y = a; 23.476 + nz = a; 23.477 + goto loop; 23.478 + 23.479 + case 0x9D: // MOV X,SP 23.480 + x = nz = GET_SP(); 23.481 + goto loop; 23.482 + 23.483 + case 0xBD: // MOV SP,X 23.484 + SET_SP( x ); 23.485 + goto loop; 23.486 + 23.487 + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) 23.488 + 23.489 + case 0xAF: // MOV (X)+,A 23.490 + WRITE_DP( 0, x, a + no_read_before_write ); 23.491 + x++; 23.492 + goto loop; 23.493 + 23.494 +// 5. 8-BIT LOGIC OPERATION COMMANDS 23.495 + 23.496 +#define LOGICAL_OP( op, func )\ 23.497 + ADDR_MODES( op ) /* addr */\ 23.498 + data = READ( 0, data );\ 23.499 + case op: /* imm */\ 23.500 + nz = a func##= data;\ 23.501 + goto inc_pc_loop;\ 23.502 + { unsigned addr;\ 23.503 + case op + 0x11: /* X,Y */\ 23.504 + data = READ_DP( -2, y );\ 23.505 + addr = x + dp;\ 23.506 + goto addr_##op;\ 23.507 + case op + 0x01: /* dp,dp */\ 23.508 + data = READ_DP( -3, data );\ 23.509 + case op + 0x10:{/*dp,imm*/\ 23.510 + uint8_t const* addr2 = pc + 1;\ 23.511 + pc += 2;\ 23.512 + addr = READ_PC( addr2 ) + dp;\ 23.513 + }\ 23.514 + addr_##op:\ 23.515 + nz = data func READ( -1, addr );\ 23.516 + WRITE( 0, addr, nz );\ 23.517 + goto loop;\ 23.518 + } 23.519 + 23.520 + LOGICAL_OP( 0x28, & ); // AND 23.521 + 23.522 + LOGICAL_OP( 0x08, | ); // OR 23.523 + 23.524 + LOGICAL_OP( 0x48, ^ ); // EOR 23.525 + 23.526 +// 4. 8-BIT ARITHMETIC OPERATION COMMANDS 23.527 + 23.528 + ADDR_MODES( 0x68 ) // CMP addr 23.529 + data = READ( 0, data ); 23.530 + case 0x68: // CMP imm 23.531 + nz = a - data; 23.532 + c = ~nz; 23.533 + nz &= 0xFF; 23.534 + goto inc_pc_loop; 23.535 + 23.536 + case 0x79: // CMP (X),(Y) 23.537 + data = READ_DP( -2, y ); 23.538 + nz = READ_DP( -1, x ) - data; 23.539 + c = ~nz; 23.540 + nz &= 0xFF; 23.541 + goto loop; 23.542 + 23.543 + case 0x69: // CMP dp,dp 23.544 + data = READ_DP( -3, data ); 23.545 + case 0x78: // CMP dp,imm 23.546 + nz = READ_DP( -1, READ_PC( ++pc ) ) - data; 23.547 + c = ~nz; 23.548 + nz &= 0xFF; 23.549 + goto inc_pc_loop; 23.550 + 23.551 + case 0x3E: // CMP X,dp 23.552 + data += dp; 23.553 + goto cmp_x_addr; 23.554 + case 0x1E: // CMP X,abs 23.555 + data = READ_PC16( pc ); 23.556 + pc++; 23.557 + cmp_x_addr: 23.558 + data = READ( 0, data ); 23.559 + case 0xC8: // CMP X,imm 23.560 + nz = x - data; 23.561 + c = ~nz; 23.562 + nz &= 0xFF; 23.563 + goto inc_pc_loop; 23.564 + 23.565 + case 0x7E: // CMP Y,dp 23.566 + data += dp; 23.567 + goto cmp_y_addr; 23.568 + case 0x5E: // CMP Y,abs 23.569 + data = READ_PC16( pc ); 23.570 + pc++; 23.571 + cmp_y_addr: 23.572 + data = READ( 0, data ); 23.573 + case 0xAD: // CMP Y,imm 23.574 + nz = y - data; 23.575 + c = ~nz; 23.576 + nz &= 0xFF; 23.577 + goto inc_pc_loop; 23.578 + 23.579 + { 23.580 + int addr; 23.581 + case 0xB9: // SBC (x),(y) 23.582 + case 0x99: // ADC (x),(y) 23.583 + pc--; // compensate for inc later 23.584 + data = READ_DP( -2, y ); 23.585 + addr = x + dp; 23.586 + goto adc_addr; 23.587 + case 0xA9: // SBC dp,dp 23.588 + case 0x89: // ADC dp,dp 23.589 + data = READ_DP( -3, data ); 23.590 + case 0xB8: // SBC dp,imm 23.591 + case 0x98: // ADC dp,imm 23.592 + addr = READ_PC( ++pc ) + dp; 23.593 + adc_addr: 23.594 + nz = READ( -1, addr ); 23.595 + goto adc_data; 23.596 + 23.597 +// catch ADC and SBC together, then decode later based on operand 23.598 +#undef CASE 23.599 +#define CASE( n ) case n: case (n) + 0x20: 23.600 + ADDR_MODES( 0x88 ) // ADC/SBC addr 23.601 + data = READ( 0, data ); 23.602 + case 0xA8: // SBC imm 23.603 + case 0x88: // ADC imm 23.604 + addr = -1; // A 23.605 + nz = a; 23.606 + adc_data: { 23.607 + int flags; 23.608 + if ( opcode >= 0xA0 ) // SBC 23.609 + data ^= 0xFF; 23.610 + 23.611 + flags = data ^ nz; 23.612 + nz += data + (c >> 8 & 1); 23.613 + flags ^= nz; 23.614 + 23.615 + psw = (psw & ~(v40 | h08)) | 23.616 + (flags >> 1 & h08) | 23.617 + ((flags + 0x80) >> 2 & v40); 23.618 + c = nz; 23.619 + if ( addr < 0 ) 23.620 + { 23.621 + a = (uint8_t) nz; 23.622 + goto inc_pc_loop; 23.623 + } 23.624 + WRITE( 0, addr, /*(uint8_t)*/ nz ); 23.625 + goto inc_pc_loop; 23.626 + } 23.627 + 23.628 + } 23.629 + 23.630 +// 6. ADDITION & SUBTRACTION COMMANDS 23.631 + 23.632 +#define INC_DEC_REG( reg, op )\ 23.633 + nz = reg op;\ 23.634 + reg = (uint8_t) nz;\ 23.635 + goto loop; 23.636 + 23.637 + case 0xBC: INC_DEC_REG( a, + 1 ) // INC A 23.638 + case 0x3D: INC_DEC_REG( x, + 1 ) // INC X 23.639 + case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y 23.640 + 23.641 + case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A 23.642 + case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X 23.643 + case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y 23.644 + 23.645 + case 0x9B: // DEC dp+X 23.646 + case 0xBB: // INC dp+X 23.647 + data = (uint8_t) (data + x); 23.648 + case 0x8B: // DEC dp 23.649 + case 0xAB: // INC dp 23.650 + data += dp; 23.651 + goto inc_abs; 23.652 + case 0x8C: // DEC abs 23.653 + case 0xAC: // INC abs 23.654 + data = READ_PC16( pc ); 23.655 + pc++; 23.656 + inc_abs: 23.657 + nz = (opcode >> 4 & 2) - 1; 23.658 + nz += READ( -1, data ); 23.659 + WRITE( 0, data, /*(uint8_t)*/ nz ); 23.660 + goto inc_pc_loop; 23.661 + 23.662 +// 7. SHIFT, ROTATION COMMANDS 23.663 + 23.664 + case 0x5C: // LSR A 23.665 + c = 0; 23.666 + case 0x7C:{// ROR A 23.667 + nz = (c >> 1 & 0x80) | (a >> 1); 23.668 + c = a << 8; 23.669 + a = nz; 23.670 + goto loop; 23.671 + } 23.672 + 23.673 + case 0x1C: // ASL A 23.674 + c = 0; 23.675 + case 0x3C:{// ROL A 23.676 + int temp = c >> 8 & 1; 23.677 + c = a << 1; 23.678 + nz = c | temp; 23.679 + a = (uint8_t) nz; 23.680 + goto loop; 23.681 + } 23.682 + 23.683 + case 0x0B: // ASL dp 23.684 + c = 0; 23.685 + data += dp; 23.686 + goto rol_mem; 23.687 + case 0x1B: // ASL dp+X 23.688 + c = 0; 23.689 + case 0x3B: // ROL dp+X 23.690 + data = (uint8_t) (data + x); 23.691 + case 0x2B: // ROL dp 23.692 + data += dp; 23.693 + goto rol_mem; 23.694 + case 0x0C: // ASL abs 23.695 + c = 0; 23.696 + case 0x2C: // ROL abs 23.697 + data = READ_PC16( pc ); 23.698 + pc++; 23.699 + rol_mem: 23.700 + nz = c >> 8 & 1; 23.701 + nz |= (c = READ( -1, data ) << 1); 23.702 + WRITE( 0, data, /*(uint8_t)*/ nz ); 23.703 + goto inc_pc_loop; 23.704 + 23.705 + case 0x4B: // LSR dp 23.706 + c = 0; 23.707 + data += dp; 23.708 + goto ror_mem; 23.709 + case 0x5B: // LSR dp+X 23.710 + c = 0; 23.711 + case 0x7B: // ROR dp+X 23.712 + data = (uint8_t) (data + x); 23.713 + case 0x6B: // ROR dp 23.714 + data += dp; 23.715 + goto ror_mem; 23.716 + case 0x4C: // LSR abs 23.717 + c = 0; 23.718 + case 0x6C: // ROR abs 23.719 + data = READ_PC16( pc ); 23.720 + pc++; 23.721 + ror_mem: { 23.722 + int temp = READ( -1, data ); 23.723 + nz = (c >> 1 & 0x80) | (temp >> 1); 23.724 + c = temp << 8; 23.725 + WRITE( 0, data, nz ); 23.726 + goto inc_pc_loop; 23.727 + } 23.728 + 23.729 + case 0x9F: // XCN 23.730 + nz = a = (a >> 4) | (uint8_t) (a << 4); 23.731 + goto loop; 23.732 + 23.733 +// 8. 16-BIT TRANSMISION COMMANDS 23.734 + 23.735 + case 0xBA: // MOVW YA,dp 23.736 + a = READ_DP( -2, data ); 23.737 + nz = (a & 0x7F) | (a >> 1); 23.738 + y = READ_DP( 0, (uint8_t) (data + 1) ); 23.739 + nz |= y; 23.740 + goto inc_pc_loop; 23.741 + 23.742 + case 0xDA: // MOVW dp,YA 23.743 + WRITE_DP( -1, data, a ); 23.744 + WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); 23.745 + goto inc_pc_loop; 23.746 + 23.747 +// 9. 16-BIT OPERATION COMMANDS 23.748 + 23.749 + case 0x3A: // INCW dp 23.750 + case 0x1A:{// DECW dp 23.751 + int temp; 23.752 + // low byte 23.753 + data += dp; 23.754 + temp = READ( -3, data ); 23.755 + temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW 23.756 + nz = ((temp >> 1) | temp) & 0x7F; 23.757 + WRITE( -2, data, /*(uint8_t)*/ temp ); 23.758 + 23.759 + // high byte 23.760 + data = (uint8_t) (data + 1) + dp; 23.761 + temp = (uint8_t) ((temp >> 8) + READ( -1, data )); 23.762 + nz |= temp; 23.763 + WRITE( 0, data, temp ); 23.764 + 23.765 + goto inc_pc_loop; 23.766 + } 23.767 + 23.768 + case 0x7A: // ADDW YA,dp 23.769 + case 0x9A:{// SUBW YA,dp 23.770 + int lo = READ_DP( -2, data ); 23.771 + int hi = READ_DP( 0, (uint8_t) (data + 1) ); 23.772 + int result; 23.773 + int flags; 23.774 + 23.775 + if ( opcode == 0x9A ) // SUBW 23.776 + { 23.777 + lo = (lo ^ 0xFF) + 1; 23.778 + hi ^= 0xFF; 23.779 + } 23.780 + 23.781 + lo += a; 23.782 + result = y + hi + (lo >> 8); 23.783 + flags = hi ^ y ^ result; 23.784 + 23.785 + psw = (psw & ~(v40 | h08)) | 23.786 + (flags >> 1 & h08) | 23.787 + ((flags + 0x80) >> 2 & v40); 23.788 + c = result; 23.789 + a = (uint8_t) lo; 23.790 + result = (uint8_t) result; 23.791 + y = result; 23.792 + nz = (((lo >> 1) | lo) & 0x7F) | result; 23.793 + 23.794 + goto inc_pc_loop; 23.795 + } 23.796 + 23.797 + case 0x5A: { // CMPW YA,dp 23.798 + int temp = a - READ_DP( -1, data ); 23.799 + nz = ((temp >> 1) | temp) & 0x7F; 23.800 + temp = y + (temp >> 8); 23.801 + temp -= READ_DP( 0, (uint8_t) (data + 1) ); 23.802 + nz |= temp; 23.803 + c = ~temp; 23.804 + nz &= 0xFF; 23.805 + goto inc_pc_loop; 23.806 + } 23.807 + 23.808 +// 10. MULTIPLICATION & DIVISON COMMANDS 23.809 + 23.810 + case 0xCF: { // MUL YA 23.811 + unsigned temp = y * a; 23.812 + a = (uint8_t) temp; 23.813 + nz = ((temp >> 1) | temp) & 0x7F; 23.814 + y = temp >> 8; 23.815 + nz |= y; 23.816 + goto loop; 23.817 + } 23.818 + 23.819 + case 0x9E: // DIV YA,X 23.820 + { 23.821 + unsigned ya = y * 0x100 + a; 23.822 + 23.823 + psw &= ~(h08 | v40); 23.824 + 23.825 + if ( y >= x ) 23.826 + psw |= v40; 23.827 + 23.828 + if ( (y & 15) >= (x & 15) ) 23.829 + psw |= h08; 23.830 + 23.831 + if ( y < x * 2 ) 23.832 + { 23.833 + a = ya / x; 23.834 + y = ya - a * x; 23.835 + } 23.836 + else 23.837 + { 23.838 + a = 255 - (ya - x * 0x200) / (256 - x); 23.839 + y = x + (ya - x * 0x200) % (256 - x); 23.840 + } 23.841 + 23.842 + nz = (uint8_t) a; 23.843 + a = (uint8_t) a; 23.844 + 23.845 + goto loop; 23.846 + } 23.847 + 23.848 +// 11. DECIMAL COMPENSATION COMMANDS 23.849 + 23.850 + case 0xDF: // DAA 23.851 + SUSPICIOUS_OPCODE( "DAA" ); 23.852 + if ( a > 0x99 || c & 0x100 ) 23.853 + { 23.854 + a += 0x60; 23.855 + c = 0x100; 23.856 + } 23.857 + 23.858 + if ( (a & 0x0F) > 9 || psw & h08 ) 23.859 + a += 0x06; 23.860 + 23.861 + nz = a; 23.862 + a = (uint8_t) a; 23.863 + goto loop; 23.864 + 23.865 + case 0xBE: // DAS 23.866 + SUSPICIOUS_OPCODE( "DAS" ); 23.867 + if ( a > 0x99 || !(c & 0x100) ) 23.868 + { 23.869 + a -= 0x60; 23.870 + c = 0; 23.871 + } 23.872 + 23.873 + if ( (a & 0x0F) > 9 || !(psw & h08) ) 23.874 + a -= 0x06; 23.875 + 23.876 + nz = a; 23.877 + a = (uint8_t) a; 23.878 + goto loop; 23.879 + 23.880 +// 12. BRANCHING COMMANDS 23.881 + 23.882 + case 0x2F: // BRA rel 23.883 + pc += (BOOST::int8_t) data; 23.884 + goto inc_pc_loop; 23.885 + 23.886 + case 0x30: // BMI 23.887 + BRANCH( (nz & nz_neg_mask) ) 23.888 + 23.889 + case 0x10: // BPL 23.890 + BRANCH( !(nz & nz_neg_mask) ) 23.891 + 23.892 + case 0xB0: // BCS 23.893 + BRANCH( c & 0x100 ) 23.894 + 23.895 + case 0x90: // BCC 23.896 + BRANCH( !(c & 0x100) ) 23.897 + 23.898 + case 0x70: // BVS 23.899 + BRANCH( psw & v40 ) 23.900 + 23.901 + case 0x50: // BVC 23.902 + BRANCH( !(psw & v40) ) 23.903 + 23.904 + #define CBRANCH( cond )\ 23.905 + {\ 23.906 + pc++;\ 23.907 + if ( cond )\ 23.908 + goto cbranch_taken_loop;\ 23.909 + rel_time -= 2;\ 23.910 + goto inc_pc_loop;\ 23.911 + } 23.912 + 23.913 + case 0x03: // BBS dp.bit,rel 23.914 + case 0x23: 23.915 + case 0x43: 23.916 + case 0x63: 23.917 + case 0x83: 23.918 + case 0xA3: 23.919 + case 0xC3: 23.920 + case 0xE3: 23.921 + CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) 23.922 + 23.923 + case 0x13: // BBC dp.bit,rel 23.924 + case 0x33: 23.925 + case 0x53: 23.926 + case 0x73: 23.927 + case 0x93: 23.928 + case 0xB3: 23.929 + case 0xD3: 23.930 + case 0xF3: 23.931 + CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) 23.932 + 23.933 + case 0xDE: // CBNE dp+X,rel 23.934 + data = (uint8_t) (data + x); 23.935 + // fall through 23.936 + case 0x2E:{// CBNE dp,rel 23.937 + int temp; 23.938 + // 61% from timer 23.939 + READ_DP_TIMER( -4, data, temp ); 23.940 + CBRANCH( temp != a ) 23.941 + } 23.942 + 23.943 + case 0x6E: { // DBNZ dp,rel 23.944 + unsigned temp = READ_DP( -4, data ) - 1; 23.945 + WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); 23.946 + CBRANCH( temp ) 23.947 + } 23.948 + 23.949 + case 0xFE: // DBNZ Y,rel 23.950 + y = (uint8_t) (y - 1); 23.951 + BRANCH( y ) 23.952 + 23.953 + case 0x1F: // JMP [abs+X] 23.954 + SET_PC( READ_PC16( pc ) + x ); 23.955 + // fall through 23.956 + case 0x5F: // JMP abs 23.957 + SET_PC( READ_PC16( pc ) ); 23.958 + goto loop; 23.959 + 23.960 +// 13. SUB-ROUTINE CALL RETURN COMMANDS 23.961 + 23.962 + case 0x0F:{// BRK 23.963 + int temp; 23.964 + int ret_addr = GET_PC(); 23.965 + SUSPICIOUS_OPCODE( "BRK" ); 23.966 + SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified 23.967 + PUSH16( ret_addr ); 23.968 + GET_PSW( temp ); 23.969 + psw = (psw | b10) & ~i04; 23.970 + PUSH( temp ); 23.971 + goto loop; 23.972 + } 23.973 + 23.974 + case 0x4F:{// PCALL offset 23.975 + int ret_addr = GET_PC() + 1; 23.976 + SET_PC( 0xFF00 | data ); 23.977 + PUSH16( ret_addr ); 23.978 + goto loop; 23.979 + } 23.980 + 23.981 + case 0x01: // TCALL n 23.982 + case 0x11: 23.983 + case 0x21: 23.984 + case 0x31: 23.985 + case 0x41: 23.986 + case 0x51: 23.987 + case 0x61: 23.988 + case 0x71: 23.989 + case 0x81: 23.990 + case 0x91: 23.991 + case 0xA1: 23.992 + case 0xB1: 23.993 + case 0xC1: 23.994 + case 0xD1: 23.995 + case 0xE1: 23.996 + case 0xF1: { 23.997 + int ret_addr = GET_PC(); 23.998 + SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); 23.999 + PUSH16( ret_addr ); 23.1000 + goto loop; 23.1001 + } 23.1002 + 23.1003 +// 14. STACK OPERATION COMMANDS 23.1004 + 23.1005 + { 23.1006 + int temp; 23.1007 + case 0x7F: // RET1 23.1008 + temp = *sp; 23.1009 + SET_PC( GET_LE16( sp + 1 ) ); 23.1010 + sp += 3; 23.1011 + goto set_psw; 23.1012 + case 0x8E: // POP PSW 23.1013 + POP( temp ); 23.1014 + set_psw: 23.1015 + SET_PSW( temp ); 23.1016 + goto loop; 23.1017 + } 23.1018 + 23.1019 + case 0x0D: { // PUSH PSW 23.1020 + int temp; 23.1021 + GET_PSW( temp ); 23.1022 + PUSH( temp ); 23.1023 + goto loop; 23.1024 + } 23.1025 + 23.1026 + case 0x2D: // PUSH A 23.1027 + PUSH( a ); 23.1028 + goto loop; 23.1029 + 23.1030 + case 0x4D: // PUSH X 23.1031 + PUSH( x ); 23.1032 + goto loop; 23.1033 + 23.1034 + case 0x6D: // PUSH Y 23.1035 + PUSH( y ); 23.1036 + goto loop; 23.1037 + 23.1038 + case 0xAE: // POP A 23.1039 + POP( a ); 23.1040 + goto loop; 23.1041 + 23.1042 + case 0xCE: // POP X 23.1043 + POP( x ); 23.1044 + goto loop; 23.1045 + 23.1046 + case 0xEE: // POP Y 23.1047 + POP( y ); 23.1048 + goto loop; 23.1049 + 23.1050 +// 15. BIT OPERATION COMMANDS 23.1051 + 23.1052 + case 0x02: // SET1 23.1053 + case 0x22: 23.1054 + case 0x42: 23.1055 + case 0x62: 23.1056 + case 0x82: 23.1057 + case 0xA2: 23.1058 + case 0xC2: 23.1059 + case 0xE2: 23.1060 + case 0x12: // CLR1 23.1061 + case 0x32: 23.1062 + case 0x52: 23.1063 + case 0x72: 23.1064 + case 0x92: 23.1065 + case 0xB2: 23.1066 + case 0xD2: 23.1067 + case 0xF2: { 23.1068 + int bit = 1 << (opcode >> 5); 23.1069 + int mask = ~bit; 23.1070 + if ( opcode & 0x10 ) 23.1071 + bit = 0; 23.1072 + data += dp; 23.1073 + WRITE( 0, data, (READ( -1, data ) & mask) | bit ); 23.1074 + goto inc_pc_loop; 23.1075 + } 23.1076 + 23.1077 + case 0x0E: // TSET1 abs 23.1078 + case 0x4E: // TCLR1 abs 23.1079 + data = READ_PC16( pc ); 23.1080 + pc += 2; 23.1081 + { 23.1082 + unsigned temp = READ( -2, data ); 23.1083 + nz = (uint8_t) (a - temp); 23.1084 + temp &= ~a; 23.1085 + if ( opcode == 0x0E ) 23.1086 + temp |= a; 23.1087 + WRITE( 0, data, temp ); 23.1088 + } 23.1089 + goto loop; 23.1090 + 23.1091 + case 0x4A: // AND1 C,mem.bit 23.1092 + c &= MEM_BIT( 0 ); 23.1093 + pc += 2; 23.1094 + goto loop; 23.1095 + 23.1096 + case 0x6A: // AND1 C,/mem.bit 23.1097 + c &= ~MEM_BIT( 0 ); 23.1098 + pc += 2; 23.1099 + goto loop; 23.1100 + 23.1101 + case 0x0A: // OR1 C,mem.bit 23.1102 + c |= MEM_BIT( -1 ); 23.1103 + pc += 2; 23.1104 + goto loop; 23.1105 + 23.1106 + case 0x2A: // OR1 C,/mem.bit 23.1107 + c |= ~MEM_BIT( -1 ); 23.1108 + pc += 2; 23.1109 + goto loop; 23.1110 + 23.1111 + case 0x8A: // EOR1 C,mem.bit 23.1112 + c ^= MEM_BIT( -1 ); 23.1113 + pc += 2; 23.1114 + goto loop; 23.1115 + 23.1116 + case 0xEA: // NOT1 mem.bit 23.1117 + data = READ_PC16( pc ); 23.1118 + pc += 2; 23.1119 + { 23.1120 + unsigned temp = READ( -1, data & 0x1FFF ); 23.1121 + temp ^= 1 << (data >> 13); 23.1122 + WRITE( 0, data & 0x1FFF, temp ); 23.1123 + } 23.1124 + goto loop; 23.1125 + 23.1126 + case 0xCA: // MOV1 mem.bit,C 23.1127 + data = READ_PC16( pc ); 23.1128 + pc += 2; 23.1129 + { 23.1130 + unsigned temp = READ( -2, data & 0x1FFF ); 23.1131 + unsigned bit = data >> 13; 23.1132 + temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); 23.1133 + WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); 23.1134 + } 23.1135 + goto loop; 23.1136 + 23.1137 + case 0xAA: // MOV1 C,mem.bit 23.1138 + c = MEM_BIT( 0 ); 23.1139 + pc += 2; 23.1140 + goto loop; 23.1141 + 23.1142 +// 16. PROGRAM PSW FLAG OPERATION COMMANDS 23.1143 + 23.1144 + case 0x60: // CLRC 23.1145 + c = 0; 23.1146 + goto loop; 23.1147 + 23.1148 + case 0x80: // SETC 23.1149 + c = ~0; 23.1150 + goto loop; 23.1151 + 23.1152 + case 0xED: // NOTC 23.1153 + c ^= 0x100; 23.1154 + goto loop; 23.1155 + 23.1156 + case 0xE0: // CLRV 23.1157 + psw &= ~(v40 | h08); 23.1158 + goto loop; 23.1159 + 23.1160 + case 0x20: // CLRP 23.1161 + dp = 0; 23.1162 + goto loop; 23.1163 + 23.1164 + case 0x40: // SETP 23.1165 + dp = 0x100; 23.1166 + goto loop; 23.1167 + 23.1168 + case 0xA0: // EI 23.1169 + SUSPICIOUS_OPCODE( "EI" ); 23.1170 + psw |= i04; 23.1171 + goto loop; 23.1172 + 23.1173 + case 0xC0: // DI 23.1174 + SUSPICIOUS_OPCODE( "DI" ); 23.1175 + psw &= ~i04; 23.1176 + goto loop; 23.1177 + 23.1178 +// 17. OTHER COMMANDS 23.1179 + 23.1180 + case 0x00: // NOP 23.1181 + goto loop; 23.1182 + 23.1183 + case 0xFF:{// STOP 23.1184 + // handle PC wrap-around 23.1185 + unsigned addr = GET_PC() - 1; 23.1186 + if ( addr >= 0x10000 ) 23.1187 + { 23.1188 + addr &= 0xFFFF; 23.1189 + SET_PC( addr ); 23.1190 + dprintf( "SPC: PC wrapped around\n" ); 23.1191 + goto loop; 23.1192 + } 23.1193 + } 23.1194 + // fall through 23.1195 + case 0xEF: // SLEEP 23.1196 + SUSPICIOUS_OPCODE( "STOP/SLEEP" ); 23.1197 + --pc; 23.1198 + rel_time = 0; 23.1199 + m.cpu_error = "SPC emulation error"; 23.1200 + goto stop; 23.1201 + } // switch 23.1202 + 23.1203 + assert( 0 ); // catch any unhandled instructions 23.1204 +} 23.1205 +out_of_time: 23.1206 + rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode 23.1207 +stop: 23.1208 + 23.1209 + // Uncache registers 23.1210 + if ( GET_PC() >= 0x10000 ) 23.1211 + dprintf( "SPC: PC wrapped around\n" ); 23.1212 + m.cpu_regs.pc = (uint16_t) GET_PC(); 23.1213 + m.cpu_regs.sp = ( uint8_t) GET_SP(); 23.1214 + m.cpu_regs.a = ( uint8_t) a; 23.1215 + m.cpu_regs.x = ( uint8_t) x; 23.1216 + m.cpu_regs.y = ( uint8_t) y; 23.1217 + { 23.1218 + int temp; 23.1219 + GET_PSW( temp ); 23.1220 + m.cpu_regs.psw = (uint8_t) temp; 23.1221 + } 23.1222 +} 23.1223 +SPC_CPU_RUN_FUNC_END
24.1 Binary file snes_spc/SPC_CPU.h.gch has changed
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 25.2 +++ b/snes_spc/SPC_DSP.cpp Fri Oct 21 05:53:11 2011 -0700 25.3 @@ -0,0 +1,1018 @@ 25.4 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 25.5 + 25.6 +#include "SPC_DSP.h" 25.7 + 25.8 +#include "blargg_endian.h" 25.9 +#include <string.h> 25.10 + 25.11 +/* Copyright (C) 2007 Shay Green. This module is free software; you 25.12 +can redistribute it and/or modify it under the terms of the GNU Lesser 25.13 +General Public License as published by the Free Software Foundation; either 25.14 +version 2.1 of the License, or (at your option) any later version. This 25.15 +module is distributed in the hope that it will be useful, but WITHOUT ANY 25.16 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 25.17 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 25.18 +details. You should have received a copy of the GNU Lesser General Public 25.19 +License along with this module; if not, write to the Free Software Foundation, 25.20 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 25.21 + 25.22 +#include "blargg_source.h" 25.23 + 25.24 +#ifdef BLARGG_ENABLE_OPTIMIZER 25.25 + #include BLARGG_ENABLE_OPTIMIZER 25.26 +#endif 25.27 + 25.28 +#if INT_MAX < 0x7FFFFFFF 25.29 + #error "Requires that int type have at least 32 bits" 25.30 +#endif 25.31 + 25.32 +// TODO: add to blargg_endian.h 25.33 +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) 25.34 +#define GET_LE16A( addr ) GET_LE16( addr ) 25.35 +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) 25.36 + 25.37 +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = 25.38 +{ 25.39 + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, 25.40 + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, 25.41 + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, 25.42 + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, 25.43 + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, 25.44 + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, 25.45 + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, 25.46 + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF 25.47 +}; 25.48 + 25.49 +// if ( io < -32768 ) io = -32768; 25.50 +// if ( io > 32767 ) io = 32767; 25.51 +#define CLAMP16( io )\ 25.52 +{\ 25.53 + if ( (int16_t) io != io )\ 25.54 + io = (io >> 31) ^ 0x7FFF;\ 25.55 +} 25.56 + 25.57 +// Access global DSP register 25.58 +#define REG(n) m.regs [r_##n] 25.59 + 25.60 +// Access voice DSP register 25.61 +#define VREG(r,n) r [v_##n] 25.62 + 25.63 +#define WRITE_SAMPLES( l, r, out ) \ 25.64 +{\ 25.65 + out [0] = l;\ 25.66 + out [1] = r;\ 25.67 + out += 2;\ 25.68 + if ( out >= m.out_end )\ 25.69 + {\ 25.70 + check( out == m.out_end );\ 25.71 + check( m.out_end != &m.extra [extra_size] || \ 25.72 + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ 25.73 + out = m.extra;\ 25.74 + m.out_end = &m.extra [extra_size];\ 25.75 + }\ 25.76 +}\ 25.77 + 25.78 +void SPC_DSP::set_output( sample_t* out, int size ) 25.79 +{ 25.80 + require( (size & 1) == 0 ); // must be even 25.81 + if ( !out ) 25.82 + { 25.83 + out = m.extra; 25.84 + size = extra_size; 25.85 + } 25.86 + m.out_begin = out; 25.87 + m.out = out; 25.88 + m.out_end = out + size; 25.89 +} 25.90 + 25.91 +// Volume registers and efb are signed! Easy to forget int8_t cast. 25.92 +// Prefixes are to avoid accidental use of locals with same names. 25.93 + 25.94 +// Gaussian interpolation 25.95 + 25.96 +static short const gauss [512] = 25.97 +{ 25.98 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25.99 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 25.100 + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 25.101 + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 25.102 + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 25.103 + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, 25.104 + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, 25.105 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 25.106 + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, 25.107 + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, 25.108 + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, 25.109 + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, 25.110 + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, 25.111 + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, 25.112 + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, 25.113 + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, 25.114 + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, 25.115 + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, 25.116 + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, 25.117 + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, 25.118 + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, 25.119 + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, 25.120 + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, 25.121 + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, 25.122 + 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, 25.123 +1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, 25.124 +1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, 25.125 +1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, 25.126 +1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, 25.127 +1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, 25.128 +1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, 25.129 +1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, 25.130 +}; 25.131 + 25.132 +inline int SPC_DSP::interpolate( voice_t const* v ) 25.133 +{ 25.134 + // Make pointers into gaussian based on fractional position between samples 25.135 + int offset = v->interp_pos >> 4 & 0xFF; 25.136 + short const* fwd = gauss + 255 - offset; 25.137 + short const* rev = gauss + offset; // mirror left half of gaussian 25.138 + 25.139 + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; 25.140 + int out; 25.141 + out = (fwd [ 0] * in [0]) >> 11; 25.142 + out += (fwd [256] * in [1]) >> 11; 25.143 + out += (rev [256] * in [2]) >> 11; 25.144 + out = (int16_t) out; 25.145 + out += (rev [ 0] * in [3]) >> 11; 25.146 + 25.147 + CLAMP16( out ); 25.148 + out &= ~1; 25.149 + return out; 25.150 +} 25.151 + 25.152 + 25.153 +//// Counters 25.154 + 25.155 +int const simple_counter_range = 2048 * 5 * 3; // 30720 25.156 + 25.157 +static unsigned const counter_rates [32] = 25.158 +{ 25.159 + simple_counter_range + 1, // never fires 25.160 + 2048, 1536, 25.161 + 1280, 1024, 768, 25.162 + 640, 512, 384, 25.163 + 320, 256, 192, 25.164 + 160, 128, 96, 25.165 + 80, 64, 48, 25.166 + 40, 32, 24, 25.167 + 20, 16, 12, 25.168 + 10, 8, 6, 25.169 + 5, 4, 3, 25.170 + 2, 25.171 + 1 25.172 +}; 25.173 + 25.174 +static unsigned const counter_offsets [32] = 25.175 +{ 25.176 + 1, 0, 1040, 25.177 + 536, 0, 1040, 25.178 + 536, 0, 1040, 25.179 + 536, 0, 1040, 25.180 + 536, 0, 1040, 25.181 + 536, 0, 1040, 25.182 + 536, 0, 1040, 25.183 + 536, 0, 1040, 25.184 + 536, 0, 1040, 25.185 + 536, 0, 1040, 25.186 + 0, 25.187 + 0 25.188 +}; 25.189 + 25.190 +inline void SPC_DSP::init_counter() 25.191 +{ 25.192 + m.counter = 0; 25.193 +} 25.194 + 25.195 +inline void SPC_DSP::run_counters() 25.196 +{ 25.197 + if ( --m.counter < 0 ) 25.198 + m.counter = simple_counter_range - 1; 25.199 +} 25.200 + 25.201 +inline unsigned SPC_DSP::read_counter( int rate ) 25.202 +{ 25.203 + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; 25.204 +} 25.205 + 25.206 + 25.207 +//// Envelope 25.208 + 25.209 +inline void SPC_DSP::run_envelope( voice_t* const v ) 25.210 +{ 25.211 + int env = v->env; 25.212 + if ( v->env_mode == env_release ) // 60% 25.213 + { 25.214 + if ( (env -= 0x8) < 0 ) 25.215 + env = 0; 25.216 + v->env = env; 25.217 + } 25.218 + else 25.219 + { 25.220 + int rate; 25.221 + int env_data = VREG(v->regs,adsr1); 25.222 + if ( m.t_adsr0 & 0x80 ) // 99% ADSR 25.223 + { 25.224 + if ( v->env_mode >= env_decay ) // 99% 25.225 + { 25.226 + env--; 25.227 + env -= env >> 8; 25.228 + rate = env_data & 0x1F; 25.229 + if ( v->env_mode == env_decay ) // 1% 25.230 + rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; 25.231 + } 25.232 + else // env_attack 25.233 + { 25.234 + rate = (m.t_adsr0 & 0x0F) * 2 + 1; 25.235 + env += rate < 31 ? 0x20 : 0x400; 25.236 + } 25.237 + } 25.238 + else // GAIN 25.239 + { 25.240 + int mode; 25.241 + env_data = VREG(v->regs,gain); 25.242 + mode = env_data >> 5; 25.243 + if ( mode < 4 ) // direct 25.244 + { 25.245 + env = env_data * 0x10; 25.246 + rate = 31; 25.247 + } 25.248 + else 25.249 + { 25.250 + rate = env_data & 0x1F; 25.251 + if ( mode == 4 ) // 4: linear decrease 25.252 + { 25.253 + env -= 0x20; 25.254 + } 25.255 + else if ( mode < 6 ) // 5: exponential decrease 25.256 + { 25.257 + env--; 25.258 + env -= env >> 8; 25.259 + } 25.260 + else // 6,7: linear increase 25.261 + { 25.262 + env += 0x20; 25.263 + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) 25.264 + env += 0x8 - 0x20; // 7: two-slope linear increase 25.265 + } 25.266 + } 25.267 + } 25.268 + 25.269 + // Sustain level 25.270 + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) 25.271 + v->env_mode = env_sustain; 25.272 + 25.273 + v->hidden_env = env; 25.274 + 25.275 + // unsigned cast because linear decrease going negative also triggers this 25.276 + if ( (unsigned) env > 0x7FF ) 25.277 + { 25.278 + env = (env < 0 ? 0 : 0x7FF); 25.279 + if ( v->env_mode == env_attack ) 25.280 + v->env_mode = env_decay; 25.281 + } 25.282 + 25.283 + if ( !read_counter( rate ) ) 25.284 + v->env = env; // nothing else is controlled by the counter 25.285 + } 25.286 +} 25.287 + 25.288 + 25.289 +//// BRR Decoding 25.290 + 25.291 +inline void SPC_DSP::decode_brr( voice_t* v ) 25.292 +{ 25.293 + // Arrange the four input nybbles in 0xABCD order for easy decoding 25.294 + int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; 25.295 + 25.296 + int const header = m.t_brr_header; 25.297 + 25.298 + // Write to next four samples in circular buffer 25.299 + int* pos = &v->buf [v->buf_pos]; 25.300 + int* end; 25.301 + if ( (v->buf_pos += 4) >= brr_buf_size ) 25.302 + v->buf_pos = 0; 25.303 + 25.304 + // Decode four samples 25.305 + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) 25.306 + { 25.307 + // Extract nybble and sign-extend 25.308 + int s = (int16_t) nybbles >> 12; 25.309 + 25.310 + // Shift sample based on header 25.311 + int const shift = header >> 4; 25.312 + s = (s << shift) >> 1; 25.313 + if ( shift >= 0xD ) // handle invalid range 25.314 + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) 25.315 + 25.316 + // Apply IIR filter (8 is the most commonly used) 25.317 + int const filter = header & 0x0C; 25.318 + int const p1 = pos [brr_buf_size - 1]; 25.319 + int const p2 = pos [brr_buf_size - 2] >> 1; 25.320 + if ( filter >= 8 ) 25.321 + { 25.322 + s += p1; 25.323 + s -= p2; 25.324 + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 25.325 + { 25.326 + s += p2 >> 4; 25.327 + s += (p1 * -3) >> 6; 25.328 + } 25.329 + else // s += p1 * 0.8984375 - p2 * 0.40625 25.330 + { 25.331 + s += (p1 * -13) >> 7; 25.332 + s += (p2 * 3) >> 4; 25.333 + } 25.334 + } 25.335 + else if ( filter ) // s += p1 * 0.46875 25.336 + { 25.337 + s += p1 >> 1; 25.338 + s += (-p1) >> 5; 25.339 + } 25.340 + 25.341 + // Adjust and write sample 25.342 + CLAMP16( s ); 25.343 + s = (int16_t) (s * 2); 25.344 + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around 25.345 + } 25.346 +} 25.347 + 25.348 + 25.349 +//// Misc 25.350 + 25.351 +#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n() 25.352 + 25.353 +MISC_CLOCK( 27 ) 25.354 +{ 25.355 + m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON 25.356 +} 25.357 +MISC_CLOCK( 28 ) 25.358 +{ 25.359 + m.t_non = REG(non); 25.360 + m.t_eon = REG(eon); 25.361 + m.t_dir = REG(dir); 25.362 +} 25.363 +MISC_CLOCK( 29 ) 25.364 +{ 25.365 + if ( (m.every_other_sample ^= 1) != 0 ) 25.366 + m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read 25.367 +} 25.368 +MISC_CLOCK( 30 ) 25.369 +{ 25.370 + if ( m.every_other_sample ) 25.371 + { 25.372 + m.kon = m.new_kon; 25.373 + m.t_koff = REG(koff) | m.mute_mask; 25.374 + } 25.375 + 25.376 + run_counters(); 25.377 + 25.378 + // Noise 25.379 + if ( !read_counter( REG(flg) & 0x1F ) ) 25.380 + { 25.381 + int feedback = (m.noise << 13) ^ (m.noise << 14); 25.382 + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); 25.383 + } 25.384 +} 25.385 + 25.386 + 25.387 +//// Voices 25.388 + 25.389 +#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v ) 25.390 + 25.391 +inline VOICE_CLOCK( V1 ) 25.392 +{ 25.393 + m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; 25.394 + m.t_srcn = VREG(v->regs,srcn); 25.395 +} 25.396 +inline VOICE_CLOCK( V2 ) 25.397 +{ 25.398 + // Read sample pointer (ignored if not needed) 25.399 + uint8_t const* entry = &m.ram [m.t_dir_addr]; 25.400 + if ( !v->kon_delay ) 25.401 + entry += 2; 25.402 + m.t_brr_next_addr = GET_LE16A( entry ); 25.403 + 25.404 + m.t_adsr0 = VREG(v->regs,adsr0); 25.405 + 25.406 + // Read pitch, spread over two clocks 25.407 + m.t_pitch = VREG(v->regs,pitchl); 25.408 +} 25.409 +inline VOICE_CLOCK( V3a ) 25.410 +{ 25.411 + m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; 25.412 +} 25.413 +inline VOICE_CLOCK( V3b ) 25.414 +{ 25.415 + // Read BRR header and byte 25.416 + m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; 25.417 + m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking 25.418 +} 25.419 +VOICE_CLOCK( V3c ) 25.420 +{ 25.421 + // Pitch modulation using previous voice's output 25.422 + if ( m.t_pmon & v->vbit ) 25.423 + m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; 25.424 + 25.425 + if ( v->kon_delay ) 25.426 + { 25.427 + // Get ready to start BRR decoding on next sample 25.428 + if ( v->kon_delay == 5 ) 25.429 + { 25.430 + v->brr_addr = m.t_brr_next_addr; 25.431 + v->brr_offset = 1; 25.432 + v->buf_pos = 0; 25.433 + m.t_brr_header = 0; // header is ignored on this sample 25.434 + m.kon_check = true; 25.435 + } 25.436 + 25.437 + // Envelope is never run during KON 25.438 + v->env = 0; 25.439 + v->hidden_env = 0; 25.440 + 25.441 + // Disable BRR decoding until last three samples 25.442 + v->interp_pos = 0; 25.443 + if ( --v->kon_delay & 3 ) 25.444 + v->interp_pos = 0x4000; 25.445 + 25.446 + // Pitch is never added during KON 25.447 + m.t_pitch = 0; 25.448 + } 25.449 + 25.450 + // Gaussian interpolation 25.451 + { 25.452 + int output = interpolate( v ); 25.453 + 25.454 + // Noise 25.455 + if ( m.t_non & v->vbit ) 25.456 + output = (int16_t) (m.noise * 2); 25.457 + 25.458 + // Apply envelope 25.459 + m.t_output = (output * v->env) >> 11 & ~1; 25.460 + v->t_envx_out = (uint8_t) (v->env >> 4); 25.461 + } 25.462 + 25.463 + // Immediate silence due to end of sample or soft reset 25.464 + if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) 25.465 + { 25.466 + v->env_mode = env_release; 25.467 + v->env = 0; 25.468 + } 25.469 + 25.470 + if ( m.every_other_sample ) 25.471 + { 25.472 + // KOFF 25.473 + if ( m.t_koff & v->vbit ) 25.474 + v->env_mode = env_release; 25.475 + 25.476 + // KON 25.477 + if ( m.kon & v->vbit ) 25.478 + { 25.479 + v->kon_delay = 5; 25.480 + v->env_mode = env_attack; 25.481 + } 25.482 + } 25.483 + 25.484 + // Run envelope for next sample 25.485 + if ( !v->kon_delay ) 25.486 + run_envelope( v ); 25.487 +} 25.488 +inline void SPC_DSP::voice_output( voice_t const* v, int ch ) 25.489 +{ 25.490 + // Apply left/right volume 25.491 + int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7; 25.492 + 25.493 + // Add to output total 25.494 + m.t_main_out [ch] += amp; 25.495 + CLAMP16( m.t_main_out [ch] ); 25.496 + 25.497 + // Optionally add to echo total 25.498 + if ( m.t_eon & v->vbit ) 25.499 + { 25.500 + m.t_echo_out [ch] += amp; 25.501 + CLAMP16( m.t_echo_out [ch] ); 25.502 + } 25.503 +} 25.504 +VOICE_CLOCK( V4 ) 25.505 +{ 25.506 + // Decode BRR 25.507 + m.t_looped = 0; 25.508 + if ( v->interp_pos >= 0x4000 ) 25.509 + { 25.510 + decode_brr( v ); 25.511 + 25.512 + if ( (v->brr_offset += 2) >= brr_block_size ) 25.513 + { 25.514 + // Start decoding next BRR block 25.515 + assert( v->brr_offset == brr_block_size ); 25.516 + v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; 25.517 + if ( m.t_brr_header & 1 ) 25.518 + { 25.519 + v->brr_addr = m.t_brr_next_addr; 25.520 + m.t_looped = v->vbit; 25.521 + } 25.522 + v->brr_offset = 1; 25.523 + } 25.524 + } 25.525 + 25.526 + // Apply pitch 25.527 + v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; 25.528 + 25.529 + // Keep from getting too far ahead (when using pitch modulation) 25.530 + if ( v->interp_pos > 0x7FFF ) 25.531 + v->interp_pos = 0x7FFF; 25.532 + 25.533 + // Output left 25.534 + voice_output( v, 0 ); 25.535 +} 25.536 +inline VOICE_CLOCK( V5 ) 25.537 +{ 25.538 + // Output right 25.539 + voice_output( v, 1 ); 25.540 + 25.541 + // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier 25.542 + int endx_buf = REG(endx) | m.t_looped; 25.543 + 25.544 + // Clear bit in ENDX if KON just began 25.545 + if ( v->kon_delay == 5 ) 25.546 + endx_buf &= ~v->vbit; 25.547 + m.endx_buf = (uint8_t) endx_buf; 25.548 +} 25.549 +inline VOICE_CLOCK( V6 ) 25.550 +{ 25.551 + (void) v; // avoid compiler warning about unused v 25.552 + m.outx_buf = (uint8_t) (m.t_output >> 8); 25.553 +} 25.554 +inline VOICE_CLOCK( V7 ) 25.555 +{ 25.556 + // Update ENDX 25.557 + REG(endx) = m.endx_buf; 25.558 + 25.559 + m.envx_buf = v->t_envx_out; 25.560 +} 25.561 +inline VOICE_CLOCK( V8 ) 25.562 +{ 25.563 + // Update OUTX 25.564 + VREG(v->regs,outx) = m.outx_buf; 25.565 +} 25.566 +inline VOICE_CLOCK( V9 ) 25.567 +{ 25.568 + // Update ENVX 25.569 + VREG(v->regs,envx) = m.envx_buf; 25.570 +} 25.571 + 25.572 +// Most voices do all these in one clock, so make a handy composite 25.573 +inline VOICE_CLOCK( V3 ) 25.574 +{ 25.575 + voice_V3a( v ); 25.576 + voice_V3b( v ); 25.577 + voice_V3c( v ); 25.578 +} 25.579 + 25.580 +// Common combinations of voice steps on different voices. This greatly reduces 25.581 +// code size and allows everything to be inlined in these functions. 25.582 +VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } 25.583 +VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } 25.584 +VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } 25.585 + 25.586 + 25.587 +//// Echo 25.588 + 25.589 +// Current echo buffer pointer for left/right channel 25.590 +#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) 25.591 + 25.592 +// Sample in echo history buffer, where 0 is the oldest 25.593 +#define ECHO_FIR( i ) (m.echo_hist_pos [i]) 25.594 + 25.595 +// Calculate FIR point for left/right channel 25.596 +#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) 25.597 + 25.598 +#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n() 25.599 + 25.600 +inline void SPC_DSP::echo_read( int ch ) 25.601 +{ 25.602 + int s = GET_LE16SA( ECHO_PTR( ch ) ); 25.603 + // second copy simplifies wrap-around handling 25.604 + ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; 25.605 +} 25.606 + 25.607 +ECHO_CLOCK( 22 ) 25.608 +{ 25.609 + // History 25.610 + if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) 25.611 + m.echo_hist_pos = m.echo_hist; 25.612 + 25.613 + m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; 25.614 + echo_read( 0 ); 25.615 + 25.616 + // FIR (using l and r temporaries below helps compiler optimize) 25.617 + int l = CALC_FIR( 0, 0 ); 25.618 + int r = CALC_FIR( 0, 1 ); 25.619 + 25.620 + m.t_echo_in [0] = l; 25.621 + m.t_echo_in [1] = r; 25.622 +} 25.623 +ECHO_CLOCK( 23 ) 25.624 +{ 25.625 + int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); 25.626 + int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); 25.627 + 25.628 + m.t_echo_in [0] += l; 25.629 + m.t_echo_in [1] += r; 25.630 + 25.631 + echo_read( 1 ); 25.632 +} 25.633 +ECHO_CLOCK( 24 ) 25.634 +{ 25.635 + int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); 25.636 + int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); 25.637 + 25.638 + m.t_echo_in [0] += l; 25.639 + m.t_echo_in [1] += r; 25.640 +} 25.641 +ECHO_CLOCK( 25 ) 25.642 +{ 25.643 + int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); 25.644 + int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); 25.645 + 25.646 + l = (int16_t) l; 25.647 + r = (int16_t) r; 25.648 + 25.649 + l += (int16_t) CALC_FIR( 7, 0 ); 25.650 + r += (int16_t) CALC_FIR( 7, 1 ); 25.651 + 25.652 + CLAMP16( l ); 25.653 + CLAMP16( r ); 25.654 + 25.655 + m.t_echo_in [0] = l & ~1; 25.656 + m.t_echo_in [1] = r & ~1; 25.657 +} 25.658 +inline int SPC_DSP::echo_output( int ch ) 25.659 +{ 25.660 + int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) + 25.661 + (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); 25.662 + CLAMP16( out ); 25.663 + return out; 25.664 +} 25.665 +ECHO_CLOCK( 26 ) 25.666 +{ 25.667 + // Left output volumes 25.668 + // (save sample for next clock so we can output both together) 25.669 + m.t_main_out [0] = echo_output( 0 ); 25.670 + 25.671 + // Echo feedback 25.672 + int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); 25.673 + int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); 25.674 + 25.675 + CLAMP16( l ); 25.676 + CLAMP16( r ); 25.677 + 25.678 + m.t_echo_out [0] = l & ~1; 25.679 + m.t_echo_out [1] = r & ~1; 25.680 +} 25.681 +ECHO_CLOCK( 27 ) 25.682 +{ 25.683 + // Output 25.684 + int l = m.t_main_out [0]; 25.685 + int r = echo_output( 1 ); 25.686 + m.t_main_out [0] = 0; 25.687 + m.t_main_out [1] = 0; 25.688 + 25.689 + // TODO: global muting isn't this simple (turns DAC on and off 25.690 + // or something, causing small ~37-sample pulse when first muted) 25.691 + if ( REG(flg) & 0x40 ) 25.692 + { 25.693 + l = 0; 25.694 + r = 0; 25.695 + } 25.696 + 25.697 + // Output sample to DAC 25.698 + #ifdef SPC_DSP_OUT_HOOK 25.699 + SPC_DSP_OUT_HOOK( l, r ); 25.700 + #else 25.701 + sample_t* out = m.out; 25.702 + WRITE_SAMPLES( l, r, out ); 25.703 + m.out = out; 25.704 + #endif 25.705 +} 25.706 +ECHO_CLOCK( 28 ) 25.707 +{ 25.708 + m.t_echo_enabled = REG(flg); 25.709 +} 25.710 +inline void SPC_DSP::echo_write( int ch ) 25.711 +{ 25.712 + if ( !(m.t_echo_enabled & 0x20) ) 25.713 + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); 25.714 + m.t_echo_out [ch] = 0; 25.715 +} 25.716 +ECHO_CLOCK( 29 ) 25.717 +{ 25.718 + m.t_esa = REG(esa); 25.719 + 25.720 + if ( !m.echo_offset ) 25.721 + m.echo_length = (REG(edl) & 0x0F) * 0x800; 25.722 + 25.723 + m.echo_offset += 4; 25.724 + if ( m.echo_offset >= m.echo_length ) 25.725 + m.echo_offset = 0; 25.726 + 25.727 + // Write left echo 25.728 + echo_write( 0 ); 25.729 + 25.730 + m.t_echo_enabled = REG(flg); 25.731 +} 25.732 +ECHO_CLOCK( 30 ) 25.733 +{ 25.734 + // Write right echo 25.735 + echo_write( 1 ); 25.736 +} 25.737 + 25.738 + 25.739 +//// Timing 25.740 + 25.741 +// Execute clock for a particular voice 25.742 +#define V( clock, voice ) voice_##clock( &m.voices [voice] ); 25.743 + 25.744 +/* The most common sequence of clocks uses composite operations 25.745 +for efficiency. For example, the following are equivalent to the 25.746 +individual steps on the right: 25.747 + 25.748 +V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) 25.749 +V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) 25.750 +V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ 25.751 + 25.752 +// Voice 0 1 2 3 4 5 6 7 25.753 +#define GEN_DSP_TIMING \ 25.754 +PHASE( 0) V(V5,0)V(V2,1)\ 25.755 +PHASE( 1) V(V6,0)V(V3,1)\ 25.756 +PHASE( 2) V(V7_V4_V1,0)\ 25.757 +PHASE( 3) V(V8_V5_V2,0)\ 25.758 +PHASE( 4) V(V9_V6_V3,0)\ 25.759 +PHASE( 5) V(V7_V4_V1,1)\ 25.760 +PHASE( 6) V(V8_V5_V2,1)\ 25.761 +PHASE( 7) V(V9_V6_V3,1)\ 25.762 +PHASE( 8) V(V7_V4_V1,2)\ 25.763 +PHASE( 9) V(V8_V5_V2,2)\ 25.764 +PHASE(10) V(V9_V6_V3,2)\ 25.765 +PHASE(11) V(V7_V4_V1,3)\ 25.766 +PHASE(12) V(V8_V5_V2,3)\ 25.767 +PHASE(13) V(V9_V6_V3,3)\ 25.768 +PHASE(14) V(V7_V4_V1,4)\ 25.769 +PHASE(15) V(V8_V5_V2,4)\ 25.770 +PHASE(16) V(V9_V6_V3,4)\ 25.771 +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ 25.772 +PHASE(18) V(V8_V5_V2,5)\ 25.773 +PHASE(19) V(V9_V6_V3,5)\ 25.774 +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ 25.775 +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ 25.776 +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ 25.777 +PHASE(23) V(V7,7) echo_23();\ 25.778 +PHASE(24) V(V8,7) echo_24();\ 25.779 +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ 25.780 +PHASE(26) echo_26();\ 25.781 +PHASE(27) misc_27(); echo_27();\ 25.782 +PHASE(28) misc_28(); echo_28();\ 25.783 +PHASE(29) misc_29(); echo_29();\ 25.784 +PHASE(30) misc_30();V(V3c,0) echo_30();\ 25.785 +PHASE(31) V(V4,0) V(V1,2)\ 25.786 + 25.787 +#if !SPC_DSP_CUSTOM_RUN 25.788 + 25.789 +void SPC_DSP::run( int clocks_remain ) 25.790 +{ 25.791 + require( clocks_remain > 0 ); 25.792 + 25.793 + int const phase = m.phase; 25.794 + m.phase = (phase + clocks_remain) & 31; 25.795 + switch ( phase ) 25.796 + { 25.797 + loop: 25.798 + 25.799 + #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: 25.800 + GEN_DSP_TIMING 25.801 + #undef PHASE 25.802 + 25.803 + if ( --clocks_remain ) 25.804 + goto loop; 25.805 + } 25.806 +} 25.807 + 25.808 +#endif 25.809 + 25.810 + 25.811 +//// Setup 25.812 + 25.813 +void SPC_DSP::init( void* ram_64k ) 25.814 +{ 25.815 + m.ram = (uint8_t*) ram_64k; 25.816 + mute_voices( 0 ); 25.817 + disable_surround( false ); 25.818 + set_output( 0, 0 ); 25.819 + reset(); 25.820 + 25.821 + #ifndef NDEBUG 25.822 + // be sure this sign-extends 25.823 + assert( (int16_t) 0x8000 == -0x8000 ); 25.824 + 25.825 + // be sure right shift preserves sign 25.826 + assert( (-1 >> 1) == -1 ); 25.827 + 25.828 + // check clamp macro 25.829 + int i; 25.830 + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); 25.831 + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); 25.832 + 25.833 + blargg_verify_byte_order(); 25.834 + #endif 25.835 +} 25.836 + 25.837 +void SPC_DSP::soft_reset_common() 25.838 +{ 25.839 + require( m.ram ); // init() must have been called already 25.840 + 25.841 + m.noise = 0x4000; 25.842 + m.echo_hist_pos = m.echo_hist; 25.843 + m.every_other_sample = 1; 25.844 + m.echo_offset = 0; 25.845 + m.phase = 0; 25.846 + 25.847 + init_counter(); 25.848 +} 25.849 + 25.850 +void SPC_DSP::soft_reset() 25.851 +{ 25.852 + REG(flg) = 0xE0; 25.853 + soft_reset_common(); 25.854 +} 25.855 + 25.856 +void SPC_DSP::load( uint8_t const regs [register_count] ) 25.857 +{ 25.858 + memcpy( m.regs, regs, sizeof m.regs ); 25.859 + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); 25.860 + 25.861 + // Internal state 25.862 + for ( int i = voice_count; --i >= 0; ) 25.863 + { 25.864 + voice_t* v = &m.voices [i]; 25.865 + v->brr_offset = 1; 25.866 + v->vbit = 1 << i; 25.867 + v->regs = &m.regs [i * 0x10]; 25.868 + } 25.869 + m.new_kon = REG(kon); 25.870 + m.t_dir = REG(dir); 25.871 + m.t_esa = REG(esa); 25.872 + 25.873 + soft_reset_common(); 25.874 +} 25.875 + 25.876 +void SPC_DSP::reset() { load( initial_regs ); } 25.877 + 25.878 + 25.879 +//// State save/load 25.880 + 25.881 +#if !SPC_NO_COPY_STATE_FUNCS 25.882 + 25.883 +void SPC_State_Copier::copy( void* state, size_t size ) 25.884 +{ 25.885 + func( buf, state, size ); 25.886 +} 25.887 + 25.888 +int SPC_State_Copier::copy_int( int state, int size ) 25.889 +{ 25.890 + BOOST::uint8_t s [2]; 25.891 + SET_LE16( s, state ); 25.892 + func( buf, &s, size ); 25.893 + return GET_LE16( s ); 25.894 +} 25.895 + 25.896 +void SPC_State_Copier::skip( int count ) 25.897 +{ 25.898 + if ( count > 0 ) 25.899 + { 25.900 + char temp [64]; 25.901 + memset( temp, 0, sizeof temp ); 25.902 + do 25.903 + { 25.904 + int n = sizeof temp; 25.905 + if ( n > count ) 25.906 + n = count; 25.907 + count -= n; 25.908 + func( buf, temp, n ); 25.909 + } 25.910 + while ( count ); 25.911 + } 25.912 +} 25.913 + 25.914 +void SPC_State_Copier::extra() 25.915 +{ 25.916 + int n = 0; 25.917 + SPC_State_Copier& copier = *this; 25.918 + SPC_COPY( uint8_t, n ); 25.919 + skip( n ); 25.920 +} 25.921 + 25.922 +void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) 25.923 +{ 25.924 + SPC_State_Copier copier( io, copy ); 25.925 + 25.926 + // DSP registers 25.927 + copier.copy( m.regs, register_count ); 25.928 + 25.929 + // Internal state 25.930 + 25.931 + // Voices 25.932 + int i; 25.933 + for ( i = 0; i < voice_count; i++ ) 25.934 + { 25.935 + voice_t* v = &m.voices [i]; 25.936 + 25.937 + // BRR buffer 25.938 + int i; 25.939 + for ( i = 0; i < brr_buf_size; i++ ) 25.940 + { 25.941 + int s = v->buf [i]; 25.942 + SPC_COPY( int16_t, s ); 25.943 + v->buf [i] = v->buf [i + brr_buf_size] = s; 25.944 + } 25.945 + 25.946 + SPC_COPY( uint16_t, v->interp_pos ); 25.947 + SPC_COPY( uint16_t, v->brr_addr ); 25.948 + SPC_COPY( uint16_t, v->env ); 25.949 + SPC_COPY( int16_t, v->hidden_env ); 25.950 + SPC_COPY( uint8_t, v->buf_pos ); 25.951 + SPC_COPY( uint8_t, v->brr_offset ); 25.952 + SPC_COPY( uint8_t, v->kon_delay ); 25.953 + { 25.954 + int m = v->env_mode; 25.955 + SPC_COPY( uint8_t, m ); 25.956 + v->env_mode = (enum env_mode_t) m; 25.957 + } 25.958 + SPC_COPY( uint8_t, v->t_envx_out ); 25.959 + 25.960 + copier.extra(); 25.961 + } 25.962 + 25.963 + // Echo history 25.964 + for ( i = 0; i < echo_hist_size; i++ ) 25.965 + { 25.966 + int j; 25.967 + for ( j = 0; j < 2; j++ ) 25.968 + { 25.969 + int s = m.echo_hist_pos [i] [j]; 25.970 + SPC_COPY( int16_t, s ); 25.971 + m.echo_hist [i] [j] = s; // write back at offset 0 25.972 + } 25.973 + } 25.974 + m.echo_hist_pos = m.echo_hist; 25.975 + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); 25.976 + 25.977 + // Misc 25.978 + SPC_COPY( uint8_t, m.every_other_sample ); 25.979 + SPC_COPY( uint8_t, m.kon ); 25.980 + 25.981 + SPC_COPY( uint16_t, m.noise ); 25.982 + SPC_COPY( uint16_t, m.counter ); 25.983 + SPC_COPY( uint16_t, m.echo_offset ); 25.984 + SPC_COPY( uint16_t, m.echo_length ); 25.985 + SPC_COPY( uint8_t, m.phase ); 25.986 + 25.987 + SPC_COPY( uint8_t, m.new_kon ); 25.988 + SPC_COPY( uint8_t, m.endx_buf ); 25.989 + SPC_COPY( uint8_t, m.envx_buf ); 25.990 + SPC_COPY( uint8_t, m.outx_buf ); 25.991 + 25.992 + SPC_COPY( uint8_t, m.t_pmon ); 25.993 + SPC_COPY( uint8_t, m.t_non ); 25.994 + SPC_COPY( uint8_t, m.t_eon ); 25.995 + SPC_COPY( uint8_t, m.t_dir ); 25.996 + SPC_COPY( uint8_t, m.t_koff ); 25.997 + 25.998 + SPC_COPY( uint16_t, m.t_brr_next_addr ); 25.999 + SPC_COPY( uint8_t, m.t_adsr0 ); 25.1000 + SPC_COPY( uint8_t, m.t_brr_header ); 25.1001 + SPC_COPY( uint8_t, m.t_brr_byte ); 25.1002 + SPC_COPY( uint8_t, m.t_srcn ); 25.1003 + SPC_COPY( uint8_t, m.t_esa ); 25.1004 + SPC_COPY( uint8_t, m.t_echo_enabled ); 25.1005 + 25.1006 + SPC_COPY( int16_t, m.t_main_out [0] ); 25.1007 + SPC_COPY( int16_t, m.t_main_out [1] ); 25.1008 + SPC_COPY( int16_t, m.t_echo_out [0] ); 25.1009 + SPC_COPY( int16_t, m.t_echo_out [1] ); 25.1010 + SPC_COPY( int16_t, m.t_echo_in [0] ); 25.1011 + SPC_COPY( int16_t, m.t_echo_in [1] ); 25.1012 + 25.1013 + SPC_COPY( uint16_t, m.t_dir_addr ); 25.1014 + SPC_COPY( uint16_t, m.t_pitch ); 25.1015 + SPC_COPY( int16_t, m.t_output ); 25.1016 + SPC_COPY( uint16_t, m.t_echo_ptr ); 25.1017 + SPC_COPY( uint8_t, m.t_looped ); 25.1018 + 25.1019 + copier.extra(); 25.1020 +} 25.1021 +#endif
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/snes_spc/SPC_DSP.h Fri Oct 21 05:53:11 2011 -0700 26.3 @@ -0,0 +1,304 @@ 26.4 +// Highly accurate SNES SPC-700 DSP emulator 26.5 + 26.6 +// snes_spc 0.9.0 26.7 +#ifndef SPC_DSP_H 26.8 +#define SPC_DSP_H 26.9 + 26.10 +#include "blargg_common.h" 26.11 + 26.12 +extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } 26.13 + 26.14 +class SPC_DSP { 26.15 +public: 26.16 + typedef BOOST::uint8_t uint8_t; 26.17 + 26.18 +// Setup 26.19 + 26.20 + // Initializes DSP and has it use the 64K RAM provided 26.21 + void init( void* ram_64k ); 26.22 + 26.23 + // Sets destination for output samples. If out is NULL or out_size is 0, 26.24 + // doesn't generate any. 26.25 + typedef short sample_t; 26.26 + void set_output( sample_t* out, int out_size ); 26.27 + 26.28 + // Number of samples written to output since it was last set, always 26.29 + // a multiple of 2. Undefined if more samples were generated than 26.30 + // output buffer could hold. 26.31 + int sample_count() const; 26.32 + 26.33 +// Emulation 26.34 + 26.35 + // Resets DSP to power-on state 26.36 + void reset(); 26.37 + 26.38 + // Emulates pressing reset switch on SNES 26.39 + void soft_reset(); 26.40 + 26.41 + // Reads/writes DSP registers. For accuracy, you must first call run() 26.42 + // to catch the DSP up to present. 26.43 + int read ( int addr ) const; 26.44 + void write( int addr, int data ); 26.45 + 26.46 + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks 26.47 + // a pair of samples is be generated. 26.48 + void run( int clock_count ); 26.49 + 26.50 +// Sound control 26.51 + 26.52 + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). 26.53 + // Reduces emulation accuracy. 26.54 + enum { voice_count = 8 }; 26.55 + void mute_voices( int mask ); 26.56 + 26.57 +// State 26.58 + 26.59 + // Resets DSP and uses supplied values to initialize registers 26.60 + enum { register_count = 128 }; 26.61 + void load( uint8_t const regs [register_count] ); 26.62 + 26.63 + // Saves/loads exact emulator state 26.64 + enum { state_size = 640 }; // maximum space needed when saving 26.65 + typedef dsp_copy_func_t copy_func_t; 26.66 + void copy_state( unsigned char** io, copy_func_t ); 26.67 + 26.68 + // Returns non-zero if new key-on events occurred since last call 26.69 + bool check_kon(); 26.70 + 26.71 +// DSP register addresses 26.72 + 26.73 + // Global registers 26.74 + enum { 26.75 + r_mvoll = 0x0C, r_mvolr = 0x1C, 26.76 + r_evoll = 0x2C, r_evolr = 0x3C, 26.77 + r_kon = 0x4C, r_koff = 0x5C, 26.78 + r_flg = 0x6C, r_endx = 0x7C, 26.79 + r_efb = 0x0D, r_pmon = 0x2D, 26.80 + r_non = 0x3D, r_eon = 0x4D, 26.81 + r_dir = 0x5D, r_esa = 0x6D, 26.82 + r_edl = 0x7D, 26.83 + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F 26.84 + }; 26.85 + 26.86 + // Voice registers 26.87 + enum { 26.88 + v_voll = 0x00, v_volr = 0x01, 26.89 + v_pitchl = 0x02, v_pitchh = 0x03, 26.90 + v_srcn = 0x04, v_adsr0 = 0x05, 26.91 + v_adsr1 = 0x06, v_gain = 0x07, 26.92 + v_envx = 0x08, v_outx = 0x09 26.93 + }; 26.94 + 26.95 +public: 26.96 + enum { extra_size = 16 }; 26.97 + sample_t* extra() { return m.extra; } 26.98 + sample_t const* out_pos() const { return m.out; } 26.99 + void disable_surround( bool ) { } // not supported 26.100 +public: 26.101 + BLARGG_DISABLE_NOTHROW 26.102 + 26.103 + typedef BOOST::int8_t int8_t; 26.104 + typedef BOOST::int16_t int16_t; 26.105 + 26.106 + enum { echo_hist_size = 8 }; 26.107 + 26.108 + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; 26.109 + enum { brr_buf_size = 12 }; 26.110 + struct voice_t 26.111 + { 26.112 + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) 26.113 + int buf_pos; // place in buffer where next samples will be decoded 26.114 + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) 26.115 + int brr_addr; // address of current BRR block 26.116 + int brr_offset; // current decoding offset in BRR block 26.117 + uint8_t* regs; // pointer to voice's DSP registers 26.118 + int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. 26.119 + int kon_delay; // KON delay/current setup phase 26.120 + env_mode_t env_mode; 26.121 + int env; // current envelope level 26.122 + int hidden_env; // used by GAIN mode 7, very obscure quirk 26.123 + uint8_t t_envx_out; 26.124 + }; 26.125 +private: 26.126 + enum { brr_block_size = 9 }; 26.127 + 26.128 + struct state_t 26.129 + { 26.130 + uint8_t regs [register_count]; 26.131 + 26.132 + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) 26.133 + int echo_hist [echo_hist_size * 2] [2]; 26.134 + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] 26.135 + 26.136 + int every_other_sample; // toggles every sample 26.137 + int kon; // KON value when last checked 26.138 + int noise; 26.139 + int counter; 26.140 + int echo_offset; // offset from ESA in echo buffer 26.141 + int echo_length; // number of bytes that echo_offset will stop at 26.142 + int phase; // next clock cycle to run (0-31) 26.143 + bool kon_check; // set when a new KON occurs 26.144 + 26.145 + // Hidden registers also written to when main register is written to 26.146 + int new_kon; 26.147 + uint8_t endx_buf; 26.148 + uint8_t envx_buf; 26.149 + uint8_t outx_buf; 26.150 + 26.151 + // Temporary state between clocks 26.152 + 26.153 + // read once per sample 26.154 + int t_pmon; 26.155 + int t_non; 26.156 + int t_eon; 26.157 + int t_dir; 26.158 + int t_koff; 26.159 + 26.160 + // read a few clocks ahead then used 26.161 + int t_brr_next_addr; 26.162 + int t_adsr0; 26.163 + int t_brr_header; 26.164 + int t_brr_byte; 26.165 + int t_srcn; 26.166 + int t_esa; 26.167 + int t_echo_enabled; 26.168 + 26.169 + // internal state that is recalculated every sample 26.170 + int t_dir_addr; 26.171 + int t_pitch; 26.172 + int t_output; 26.173 + int t_looped; 26.174 + int t_echo_ptr; 26.175 + 26.176 + // left/right sums 26.177 + int t_main_out [2]; 26.178 + int t_echo_out [2]; 26.179 + int t_echo_in [2]; 26.180 + 26.181 + voice_t voices [voice_count]; 26.182 + 26.183 + // non-emulation state 26.184 + uint8_t* ram; // 64K shared RAM between DSP and SMP 26.185 + int mute_mask; 26.186 + sample_t* out; 26.187 + sample_t* out_end; 26.188 + sample_t* out_begin; 26.189 + sample_t extra [extra_size]; 26.190 + }; 26.191 + state_t m; 26.192 + 26.193 + void init_counter(); 26.194 + void run_counters(); 26.195 + unsigned read_counter( int rate ); 26.196 + 26.197 + int interpolate( voice_t const* v ); 26.198 + void run_envelope( voice_t* const v ); 26.199 + void decode_brr( voice_t* v ); 26.200 + 26.201 + void misc_27(); 26.202 + void misc_28(); 26.203 + void misc_29(); 26.204 + void misc_30(); 26.205 + 26.206 + void voice_output( voice_t const* v, int ch ); 26.207 + void voice_V1( voice_t* const ); 26.208 + void voice_V2( voice_t* const ); 26.209 + void voice_V3( voice_t* const ); 26.210 + void voice_V3a( voice_t* const ); 26.211 + void voice_V3b( voice_t* const ); 26.212 + void voice_V3c( voice_t* const ); 26.213 + void voice_V4( voice_t* const ); 26.214 + void voice_V5( voice_t* const ); 26.215 + void voice_V6( voice_t* const ); 26.216 + void voice_V7( voice_t* const ); 26.217 + void voice_V8( voice_t* const ); 26.218 + void voice_V9( voice_t* const ); 26.219 + void voice_V7_V4_V1( voice_t* const ); 26.220 + void voice_V8_V5_V2( voice_t* const ); 26.221 + void voice_V9_V6_V3( voice_t* const ); 26.222 + 26.223 + void echo_read( int ch ); 26.224 + int echo_output( int ch ); 26.225 + void echo_write( int ch ); 26.226 + void echo_22(); 26.227 + void echo_23(); 26.228 + void echo_24(); 26.229 + void echo_25(); 26.230 + void echo_26(); 26.231 + void echo_27(); 26.232 + void echo_28(); 26.233 + void echo_29(); 26.234 + void echo_30(); 26.235 + 26.236 + void soft_reset_common(); 26.237 +}; 26.238 + 26.239 +#include <assert.h> 26.240 + 26.241 +inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } 26.242 + 26.243 +inline int SPC_DSP::read( int addr ) const 26.244 +{ 26.245 + assert( (unsigned) addr < register_count ); 26.246 + return m.regs [addr]; 26.247 +} 26.248 + 26.249 +inline void SPC_DSP::write( int addr, int data ) 26.250 +{ 26.251 + assert( (unsigned) addr < register_count ); 26.252 + 26.253 + m.regs [addr] = (uint8_t) data; 26.254 + switch ( addr & 0x0F ) 26.255 + { 26.256 + case v_envx: 26.257 + m.envx_buf = (uint8_t) data; 26.258 + break; 26.259 + 26.260 + case v_outx: 26.261 + m.outx_buf = (uint8_t) data; 26.262 + break; 26.263 + 26.264 + case 0x0C: 26.265 + if ( addr == r_kon ) 26.266 + m.new_kon = (uint8_t) data; 26.267 + 26.268 + if ( addr == r_endx ) // always cleared, regardless of data written 26.269 + { 26.270 + m.endx_buf = 0; 26.271 + m.regs [r_endx] = 0; 26.272 + } 26.273 + break; 26.274 + } 26.275 +} 26.276 + 26.277 +inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; } 26.278 + 26.279 +inline bool SPC_DSP::check_kon() 26.280 +{ 26.281 + bool old = m.kon_check; 26.282 + m.kon_check = 0; 26.283 + return old; 26.284 +} 26.285 + 26.286 +#if !SPC_NO_COPY_STATE_FUNCS 26.287 + 26.288 +class SPC_State_Copier { 26.289 + SPC_DSP::copy_func_t func; 26.290 + unsigned char** buf; 26.291 +public: 26.292 + SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; } 26.293 + void copy( void* state, size_t size ); 26.294 + int copy_int( int state, int size ); 26.295 + void skip( int count ); 26.296 + void extra(); 26.297 +}; 26.298 + 26.299 +#define SPC_COPY( type, state )\ 26.300 +{\ 26.301 + state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ 26.302 + assert( (BOOST::type) state == state );\ 26.303 +} 26.304 + 26.305 +#endif 26.306 + 26.307 +#endif
27.1 Binary file snes_spc/SPC_DSP.h.gch has changed
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/snes_spc/SPC_Filter.cpp Fri Oct 21 05:53:11 2011 -0700 28.3 @@ -0,0 +1,68 @@ 28.4 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 28.5 + 28.6 +#include "SPC_Filter.h" 28.7 + 28.8 +#include <string.h> 28.9 + 28.10 +/* Copyright (C) 2007 Shay Green. This module is free software; you 28.11 +can redistribute it and/or modify it under the terms of the GNU Lesser 28.12 +General Public License as published by the Free Software Foundation; either 28.13 +version 2.1 of the License, or (at your option) any later version. This 28.14 +module is distributed in the hope that it will be useful, but WITHOUT ANY 28.15 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 28.16 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 28.17 +details. You should have received a copy of the GNU Lesser General Public 28.18 +License along with this module; if not, write to the Free Software Foundation, 28.19 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 28.20 + 28.21 +#include "blargg_source.h" 28.22 + 28.23 +void SPC_Filter::clear() { memset( ch, 0, sizeof ch ); } 28.24 + 28.25 +SPC_Filter::SPC_Filter() 28.26 +{ 28.27 + gain = gain_unit; 28.28 + bass = bass_norm; 28.29 + clear(); 28.30 +} 28.31 + 28.32 +void SPC_Filter::run( short* io, int count ) 28.33 +{ 28.34 + require( (count & 1) == 0 ); // must be even 28.35 + 28.36 + int const gain = this->gain; 28.37 + int const bass = this->bass; 28.38 + chan_t* c = &ch [2]; 28.39 + do 28.40 + { 28.41 + // cache in registers 28.42 + int sum = (--c)->sum; 28.43 + int pp1 = c->pp1; 28.44 + int p1 = c->p1; 28.45 + 28.46 + for ( int i = 0; i < count; i += 2 ) 28.47 + { 28.48 + // Low-pass filter (two point FIR with coeffs 0.25, 0.75) 28.49 + int f = io [i] + p1; 28.50 + p1 = io [i] * 3; 28.51 + 28.52 + // High-pass filter ("leaky integrator") 28.53 + int delta = f - pp1; 28.54 + pp1 = f; 28.55 + int s = sum >> (gain_bits + 2); 28.56 + sum += (delta * gain) - (sum >> bass); 28.57 + 28.58 + // Clamp to 16 bits 28.59 + if ( (short) s != s ) 28.60 + s = (s >> 31) ^ 0x7FFF; 28.61 + 28.62 + io [i] = (short) s; 28.63 + } 28.64 + 28.65 + c->p1 = p1; 28.66 + c->pp1 = pp1; 28.67 + c->sum = sum; 28.68 + ++io; 28.69 + } 28.70 + while ( c != ch ); 28.71 +}
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 29.2 +++ b/snes_spc/SPC_Filter.h Fri Oct 21 05:53:11 2011 -0700 29.3 @@ -0,0 +1,47 @@ 29.4 +// Simple low-pass and high-pass filter to better match sound output of a SNES 29.5 + 29.6 +// snes_spc 0.9.0 29.7 +#ifndef SPC_FILTER_H 29.8 +#define SPC_FILTER_H 29.9 + 29.10 +#include "blargg_common.h" 29.11 + 29.12 +struct SPC_Filter { 29.13 +public: 29.14 + 29.15 + // Filters count samples of stereo sound in place. Count must be a multiple of 2. 29.16 + typedef short sample_t; 29.17 + void run( sample_t* io, int count ); 29.18 + 29.19 +// Optional features 29.20 + 29.21 + // Clears filter to silence 29.22 + void clear(); 29.23 + 29.24 + // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit 29.25 + // are fine, since output is clamped to 16-bit sample range. 29.26 + enum { gain_unit = 0x100 }; 29.27 + void set_gain( int gain ); 29.28 + 29.29 + // Sets amount of bass (logarithmic scale) 29.30 + enum { bass_none = 0 }; 29.31 + enum { bass_norm = 8 }; // normal amount 29.32 + enum { bass_max = 31 }; 29.33 + void set_bass( int bass ); 29.34 + 29.35 +public: 29.36 + SPC_Filter(); 29.37 + BLARGG_DISABLE_NOTHROW 29.38 +private: 29.39 + enum { gain_bits = 8 }; 29.40 + int gain; 29.41 + int bass; 29.42 + struct chan_t { int p1, pp1, sum; }; 29.43 + chan_t ch [2]; 29.44 +}; 29.45 + 29.46 +inline void SPC_Filter::set_gain( int g ) { gain = g; } 29.47 + 29.48 +inline void SPC_Filter::set_bass( int b ) { bass = b; } 29.49 + 29.50 +#endif
30.1 Binary file snes_spc/SPC_Filter.h.gch has changed
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 31.2 +++ b/snes_spc/blargg_common.h Fri Oct 21 05:53:11 2011 -0700 31.3 @@ -0,0 +1,186 @@ 31.4 +// Sets up common environment for Shay Green's libraries. 31.5 +// To change configuration options, modify blargg_config.h, not this file. 31.6 + 31.7 +// snes_spc 0.9.0 31.8 +#ifndef BLARGG_COMMON_H 31.9 +#define BLARGG_COMMON_H 31.10 + 31.11 +#include <stddef.h> 31.12 +#include <stdlib.h> 31.13 +#include <assert.h> 31.14 +#include <limits.h> 31.15 + 31.16 +#undef BLARGG_COMMON_H 31.17 +// allow blargg_config.h to #include blargg_common.h 31.18 +#include "blargg_config.h" 31.19 +#ifndef BLARGG_COMMON_H 31.20 +#define BLARGG_COMMON_H 31.21 + 31.22 +// BLARGG_RESTRICT: equivalent to restrict, where supported 31.23 +#if defined (__GNUC__) || _MSC_VER >= 1100 31.24 + #define BLARGG_RESTRICT __restrict 31.25 +#else 31.26 + #define BLARGG_RESTRICT 31.27 +#endif 31.28 + 31.29 +// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr) 31.30 +#ifndef STATIC_CAST 31.31 + #define STATIC_CAST(T,expr) ((T) (expr)) 31.32 +#endif 31.33 + 31.34 +// blargg_err_t (0 on success, otherwise error string) 31.35 +#ifndef blargg_err_t 31.36 + typedef const char* blargg_err_t; 31.37 +#endif 31.38 + 31.39 +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) 31.40 +template<class T> 31.41 +class blargg_vector { 31.42 + T* begin_; 31.43 + size_t size_; 31.44 +public: 31.45 + blargg_vector() : begin_( 0 ), size_( 0 ) { } 31.46 + ~blargg_vector() { free( begin_ ); } 31.47 + size_t size() const { return size_; } 31.48 + T* begin() const { return begin_; } 31.49 + T* end() const { return begin_ + size_; } 31.50 + blargg_err_t resize( size_t n ) 31.51 + { 31.52 + // TODO: blargg_common.cpp to hold this as an outline function, ugh 31.53 + void* p = realloc( begin_, n * sizeof (T) ); 31.54 + if ( p ) 31.55 + begin_ = (T*) p; 31.56 + else if ( n > size_ ) // realloc failure only a problem if expanding 31.57 + return "Out of memory"; 31.58 + size_ = n; 31.59 + return 0; 31.60 + } 31.61 + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } 31.62 + T& operator [] ( size_t n ) const 31.63 + { 31.64 + assert( n <= size_ ); // <= to allow past-the-end value 31.65 + return begin_ [n]; 31.66 + } 31.67 +}; 31.68 + 31.69 +#ifndef BLARGG_DISABLE_NOTHROW 31.70 + // throw spec mandatory in ISO C++ if operator new can return NULL 31.71 + #if __cplusplus >= 199711 || defined (__GNUC__) 31.72 + #define BLARGG_THROWS( spec ) throw spec 31.73 + #else 31.74 + #define BLARGG_THROWS( spec ) 31.75 + #endif 31.76 + #define BLARGG_DISABLE_NOTHROW \ 31.77 + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ 31.78 + void operator delete ( void* p ) { free( p ); } 31.79 + #define BLARGG_NEW new 31.80 +#else 31.81 + #include <new> 31.82 + #define BLARGG_NEW new (std::nothrow) 31.83 +#endif 31.84 + 31.85 +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) 31.86 +#define BLARGG_4CHAR( a, b, c, d ) \ 31.87 + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) 31.88 + 31.89 +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. 31.90 +#ifndef BOOST_STATIC_ASSERT 31.91 + #ifdef _MSC_VER 31.92 + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified 31.93 + #define BOOST_STATIC_ASSERT( expr ) \ 31.94 + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) 31.95 + #else 31.96 + // Some other compilers fail when declaring same function multiple times in class, 31.97 + // so differentiate them by line 31.98 + #define BOOST_STATIC_ASSERT( expr ) \ 31.99 + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) 31.100 + #endif 31.101 +#endif 31.102 + 31.103 +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, 31.104 +// compiler is assumed to support bool. If undefined, availability is determined. 31.105 +#ifndef BLARGG_COMPILER_HAS_BOOL 31.106 + #if defined (__MWERKS__) 31.107 + #if !__option(bool) 31.108 + #define BLARGG_COMPILER_HAS_BOOL 0 31.109 + #endif 31.110 + #elif defined (_MSC_VER) 31.111 + #if _MSC_VER < 1100 31.112 + #define BLARGG_COMPILER_HAS_BOOL 0 31.113 + #endif 31.114 + #elif defined (__GNUC__) 31.115 + // supports bool 31.116 + #elif __cplusplus < 199711 31.117 + #define BLARGG_COMPILER_HAS_BOOL 0 31.118 + #endif 31.119 +#endif 31.120 +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL 31.121 + // If you get errors here, modify your blargg_config.h file 31.122 + typedef int bool; 31.123 + const bool true = 1; 31.124 + const bool false = 0; 31.125 +#endif 31.126 + 31.127 +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough 31.128 + 31.129 +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF 31.130 + typedef long blargg_long; 31.131 +#else 31.132 + typedef int blargg_long; 31.133 +#endif 31.134 + 31.135 +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF 31.136 + typedef unsigned long blargg_ulong; 31.137 +#else 31.138 + typedef unsigned blargg_ulong; 31.139 +#endif 31.140 + 31.141 +// BOOST::int8_t etc. 31.142 + 31.143 +// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc. 31.144 +#if defined (HAVE_STDINT_H) 31.145 + #include <stdint.h> 31.146 + #define BOOST 31.147 + 31.148 +// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc. 31.149 +#elif defined (HAVE_INTTYPES_H) 31.150 + #include <inttypes.h> 31.151 + #define BOOST 31.152 + 31.153 +#else 31.154 + struct BOOST 31.155 + { 31.156 + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F 31.157 + typedef signed char int8_t; 31.158 + typedef unsigned char uint8_t; 31.159 + #else 31.160 + // No suitable 8-bit type available 31.161 + typedef struct see_blargg_common_h int8_t; 31.162 + typedef struct see_blargg_common_h uint8_t; 31.163 + #endif 31.164 + 31.165 + #if USHRT_MAX == 0xFFFF 31.166 + typedef short int16_t; 31.167 + typedef unsigned short uint16_t; 31.168 + #else 31.169 + // No suitable 16-bit type available 31.170 + typedef struct see_blargg_common_h int16_t; 31.171 + typedef struct see_blargg_common_h uint16_t; 31.172 + #endif 31.173 + 31.174 + #if ULONG_MAX == 0xFFFFFFFF 31.175 + typedef long int32_t; 31.176 + typedef unsigned long uint32_t; 31.177 + #elif UINT_MAX == 0xFFFFFFFF 31.178 + typedef int int32_t; 31.179 + typedef unsigned int uint32_t; 31.180 + #else 31.181 + // No suitable 32-bit type available 31.182 + typedef struct see_blargg_common_h int32_t; 31.183 + typedef struct see_blargg_common_h uint32_t; 31.184 + #endif 31.185 + }; 31.186 +#endif 31.187 + 31.188 +#endif 31.189 +#endif
32.1 Binary file snes_spc/blargg_common.h.gch has changed
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 33.2 +++ b/snes_spc/blargg_config.h Fri Oct 21 05:53:11 2011 -0700 33.3 @@ -0,0 +1,24 @@ 33.4 +// snes_spc 0.9.0 user configuration file. Don't replace when updating library. 33.5 + 33.6 +// snes_spc 0.9.0 33.7 +#ifndef BLARGG_CONFIG_H 33.8 +#define BLARGG_CONFIG_H 33.9 + 33.10 +// Uncomment to disable debugging checks 33.11 +//#define NDEBUG 1 33.12 + 33.13 +// Uncomment to enable platform-specific (and possibly non-portable) optimizations 33.14 +//#define BLARGG_NONPORTABLE 1 33.15 + 33.16 +// Uncomment if automatic byte-order determination doesn't work 33.17 +//#define BLARGG_BIG_ENDIAN 1 33.18 + 33.19 +// Uncomment if you get errors in the bool section of blargg_common.h 33.20 +//#define BLARGG_COMPILER_HAS_BOOL 1 33.21 + 33.22 +// Use standard config.h if present 33.23 +#ifdef HAVE_CONFIG_H 33.24 + #include "config.h" 33.25 +#endif 33.26 + 33.27 +#endif
34.1 Binary file snes_spc/blargg_config.h.gch has changed
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 35.2 +++ b/snes_spc/blargg_endian.h Fri Oct 21 05:53:11 2011 -0700 35.3 @@ -0,0 +1,185 @@ 35.4 +// CPU Byte Order Utilities 35.5 + 35.6 +// snes_spc 0.9.0 35.7 +#ifndef BLARGG_ENDIAN 35.8 +#define BLARGG_ENDIAN 35.9 + 35.10 +#include "blargg_common.h" 35.11 + 35.12 +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) 35.13 +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ 35.14 + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) 35.15 + #define BLARGG_CPU_X86 1 35.16 + #define BLARGG_CPU_CISC 1 35.17 +#endif 35.18 + 35.19 +#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) 35.20 + #define BLARGG_CPU_POWERPC 1 35.21 + #define BLARGG_CPU_RISC 1 35.22 +#endif 35.23 + 35.24 +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only 35.25 +// one may be #defined to 1. Only needed if something actually depends on byte order. 35.26 +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) 35.27 +#ifdef __GLIBC__ 35.28 + // GCC handles this for us 35.29 + #include <endian.h> 35.30 + #if __BYTE_ORDER == __LITTLE_ENDIAN 35.31 + #define BLARGG_LITTLE_ENDIAN 1 35.32 + #elif __BYTE_ORDER == __BIG_ENDIAN 35.33 + #define BLARGG_BIG_ENDIAN 1 35.34 + #endif 35.35 +#else 35.36 + 35.37 +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ 35.38 + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) 35.39 + #define BLARGG_LITTLE_ENDIAN 1 35.40 +#endif 35.41 + 35.42 +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ 35.43 + defined (__sparc__) || BLARGG_CPU_POWERPC || \ 35.44 + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) 35.45 + #define BLARGG_BIG_ENDIAN 1 35.46 +#elif !defined (__mips__) 35.47 + // No endian specified; assume little-endian, since it's most common 35.48 + #define BLARGG_LITTLE_ENDIAN 1 35.49 +#endif 35.50 +#endif 35.51 +#endif 35.52 + 35.53 +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN 35.54 + #undef BLARGG_LITTLE_ENDIAN 35.55 + #undef BLARGG_BIG_ENDIAN 35.56 +#endif 35.57 + 35.58 +inline void blargg_verify_byte_order() 35.59 +{ 35.60 + #ifndef NDEBUG 35.61 + #if BLARGG_BIG_ENDIAN 35.62 + volatile int i = 1; 35.63 + assert( *(volatile char*) &i == 0 ); 35.64 + #elif BLARGG_LITTLE_ENDIAN 35.65 + volatile int i = 1; 35.66 + assert( *(volatile char*) &i != 0 ); 35.67 + #endif 35.68 + #endif 35.69 +} 35.70 + 35.71 +inline unsigned get_le16( void const* p ) 35.72 +{ 35.73 + return (unsigned) ((unsigned char const*) p) [1] << 8 | 35.74 + (unsigned) ((unsigned char const*) p) [0]; 35.75 +} 35.76 + 35.77 +inline unsigned get_be16( void const* p ) 35.78 +{ 35.79 + return (unsigned) ((unsigned char const*) p) [0] << 8 | 35.80 + (unsigned) ((unsigned char const*) p) [1]; 35.81 +} 35.82 + 35.83 +inline blargg_ulong get_le32( void const* p ) 35.84 +{ 35.85 + return (blargg_ulong) ((unsigned char const*) p) [3] << 24 | 35.86 + (blargg_ulong) ((unsigned char const*) p) [2] << 16 | 35.87 + (blargg_ulong) ((unsigned char const*) p) [1] << 8 | 35.88 + (blargg_ulong) ((unsigned char const*) p) [0]; 35.89 +} 35.90 + 35.91 +inline blargg_ulong get_be32( void const* p ) 35.92 +{ 35.93 + return (blargg_ulong) ((unsigned char const*) p) [0] << 24 | 35.94 + (blargg_ulong) ((unsigned char const*) p) [1] << 16 | 35.95 + (blargg_ulong) ((unsigned char const*) p) [2] << 8 | 35.96 + (blargg_ulong) ((unsigned char const*) p) [3]; 35.97 +} 35.98 + 35.99 +inline void set_le16( void* p, unsigned n ) 35.100 +{ 35.101 + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); 35.102 + ((unsigned char*) p) [0] = (unsigned char) n; 35.103 +} 35.104 + 35.105 +inline void set_be16( void* p, unsigned n ) 35.106 +{ 35.107 + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); 35.108 + ((unsigned char*) p) [1] = (unsigned char) n; 35.109 +} 35.110 + 35.111 +inline void set_le32( void* p, blargg_ulong n ) 35.112 +{ 35.113 + ((unsigned char*) p) [0] = (unsigned char) n; 35.114 + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); 35.115 + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); 35.116 + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); 35.117 +} 35.118 + 35.119 +inline void set_be32( void* p, blargg_ulong n ) 35.120 +{ 35.121 + ((unsigned char*) p) [3] = (unsigned char) n; 35.122 + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); 35.123 + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); 35.124 + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); 35.125 +} 35.126 + 35.127 +#if BLARGG_NONPORTABLE 35.128 + // Optimized implementation if byte order is known 35.129 + #if BLARGG_LITTLE_ENDIAN 35.130 + #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) 35.131 + #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) 35.132 + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) 35.133 + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) 35.134 + #elif BLARGG_BIG_ENDIAN 35.135 + #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) 35.136 + #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) 35.137 + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) 35.138 + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) 35.139 + 35.140 + #if BLARGG_CPU_POWERPC 35.141 + // PowerPC has special byte-reversed instructions 35.142 + #if defined (__MWERKS__) 35.143 + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) 35.144 + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) 35.145 + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) 35.146 + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) 35.147 + #elif defined (__GNUC__) 35.148 + #define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;}) 35.149 + #define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;}) 35.150 + #define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );}) 35.151 + #define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );}) 35.152 + #endif 35.153 + #endif 35.154 + #endif 35.155 +#endif 35.156 + 35.157 +#ifndef GET_LE16 35.158 + #define GET_LE16( addr ) get_le16( addr ) 35.159 + #define SET_LE16( addr, data ) set_le16( addr, data ) 35.160 +#endif 35.161 + 35.162 +#ifndef GET_LE32 35.163 + #define GET_LE32( addr ) get_le32( addr ) 35.164 + #define SET_LE32( addr, data ) set_le32( addr, data ) 35.165 +#endif 35.166 + 35.167 +#ifndef GET_BE16 35.168 + #define GET_BE16( addr ) get_be16( addr ) 35.169 + #define SET_BE16( addr, data ) set_be16( addr, data ) 35.170 +#endif 35.171 + 35.172 +#ifndef GET_BE32 35.173 + #define GET_BE32( addr ) get_be32( addr ) 35.174 + #define SET_BE32( addr, data ) set_be32( addr, data ) 35.175 +#endif 35.176 + 35.177 +// auto-selecting versions 35.178 + 35.179 +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } 35.180 +inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } 35.181 +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } 35.182 +inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } 35.183 +inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } 35.184 +inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } 35.185 +inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } 35.186 +inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } 35.187 + 35.188 +#endif
36.1 Binary file snes_spc/blargg_endian.h.gch has changed
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 37.2 +++ b/snes_spc/blargg_source.h Fri Oct 21 05:53:11 2011 -0700 37.3 @@ -0,0 +1,100 @@ 37.4 +/* Included at the beginning of library source files, after all other #include lines. 37.5 +Sets up helpful macros and services used in my source code. They don't need 37.6 +module an annoying module prefix on their names since they are defined after 37.7 +all other #include lines. */ 37.8 + 37.9 +// snes_spc 0.9.0 37.10 +#ifndef BLARGG_SOURCE_H 37.11 +#define BLARGG_SOURCE_H 37.12 + 37.13 +// If debugging is enabled, abort program if expr is false. Meant for checking 37.14 +// internal state and consistency. A failed assertion indicates a bug in the module. 37.15 +// void assert( bool expr ); 37.16 +#include <assert.h> 37.17 + 37.18 +// If debugging is enabled and expr is false, abort program. Meant for checking 37.19 +// caller-supplied parameters and operations that are outside the control of the 37.20 +// module. A failed requirement indicates a bug outside the module. 37.21 +// void require( bool expr ); 37.22 +#undef require 37.23 +#define require( expr ) assert( expr ) 37.24 + 37.25 +// Like printf() except output goes to debug log file. Might be defined to do 37.26 +// nothing (not even evaluate its arguments). 37.27 +// void dprintf( const char* format, ... ); 37.28 +static inline void blargg_dprintf_( const char*, ... ) { } 37.29 +#undef dprintf 37.30 +#define dprintf (1) ? (void) 0 : blargg_dprintf_ 37.31 + 37.32 +// If enabled, evaluate expr and if false, make debug log entry with source file 37.33 +// and line. Meant for finding situations that should be examined further, but that 37.34 +// don't indicate a problem. In all cases, execution continues normally. 37.35 +#undef check 37.36 +#define check( expr ) ((void) 0) 37.37 + 37.38 +// If expr yields error string, return it from current function, otherwise continue. 37.39 +#undef RETURN_ERR 37.40 +#define RETURN_ERR( expr ) do { \ 37.41 + blargg_err_t blargg_return_err_ = (expr); \ 37.42 + if ( blargg_return_err_ ) return blargg_return_err_; \ 37.43 + } while ( 0 ) 37.44 + 37.45 +// If ptr is 0, return out of memory error string. 37.46 +#undef CHECK_ALLOC 37.47 +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) 37.48 + 37.49 +// Avoid any macros which evaluate their arguments multiple times 37.50 +#undef min 37.51 +#undef max 37.52 + 37.53 +#define DEF_MIN_MAX( type ) \ 37.54 + static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ 37.55 + static inline type max( type x, type y ) { if ( y < x ) return x; return y; } 37.56 + 37.57 +DEF_MIN_MAX( int ) 37.58 +DEF_MIN_MAX( unsigned ) 37.59 +DEF_MIN_MAX( long ) 37.60 +DEF_MIN_MAX( unsigned long ) 37.61 +DEF_MIN_MAX( float ) 37.62 +DEF_MIN_MAX( double ) 37.63 + 37.64 +#undef DEF_MIN_MAX 37.65 + 37.66 +/* 37.67 +// using const references generates crappy code, and I am currenly only using these 37.68 +// for built-in types, so they take arguments by value 37.69 + 37.70 +// TODO: remove 37.71 +inline int min( int x, int y ) 37.72 +template<class T> 37.73 +inline T min( T x, T y ) 37.74 +{ 37.75 + if ( x < y ) 37.76 + return x; 37.77 + return y; 37.78 +} 37.79 + 37.80 +template<class T> 37.81 +inline T max( T x, T y ) 37.82 +{ 37.83 + if ( x < y ) 37.84 + return y; 37.85 + return x; 37.86 +} 37.87 +*/ 37.88 + 37.89 +// TODO: good idea? bad idea? 37.90 +#undef byte 37.91 +#define byte byte_ 37.92 +typedef unsigned char byte; 37.93 + 37.94 +// deprecated 37.95 +#define BLARGG_CHECK_ALLOC CHECK_ALLOC 37.96 +#define BLARGG_RETURN_ERR RETURN_ERR 37.97 + 37.98 +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check 37.99 +#ifdef BLARGG_SOURCE_BEGIN 37.100 + #include BLARGG_SOURCE_BEGIN 37.101 +#endif 37.102 + 37.103 +#endif
38.1 Binary file snes_spc/blargg_source.h.gch has changed
39.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 39.2 +++ b/snes_spc/dsp.cpp Fri Oct 21 05:53:11 2011 -0700 39.3 @@ -0,0 +1,48 @@ 39.4 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 39.5 + 39.6 +#include "dsp.h" 39.7 + 39.8 +#include "SPC_DSP.h" 39.9 + 39.10 +/* Copyright (C) 2007 Shay Green. This module is free software; you 39.11 +can redistribute it and/or modify it under the terms of the GNU Lesser 39.12 +General Public License as published by the Free Software Foundation; either 39.13 +version 2.1 of the License, or (at your option) any later version. This 39.14 +module is distributed in the hope that it will be useful, but WITHOUT ANY 39.15 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 39.16 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 39.17 +details. You should have received a copy of the GNU Lesser General Public 39.18 +License along with this module; if not, write to the Free Software Foundation, 39.19 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 39.20 + 39.21 +#include "blargg_source.h" 39.22 + 39.23 +SPC_DSP* spc_dsp_new( void ) 39.24 +{ 39.25 + // be sure constants match 39.26 + assert( spc_dsp_voice_count == (int) SPC_DSP::voice_count ); 39.27 + assert( spc_dsp_register_count == (int) SPC_DSP::register_count ); 39.28 + #if !SPC_NO_COPY_STATE_FUNCS 39.29 + assert( spc_dsp_state_size == (int) SPC_DSP::state_size ); 39.30 + #endif 39.31 + 39.32 + return new SPC_DSP; 39.33 +} 39.34 + 39.35 +void spc_dsp_delete ( SPC_DSP* s ) { delete s; } 39.36 +void spc_dsp_init ( SPC_DSP* s, void* ram_64k ) { s->init( ram_64k ); } 39.37 +void spc_dsp_set_output ( SPC_DSP* s, spc_dsp_sample_t* p, int n ) { s->set_output( p, n ); } 39.38 +int spc_dsp_sample_count( SPC_DSP const* s ) { return s->sample_count(); } 39.39 +void spc_dsp_reset ( SPC_DSP* s ) { s->reset(); } 39.40 +void spc_dsp_soft_reset ( SPC_DSP* s ) { s->soft_reset(); } 39.41 +int spc_dsp_read ( SPC_DSP const* s, int addr ) { return s->read( addr ); } 39.42 +void spc_dsp_write ( SPC_DSP* s, int addr, int data ) { s->write( addr, data ); } 39.43 +void spc_dsp_run ( SPC_DSP* s, int clock_count ) { s->run( clock_count ); } 39.44 +void spc_dsp_mute_voices ( SPC_DSP* s, int mask ) { s->mute_voices( mask ); } 39.45 +void spc_dsp_disable_surround( SPC_DSP* s, int disable ) { s->disable_surround( disable ); } 39.46 +void spc_dsp_load ( SPC_DSP* s, unsigned char const regs [spc_dsp_register_count] ) { s->load( regs ); } 39.47 + 39.48 +#if !SPC_NO_COPY_STATE_FUNCS 39.49 +void spc_dsp_copy_state ( SPC_DSP* s, unsigned char** p, spc_dsp_copy_func_t f ) { s->copy_state( p, f ); } 39.50 +int spc_dsp_check_kon ( SPC_DSP* s ) { return s->check_kon(); } 39.51 +#endif
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 40.2 +++ b/snes_spc/dsp.h Fri Oct 21 05:53:11 2011 -0700 40.3 @@ -0,0 +1,83 @@ 40.4 +/* SNES SPC-700 DSP emulator C interface (also usable from C++) */ 40.5 + 40.6 +/* snes_spc 0.9.0 */ 40.7 +#ifndef DSP_H 40.8 +#define DSP_H 40.9 + 40.10 +#include <stddef.h> 40.11 + 40.12 +#ifdef __cplusplus 40.13 + extern "C" { 40.14 +#endif 40.15 + 40.16 +typedef struct SPC_DSP SPC_DSP; 40.17 + 40.18 +/* Creates new DSP emulator. NULL if out of memory. */ 40.19 +SPC_DSP* spc_dsp_new( void ); 40.20 + 40.21 +/* Frees DSP emulator */ 40.22 +void spc_dsp_delete( SPC_DSP* ); 40.23 + 40.24 +/* Initializes DSP and has it use the 64K RAM provided */ 40.25 +void spc_dsp_init( SPC_DSP*, void* ram_64k ); 40.26 + 40.27 +/* Sets destination for output samples. If out is NULL or out_size is 0, 40.28 +doesn't generate any. */ 40.29 +typedef short spc_dsp_sample_t; 40.30 +void spc_dsp_set_output( SPC_DSP*, spc_dsp_sample_t* out, int out_size ); 40.31 + 40.32 +/* Number of samples written to output since it was last set, always 40.33 +a multiple of 2. Undefined if more samples were generated than 40.34 +output buffer could hold. */ 40.35 +int spc_dsp_sample_count( SPC_DSP const* ); 40.36 + 40.37 + 40.38 +/**** Emulation *****/ 40.39 + 40.40 +/* Resets DSP to power-on state */ 40.41 +void spc_dsp_reset( SPC_DSP* ); 40.42 + 40.43 +/* Emulates pressing reset switch on SNES */ 40.44 +void spc_dsp_soft_reset( SPC_DSP* ); 40.45 + 40.46 +/* Reads/writes DSP registers. For accuracy, you must first call spc_dsp_run() */ 40.47 +/* to catch the DSP up to present. */ 40.48 +int spc_dsp_read ( SPC_DSP const*, int addr ); 40.49 +void spc_dsp_write( SPC_DSP*, int addr, int data ); 40.50 + 40.51 +/* Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks */ 40.52 +/* a pair of samples is be generated. */ 40.53 +void spc_dsp_run( SPC_DSP*, int clock_count ); 40.54 + 40.55 + 40.56 +/**** Sound control *****/ 40.57 + 40.58 +/* Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */ 40.59 +enum { spc_dsp_voice_count = 8 }; 40.60 +void spc_dsp_mute_voices( SPC_DSP*, int mask ); 40.61 + 40.62 +/* If true, prevents channels and global volumes from being phase-negated. 40.63 +Only supported by fast DSP; has no effect on accurate DSP. */ 40.64 +void spc_dsp_disable_surround( SPC_DSP*, int disable ); 40.65 + 40.66 + 40.67 +/**** State save/load *****/ 40.68 + 40.69 +/* Resets DSP and uses supplied values to initialize registers */ 40.70 +enum { spc_dsp_register_count = 128 }; 40.71 +void spc_dsp_load( SPC_DSP*, unsigned char const regs [spc_dsp_register_count] ); 40.72 + 40.73 +/* Saves/loads exact emulator state (accurate DSP only) */ 40.74 +enum { spc_dsp_state_size = 640 }; /* maximum space needed when saving */ 40.75 +typedef void (*spc_dsp_copy_func_t)( unsigned char** io, void* state, size_t ); 40.76 +void spc_dsp_copy_state( SPC_DSP*, unsigned char** io, spc_dsp_copy_func_t ); 40.77 + 40.78 +/* Returns non-zero if new key-on events occurred since last call (accurate DSP only) */ 40.79 +int spc_dsp_check_kon( SPC_DSP* ); 40.80 + 40.81 + 40.82 +#ifdef __cplusplus 40.83 + } 40.84 +#endif 40.85 + 40.86 +#endif
41.1 Binary file snes_spc/dsp.h.gch has changed
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 42.2 +++ b/snes_spc/spc.cpp Fri Oct 21 05:53:11 2011 -0700 42.3 @@ -0,0 +1,73 @@ 42.4 +// snes_spc 0.9.0. http://www.slack.net/~ant/ 42.5 + 42.6 +#include "spc.h" 42.7 + 42.8 +#include "SNES_SPC.h" 42.9 +#include "SPC_Filter.h" 42.10 + 42.11 +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you 42.12 +can redistribute it and/or modify it under the terms of the GNU Lesser 42.13 +General Public License as published by the Free Software Foundation; either 42.14 +version 2.1 of the License, or (at your option) any later version. This 42.15 +module is distributed in the hope that it will be useful, but WITHOUT ANY 42.16 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 42.17 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 42.18 +details. You should have received a copy of the GNU Lesser General Public 42.19 +License along with this module; if not, write to the Free Software Foundation, 42.20 +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 42.21 + 42.22 +#include "blargg_source.h" 42.23 + 42.24 +SNES_SPC* spc_new( void ) 42.25 +{ 42.26 + // be sure constants match 42.27 + assert( spc_sample_rate == (int) SNES_SPC::sample_rate ); 42.28 + assert( spc_rom_size == (int) SNES_SPC::rom_size ); 42.29 + assert( spc_clock_rate == (int) SNES_SPC::clock_rate ); 42.30 + assert( spc_clocks_per_sample == (int) SNES_SPC::clocks_per_sample ); 42.31 + assert( spc_port_count == (int) SNES_SPC::port_count ); 42.32 + assert( spc_voice_count == (int) SNES_SPC::voice_count ); 42.33 + assert( spc_tempo_unit == (int) SNES_SPC::tempo_unit ); 42.34 + assert( spc_file_size == (int) SNES_SPC::spc_file_size ); 42.35 + #if !SPC_NO_COPY_STATE_FUNCS 42.36 + assert( spc_state_size == (int) SNES_SPC::state_size ); 42.37 + #endif 42.38 + 42.39 + SNES_SPC* s = new SNES_SPC; 42.40 + if ( s && s->init() ) 42.41 + { 42.42 + delete s; 42.43 + s = 0; 42.44 + } 42.45 + return s; 42.46 +} 42.47 + 42.48 +void spc_delete ( SNES_SPC* s ) { delete s; } 42.49 +void spc_init_rom ( SNES_SPC* s, unsigned char const r [64] ) { s->init_rom( r ); } 42.50 +void spc_set_output ( SNES_SPC* s, spc_sample_t* p, int n ) { s->set_output( p, n ); } 42.51 +int spc_sample_count ( SNES_SPC const* s ) { return s->sample_count(); } 42.52 +void spc_reset ( SNES_SPC* s ) { s->reset(); } 42.53 +void spc_soft_reset ( SNES_SPC* s ) { s->soft_reset(); } 42.54 +int spc_read_port ( SNES_SPC* s, spc_time_t t, int p ) { return s->read_port( t, p ); } 42.55 +void spc_write_port ( SNES_SPC* s, spc_time_t t, int p, int d ) { s->write_port( t, p, d ); } 42.56 +void spc_end_frame ( SNES_SPC* s, spc_time_t t ) { s->end_frame( t ); } 42.57 +void spc_mute_voices ( SNES_SPC* s, int mask ) { s->mute_voices( mask ); } 42.58 +void spc_disable_surround( SNES_SPC* s, int disable ) { s->disable_surround( disable ); } 42.59 +void spc_set_tempo ( SNES_SPC* s, int tempo ) { s->set_tempo( tempo ); } 42.60 +spc_err_t spc_load_spc ( SNES_SPC* s, void const* p, long n ) { return s->load_spc( p, n ); } 42.61 +void spc_clear_echo ( SNES_SPC* s ) { s->clear_echo(); } 42.62 +spc_err_t spc_play ( SNES_SPC* s, int count, short* out ) { return s->play( count, out ); } 42.63 +spc_err_t spc_skip ( SNES_SPC* s, int count ) { return s->skip( count ); } 42.64 +#if !SPC_NO_COPY_STATE_FUNCS 42.65 +void spc_copy_state ( SNES_SPC* s, unsigned char** p, spc_copy_func_t f ) { s->copy_state( p, f ); } 42.66 +void spc_init_header ( void* spc_out ) { SNES_SPC::init_header( spc_out ); } 42.67 +void spc_save_spc ( SNES_SPC* s, void* spc_out ) { s->save_spc( spc_out ); } 42.68 +int spc_check_kon ( SNES_SPC* s ) { return s->check_kon(); } 42.69 +#endif 42.70 + 42.71 +SPC_Filter* spc_filter_new( void ) { return new SPC_Filter; } 42.72 +void spc_filter_delete( SPC_Filter* f ) { delete f; } 42.73 +void spc_filter_run( SPC_Filter* f, spc_sample_t* p, int s ) { f->run( p, s ); } 42.74 +void spc_filter_clear( SPC_Filter* f ) { f->clear(); } 42.75 +void spc_filter_set_gain( SPC_Filter* f, int gain ) { f->set_gain( gain ); } 42.76 +void spc_filter_set_bass( SPC_Filter* f, int bass ) { f->set_bass( bass ); }
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 43.2 +++ b/snes_spc/spc.h Fri Oct 21 05:53:11 2011 -0700 43.3 @@ -0,0 +1,147 @@ 43.4 +/* SNES SPC-700 APU emulator C interface (also usable from C++) */ 43.5 + 43.6 +/* snes_spc 0.9.0 */ 43.7 +#ifndef SPC_H 43.8 +#define SPC_H 43.9 + 43.10 +#include <stddef.h> 43.11 + 43.12 +#ifdef __cplusplus 43.13 + extern "C" { 43.14 +#endif 43.15 + 43.16 +/* Error string return. NULL if success, otherwise error message. */ 43.17 +typedef const char* spc_err_t; 43.18 + 43.19 +typedef struct SNES_SPC SNES_SPC; 43.20 + 43.21 +/* Creates new SPC emulator. NULL if out of memory. */ 43.22 +SNES_SPC* spc_new( void ); 43.23 + 43.24 +/* Frees SPC emulator */ 43.25 +void spc_delete( SNES_SPC* ); 43.26 + 43.27 +/* Sample pairs generated per second */ 43.28 +enum { spc_sample_rate = 32000 }; 43.29 + 43.30 + 43.31 +/**** Emulator use ****/ 43.32 + 43.33 +/* Sets IPL ROM data. Library does not include ROM data. Most SPC music files 43.34 +don't need ROM, but a full emulator must provide this. */ 43.35 +enum { spc_rom_size = 0x40 }; 43.36 +void spc_init_rom( SNES_SPC*, unsigned char const rom [spc_rom_size] ); 43.37 + 43.38 +/* Sets destination for output samples */ 43.39 +typedef short spc_sample_t; 43.40 +void spc_set_output( SNES_SPC*, spc_sample_t* out, int out_size ); 43.41 + 43.42 +/* Number of samples written to output since last set */ 43.43 +int spc_sample_count( SNES_SPC const* ); 43.44 + 43.45 +/* Resets SPC to power-on state. This resets your output buffer, so you must 43.46 +call spc_set_output() after this. */ 43.47 +void spc_reset( SNES_SPC* ); 43.48 + 43.49 +/* Emulates pressing reset switch on SNES. This resets your output buffer, so 43.50 +you must call spc_set_output() after this. */ 43.51 +void spc_soft_reset( SNES_SPC* ); 43.52 + 43.53 +/* 1024000 SPC clocks per second, sample pair every 32 clocks */ 43.54 +typedef int spc_time_t; 43.55 +enum { spc_clock_rate = 1024000 }; 43.56 +enum { spc_clocks_per_sample = 32 }; 43.57 + 43.58 +/* Reads/writes port at specified time */ 43.59 +enum { spc_port_count = 4 }; 43.60 +int spc_read_port ( SNES_SPC*, spc_time_t, int port ); 43.61 +void spc_write_port( SNES_SPC*, spc_time_t, int port, int data ); 43.62 + 43.63 +/* Runs SPC to end_time and starts a new time frame at 0 */ 43.64 +void spc_end_frame( SNES_SPC*, spc_time_t end_time ); 43.65 + 43.66 + 43.67 +/**** Sound control ****/ 43.68 + 43.69 +/*Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */ 43.70 +enum { spc_voice_count = 8 }; 43.71 +void spc_mute_voices( SNES_SPC*, int mask ); 43.72 + 43.73 +/* If true, prevents channels and global volumes from being phase-negated. 43.74 +Only supported by fast DSP; has no effect on accurate DSP. */ 43.75 +void spc_disable_surround( SNES_SPC*, int disable ); 43.76 + 43.77 +/* Sets tempo, where spc_tempo_unit = normal, spc_tempo_unit / 2 = half speed, etc. */ 43.78 +enum { spc_tempo_unit = 0x100 }; 43.79 +void spc_set_tempo( SNES_SPC*, int ); 43.80 + 43.81 + 43.82 +/**** SPC music playback *****/ 43.83 + 43.84 +/* Loads SPC data into emulator. Returns NULL on success, otherwise error string. */ 43.85 +spc_err_t spc_load_spc( SNES_SPC*, void const* spc_in, long size ); 43.86 + 43.87 +/* Clears echo region. Useful after loading an SPC as many have garbage in echo. */ 43.88 +void spc_clear_echo( SNES_SPC* ); 43.89 + 43.90 +/* Plays for count samples and write samples to out. Discards samples if out 43.91 +is NULL. Count must be a multiple of 2 since output is stereo. */ 43.92 +spc_err_t spc_play( SNES_SPC*, int count, short* out ); 43.93 + 43.94 +/* Skips count samples. Several times faster than spc_play(). */ 43.95 +spc_err_t spc_skip( SNES_SPC*, int count ); 43.96 + 43.97 + 43.98 +/**** State save/load (only available with accurate DSP) ****/ 43.99 + 43.100 +/* Saves/loads exact emulator state */ 43.101 +enum { spc_state_size = 67 * 1024L }; /* maximum space needed when saving */ 43.102 +typedef void (*spc_copy_func_t)( unsigned char** io, void* state, size_t ); 43.103 +void spc_copy_state( SNES_SPC*, unsigned char** io, spc_copy_func_t ); 43.104 + 43.105 +/* Writes minimal SPC file header to spc_out */ 43.106 +void spc_init_header( void* spc_out ); 43.107 + 43.108 +/* Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. 43.109 +Does not set up SPC header; use spc_init_header() for that. */ 43.110 +enum { spc_file_size = 0x10200 }; /* spc_out must have this many bytes allocated */ 43.111 +void spc_save_spc( SNES_SPC*, void* spc_out ); 43.112 + 43.113 +/* Returns non-zero if new key-on events occurred since last check. Useful for 43.114 +trimming silence while saving an SPC. */ 43.115 +int spc_check_kon( SNES_SPC* ); 43.116 + 43.117 + 43.118 +/**** SPC_Filter ****/ 43.119 + 43.120 +typedef struct SPC_Filter SPC_Filter; 43.121 + 43.122 +/* Creates new filter. NULL if out of memory. */ 43.123 +SPC_Filter* spc_filter_new( void ); 43.124 + 43.125 +/* Frees filter */ 43.126 +void spc_filter_delete( SPC_Filter* ); 43.127 + 43.128 +/* Filters count samples of stereo sound in place. Count must be a multiple of 2. */ 43.129 +void spc_filter_run( SPC_Filter*, spc_sample_t* io, int count ); 43.130 + 43.131 +/* Clears filter to silence */ 43.132 +void spc_filter_clear( SPC_Filter* ); 43.133 + 43.134 +/* Sets gain (volume), where spc_filter_gain_unit is normal. Gains greater than 43.135 +spc_filter_gain_unit are fine, since output is clamped to 16-bit sample range. */ 43.136 +enum { spc_filter_gain_unit = 0x100 }; 43.137 +void spc_filter_set_gain( SPC_Filter*, int gain ); 43.138 + 43.139 +/* Sets amount of bass (logarithmic scale) */ 43.140 +enum { spc_filter_bass_none = 0 }; 43.141 +enum { spc_filter_bass_norm = 8 }; /* normal amount */ 43.142 +enum { spc_filter_bass_max = 31 }; 43.143 +void spc_filter_set_bass( SPC_Filter*, int bass ); 43.144 + 43.145 + 43.146 +#ifdef __cplusplus 43.147 + } 43.148 +#endif 43.149 + 43.150 +#endif
44.1 Binary file snes_spc/spc.h.gch has changed