Updated to v067r26 release.

byuu says:

Re-added blargg's DSP, but this time I merged only spc_dsp and also
fixed the initial regs for Dual Orb II. Which restores the 5-10% speedup
on the compatibility and performance cores. Fixed the initial register
values for the fast CPU core, which fixes Armored Police and the other
Atlus game's title screen in the performance core. Added a missing
debugvirtual prefix to op_step, which fixes CPU stepping in the
performance core. I was using the description field for profile
identification in savestates, but that was of course the description for
the state manager. Whoops. Added a new field for profile name to the
save states, and fixed the state manager to work with that change.
Adjusted the about screen colors, which is how you can tell which core
you're using without it being annoying. Probably did some other stuff
too, meh.
This commit is contained in:
Tim Allen 2010-08-20 23:01:32 +10:00
parent 3c2ca5a383
commit 8d8bfe9e7e
28 changed files with 1918 additions and 1185 deletions

View File

@ -1,6 +1,6 @@
include nall/Makefile
snes := snes
profile := accuracy
profile := compatibility
ui := qt
# compiler

View File

@ -8,13 +8,13 @@ AboutWindow::AboutWindow() {
application.windowList.append(this);
#if defined(DEBUGGER)
setStyleSheet("background: #e0e0a0");
setStyleSheet("background: #c0c080");
#elif defined(PROFILE_ACCURACY)
setStyleSheet("background: #e0a0a0");
setStyleSheet("background: #c08080");
#elif defined(PROFILE_COMPATIBILITY)
setStyleSheet("background: #a0a0e0");
setStyleSheet("background: #8080c0");
#elif defined(PROFILE_PERFORMANCE)
setStyleSheet("background: #a0e0a0");
setStyleSheet("background: #80c080");
#endif
layout = new QVBoxLayout;

View File

@ -177,6 +177,10 @@ bool StateManagerWindow::isStateValid(unsigned slot) {
if(signature == 0) { fp.close(); return false; }
uint32_t version = fp.readl(4);
if(version != SNES::Info::SerializerVersion) { fp.close(); return false; }
fp.readl(4); //skip CRC32
char profile[16];
fp.read((uint8_t*)profile, 16);
if(strcmp(profile, SNES::Info::Profile)) { fp.close(); return false; }
fp.close();
return true;
}
@ -185,11 +189,11 @@ string StateManagerWindow::getStateDescription(unsigned slot) {
if(isStateValid(slot) == false) return "";
file fp;
fp.open(filename(), file::mode_read);
char description[513];
fp.seek(slot * SNES::system.serialize_size() + 12);
char description[512];
fp.seek(slot * SNES::system.serialize_size() + 28);
fp.read((uint8_t*)description, 512);
fp.close();
description[512] = 0;
description[511] = 0;
return description;
}
@ -200,7 +204,7 @@ void StateManagerWindow::setStateDescription(unsigned slot, const string &text)
char description[512];
memset(&description, 0, sizeof description);
strncpy(description, text, 512);
fp.seek(slot * SNES::system.serialize_size() + 12);
fp.seek(slot * SNES::system.serialize_size() + 28);
fp.write((uint8_t*)description, 512);
fp.close();
}

View File

@ -11,7 +11,7 @@ struct {
bool unused;
bool reverse_transfer;
bool fixed_transfer;
uint8 transfer_mode;
uint3 transfer_mode;
//$43x1
uint8 dest_addr;

View File

@ -30,7 +30,7 @@ public:
private:
//cpu
static void Enter();
void op_step();
debugvirtual void op_step();
void op_irq(uint16 vector);
//timing

View File

@ -82,6 +82,8 @@ void CPU::dma_run() {
do {
dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i));
} while(channel[i].dma_enabled && --channel[i].transfer_size);
channel[i].dma_enabled = false;
}
status.irq_lock = true;
@ -175,24 +177,24 @@ void CPU::dma_reset() {
channel[i].dma_enabled = false;
channel[i].hdma_enabled = false;
channel[i].direction = 0;
channel[i].indirect = false;
channel[i].unused = false;
channel[i].reverse_transfer = false;
channel[i].fixed_transfer = false;
channel[i].transfer_mode = 0x00;
channel[i].direction = 1;
channel[i].indirect = true;
channel[i].unused = true;
channel[i].reverse_transfer = true;
channel[i].fixed_transfer = true;
channel[i].transfer_mode = 0x07;
channel[i].dest_addr = 0x0000;
channel[i].source_addr = 0x0000;
channel[i].source_bank = 0x00;
channel[i].dest_addr = 0xff;
channel[i].source_addr = 0xffff;
channel[i].source_bank = 0xff;
channel[i].transfer_size = 0x0000;
channel[i].indirect_addr = 0x0000;
channel[i].transfer_size = 0xffff;
channel[i].indirect_addr = 0xffff;
channel[i].indirect_bank = 0x00;
channel[i].hdma_addr = 0x00;
channel[i].line_counter = 0x00;
channel[i].unknown = 0x00;
channel[i].indirect_bank = 0xff;
channel[i].hdma_addr = 0xff;
channel[i].line_counter = 0xff;
channel[i].unknown = 0xff;
channel[i].hdma_completed = false;
channel[i].hdma_do_transfer = false;

1027
snes/fast/dsp/SPC_DSP.cpp Executable file

File diff suppressed because it is too large Load Diff

304
snes/fast/dsp/SPC_DSP.h Executable file
View File

@ -0,0 +1,304 @@
// Highly accurate SNES SPC-700 DSP emulator
// snes_spc 0.9.0
#ifndef SPC_DSP_H
#define SPC_DSP_H
#include "blargg_common.h"
extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
class SPC_DSP {
public:
typedef BOOST::uint8_t uint8_t;
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
// Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any.
typedef short sample_t;
void set_output( sample_t* out, int out_size );
// Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than
// output buffer could hold.
int sample_count() const;
// Emulation
// Resets DSP to power-on state
void reset();
// Emulates pressing reset switch on SNES
void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call run()
// to catch the DSP up to present.
int read ( int addr ) const;
void write( int addr, int data );
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
void run( int clock_count );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
// Saves/loads exact emulator state
enum { state_size = 640 }; // maximum space needed when saving
typedef dsp_copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Returns non-zero if new key-on events occurred since last call
bool check_kon();
// DSP register addresses
// Global registers
enum {
r_mvoll = 0x0C, r_mvolr = 0x1C,
r_evoll = 0x2C, r_evolr = 0x3C,
r_kon = 0x4C, r_koff = 0x5C,
r_flg = 0x6C, r_endx = 0x7C,
r_efb = 0x0D, r_pmon = 0x2D,
r_non = 0x3D, r_eon = 0x4D,
r_dir = 0x5D, r_esa = 0x6D,
r_edl = 0x7D,
r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
};
// Voice registers
enum {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09
};
public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; }
void disable_surround( bool ) { } // not supported
public:
BLARGG_DISABLE_NOTHROW
typedef BOOST::int8_t int8_t;
typedef BOOST::int16_t int16_t;
enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
enum { brr_buf_size = 12 };
struct voice_t
{
int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
uint8_t* regs; // pointer to voice's DSP registers
int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
int kon_delay; // KON delay/current setup phase
env_mode_t env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
uint8_t t_envx_out;
};
private:
enum { brr_block_size = 9 };
struct state_t
{
uint8_t regs [register_count];
// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
int echo_hist [echo_hist_size * 2] [2];
int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int noise;
int counter;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
bool kon_check; // set when a new KON occurs
// Hidden registers also written to when main register is written to
int new_kon;
uint8_t endx_buf;
uint8_t envx_buf;
uint8_t outx_buf;
// Temporary state between clocks
// read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
// read a few clocks ahead then used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_enabled;
// internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
// left/right sums
int t_main_out [2];
int t_echo_out [2];
int t_echo_in [2];
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
sample_t extra [extra_size];
};
state_t m;
void init_counter();
void run_counters();
unsigned read_counter( int rate );
int interpolate( voice_t const* v );
void run_envelope( voice_t* const v );
void decode_brr( voice_t* v );
void misc_27();
void misc_28();
void misc_29();
void misc_30();
void voice_output( voice_t const* v, int ch );
void voice_V1( voice_t* const );
void voice_V2( voice_t* const );
void voice_V3( voice_t* const );
void voice_V3a( voice_t* const );
void voice_V3b( voice_t* const );
void voice_V3c( voice_t* const );
void voice_V4( voice_t* const );
void voice_V5( voice_t* const );
void voice_V6( voice_t* const );
void voice_V7( voice_t* const );
void voice_V8( voice_t* const );
void voice_V9( voice_t* const );
void voice_V7_V4_V1( voice_t* const );
void voice_V8_V5_V2( voice_t* const );
void voice_V9_V6_V3( voice_t* const );
void echo_read( int ch );
int echo_output( int ch );
void echo_write( int ch );
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
void soft_reset_common();
};
#include <assert.h>
inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; }
inline int SPC_DSP::read( int addr ) const
{
assert( (unsigned) addr < register_count );
return m.regs [addr];
}
inline void SPC_DSP::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
switch ( addr & 0x0F )
{
case v_envx:
m.envx_buf = (uint8_t) data;
break;
case v_outx:
m.outx_buf = (uint8_t) data;
break;
case 0x0C:
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
{
m.endx_buf = 0;
m.regs [r_endx] = 0;
}
break;
}
}
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
inline bool SPC_DSP::check_kon()
{
bool old = m.kon_check;
m.kon_check = 0;
return old;
}
#if !SPC_NO_COPY_STATE_FUNCS
class SPC_State_Copier {
SPC_DSP::copy_func_t func;
unsigned char** buf;
public:
SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }
void copy( void* state, size_t size );
int copy_int( int state, int size );
void skip( int count );
void extra();
};
#define SPC_COPY( type, state )\
{\
state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
assert( (BOOST::type) state == state );\
}
#endif
#endif

186
snes/fast/dsp/blargg_common.h Executable file
View File

@ -0,0 +1,186 @@
// Sets up common environment for Shay Green's libraries.
// To change configuration options, modify blargg_config.h, not this file.
// snes_spc 0.9.0
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#undef BLARGG_COMMON_H
// allow blargg_config.h to #include blargg_common.h
#include "blargg_config.h"
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
// BLARGG_RESTRICT: equivalent to restrict, where supported
#if defined (__GNUC__) || _MSC_VER >= 1100
#define BLARGG_RESTRICT __restrict
#else
#define BLARGG_RESTRICT
#endif
// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
#ifndef STATIC_CAST
#define STATIC_CAST(T,expr) ((T) (expr))
#endif
// blargg_err_t (0 on success, otherwise error string)
#ifndef blargg_err_t
typedef const char* blargg_err_t;
#endif
// blargg_vector - very lightweight vector of POD types (no constructor/destructor)
template<class T>
class blargg_vector {
T* begin_;
size_t size_;
public:
blargg_vector() : begin_( 0 ), size_( 0 ) { }
~blargg_vector() { free( begin_ ); }
size_t size() const { return size_; }
T* begin() const { return begin_; }
T* end() const { return begin_ + size_; }
blargg_err_t resize( size_t n )
{
// TODO: blargg_common.cpp to hold this as an outline function, ugh
void* p = realloc( begin_, n * sizeof (T) );
if ( p )
begin_ = (T*) p;
else if ( n > size_ ) // realloc failure only a problem if expanding
return "Out of memory";
size_ = n;
return 0;
}
void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); }
T& operator [] ( size_t n ) const
{
assert( n <= size_ ); // <= to allow past-the-end value
return begin_ [n];
}
};
#ifndef BLARGG_DISABLE_NOTHROW
// throw spec mandatory in ISO C++ if operator new can return NULL
#if __cplusplus >= 199711 || defined (__GNUC__)
#define BLARGG_THROWS( spec ) throw spec
#else
#define BLARGG_THROWS( spec )
#endif
#define BLARGG_DISABLE_NOTHROW \
void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\
void operator delete ( void* p ) { free( p ); }
#define BLARGG_NEW new
#else
#include <new>
#define BLARGG_NEW new (std::nothrow)
#endif
// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant)
#define BLARGG_4CHAR( a, b, c, d ) \
((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF))
// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
#ifndef BOOST_STATIC_ASSERT
#ifdef _MSC_VER
// MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
#else
// Some other compilers fail when declaring same function multiple times in class,
// so differentiate them by line
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
#endif
#endif
// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
// compiler is assumed to support bool. If undefined, availability is determined.
#ifndef BLARGG_COMPILER_HAS_BOOL
#if defined (__MWERKS__)
#if !__option(bool)
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (_MSC_VER)
#if _MSC_VER < 1100
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (__GNUC__)
// supports bool
#elif __cplusplus < 199711
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#endif
#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
// If you get errors here, modify your blargg_config.h file
typedef int bool;
const bool true = 1;
const bool false = 0;
#endif
// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
typedef long blargg_long;
#else
typedef int blargg_long;
#endif
#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF
typedef unsigned long blargg_ulong;
#else
typedef unsigned blargg_ulong;
#endif
// BOOST::int8_t etc.
// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
#if defined (HAVE_STDINT_H)
#include <stdint.h>
#define BOOST
// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
#elif defined (HAVE_INTTYPES_H)
#include <inttypes.h>
#define BOOST
#else
struct BOOST
{
#if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
typedef signed char int8_t;
typedef unsigned char uint8_t;
#else
// No suitable 8-bit type available
typedef struct see_blargg_common_h int8_t;
typedef struct see_blargg_common_h uint8_t;
#endif
#if USHRT_MAX == 0xFFFF
typedef short int16_t;
typedef unsigned short uint16_t;
#else
// No suitable 16-bit type available
typedef struct see_blargg_common_h int16_t;
typedef struct see_blargg_common_h uint16_t;
#endif
#if ULONG_MAX == 0xFFFFFFFF
typedef long int32_t;
typedef unsigned long uint32_t;
#elif UINT_MAX == 0xFFFFFFFF
typedef int int32_t;
typedef unsigned int uint32_t;
#else
// No suitable 32-bit type available
typedef struct see_blargg_common_h int32_t;
typedef struct see_blargg_common_h uint32_t;
#endif
};
#endif
#endif
#endif

24
snes/fast/dsp/blargg_config.h Executable file
View File

@ -0,0 +1,24 @@
// snes_spc 0.9.0 user configuration file. Don't replace when updating library.
// snes_spc 0.9.0
#ifndef BLARGG_CONFIG_H
#define BLARGG_CONFIG_H
// Uncomment to disable debugging checks
#define NDEBUG 1
// Uncomment to enable platform-specific (and possibly non-portable) optimizations
//#define BLARGG_NONPORTABLE 1
// Uncomment if automatic byte-order determination doesn't work
//#define BLARGG_BIG_ENDIAN 1
// Uncomment if you get errors in the bool section of blargg_common.h
//#define BLARGG_COMPILER_HAS_BOOL 1
// Use standard config.h if present
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif

185
snes/fast/dsp/blargg_endian.h Executable file
View File

@ -0,0 +1,185 @@
// CPU Byte Order Utilities
// snes_spc 0.9.0
#ifndef BLARGG_ENDIAN
#define BLARGG_ENDIAN
#include "blargg_common.h"
// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
#define BLARGG_CPU_X86 1
#define BLARGG_CPU_CISC 1
#endif
#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc)
#define BLARGG_CPU_POWERPC 1
#define BLARGG_CPU_RISC 1
#endif
// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
// one may be #defined to 1. Only needed if something actually depends on byte order.
#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
#ifdef __GLIBC__
// GCC handles this for us
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define BLARGG_LITTLE_ENDIAN 1
#elif __BYTE_ORDER == __BIG_ENDIAN
#define BLARGG_BIG_ENDIAN 1
#endif
#else
#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \
(defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234)
#define BLARGG_LITTLE_ENDIAN 1
#endif
#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \
defined (__sparc__) || BLARGG_CPU_POWERPC || \
(defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
#define BLARGG_BIG_ENDIAN 1
#elif !defined (__mips__)
// No endian specified; assume little-endian, since it's most common
#define BLARGG_LITTLE_ENDIAN 1
#endif
#endif
#endif
#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN
#undef BLARGG_LITTLE_ENDIAN
#undef BLARGG_BIG_ENDIAN
#endif
inline void blargg_verify_byte_order()
{
#ifndef NDEBUG
#if BLARGG_BIG_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i == 0 );
#elif BLARGG_LITTLE_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i != 0 );
#endif
#endif
}
inline unsigned get_le16( void const* p )
{
return (unsigned) ((unsigned char const*) p) [1] << 8 |
(unsigned) ((unsigned char const*) p) [0];
}
inline unsigned get_be16( void const* p )
{
return (unsigned) ((unsigned char const*) p) [0] << 8 |
(unsigned) ((unsigned char const*) p) [1];
}
inline blargg_ulong get_le32( void const* p )
{
return (blargg_ulong) ((unsigned char const*) p) [3] << 24 |
(blargg_ulong) ((unsigned char const*) p) [2] << 16 |
(blargg_ulong) ((unsigned char const*) p) [1] << 8 |
(blargg_ulong) ((unsigned char const*) p) [0];
}
inline blargg_ulong get_be32( void const* p )
{
return (blargg_ulong) ((unsigned char const*) p) [0] << 24 |
(blargg_ulong) ((unsigned char const*) p) [1] << 16 |
(blargg_ulong) ((unsigned char const*) p) [2] << 8 |
(blargg_ulong) ((unsigned char const*) p) [3];
}
inline void set_le16( void* p, unsigned n )
{
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [0] = (unsigned char) n;
}
inline void set_be16( void* p, unsigned n )
{
((unsigned char*) p) [0] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) n;
}
inline void set_le32( void* p, blargg_ulong n )
{
((unsigned char*) p) [0] = (unsigned char) n;
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [2] = (unsigned char) (n >> 16);
((unsigned char*) p) [3] = (unsigned char) (n >> 24);
}
inline void set_be32( void* p, blargg_ulong n )
{
((unsigned char*) p) [3] = (unsigned char) n;
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
((unsigned char*) p) [0] = (unsigned char) (n >> 24);
}
#if BLARGG_NONPORTABLE
// Optimized implementation if byte order is known
#if BLARGG_LITTLE_ENDIAN
#define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr))
#define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr))
#define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
#define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
#elif BLARGG_BIG_ENDIAN
#define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr))
#define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr))
#define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
#define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
#if BLARGG_CPU_POWERPC
// PowerPC has special byte-reversed instructions
#if defined (__MWERKS__)
#define GET_LE16( addr ) (__lhbrx( addr, 0 ))
#define GET_LE32( addr ) (__lwbrx( addr, 0 ))
#define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 ))
#define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 ))
#elif defined (__GNUC__)
#define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;})
#define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;})
#define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );})
#define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );})
#endif
#endif
#endif
#endif
#ifndef GET_LE16
#define GET_LE16( addr ) get_le16( addr )
#define SET_LE16( addr, data ) set_le16( addr, data )
#endif
#ifndef GET_LE32
#define GET_LE32( addr ) get_le32( addr )
#define SET_LE32( addr, data ) set_le32( addr, data )
#endif
#ifndef GET_BE16
#define GET_BE16( addr ) get_be16( addr )
#define SET_BE16( addr, data ) set_be16( addr, data )
#endif
#ifndef GET_BE32
#define GET_BE32( addr ) get_be32( addr )
#define SET_BE32( addr, data ) set_be32( addr, data )
#endif
// auto-selecting versions
inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); }
inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); }
inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); }
inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); }
inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); }
inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); }
inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); }
inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); }
#endif

100
snes/fast/dsp/blargg_source.h Executable file
View File

@ -0,0 +1,100 @@
/* Included at the beginning of library source files, after all other #include lines.
Sets up helpful macros and services used in my source code. They don't need
module an annoying module prefix on their names since they are defined after
all other #include lines. */
// snes_spc 0.9.0
#ifndef BLARGG_SOURCE_H
#define BLARGG_SOURCE_H
// If debugging is enabled, abort program if expr is false. Meant for checking
// internal state and consistency. A failed assertion indicates a bug in the module.
// void assert( bool expr );
#include <assert.h>
// If debugging is enabled and expr is false, abort program. Meant for checking
// caller-supplied parameters and operations that are outside the control of the
// module. A failed requirement indicates a bug outside the module.
// void require( bool expr );
#undef require
#define require( expr ) assert( expr )
// Like printf() except output goes to debug log file. Might be defined to do
// nothing (not even evaluate its arguments).
// void dprintf( const char* format, ... );
static inline void blargg_dprintf_( const char*, ... ) { }
#undef dprintf
#define dprintf (1) ? (void) 0 : blargg_dprintf_
// If enabled, evaluate expr and if false, make debug log entry with source file
// and line. Meant for finding situations that should be examined further, but that
// don't indicate a problem. In all cases, execution continues normally.
#undef check
#define check( expr ) ((void) 0)
// If expr yields error string, return it from current function, otherwise continue.
#undef RETURN_ERR
#define RETURN_ERR( expr ) do { \
blargg_err_t blargg_return_err_ = (expr); \
if ( blargg_return_err_ ) return blargg_return_err_; \
} while ( 0 )
// If ptr is 0, return out of memory error string.
#undef CHECK_ALLOC
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
// Avoid any macros which evaluate their arguments multiple times
#undef min
#undef max
#define DEF_MIN_MAX( type ) \
static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\
static inline type max( type x, type y ) { if ( y < x ) return x; return y; }
DEF_MIN_MAX( int )
DEF_MIN_MAX( unsigned )
DEF_MIN_MAX( long )
DEF_MIN_MAX( unsigned long )
DEF_MIN_MAX( float )
DEF_MIN_MAX( double )
#undef DEF_MIN_MAX
/*
// using const references generates crappy code, and I am currenly only using these
// for built-in types, so they take arguments by value
// TODO: remove
inline int min( int x, int y )
template<class T>
inline T min( T x, T y )
{
if ( x < y )
return x;
return y;
}
template<class T>
inline T max( T x, T y )
{
if ( x < y )
return y;
return x;
}
*/
// TODO: good idea? bad idea?
#undef byte
#define byte byte_
typedef unsigned char byte;
// deprecated
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
#define BLARGG_RETURN_ERR RETURN_ERR
// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
#ifdef BLARGG_SOURCE_BEGIN
#include BLARGG_SOURCE_BEGIN
#endif
#endif

View File

@ -1,62 +0,0 @@
#ifdef DSP_CPP
void DSP::brr_decode(voice_t &v) {
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
int nybbles = (state.t_brr_byte << 8) + memory::apuram[(uint16)(v.brr_addr + v.brr_offset + 1)];
const int filter = (state.t_brr_header >> 2) & 3;
const int scale = (state.t_brr_header >> 4);
//decode four samples
for(unsigned i = 0; i < 4; i++) {
//bits 12-15 = current nybble; sign extend, then shift right to 4-bit precision
//result: s = 4-bit sign-extended sample value
int s = (int16)nybbles >> 12;
nybbles <<= 4; //slide nybble so that on next loop iteration, bits 12-15 = current nybble
if(scale <= 12) {
s <<= scale;
s >>= 1;
} else {
s &= ~0x7ff;
}
//apply IIR filter (2 is the most commonly used)
const int p1 = v.buffer[v.buf_pos - 1];
const int p2 = v.buffer[v.buf_pos - 2] >> 1;
switch(filter) {
case 0: break; //no filter
case 1: {
//s += p1 * 0.46875
s += p1 >> 1;
s += (-p1) >> 5;
} break;
case 2: {
//s += p1 * 0.953125 - p2 * 0.46875
s += p1;
s -= p2;
s += p2 >> 4;
s += (p1 * -3) >> 6;
} break;
case 3: {
//s += p1 * 0.8984375 - p2 * 0.40625
s += p1;
s -= p2;
s += (p1 * -13) >> 7;
s += (p2 * 3) >> 4;
} break;
}
//adjust and write sample
s = sclamp<16>(s);
s = (int16)(s << 1);
v.buffer.write(v.buf_pos++, s);
if(v.buf_pos >= brr_buf_size) v.buf_pos = 0;
}
}
#endif

View File

@ -1,52 +0,0 @@
#ifdef DSP_CPP
//counter_rate = number of samples per counter event
//all rates are evenly divisible by counter_range (0x7800, 30720, or 2048 * 5 * 3)
//note that rate[0] is a special case, which never triggers
const uint16 DSP::counter_rate[32] = {
0, 2048, 1536,
1280, 1024, 768,
640, 512, 384,
320, 256, 192,
160, 128, 96,
80, 64, 48,
40, 32, 24,
20, 16, 12,
10, 8, 6,
5, 4, 3,
2,
1,
};
//counter_offset = counter offset from zero
//counters do not appear to be aligned at zero for all rates
const uint16 DSP::counter_offset[32] = {
0, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
536, 0, 1040,
0,
0,
};
inline void DSP::counter_tick() {
state.counter--;
if(state.counter < 0) state.counter = counter_range - 1;
}
//return true if counter event should trigger
inline bool DSP::counter_poll(unsigned rate) {
if(rate == 0) return false;
return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0;
}
#endif

View File

@ -1,53 +0,0 @@
#ifdef DSP_CPP
bool DSPDebugger::property(unsigned id, string &name, string &value) {
unsigned n = 0;
#define item(name_, value_) \
if(id == n++) { \
name = name_; \
value = value_; \
return true; \
}
item("Main Volume - Left", (unsigned)state.regs[0x0c]);
item("Main Volume - Right", (unsigned)state.regs[0x1c]);
item("Echo Volume - Left", (unsigned)state.regs[0x2c]);
item("Echo Volume - Right", (unsigned)state.regs[0x3c]);
item("Key On", string("0x", strhex<2>(state.regs[0x4c])));
item("Key Off", string("0x", strhex<2>(state.regs[0x5c])));
item("Flag - Reset", (bool)(state.regs[0x6c] & 0x80));
item("Flag - Mute", (bool)(state.regs[0x6c] & 0x40));
item("Flag - Echo Disable", (bool)(state.regs[0x6c] & 0x20));
item("Flag - Noise Clock", (unsigned)state.regs[0x6c] & 0x1f);
item("Source End Block", (unsigned)state.regs[0x7c]);
item("Echo Feedback", (unsigned)state.regs[0x0d]);
item("Pitch Modulation Enable", string("0x", strhex<2>(state.regs[0x2d])));
item("Noise Enable", string("0x", strhex<2>(state.regs[0x3d])));
item("Echo Enable", string("0x", strhex<2>(state.regs[0x4d])));
item("Source Directory", (unsigned)state.regs[0x5d]);
item("Echo Start Address", (unsigned)state.regs[0x6d]);
item("Echo Directory", (unsigned)state.regs[0x7d]);
for(unsigned i = 0; i < 8; i++) {
item(string("Coefficient ", i), string("0x", strhex<2>(state.regs[(i << 4) + 0x0f])));
}
for(unsigned i = 0; i < 8; i++) {
item(string("Voice ", i), "");
item("Volume - Left", (unsigned)state.regs[(i << 4) + 0x00]);
item("Volume - Right", (unsigned)state.regs[(i << 4) + 0x01]);
item("Pitch Height", string("0x", strhex<4>(state.regs[(i << 4) + 0x02] + (state.regs[(i << 4) + 0x03] << 8))));
item("Source Number", (unsigned)state.regs[(i << 4) + 0x04]);
item("ADSR1", (unsigned)state.regs[(i << 4) + 0x05]);
item("ADSR2", (unsigned)state.regs[(i << 4) + 0x06]);
item("GAIN", (unsigned)state.regs[(i << 4) + 0x07]);
item("ENVX", (unsigned)state.regs[(i << 4) + 0x08]);
item("OUTX", (unsigned)state.regs[(i << 4) + 0x09]);
}
#undef item
return false;
}
#endif

View File

@ -1,4 +0,0 @@
class DSPDebugger : public DSP, public ChipDebugger {
public:
bool property(unsigned id, string &name, string &value);
};

View File

@ -3,27 +3,10 @@
#define DSP_CPP
namespace SNES {
#if defined(DEBUGGER)
#include "debugger/debugger.cpp"
DSPDebugger dsp;
#else
DSP dsp;
#endif
DSP dsp;
#include "serialization.cpp"
#define REG(n) state.regs[r_##n]
#define VREG(n) state.regs[v.vidx + v_##n]
#include "gaussian.cpp"
#include "counter.cpp"
#include "envelope.cpp"
#include "brr.cpp"
#include "misc.cpp"
#include "voice.cpp"
#include "echo.cpp"
/* timing */
#include "SPC_DSP.cpp"
void DSP::step(unsigned clocks) {
clock += clocks;
@ -37,301 +20,34 @@ void DSP::synchronize_smp() {
}
}
void DSP::Enter() { dsp.enter(); }
void DSP::enter() {
switch(phase) {
case 0:
voice_5(voice[0]);
voice_2(voice[1]);
return tick();
spc_dsp.run(1);
step(24);
case 1:
voice_6(voice[0]);
voice_3(voice[1]);
return tick();
case 2:
voice_7(voice[0]);
voice_4(voice[1]);
voice_1(voice[3]);
return tick();
case 3:
voice_8(voice[0]);
voice_5(voice[1]);
voice_2(voice[2]);
return tick();
case 4:
voice_9(voice[0]);
voice_6(voice[1]);
voice_3(voice[2]);
return tick();
case 5:
voice_7(voice[1]);
voice_4(voice[2]);
voice_1(voice[4]);
return tick();
case 6:
voice_8(voice[1]);
voice_5(voice[2]);
voice_2(voice[3]);
return tick();
case 7:
voice_9(voice[1]);
voice_6(voice[2]);
voice_3(voice[3]);
return tick();
case 8:
voice_7(voice[2]);
voice_4(voice[3]);
voice_1(voice[5]);
return tick();
case 9:
voice_8(voice[2]);
voice_5(voice[3]);
voice_2(voice[4]);
return tick();
case 10:
voice_9(voice[2]);
voice_6(voice[3]);
voice_3(voice[4]);
return tick();
case 11:
voice_7(voice[3]);
voice_4(voice[4]);
voice_1(voice[6]);
return tick();
case 12:
voice_8(voice[3]);
voice_5(voice[4]);
voice_2(voice[5]);
return tick();
case 13:
voice_9(voice[3]);
voice_6(voice[4]);
voice_3(voice[5]);
return tick();
case 14:
voice_7(voice[4]);
voice_4(voice[5]);
voice_1(voice[7]);
return tick();
case 15:
voice_8(voice[4]);
voice_5(voice[5]);
voice_2(voice[6]);
return tick();
case 16:
voice_9(voice[4]);
voice_6(voice[5]);
voice_3(voice[6]);
return tick();
case 17:
voice_1(voice[0]);
voice_7(voice[5]);
voice_4(voice[6]);
return tick();
case 18:
voice_8(voice[5]);
voice_5(voice[6]);
voice_2(voice[7]);
return tick();
case 19:
voice_9(voice[5]);
voice_6(voice[6]);
voice_3(voice[7]);
return tick();
case 20:
voice_1(voice[1]);
voice_7(voice[6]);
voice_4(voice[7]);
return tick();
case 21:
voice_8(voice[6]);
voice_5(voice[7]);
voice_2(voice[0]);
return tick();
case 22:
voice_3a(voice[0]);
voice_9(voice[6]);
voice_6(voice[7]);
echo_22();
return tick();
case 23:
voice_7(voice[7]);
echo_23();
return tick();
case 24:
voice_8(voice[7]);
echo_24();
return tick();
case 25:
voice_3b(voice[0]);
voice_9(voice[7]);
echo_25();
return tick();
case 26:
echo_26();
return tick();
case 27:
misc_27();
echo_27();
return tick();
case 28:
misc_28();
echo_28();
return tick();
case 29:
misc_29();
echo_29();
return tick();
case 30:
misc_30();
voice_3c(voice[0]);
echo_30();
return tick();
case 31:
voice_4(voice[0]);
voice_1(voice[2]);
return tick();
signed count = spc_dsp.sample_count();
if(count > 0) {
for(unsigned n = 0; n < count; n += 2) audio.sample(samplebuffer[n + 0], samplebuffer[n + 1]);
spc_dsp.set_output(samplebuffer, 8192);
}
}
void DSP::tick() {
step(3 * 8);
synchronize_smp();
phase = (phase + 1) & 31;
}
/* register interface for S-SMP $00f2,$00f3 */
uint8 DSP::read(uint8 addr) {
return state.regs[addr];
return spc_dsp.read(addr);
}
void DSP::write(uint8 addr, uint8 data) {
state.regs[addr] = data;
if((addr & 0x0f) == v_envx) {
state.envx_buf = data;
} else if((addr & 0x0f) == v_outx) {
state.outx_buf = data;
} else if(addr == r_kon) {
state.new_kon = data;
} else if(addr == r_endx) {
//always cleared, regardless of data written
state.endx_buf = 0;
state.regs[r_endx] = 0;
}
spc_dsp.write(addr, data);
}
/* initialization */
void DSP::power() {
memset(&state.regs, 0, sizeof state.regs);
state.echo_hist_pos = 0;
state.every_other_sample = false;
state.kon = 0;
state.noise = 0;
state.counter = 0;
state.echo_offset = 0;
state.echo_length = 0;
state.new_kon = 0;
state.endx_buf = 0;
state.envx_buf = 0;
state.outx_buf = 0;
state.t_pmon = 0;
state.t_non = 0;
state.t_eon = 0;
state.t_dir = 0;
state.t_koff = 0;
state.t_brr_next_addr = 0;
state.t_adsr0 = 0;
state.t_brr_header = 0;
state.t_brr_byte = 0;
state.t_srcn = 0;
state.t_esa = 0;
state.t_echo_disabled = 0;
state.t_dir_addr = 0;
state.t_pitch = 0;
state.t_output = 0;
state.t_looped = 0;
state.t_echo_ptr = 0;
state.t_main_out[0] = state.t_main_out[1] = 0;
state.t_echo_out[0] = state.t_echo_out[1] = 0;
state.t_echo_in[0] = state.t_echo_in[1] = 0;
for(unsigned i = 0; i < 8; i++) {
voice[i].buf_pos = 0;
voice[i].interp_pos = 0;
voice[i].brr_addr = 0;
voice[i].brr_offset = 1;
voice[i].vbit = 1 << i;
voice[i].vidx = i * 0x10;
voice[i].kon_delay = 0;
voice[i].env_mode = env_release;
voice[i].env = 0;
voice[i].t_envx_out = 0;
voice[i].hidden_env = 0;
}
reset();
spc_dsp.init(memory::apuram.data());
spc_dsp.reset();
spc_dsp.set_output(samplebuffer, 8192);
}
void DSP::reset() {
create(Enter, system.apu_frequency());
REG(flg) = 0xe0;
state.noise = 0x4000;
state.echo_hist_pos = 0;
state.every_other_sample = 1;
state.echo_offset = 0;
state.counter = 0;
}
DSP::DSP() {
static_assert(sizeof(int) >= 32 / 8, "int >= 32-bits");
static_assert((int8)0x80 == -0x80, "8-bit sign extension");
static_assert((int16)0x8000 == -0x8000, "16-bit sign extension");
static_assert((uint16)0xffff0000 == 0, "16-bit unsigned clip");
static_assert((-1 >> 1) == -1, "arithmetic shift right");
//-0x8000 <= n <= +0x7fff
assert(sclamp<16>(+0x8000) == +0x7fff);
assert(sclamp<16>(-0x8001) == -0x8000);
}
DSP::~DSP() {
spc_dsp.soft_reset();
spc_dsp.set_output(samplebuffer, 8192);
}
}

View File

@ -1,4 +1,6 @@
class DSP : public Processor {
#include "SPC_DSP.h"
class DSP : public Processor, public ChipDebugger {
public:
enum : bool { Threaded = false };
alwaysinline void step(unsigned clocks);
@ -12,171 +14,11 @@ public:
void reset();
void serialize(serializer&);
DSP();
~DSP();
bool property(unsigned id, string &name, string &value) { return false; }
private:
unsigned phase;
//global registers
enum global_reg_t {
r_mvoll = 0x0c, r_mvolr = 0x1c,
r_evoll = 0x2c, r_evolr = 0x3c,
r_kon = 0x4c, r_koff = 0x5c,
r_flg = 0x6c, r_endx = 0x7c,
r_efb = 0x0d, r_pmon = 0x2d,
r_non = 0x3d, r_eon = 0x4d,
r_dir = 0x5d, r_esa = 0x6d,
r_edl = 0x7d, r_fir = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f
};
//voice registers
enum voice_reg_t {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09,
};
//internal envelope modes
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
//internal constants
enum { echo_hist_size = 8 };
enum { brr_buf_size = 12 };
enum { brr_block_size = 9 };
//global state
struct state_t {
uint8 regs[128];
modulo_array<int, echo_hist_size> echo_hist[2]; //echo history keeps most recent 8 samples
int echo_hist_pos;
bool every_other_sample; //toggles every sample
int kon; //KON value when last checked
int noise;
int counter;
int echo_offset; //offset from ESA in echo buffer
int echo_length; //number of bytes that echo_offset will stop at
//hidden registers also written to when main register is written to
int new_kon;
int endx_buf;
int envx_buf;
int outx_buf;
//temporary state between clocks
//read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
//read a few clocks ahead before used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_disabled;
//internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
//left/right sums
int t_main_out[2];
int t_echo_out[2];
int t_echo_in [2];
} state;
//voice state
struct voice_t {
modulo_array<int, brr_buf_size> buffer; //decoded samples
int buf_pos; //place in buffer where next samples will be decoded
int interp_pos; //relative fractional position in sample (0x1000 = 1.0)
int brr_addr; //address of current BRR block
int brr_offset; //current decoding offset in BRR block
int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
int kon_delay; //KON delay/current setup phase
int env_mode;
int env; //current envelope level
int t_envx_out;
int hidden_env; //used by GAIN mode 7, very obscure quirk
} voice[8];
//gaussian
static const int16 gaussian_table[512];
int gaussian_interpolate(const voice_t &v);
//counter
enum { counter_range = 2048 * 5 * 3 }; //30720 (0x7800)
static const uint16 counter_rate[32];
static const uint16 counter_offset[32];
void counter_tick();
bool counter_poll(unsigned rate);
//envelope
void envelope_run(voice_t &v);
//brr
void brr_decode(voice_t &v);
//misc
void misc_27();
void misc_28();
void misc_29();
void misc_30();
//voice
void voice_output(voice_t &v, bool channel);
void voice_1 (voice_t &v);
void voice_2 (voice_t &v);
void voice_3 (voice_t &v);
void voice_3a(voice_t &v);
void voice_3b(voice_t &v);
void voice_3c(voice_t &v);
void voice_4 (voice_t &v);
void voice_5 (voice_t &v);
void voice_6 (voice_t &v);
void voice_7 (voice_t &v);
void voice_8 (voice_t &v);
void voice_9 (voice_t &v);
//echo
int calc_fir(int i, bool channel);
int echo_output(bool channel);
void echo_read(bool channel);
void echo_write(bool channel);
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
//dsp
static void Enter();
alwaysinline void tick();
friend class DSPDebugger;
SPC_DSP spc_dsp;
int16 samplebuffer[8192];
};
#if defined(DEBUGGER)
#include "debugger/debugger.hpp"
extern DSPDebugger dsp;
#else
extern DSP dsp;
#endif
extern DSP dsp;

View File

@ -1,135 +0,0 @@
#ifdef DSP_CPP
int DSP::calc_fir(int i, bool channel) {
int s = state.echo_hist[channel][state.echo_hist_pos + i + 1];
return (s * (int8)REG(fir + i * 0x10)) >> 6;
}
int DSP::echo_output(bool channel) {
int output = (int16)((state.t_main_out[channel] * (int8)REG(mvoll + channel * 0x10)) >> 7)
+ (int16)((state.t_echo_in [channel] * (int8)REG(evoll + channel * 0x10)) >> 7);
return sclamp<16>(output);
}
void DSP::echo_read(bool channel) {
unsigned addr = state.t_echo_ptr + channel * 2;
uint8 lo = memory::apuram[(uint16)(addr + 0)];
uint8 hi = memory::apuram[(uint16)(addr + 1)];
int s = (int16)((hi << 8) + lo);
state.echo_hist[channel].write(state.echo_hist_pos, s >> 1);
}
void DSP::echo_write(bool channel) {
if(!(state.t_echo_disabled & 0x20)) {
unsigned addr = state.t_echo_ptr + channel * 2;
int s = state.t_echo_out[channel];
memory::apuram[(uint16)(addr + 0)] = s;
memory::apuram[(uint16)(addr + 1)] = s >> 8;
}
state.t_echo_out[channel] = 0;
}
void DSP::echo_22() {
//history
state.echo_hist_pos++;
if(state.echo_hist_pos >= echo_hist_size) state.echo_hist_pos = 0;
state.t_echo_ptr = (uint16)((state.t_esa << 8) + state.echo_offset);
echo_read(0);
//FIR
int l = calc_fir(0, 0);
int r = calc_fir(0, 1);
state.t_echo_in[0] = l;
state.t_echo_in[1] = r;
}
void DSP::echo_23() {
int l = calc_fir(1, 0) + calc_fir(2, 0);
int r = calc_fir(1, 1) + calc_fir(2, 1);
state.t_echo_in[0] += l;
state.t_echo_in[1] += r;
echo_read(1);
}
void DSP::echo_24() {
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);
state.t_echo_in[0] += l;
state.t_echo_in[1] += r;
}
void DSP::echo_25() {
int l = state.t_echo_in[0] + calc_fir(6, 0);
int r = state.t_echo_in[1] + calc_fir(6, 1);
l = (int16)l;
r = (int16)r;
l += (int16)calc_fir(7, 0);
r += (int16)calc_fir(7, 1);
state.t_echo_in[0] = sclamp<16>(l) & ~1;
state.t_echo_in[1] = sclamp<16>(r) & ~1;
}
void DSP::echo_26() {
//left output volumes
//(save sample for next clock so we can output both together)
state.t_main_out[0] = echo_output(0);
//echo feedback
int l = state.t_echo_out[0] + (int16)((state.t_echo_in[0] * (int8)REG(efb)) >> 7);
int r = state.t_echo_out[1] + (int16)((state.t_echo_in[1] * (int8)REG(efb)) >> 7);
state.t_echo_out[0] = sclamp<16>(l) & ~1;
state.t_echo_out[1] = sclamp<16>(r) & ~1;
}
void DSP::echo_27() {
//output
int outl = state.t_main_out[0];
int outr = echo_output(1);
state.t_main_out[0] = 0;
state.t_main_out[1] = 0;
//TODO: global muting isn't this simple
//(turns DAC on and off or something, causing small ~37-sample pulse when first muted)
if(REG(flg) & 0x40) {
outl = 0;
outr = 0;
}
//output sample to DAC
audio.sample(outl, outr);
}
void DSP::echo_28() {
state.t_echo_disabled = REG(flg);
}
void DSP::echo_29() {
state.t_esa = REG(esa);
if(!state.echo_offset) state.echo_length = (REG(edl) & 0x0f) << 11;
state.echo_offset += 4;
if(state.echo_offset >= state.echo_length) state.echo_offset = 0;
//write left echo
echo_write(0);
state.t_echo_disabled = REG(flg);
}
void DSP::echo_30() {
//write right echo
echo_write(1);
}
#endif

View File

@ -1,62 +0,0 @@
#ifdef DSP_CPP
void DSP::envelope_run(voice_t &v) {
int env = v.env;
if(v.env_mode == env_release) { //60%
env -= 0x8;
if(env < 0) env = 0;
v.env = env;
return;
}
int rate;
int env_data = VREG(adsr1);
if(state.t_adsr0 & 0x80) { //99% ADSR
if(v.env_mode >= env_decay) { //99%
env--;
env -= env >> 8;
rate = env_data & 0x1f;
if(v.env_mode == env_decay) { //1%
rate = ((state.t_adsr0 >> 3) & 0x0e) + 0x10;
}
} else { //env_attack
rate = ((state.t_adsr0 & 0x0f) << 1) + 1;
env += rate < 31 ? 0x20 : 0x400;
}
} else { //GAIN
env_data = VREG(gain);
int mode = env_data >> 5;
if(mode < 4) { //direct
env = env_data << 4;
rate = 31;
} else {
rate = env_data & 0x1f;
if(mode == 4) { //4: linear decrease
env -= 0x20;
} else if(mode < 6) { //5: exponential decrease
env--;
env -= env >> 8;
} else { //6, 7: linear increase
env += 0x20;
if(mode > 6 && (unsigned)v.hidden_env >= 0x600) {
env += 0x8 - 0x20; //7: two-slope linear increase
}
}
}
}
//sustain level
if((env >> 8) == (env_data >> 5) && v.env_mode == env_decay) v.env_mode = env_sustain;
v.hidden_env = env;
//unsigned cast because linear decrease underflowing also triggers this
if((unsigned)env > 0x7ff) {
env = (env < 0 ? 0 : 0x7ff);
if(v.env_mode == env_attack) v.env_mode = env_decay;
}
if(counter_poll(rate) == true) v.env = env;
}
#endif

View File

@ -1,54 +0,0 @@
#ifdef DSP_CPP
const int16 DSP::gaussian_table[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,
28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,
78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,
104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,
134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,
171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,
212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,
260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,
314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,
374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,
439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,
508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,
582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,
659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,
737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,
816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,
894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,
969, 974, 978, 983, 988, 992, 997, 1001, 1005, 1010, 1014, 1019, 1023, 1027, 1032, 1036,
1040, 1045, 1049, 1053, 1057, 1061, 1066, 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102,
1106, 1109, 1113, 1117, 1121, 1125, 1128, 1132, 1136, 1139, 1143, 1146, 1150, 1153, 1157, 1160,
1164, 1167, 1170, 1174, 1177, 1180, 1183, 1186, 1190, 1193, 1196, 1199, 1202, 1205, 1207, 1210,
1213, 1216, 1219, 1221, 1224, 1227, 1229, 1232, 1234, 1237, 1239, 1241, 1244, 1246, 1248, 1251,
1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280,
1282, 1283, 1284, 1286, 1287, 1288, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1297, 1298,
1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305,
};
int DSP::gaussian_interpolate(const voice_t &v) {
//make pointers into gaussian table based on fractional position between samples
int offset = (v.interp_pos >> 4) & 0xff;
const int16 *fwd = gaussian_table + 255 - offset;
const int16 *rev = gaussian_table + offset; //mirror left half of gaussian table
offset = v.buf_pos + (v.interp_pos >> 12);
int output;
output = (fwd[ 0] * v.buffer[offset + 0]) >> 11;
output += (fwd[256] * v.buffer[offset + 1]) >> 11;
output += (rev[256] * v.buffer[offset + 2]) >> 11;
output = (int16)output;
output += (rev[ 0] * v.buffer[offset + 3]) >> 11;
return sclamp<16>(output) & ~1;
}
#endif

View File

@ -1,35 +0,0 @@
#ifdef DSP_CPP
void DSP::misc_27() {
state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON
}
void DSP::misc_28() {
state.t_non = REG(non);
state.t_eon = REG(eon);
state.t_dir = REG(dir);
}
void DSP::misc_29() {
state.every_other_sample ^= 1;
if(state.every_other_sample) {
state.new_kon &= ~state.kon; //clears KON 63 clocks after it was last read
}
}
void DSP::misc_30() {
if(state.every_other_sample) {
state.kon = state.new_kon;
state.t_koff = REG(koff);
}
counter_tick();
//noise
if(counter_poll(REG(flg) & 0x1f) == true) {
int feedback = (state.noise << 13) ^ (state.noise << 14);
state.noise = (feedback & 0x4000) ^ (state.noise >> 1);
}
}
#endif

View File

@ -1,66 +1,30 @@
#ifdef DSP_CPP
static void dsp_state_save(unsigned char **out, void *in, size_t size) {
memcpy(*out, in, size);
*out += size;
}
static void dsp_state_load(unsigned char **in, void *out, size_t size) {
memcpy(out, *in, size);
*in += size;
}
void DSP::serialize(serializer &s) {
Processor::serialize(s);
s.integer(phase);
s.array(samplebuffer);
s.array(state.regs, 128);
state.echo_hist[0].serialize(s);
state.echo_hist[1].serialize(s);
s.integer(state.echo_hist_pos);
s.integer(state.every_other_sample);
s.integer(state.kon);
s.integer(state.noise);
s.integer(state.counter);
s.integer(state.echo_offset);
s.integer(state.echo_length);
s.integer(state.new_kon);
s.integer(state.endx_buf);
s.integer(state.envx_buf);
s.integer(state.outx_buf);
s.integer(state.t_pmon);
s.integer(state.t_non);
s.integer(state.t_eon);
s.integer(state.t_dir);
s.integer(state.t_koff);
s.integer(state.t_brr_next_addr);
s.integer(state.t_adsr0);
s.integer(state.t_brr_header);
s.integer(state.t_brr_byte);
s.integer(state.t_srcn);
s.integer(state.t_esa);
s.integer(state.t_echo_disabled);
s.integer(state.t_dir_addr);
s.integer(state.t_pitch);
s.integer(state.t_output);
s.integer(state.t_looped);
s.integer(state.t_echo_ptr);
s.integer(state.t_main_out[0]);
s.integer(state.t_main_out[1]);
s.integer(state.t_echo_out[0]);
s.integer(state.t_echo_out[1]);
s.integer(state.t_echo_in [0]);
s.integer(state.t_echo_in [1]);
for(unsigned n = 0; n < 8; n++) {
voice[n].buffer.serialize(s);
s.integer(voice[n].buf_pos);
s.integer(voice[n].interp_pos);
s.integer(voice[n].brr_addr);
s.integer(voice[n].brr_offset);
s.integer(voice[n].vbit);
s.integer(voice[n].vidx);
s.integer(voice[n].kon_delay);
s.integer(voice[n].env_mode);
s.integer(voice[n].env);
s.integer(voice[n].t_envx_out);
s.integer(voice[n].hidden_env);
unsigned char state[SPC_DSP::state_size];
unsigned char *p = state;
memset(&state, 0, SPC_DSP::state_size);
if(s.mode() == serializer::Save) {
spc_dsp.copy_state(&p, dsp_state_save);
s.array(state);
} else if(s.mode() == serializer::Load) {
s.array(state);
spc_dsp.copy_state(&p, dsp_state_load);
} else {
s.array(state);
}
}

View File

@ -1,174 +0,0 @@
#ifdef DSP_CPP
inline void DSP::voice_output(voice_t &v, bool channel) {
//apply left/right volume
int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7;
//add to output total
state.t_main_out[channel] += amp;
state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]);
//optionally add to echo total
if(state.t_eon & v.vbit) {
state.t_echo_out[channel] += amp;
state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]);
}
}
void DSP::voice_1(voice_t &v) {
state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2);
state.t_srcn = VREG(srcn);
}
void DSP::voice_2(voice_t &v) {
//read sample pointer (ignored if not needed)
uint16 addr = state.t_dir_addr;
if(!v.kon_delay) addr += 2;
uint8 lo = memory::apuram[(uint16)(addr + 0)];
uint8 hi = memory::apuram[(uint16)(addr + 1)];
state.t_brr_next_addr = ((hi << 8) + lo);
state.t_adsr0 = VREG(adsr0);
//read pitch, spread over two clocks
state.t_pitch = VREG(pitchl);
}
void DSP::voice_3(voice_t &v) {
voice_3a(v);
voice_3b(v);
voice_3c(v);
}
void DSP::voice_3a(voice_t &v) {
state.t_pitch += (VREG(pitchh) & 0x3f) << 8;
}
void DSP::voice_3b(voice_t &v) {
state.t_brr_byte = memory::apuram[(uint16)(v.brr_addr + v.brr_offset)];
state.t_brr_header = memory::apuram[(uint16)(v.brr_addr)];
}
void DSP::voice_3c(voice_t &v) {
//pitch modulation using previous voice's output
if(state.t_pmon & v.vbit) {
state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10;
}
if(v.kon_delay) {
//get ready to start BRR decoding on next sample
if(v.kon_delay == 5) {
v.brr_addr = state.t_brr_next_addr;
v.brr_offset = 1;
v.buf_pos = 0;
state.t_brr_header = 0; //header is ignored on this sample
}
//envelope is never run during KON
v.env = 0;
v.hidden_env = 0;
//disable BRR decoding until last three samples
v.interp_pos = 0;
v.kon_delay--;
if(v.kon_delay & 3) v.interp_pos = 0x4000;
//pitch is never added during KON
state.t_pitch = 0;
}
//gaussian interpolation
int output = gaussian_interpolate(v);
//noise
if(state.t_non & v.vbit) {
output = (int16)(state.noise << 1);
}
//apply envelope
state.t_output = ((output * v.env) >> 11) & ~1;
v.t_envx_out = v.env >> 4;
//immediate silence due to end of sample or soft reset
if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) {
v.env_mode = env_release;
v.env = 0;
}
if(state.every_other_sample) {
//KOFF
if(state.t_koff & v.vbit) {
v.env_mode = env_release;
}
//KON
if(state.kon & v.vbit) {
v.kon_delay = 5;
v.env_mode = env_attack;
}
}
//run envelope for next sample
if(!v.kon_delay) envelope_run(v);
}
void DSP::voice_4(voice_t &v) {
//decode BRR
state.t_looped = 0;
if(v.interp_pos >= 0x4000) {
brr_decode(v);
v.brr_offset += 2;
if(v.brr_offset >= 9) {
//start decoding next BRR block
v.brr_addr = (uint16)(v.brr_addr + 9);
if(state.t_brr_header & 1) {
v.brr_addr = state.t_brr_next_addr;
state.t_looped = v.vbit;
}
v.brr_offset = 1;
}
}
//apply pitch
v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch;
//keep from getting too far ahead (when using pitch modulation)
if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff;
//output left
voice_output(v, 0);
}
void DSP::voice_5(voice_t &v) {
//output right
voice_output(v, 1);
//ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier
state.endx_buf = REG(endx) | state.t_looped;
//clear bit in ENDX if KON just began
if(v.kon_delay == 5) state.endx_buf &= ~v.vbit;
}
void DSP::voice_6(voice_t &v) {
state.outx_buf = state.t_output >> 8;
}
void DSP::voice_7(voice_t &v) {
//update ENDX
REG(endx) = (uint8)state.endx_buf;
state.envx_buf = v.t_envx_out;
}
void DSP::voice_8(voice_t &v) {
//update OUTX
VREG(outx) = (uint8)state.outx_buf;
}
void DSP::voice_9(voice_t &v) {
//update ENVX
VREG(envx) = (uint8)state.envx_buf;
}
#endif

View File

@ -42,6 +42,10 @@ void MMIOAccess::write(unsigned addr, uint8 data) {
mmio[addr & 0x7fff]->mmio_write(addr, data);
}
MMIOAccess::MMIOAccess() {
for(unsigned i = 0; i < 0x8000; i++) mmio[i] = &memory::mmio_unmapped;
}
unsigned Bus::mirror(unsigned addr, unsigned size) {
unsigned base = 0;
if(size) {

View File

@ -62,6 +62,7 @@ struct MMIOAccess : Memory {
void map(unsigned addr, MMIO &access);
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
MMIOAccess();
private:
MMIO *mmio[0x8000];

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "067.25";
static const char Version[] = "067.26";
static const unsigned SerializerVersion = 12;
}
}
@ -49,6 +49,7 @@ namespace SNES {
typedef uint64_t uint64;
typedef uint_t<2> uint2;
typedef uint_t<3> uint3;
typedef uint_t<10> uint10;
typedef uint_t<17> uint17;
typedef uint_t<24> uint24;

View File

@ -4,13 +4,15 @@ serializer System::serialize() {
serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32();
char description[512];
char profile[16], description[512];
memset(&profile, 0, sizeof profile);
memset(&description, 0, sizeof description);
strcpy(description, Info::Profile);
strlcpy(profile, Info::Profile, sizeof profile);
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(profile);
s.array(description);
serialize_all(s);
@ -19,17 +21,18 @@ serializer System::serialize() {
bool System::unserialize(serializer &s) {
unsigned signature, version, crc32;
char description[512];
char profile[16], description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(profile);
s.array(description);
if(signature != 0x31545342) return false;
if(version != Info::SerializerVersion) return false;
//if(crc32 != cartridge.crc32()) return false;
if(strcmp(description, Info::Profile)) return false;
if(strcmp(profile, Info::Profile)) return false;
reset();
serialize_all(s);
@ -76,11 +79,12 @@ void System::serialize_init() {
serializer s;
unsigned signature = 0, version = 0, crc32 = 0;
char description[512];
char profile[16], description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(profile);
s.array(description);
serialize_all(s);