mirror of https://github.com/snes9xgit/snes9x.git
Add SPC dumping support.
This commit is contained in:
parent
2e94b98e90
commit
ebc9e721f6
29
apu/apu.cpp
29
apu/apu.cpp
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
26
snapshot.cpp
26
snapshot.cpp
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue