Add SPC dumping support.

This commit is contained in:
Brandon Wright 2011-06-26 05:33:14 -05:00
parent 2e94b98e90
commit ebc9e721f6
7 changed files with 179 additions and 111 deletions

View File

@ -476,8 +476,8 @@ void S9xSetSoundMute (bool8 mute)
void S9xDumpSPCSnapshot (void) void S9xDumpSPCSnapshot (void)
{ {
/* TODO: SPC dumping */ SNES::dsp.spc_dsp.dump_spc_snapshot();
/* spc_core->dsp_dump_spc_snapshot(); */
} }
static void SPCSnapshotCallback (void) static void SPCSnapshotCallback (void)
@ -492,6 +492,8 @@ bool8 S9xInitAPU (void)
spc::shrink_buffer = NULL; spc::shrink_buffer = NULL;
spc::resampler = NULL; spc::resampler = NULL;
SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback);
return (TRUE); return (TRUE);
} }
@ -632,3 +634,26 @@ void S9xAPULoadState (uint8 *block)
ptr += sizeof(int32); ptr += sizeof(int32);
memcpy (SNES::cpu.registers, ptr, 4); memcpy (SNES::cpu.registers, ptr, 4);
} }
bool8 S9xSPCDump (const char *filename)
{
FILE *fs;
uint8 buf[SPC_FILE_SIZE];
size_t ignore;
fs = fopen(filename, "wb");
if (!fs)
return (FALSE);
S9xSetSoundMute(TRUE);
SNES::smp.save_spc (buf);
ignore = fwrite(buf, SPC_FILE_SIZE, 1, fs);
fclose(fs);
S9xSetSoundMute(FALSE);
return (TRUE);
}

View File

@ -183,7 +183,8 @@
typedef void (*apu_callback) (void *); typedef void (*apu_callback) (void *);
#define SPC_SAVE_STATE_BLOCK_SIZE (1024 * 65) #define SPC_SAVE_STATE_BLOCK_SIZE (1024 * 65)
#define SPC_FILE_SIZE (66048)
bool8 S9xInitAPU (void); bool8 S9xInitAPU (void);
void S9xDeinitAPU (void); void S9xDeinitAPU (void);
@ -199,6 +200,7 @@ void S9xAPUAllowTimeOverflow (bool);
void S9xAPULoadState (uint8 *); void S9xAPULoadState (uint8 *);
void S9xAPUSaveState (uint8 *); void S9xAPUSaveState (uint8 *);
void S9xDumpSPCSnapshot (void); void S9xDumpSPCSnapshot (void);
bool8 S9xSPCDump (const char *);
bool8 S9xInitSound (int, int); bool8 S9xInitSound (int, int);
bool8 S9xOpenSoundDevice (void); bool8 S9xOpenSoundDevice (void);

View File

@ -132,7 +132,7 @@ inline int SPC_DSP::interpolate( voice_t const* v )
int offset = v->interp_pos >> 4 & 0xFF; int offset = v->interp_pos >> 4 & 0xFF;
short const* fwd = gauss + 255 - offset; short const* fwd = gauss + 255 - offset;
short const* rev = gauss + offset; // mirror left half of gaussian short const* rev = gauss + offset; // mirror left half of gaussian
int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
int out; int out;
out = (fwd [ 0] * in [0]) >> 11; out = (fwd [ 0] * in [0]) >> 11;
@ -140,7 +140,7 @@ inline int SPC_DSP::interpolate( voice_t const* v )
out += (rev [256] * in [2]) >> 11; out += (rev [256] * in [2]) >> 11;
out = (int16_t) out; out = (int16_t) out;
out += (rev [ 0] * in [3]) >> 11; out += (rev [ 0] * in [3]) >> 11;
CLAMP16( out ); CLAMP16( out );
out &= ~1; out &= ~1;
return out; return out;
@ -262,13 +262,13 @@ inline void SPC_DSP::run_envelope( voice_t* const v )
} }
} }
} }
// Sustain level // Sustain level
if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )
v->env_mode = env_sustain; v->env_mode = env_sustain;
v->hidden_env = env; v->hidden_env = env;
// unsigned cast because linear decrease going negative also triggers this // unsigned cast because linear decrease going negative also triggers this
if ( (unsigned) env > 0x7FF ) if ( (unsigned) env > 0x7FF )
{ {
@ -276,7 +276,7 @@ inline void SPC_DSP::run_envelope( voice_t* const v )
if ( v->env_mode == env_attack ) if ( v->env_mode == env_attack )
v->env_mode = env_decay; v->env_mode = env_decay;
} }
if ( !read_counter( rate ) ) if ( !read_counter( rate ) )
v->env = env; // nothing else is controlled by the counter v->env = env; // nothing else is controlled by the counter
} }
@ -289,27 +289,27 @@ inline void SPC_DSP::decode_brr( voice_t* v )
{ {
// Arrange the four input nybbles in 0xABCD order for easy decoding // Arrange the four input nybbles in 0xABCD order for easy decoding
int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
int const header = m.t_brr_header; int const header = m.t_brr_header;
// Write to next four samples in circular buffer // Write to next four samples in circular buffer
int* pos = &v->buf [v->buf_pos]; int* pos = &v->buf [v->buf_pos];
int* end; int* end;
if ( (v->buf_pos += 4) >= brr_buf_size ) if ( (v->buf_pos += 4) >= brr_buf_size )
v->buf_pos = 0; v->buf_pos = 0;
// Decode four samples // Decode four samples
for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
{ {
// Extract nybble and sign-extend // Extract nybble and sign-extend
int s = (int16_t) nybbles >> 12; int s = (int16_t) nybbles >> 12;
// Shift sample based on header // Shift sample based on header
int const shift = header >> 4; int const shift = header >> 4;
s = (s << shift) >> 1; s = (s << shift) >> 1;
if ( shift >= 0xD ) // handle invalid range if ( shift >= 0xD ) // handle invalid range
s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0)
// Apply IIR filter (8 is the most commonly used) // Apply IIR filter (8 is the most commonly used)
int const filter = header & 0x0C; int const filter = header & 0x0C;
int const p1 = pos [brr_buf_size - 1]; int const p1 = pos [brr_buf_size - 1];
@ -334,7 +334,7 @@ inline void SPC_DSP::decode_brr( voice_t* v )
s += p1 >> 1; s += p1 >> 1;
s += (-p1) >> 5; s += (-p1) >> 5;
} }
// Adjust and write sample // Adjust and write sample
CLAMP16( s ); CLAMP16( s );
s = (int16_t) (s * 2); s = (int16_t) (s * 2);
@ -367,11 +367,11 @@ MISC_CLOCK( 30 )
if ( m.every_other_sample ) if ( m.every_other_sample )
{ {
m.kon = m.new_kon; m.kon = m.new_kon;
m.t_koff = REG(koff) | m.mute_mask; m.t_koff = REG(koff) | m.mute_mask;
} }
run_counters(); run_counters();
// Noise // Noise
if ( !read_counter( REG(flg) & 0x1F ) ) if ( !read_counter( REG(flg) & 0x1F ) )
{ {
@ -397,9 +397,9 @@ inline VOICE_CLOCK( V2 )
if ( !v->kon_delay ) if ( !v->kon_delay )
entry += 2; entry += 2;
m.t_brr_next_addr = GET_LE16A( entry ); m.t_brr_next_addr = GET_LE16A( entry );
m.t_adsr0 = VREG(v->regs,adsr0); m.t_adsr0 = VREG(v->regs,adsr0);
// Read pitch, spread over two clocks // Read pitch, spread over two clocks
m.t_pitch = VREG(v->regs,pitchl); m.t_pitch = VREG(v->regs,pitchl);
} }
@ -418,7 +418,7 @@ VOICE_CLOCK( V3c )
// Pitch modulation using previous voice's output // Pitch modulation using previous voice's output
if ( m.t_pmon & v->vbit ) if ( m.t_pmon & v->vbit )
m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10;
if ( v->kon_delay ) if ( v->kon_delay )
{ {
// Get ready to start BRR decoding on next sample // Get ready to start BRR decoding on next sample
@ -437,46 +437,46 @@ VOICE_CLOCK( V3c )
spc_snapshot_callback(); spc_snapshot_callback();
} }
} }
// Envelope is never run during KON // Envelope is never run during KON
v->env = 0; v->env = 0;
v->hidden_env = 0; v->hidden_env = 0;
// Disable BRR decoding until last three samples // Disable BRR decoding until last three samples
v->interp_pos = 0; v->interp_pos = 0;
if ( --v->kon_delay & 3 ) if ( --v->kon_delay & 3 )
v->interp_pos = 0x4000; v->interp_pos = 0x4000;
// Pitch is never added during KON // Pitch is never added during KON
m.t_pitch = 0; m.t_pitch = 0;
} }
// Gaussian interpolation // Gaussian interpolation
{ {
int output = interpolate( v ); int output = interpolate( v );
// Noise // Noise
if ( m.t_non & v->vbit ) if ( m.t_non & v->vbit )
output = (int16_t) (m.noise * 2); output = (int16_t) (m.noise * 2);
// Apply envelope // Apply envelope
m.t_output = (output * v->env) >> 11 & ~1; m.t_output = (output * v->env) >> 11 & ~1;
v->t_envx_out = (uint8_t) (v->env >> 4); v->t_envx_out = (uint8_t) (v->env >> 4);
} }
// Immediate silence due to end of sample or soft reset // Immediate silence due to end of sample or soft reset
if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 )
{ {
v->env_mode = env_release; v->env_mode = env_release;
v->env = 0; v->env = 0;
} }
if ( m.every_other_sample ) if ( m.every_other_sample )
{ {
// KOFF // KOFF
if ( m.t_koff & v->vbit ) if ( m.t_koff & v->vbit )
v->env_mode = env_release; v->env_mode = env_release;
// KON // KON
if ( m.kon & v->vbit ) if ( m.kon & v->vbit )
{ {
@ -484,7 +484,7 @@ VOICE_CLOCK( V3c )
v->env_mode = env_attack; v->env_mode = env_attack;
} }
} }
// Run envelope for next sample // Run envelope for next sample
if ( !v->kon_delay ) if ( !v->kon_delay )
run_envelope( v ); run_envelope( v );
@ -499,7 +499,7 @@ inline void SPC_DSP::voice_output( voice_t const* v, int ch )
// Add to output total // Add to output total
m.t_main_out [ch] += amp; m.t_main_out [ch] += amp;
CLAMP16( m.t_main_out [ch] ); CLAMP16( m.t_main_out [ch] );
// Optionally add to echo total // Optionally add to echo total
if ( m.t_eon & v->vbit ) if ( m.t_eon & v->vbit )
{ {
@ -514,7 +514,7 @@ VOICE_CLOCK( V4 )
if ( v->interp_pos >= 0x4000 ) if ( v->interp_pos >= 0x4000 )
{ {
decode_brr( v ); decode_brr( v );
if ( (v->brr_offset += 2) >= brr_block_size ) if ( (v->brr_offset += 2) >= brr_block_size )
{ {
// Start decoding next BRR block // Start decoding next BRR block
@ -528,14 +528,14 @@ VOICE_CLOCK( V4 )
v->brr_offset = 1; v->brr_offset = 1;
} }
} }
// Apply pitch // Apply pitch
v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch;
// Keep from getting too far ahead (when using pitch modulation) // Keep from getting too far ahead (when using pitch modulation)
if ( v->interp_pos > 0x7FFF ) if ( v->interp_pos > 0x7FFF )
v->interp_pos = 0x7FFF; v->interp_pos = 0x7FFF;
// Output left // Output left
voice_output( v, 0 ); voice_output( v, 0 );
} }
@ -543,10 +543,10 @@ inline VOICE_CLOCK( V5 )
{ {
// Output right // Output right
voice_output( v, 1 ); voice_output( v, 1 );
// ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier
int endx_buf = REG(endx) | m.t_looped; int endx_buf = REG(endx) | m.t_looped;
// Clear bit in ENDX if KON just began // Clear bit in ENDX if KON just began
if ( v->kon_delay == 5 ) if ( v->kon_delay == 5 )
endx_buf &= ~v->vbit; endx_buf &= ~v->vbit;
@ -561,7 +561,7 @@ inline VOICE_CLOCK( V7 )
{ {
// Update ENDX // Update ENDX
REG(endx) = m.endx_buf; REG(endx) = m.endx_buf;
m.envx_buf = v->t_envx_out; m.envx_buf = v->t_envx_out;
} }
inline VOICE_CLOCK( V8 ) inline VOICE_CLOCK( V8 )
@ -619,14 +619,14 @@ ECHO_CLOCK( 22 )
// History // History
if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] )
m.echo_hist_pos = m.echo_hist; m.echo_hist_pos = m.echo_hist;
m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF;
echo_read( 0 ); echo_read( 0 );
// FIR (using l and r temporaries below helps compiler optimize) // FIR (using l and r temporaries below helps compiler optimize)
int l = CALC_FIR( 0, 0 ); int l = CALC_FIR( 0, 0 );
int r = CALC_FIR( 0, 1 ); int r = CALC_FIR( 0, 1 );
m.t_echo_in [0] = l; m.t_echo_in [0] = l;
m.t_echo_in [1] = r; m.t_echo_in [1] = r;
} }
@ -634,17 +634,17 @@ ECHO_CLOCK( 23 )
{ {
int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 );
int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 );
m.t_echo_in [0] += l; m.t_echo_in [0] += l;
m.t_echo_in [1] += r; m.t_echo_in [1] += r;
echo_read( 1 ); echo_read( 1 );
} }
ECHO_CLOCK( 24 ) ECHO_CLOCK( 24 )
{ {
int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 );
int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 );
m.t_echo_in [0] += l; m.t_echo_in [0] += l;
m.t_echo_in [1] += r; m.t_echo_in [1] += r;
} }
@ -652,16 +652,16 @@ ECHO_CLOCK( 25 )
{ {
int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); int l = m.t_echo_in [0] + CALC_FIR( 6, 0 );
int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); int r = m.t_echo_in [1] + CALC_FIR( 6, 1 );
l = (int16_t) l; l = (int16_t) l;
r = (int16_t) r; r = (int16_t) r;
l += (int16_t) CALC_FIR( 7, 0 ); l += (int16_t) CALC_FIR( 7, 0 );
r += (int16_t) CALC_FIR( 7, 1 ); r += (int16_t) CALC_FIR( 7, 1 );
CLAMP16( l ); CLAMP16( l );
CLAMP16( r ); CLAMP16( r );
m.t_echo_in [0] = l & ~1; m.t_echo_in [0] = l & ~1;
m.t_echo_in [1] = r & ~1; m.t_echo_in [1] = r & ~1;
} }
@ -677,14 +677,14 @@ ECHO_CLOCK( 26 )
// Left output volumes // Left output volumes
// (save sample for next clock so we can output both together) // (save sample for next clock so we can output both together)
m.t_main_out [0] = echo_output( 0 ); m.t_main_out [0] = echo_output( 0 );
// Echo feedback // Echo feedback
int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7);
int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7);
CLAMP16( l ); CLAMP16( l );
CLAMP16( r ); CLAMP16( r );
m.t_echo_out [0] = l & ~1; m.t_echo_out [0] = l & ~1;
m.t_echo_out [1] = r & ~1; m.t_echo_out [1] = r & ~1;
} }
@ -695,7 +695,7 @@ ECHO_CLOCK( 27 )
int r = echo_output( 1 ); int r = echo_output( 1 );
m.t_main_out [0] = 0; m.t_main_out [0] = 0;
m.t_main_out [1] = 0; m.t_main_out [1] = 0;
// TODO: global muting isn't this simple (turns DAC on and off // TODO: global muting isn't this simple (turns DAC on and off
// or something, causing small ~37-sample pulse when first muted) // or something, causing small ~37-sample pulse when first muted)
if ( REG(flg) & 0x40 ) if ( REG(flg) & 0x40 )
@ -703,7 +703,7 @@ ECHO_CLOCK( 27 )
l = 0; l = 0;
r = 0; r = 0;
} }
// Output sample to DAC // Output sample to DAC
#ifdef SPC_DSP_OUT_HOOK #ifdef SPC_DSP_OUT_HOOK
SPC_DSP_OUT_HOOK( l, r ); SPC_DSP_OUT_HOOK( l, r );
@ -732,17 +732,17 @@ inline void SPC_DSP::echo_write( int ch )
ECHO_CLOCK( 29 ) ECHO_CLOCK( 29 )
{ {
m.t_esa = REG(esa); m.t_esa = REG(esa);
if ( !m.echo_offset ) if ( !m.echo_offset )
m.echo_length = (REG(edl) & 0x0F) * 0x800; m.echo_length = (REG(edl) & 0x0F) * 0x800;
m.echo_offset += 4; m.echo_offset += 4;
if ( m.echo_offset >= m.echo_length ) if ( m.echo_offset >= m.echo_length )
m.echo_offset = 0; m.echo_offset = 0;
// Write left echo // Write left echo
echo_write( 0 ); echo_write( 0 );
m.t_echo_enabled = REG(flg); m.t_echo_enabled = REG(flg);
} }
ECHO_CLOCK( 30 ) ECHO_CLOCK( 30 )
@ -805,17 +805,17 @@ PHASE(31) V(V4,0) V(V1,2)\
void SPC_DSP::run( int clocks_remain ) void SPC_DSP::run( int clocks_remain )
{ {
require( clocks_remain > 0 ); require( clocks_remain > 0 );
int const phase = m.phase; int const phase = m.phase;
m.phase = (phase + clocks_remain) & 31; m.phase = (phase + clocks_remain) & 31;
switch ( phase ) switch ( phase )
{ {
loop: loop:
#define PHASE( n ) if ( n && !--clocks_remain ) break; case n: #define PHASE( n ) if ( n && !--clocks_remain ) break; case n:
GEN_DSP_TIMING GEN_DSP_TIMING
#undef PHASE #undef PHASE
if ( --clocks_remain ) if ( --clocks_remain )
goto loop; goto loop;
} }
@ -837,19 +837,20 @@ void SPC_DSP::init( void* ram_64k )
stereo_switch = 0xffff; stereo_switch = 0xffff;
take_spc_snapshot = 0; take_spc_snapshot = 0;
spc_snapshot_callback = 0; spc_snapshot_callback = 0;
rom_enabled = 0;
#ifndef NDEBUG #ifndef NDEBUG
// be sure this sign-extends // be sure this sign-extends
assert( (int16_t) 0x8000 == -0x8000 ); assert( (int16_t) 0x8000 == -0x8000 );
// be sure right shift preserves sign // be sure right shift preserves sign
assert( (-1 >> 1) == -1 ); assert( (-1 >> 1) == -1 );
// check clamp macro // check clamp macro
int i; int i;
i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );
i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );
blargg_verify_byte_order(); blargg_verify_byte_order();
#endif #endif
} }
@ -857,13 +858,13 @@ void SPC_DSP::init( void* ram_64k )
void SPC_DSP::soft_reset_common() void SPC_DSP::soft_reset_common()
{ {
require( m.ram ); // init() must have been called already require( m.ram ); // init() must have been called already
m.noise = 0x4000; m.noise = 0x4000;
m.echo_hist_pos = m.echo_hist; m.echo_hist_pos = m.echo_hist;
m.every_other_sample = 1; m.every_other_sample = 1;
m.echo_offset = 0; m.echo_offset = 0;
m.phase = 0; m.phase = 0;
init_counter(); init_counter();
for (int i = 0; i < voice_count; i++) for (int i = 0; i < voice_count; i++)
@ -880,7 +881,7 @@ void SPC_DSP::load( uint8_t const regs [register_count] )
{ {
memcpy( m.regs, regs, sizeof m.regs ); memcpy( m.regs, regs, sizeof m.regs );
memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );
// Internal state // Internal state
for ( int i = voice_count; --i >= 0; ) for ( int i = voice_count; --i >= 0; )
{ {
@ -892,7 +893,7 @@ void SPC_DSP::load( uint8_t const regs [register_count] )
m.new_kon = REG(kon); m.new_kon = REG(kon);
m.t_dir = REG(dir); m.t_dir = REG(dir);
m.t_esa = REG(esa); m.t_esa = REG(esa);
soft_reset_common(); soft_reset_common();
} }
@ -945,18 +946,18 @@ void SPC_State_Copier::extra()
void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
{ {
SPC_State_Copier copier( io, copy ); SPC_State_Copier copier( io, copy );
// DSP registers // DSP registers
copier.copy( m.regs, register_count ); copier.copy( m.regs, register_count );
// Internal state // Internal state
// Voices // Voices
int i; int i;
for ( i = 0; i < voice_count; i++ ) for ( i = 0; i < voice_count; i++ )
{ {
voice_t* v = &m.voices [i]; voice_t* v = &m.voices [i];
// BRR buffer // BRR buffer
int i; int i;
for ( i = 0; i < brr_buf_size; i++ ) for ( i = 0; i < brr_buf_size; i++ )
@ -965,7 +966,7 @@ void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
SPC_COPY( int16_t, s ); SPC_COPY( int16_t, s );
v->buf [i] = v->buf [i + brr_buf_size] = s; v->buf [i] = v->buf [i + brr_buf_size] = s;
} }
SPC_COPY( uint16_t, v->interp_pos ); SPC_COPY( uint16_t, v->interp_pos );
SPC_COPY( uint16_t, v->brr_addr ); SPC_COPY( uint16_t, v->brr_addr );
SPC_COPY( uint16_t, v->env ); SPC_COPY( uint16_t, v->env );
@ -979,10 +980,10 @@ void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
v->env_mode = (enum env_mode_t) m; v->env_mode = (enum env_mode_t) m;
} }
SPC_COPY( uint8_t, v->t_envx_out ); SPC_COPY( uint8_t, v->t_envx_out );
copier.extra(); copier.extra();
} }
// Echo history // Echo history
for ( i = 0; i < echo_hist_size; i++ ) for ( i = 0; i < echo_hist_size; i++ )
{ {
@ -996,28 +997,28 @@ void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
} }
m.echo_hist_pos = m.echo_hist; m.echo_hist_pos = m.echo_hist;
memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] );
// Misc // Misc
SPC_COPY( uint8_t, m.every_other_sample ); SPC_COPY( uint8_t, m.every_other_sample );
SPC_COPY( uint8_t, m.kon ); SPC_COPY( uint8_t, m.kon );
SPC_COPY( uint16_t, m.noise ); SPC_COPY( uint16_t, m.noise );
SPC_COPY( uint16_t, m.counter ); SPC_COPY( uint16_t, m.counter );
SPC_COPY( uint16_t, m.echo_offset ); SPC_COPY( uint16_t, m.echo_offset );
SPC_COPY( uint16_t, m.echo_length ); SPC_COPY( uint16_t, m.echo_length );
SPC_COPY( uint8_t, m.phase ); SPC_COPY( uint8_t, m.phase );
SPC_COPY( uint8_t, m.new_kon ); SPC_COPY( uint8_t, m.new_kon );
SPC_COPY( uint8_t, m.endx_buf ); SPC_COPY( uint8_t, m.endx_buf );
SPC_COPY( uint8_t, m.envx_buf ); SPC_COPY( uint8_t, m.envx_buf );
SPC_COPY( uint8_t, m.outx_buf ); SPC_COPY( uint8_t, m.outx_buf );
SPC_COPY( uint8_t, m.t_pmon ); SPC_COPY( uint8_t, m.t_pmon );
SPC_COPY( uint8_t, m.t_non ); SPC_COPY( uint8_t, m.t_non );
SPC_COPY( uint8_t, m.t_eon ); SPC_COPY( uint8_t, m.t_eon );
SPC_COPY( uint8_t, m.t_dir ); SPC_COPY( uint8_t, m.t_dir );
SPC_COPY( uint8_t, m.t_koff ); SPC_COPY( uint8_t, m.t_koff );
SPC_COPY( uint16_t, m.t_brr_next_addr ); SPC_COPY( uint16_t, m.t_brr_next_addr );
SPC_COPY( uint8_t, m.t_adsr0 ); SPC_COPY( uint8_t, m.t_adsr0 );
SPC_COPY( uint8_t, m.t_brr_header ); SPC_COPY( uint8_t, m.t_brr_header );
@ -1025,20 +1026,20 @@ void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
SPC_COPY( uint8_t, m.t_srcn ); SPC_COPY( uint8_t, m.t_srcn );
SPC_COPY( uint8_t, m.t_esa ); SPC_COPY( uint8_t, m.t_esa );
SPC_COPY( uint8_t, m.t_echo_enabled ); SPC_COPY( uint8_t, m.t_echo_enabled );
SPC_COPY( int16_t, m.t_main_out [0] ); SPC_COPY( int16_t, m.t_main_out [0] );
SPC_COPY( int16_t, m.t_main_out [1] ); SPC_COPY( int16_t, m.t_main_out [1] );
SPC_COPY( int16_t, m.t_echo_out [0] ); SPC_COPY( int16_t, m.t_echo_out [0] );
SPC_COPY( int16_t, m.t_echo_out [1] ); SPC_COPY( int16_t, m.t_echo_out [1] );
SPC_COPY( int16_t, m.t_echo_in [0] ); SPC_COPY( int16_t, m.t_echo_in [0] );
SPC_COPY( int16_t, m.t_echo_in [1] ); SPC_COPY( int16_t, m.t_echo_in [1] );
SPC_COPY( uint16_t, m.t_dir_addr ); SPC_COPY( uint16_t, m.t_dir_addr );
SPC_COPY( uint16_t, m.t_pitch ); SPC_COPY( uint16_t, m.t_pitch );
SPC_COPY( int16_t, m.t_output ); SPC_COPY( int16_t, m.t_output );
SPC_COPY( uint16_t, m.t_echo_ptr ); SPC_COPY( uint16_t, m.t_echo_ptr );
SPC_COPY( uint8_t, m.t_looped ); SPC_COPY( uint8_t, m.t_looped );
copier.extra(); copier.extra();
} }
#endif #endif

View File

@ -22,6 +22,7 @@ public:
#else #else
void load_state(uint8 **); void load_state(uint8 **);
void save_state(uint8 **); void save_state(uint8 **);
void save_spc (uint8 *);
#endif #endif
SMP(); SMP();
~SMP(); ~SMP();

View File

@ -1,9 +1,75 @@
#include "snes/snes.hpp" #include "snes/snes.hpp"
#include <stdio.h>
typedef struct spc_file {
uint8 header[33];
uint8 idtag[3];
uint8 version_minor;
uint8 pc_low;
uint8 pc_high;
uint8 a;
uint8 x;
uint8 y;
uint8 psw;
uint8 sp;
uint8 unused_a[2];
uint8 id666[210];
uint8 apuram[65536];
uint8 dsp_registers[128];
uint8 unused_b[64];
uint8 iplrom[64];
} spc_file;
namespace SNES { namespace SNES {
#include "dsp/blargg_endian.h" #include "dsp/blargg_endian.h"
void SMP::save_spc (uint8 *block) {
spc_file out;
const char *header = "SNES-SPC700 Sound File Data v0.30";
memcpy (out.header, header, 33);
out.idtag[0] = out.idtag[1] = 26;
out.idtag[2] = 27;
out.version_minor = 30;
out.pc_low = regs.pc & 0xff;
out.pc_high = (regs.pc >> 8) & 0xff;
out.a = regs.a;
out.x = regs.x;
out.y = regs.y;
out.psw = (uint8) ((unsigned) regs.p);
out.sp = regs.sp;
out.unused_a[0] = out.unused_a[1] = 0;
memset (out.id666, 0, 210);
memcpy (out.apuram, apuram, 65536);
for (int i = 0xf2; i <= 0xf7; i++)
{
out.apuram[i] = mmio_read (i);
}
for (int i = 0xfd; i <= 0xff; i++)
{
out.apuram[i] = mmio_read (i);
}
for (int i = 0; i < 128; i++)
{
out.dsp_registers[i] = dsp.read (i);
}
memset (out.unused_b, 0, 64);
memcpy (out.iplrom, iplrom, 64);
memcpy (block, &out, 66048);
}
void SMP::save_state(uint8 **block) { void SMP::save_state(uint8 **block) {
uint8 *ptr = *block; uint8 *ptr = *block;
memcpy(ptr, apuram, 64 * 1024); memcpy(ptr, apuram, 64 * 1024);
@ -116,4 +182,4 @@ void SMP::load_state(uint8 **block) {
*block = ptr; *block = ptr;
} }
} } /* namespace SNES */

View File

@ -2275,29 +2275,3 @@ static void UnfreezeStructFromCopy (void *sbase, FreezeData *fields, int num_fie
} }
} }
} }
bool8 S9xSPCDump (const char *filename)
{
/* TODO: No SPC dumping in byuu SMP */
/*
FILE *fs;
uint8 buf[SNES_SPC::spc_file_size];
size_t ignore;
fs = fopen(filename, "wb");
if (!fs)
return (FALSE);
S9xSetSoundMute(TRUE);
spc_core->init_header(buf);
spc_core->save_spc(buf);
ignore = fwrite(buf, SNES_SPC::spc_file_size, 1, fs);
fclose(fs);
S9xSetSoundMute(FALSE);
*/
return (TRUE);
}

View File

@ -195,6 +195,5 @@ bool8 S9xFreezeGame (const char *);
bool8 S9xUnfreezeGame (const char *); bool8 S9xUnfreezeGame (const char *);
void S9xFreezeToStream (STREAM); void S9xFreezeToStream (STREAM);
int S9xUnfreezeFromStream (STREAM); int S9xUnfreezeFromStream (STREAM);
bool8 S9xSPCDump (const char *);
#endif #endif