changeset 90:252843aac42f trunk

[svn] Import the initial sources for console music support.
author nenolod
date Tue, 01 Nov 2005 19:57:26 -0800
parents feeda0dda3ce
children 896d763051da
files Plugins/Input/console/Audacious_Driver.cpp Plugins/Input/console/Blip_Buffer.cpp Plugins/Input/console/Blip_Buffer.h Plugins/Input/console/Blip_Synth.h Plugins/Input/console/Classic_Emu.cpp Plugins/Input/console/Classic_Emu.h Plugins/Input/console/Effects_Buffer.cpp Plugins/Input/console/Effects_Buffer.h Plugins/Input/console/Fir_Resampler.cpp Plugins/Input/console/Fir_Resampler.h Plugins/Input/console/Gb_Apu.cpp Plugins/Input/console/Gb_Apu.h Plugins/Input/console/Gb_Cpu.cpp Plugins/Input/console/Gb_Cpu.h Plugins/Input/console/Gb_Oscs.cpp Plugins/Input/console/Gb_Oscs.h Plugins/Input/console/Gbs_Emu.cpp Plugins/Input/console/Gbs_Emu.h Plugins/Input/console/Gym_Emu.cpp Plugins/Input/console/Gym_Emu.h Plugins/Input/console/Makefile.am Plugins/Input/console/Multi_Buffer.cpp Plugins/Input/console/Multi_Buffer.h Plugins/Input/console/Music_Emu.cpp Plugins/Input/console/Music_Emu.h Plugins/Input/console/Nes_Apu.cpp Plugins/Input/console/Nes_Apu.h Plugins/Input/console/Nes_Cpu.cpp Plugins/Input/console/Nes_Cpu.h Plugins/Input/console/Nes_Namco.cpp Plugins/Input/console/Nes_Namco.h Plugins/Input/console/Nes_Oscs.cpp Plugins/Input/console/Nes_Oscs.h Plugins/Input/console/Nes_Vrc6.cpp Plugins/Input/console/Nes_Vrc6.h Plugins/Input/console/Nsf_Emu.cpp Plugins/Input/console/Nsf_Emu.h Plugins/Input/console/Panning_Buffer.cpp Plugins/Input/console/Panning_Buffer.h Plugins/Input/console/Sms_Apu.cpp Plugins/Input/console/Sms_Apu.h Plugins/Input/console/Sms_Oscs.h Plugins/Input/console/Snes_Spc.cpp Plugins/Input/console/Snes_Spc.h Plugins/Input/console/Spc_Cpu.cpp Plugins/Input/console/Spc_Cpu.h Plugins/Input/console/Spc_Dsp.cpp Plugins/Input/console/Spc_Dsp.h Plugins/Input/console/Spc_Emu.cpp Plugins/Input/console/Spc_Emu.h Plugins/Input/console/Tagged_Data.h Plugins/Input/console/Vgm_Emu.cpp Plugins/Input/console/Vgm_Emu.h Plugins/Input/console/abstract_file.cpp Plugins/Input/console/abstract_file.h Plugins/Input/console/blargg_common.h Plugins/Input/console/blargg_endian.h Plugins/Input/console/blargg_source.h Plugins/Input/console/boost/config.hpp Plugins/Input/console/boost/cstdint.hpp Plugins/Input/console/boost/static_assert.hpp Plugins/Input/console/changes.txt Plugins/Input/console/demo.cpp Plugins/Input/console/demo_effects.cpp Plugins/Input/console/demo_panning.cpp Plugins/Input/console/design.txt Plugins/Input/console/notes.txt Plugins/Input/console/readme.txt Plugins/Input/console/todo.txt Plugins/Input/console/ym2612.cpp Plugins/Input/console/ym2612.h
diffstat 71 files changed, 15523 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Audacious_Driver.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,9 @@
+/*
+ * Audacious: Cross platform multimedia player
+ * Copyright (c) 2005  Audacious Team
+ *
+ * Driver for Game_Music_Emu library. See details at:
+ * http://www.slack.net/~ant/libs/
+ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Blip_Buffer.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,394 @@
+
+// Blip_Buffer 0.3.3. http://www.slack.net/~ant/libs/
+
+#include "Blip_Buffer.h"
+
+#include <string.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Blip_Buffer::Blip_Buffer()
+{
+	samples_per_sec = 44100;
+	buffer_ = NULL;
+	
+	// try to cause assertion failure if buffer is used before these are set
+	clocks_per_sec = 0;
+	factor_ = ~0ul;
+	offset_ = 0;
+	buffer_size_ = 0;
+	length_ = 0;
+	
+	bass_freq_ = 16;
+}
+
+void Blip_Buffer::clear( bool entire_buffer )
+{
+	long count = (entire_buffer ? buffer_size_ : samples_avail());
+	offset_ = 0;
+	reader_accum = 0;
+	memset( buffer_, sample_offset & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) );
+}
+
+blargg_err_t Blip_Buffer::sample_rate( long new_rate, int msec )
+{
+	size_t new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64;
+	if ( msec != blip_default_length )
+	{
+		size_t s = (new_rate * (msec + 1) + 999) / 1000;
+		if ( s < new_size )
+			new_size = s;
+		else
+			require( false ); // requested buffer length exceeds limit
+	}
+	
+	if ( buffer_size_ != new_size )
+	{
+		delete [] buffer_;
+		buffer_ = NULL; // allow for exception in allocation below
+		buffer_size_ = 0;
+		offset_ = 0;
+		
+		buffer_ = new buf_t_ [new_size + widest_impulse_];
+		if ( !buffer_ )
+			return "Out of memory";
+	}
+	
+	buffer_size_ = new_size;
+	length_ = new_size * 1000 / new_rate - 1;
+	if ( msec )
+		assert( length_ == msec ); // ensure length is same as that passed in
+	
+	samples_per_sec = new_rate;
+	if ( clocks_per_sec )
+		clock_rate( clocks_per_sec ); // recalculate factor
+	
+	bass_freq( bass_freq_ ); // recalculate shift
+	
+	clear();
+	
+	return blargg_success;
+}
+
+void Blip_Buffer::clock_rate( long cps )
+{
+	clocks_per_sec = cps;
+	factor_ = (unsigned long) floor( (double) samples_per_sec / cps *
+			(1L << BLIP_BUFFER_ACCURACY) + 0.5 );
+	require( factor_ > 0 ); // clock_rate/sample_rate ratio is too large
+}
+
+Blip_Buffer::~Blip_Buffer()
+{
+	delete [] buffer_;
+}
+
+void Blip_Buffer::bass_freq( int freq )
+{
+	bass_freq_ = freq;
+	if ( freq == 0 ) {
+		bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere
+		return;
+	}
+	bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) );
+	if ( bass_shift < 0 )
+		bass_shift = 0;
+	if ( bass_shift > 24 )
+		bass_shift = 24;
+}
+
+long Blip_Buffer::count_samples( blip_time_t t ) const {
+	return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY);
+}
+
+void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb )
+{
+	fine_bits = fb;
+	width = w;
+	impulses = (imp_t*) imps;
+	generate = true;
+	volume_unit_ = -1.0;
+	res = r;
+	buf = NULL;
+	
+	impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)];
+	offset = 0;
+}
+
+const int impulse_bits = 15;
+const long impulse_amp = 1L << impulse_bits;
+const long impulse_offset = impulse_amp / 2;
+
+void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const
+{
+	long offset = ((long) unit << impulse_bits) - impulse_offset * unit +
+			(1 << (impulse_bits - 1));
+	imp_t* imp = imp_in;
+	imp_t* fimp = impulse;
+	for ( int n = res / 2 + 1; n--; )
+	{
+		int error = unit;
+		for ( int nn = width; nn--; )
+		{
+			long a = ((long) *fimp++ * unit + offset) >> impulse_bits;
+			error -= a - unit;
+			*imp++ = (imp_t) a;
+		}
+		
+		// add error to middle
+		imp [-width / 2 - 1] += (imp_t) error;
+	}
+	
+	if ( res > 2 ) {
+		// second half is mirror-image
+		const imp_t* rev = imp - width - 1;
+		for ( int nn = (res / 2 - 1) * width - 1; nn--; )
+			*imp++ = *--rev;
+		*imp++ = (imp_t) unit;
+	}
+	
+	// copy to odd offset
+	*imp++ = (imp_t) unit;
+	memcpy( imp, imp_in, (res * width - 1) * sizeof *imp );
+}
+
+const int max_res = 1 << blip_res_bits_;
+
+void Blip_Impulse_::fine_volume_unit()
+{
+	// to do: find way of merging in-place without temporary buffer
+	
+	imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_];
+	scale_impulse( (offset & 0xffff) << fine_bits, temp );
+	imp_t* imp2 = impulses + res * 2 * width;
+	scale_impulse( offset & 0xffff, imp2 );
+	
+	// merge impulses
+	imp_t* imp = impulses;
+	imp_t* src2 = temp;
+	for ( int n = res / 2 * 2 * width; n--; ) {
+		*imp++ = *imp2++;
+		*imp++ = *imp2++;
+		*imp++ = *src2++;
+		*imp++ = *src2++;
+	}
+}
+
+void Blip_Impulse_::volume_unit( double new_unit )
+{
+	if ( new_unit == volume_unit_ )
+		return;
+	
+	if ( generate )
+		treble_eq( blip_eq_t( -8.87, 8800, 44100 ) );
+	
+	volume_unit_ = new_unit;
+	
+	offset = 0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 );
+	
+	if ( fine_bits )
+		fine_volume_unit();
+	else
+		scale_impulse( offset & 0xffff, impulses );
+}
+
+static const double pi = 3.1415926535897932384626433832795029L;
+
+void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq )
+{
+	if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff &&
+			new_eq.sample_rate == eq.sample_rate )
+		return; // already calculated with same parameters
+	
+	generate = false;
+	eq = new_eq;
+	
+	double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50)
+	if ( treble < 0.000005 )
+		treble = 0.000005;
+	
+	const double treble_freq = 22050.0; // treble level at 22 kHz harmonic
+	const double sample_rate = eq.sample_rate;
+	const double pt = treble_freq * 2 / sample_rate;
+	double cutoff = eq.cutoff * 2 / sample_rate;
+	if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) {
+		cutoff = 0.5;
+		treble = 1.0;
+	}
+	
+	// DSF Synthesis (See T. Stilson & J. Smith (1996),
+	// Alias-free digital synthesis of classic analog waveforms)
+	
+	// reduce adjacent impulse interference by using small part of wide impulse
+	const double n_harm = 4096;
+	const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) );
+	const double rescale = 1.0 / pow( rolloff, n_harm * cutoff );
+	
+	const double pow_a_n = rescale * pow( rolloff, n_harm );
+	const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff );
+	
+	double total = 0.0;
+	const double to_angle = pi / 2 / n_harm / max_res;
+	
+	float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2];
+	const int size = max_res * (width - 2) / 2;
+	for ( int i = size; i--; )
+	{
+		double angle = (i * 2 + 1) * to_angle;
+		
+		// equivalent
+		//double y =     dsf( angle, n_harm * cutoff, 1.0 );
+		//y -= rescale * dsf( angle, n_harm * cutoff, rolloff );
+		//y += rescale * dsf( angle, n_harm,          rolloff );
+		
+		const double cos_angle = cos( angle );
+		const double cos_nc_angle = cos( n_harm * cutoff * angle );
+		const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle );
+		
+		double b = 2.0 - 2.0 * cos_angle;
+		double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
+		
+		double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle);
+		double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) -
+				pow_a_n * cos( n_harm * angle ) -
+				pow_a_nc * rolloff * cos_nc1_angle +
+				pow_a_nc * cos_nc_angle;
+		
+		// optimization of a / b + c / d
+		double y = (a * d + c * b) / (b * d);
+		
+		// fixed window which affects wider impulses more
+		if ( width > 12 ) {
+			double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle );
+			y *= window * window;
+		}
+		
+		total += (float) y;
+		buf [i] = (float) y;
+	}
+	
+	// integrate runs of length 'max_res'
+	double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half
+	imp_t* imp = impulse;
+	const int step = max_res / res;
+	int offset = res > 1 ? max_res : max_res / 2;
+	for ( int n = res / 2 + 1; n--; offset -= step )
+	{
+		for ( int w = -width / 2; w < width / 2; w++ )
+		{
+			double sum = 0;
+			for ( int i = max_res; i--; )
+			{
+				int index = w * max_res + offset + i;
+				if ( index < 0 )
+					index = -index - 1;
+				if ( index < size )
+					sum += buf [index];
+			}
+			*imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) );
+		}
+	}
+	
+	// rescale
+	double unit = volume_unit_;
+	if ( unit >= 0 ) {
+		volume_unit_ = -1;
+		volume_unit( unit );
+	}
+}
+
+void Blip_Buffer::remove_samples( long count )
+{
+	require( buffer_ ); // sample rate must have been set
+	
+	if ( !count ) // optimization
+		return;
+	
+	remove_silence( count );
+	
+	// copy remaining samples to beginning and clear old samples
+	long remain = samples_avail() + widest_impulse_;
+	if ( count >= remain )
+		memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) );
+	else
+		memcpy(  buffer_, buffer_ + count, remain * sizeof (buf_t_) );
+	memset( buffer_ + remain, sample_offset & 0xFF, count * sizeof (buf_t_) );
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo )
+{
+	require( buffer_ ); // sample rate must have been set
+	
+	long count = samples_avail();
+	if ( count > max_samples )
+		count = max_samples;
+	
+	if ( !count )
+		return 0; // optimization
+	
+	int sample_offset = this->sample_offset;
+	int bass_shift = this->bass_shift;
+	buf_t_* buf = buffer_;
+	long accum = reader_accum;
+	
+	if ( !stereo ) {
+		for ( long n = count; n--; ) {
+			long s = accum >> accum_fract;
+			accum -= accum >> bass_shift;
+			accum += (long (*buf++) - sample_offset) << accum_fract;
+			*out++ = (blip_sample_t) s;
+			
+			// clamp sample
+			if ( (BOOST::int16_t) s != s )
+				out [-1] = blip_sample_t (0x7FFF - (s >> 24));
+		}
+	}
+	else {
+		for ( long n = count; n--; ) {
+			long s = accum >> accum_fract;
+			accum -= accum >> bass_shift;
+			accum += (long (*buf++) - sample_offset) << accum_fract;
+			*out = (blip_sample_t) s;
+			out += 2;
+			
+			// clamp sample
+			if ( (BOOST::int16_t) s != s )
+				out [-2] = blip_sample_t (0x7FFF - (s >> 24));
+		}
+	}
+	
+	reader_accum = accum;
+	
+	remove_samples( count );
+	
+	return count;
+}
+
+void Blip_Buffer::mix_samples( const blip_sample_t* in, long count )
+{
+	buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)];
+	
+	int prev = 0;
+	while ( count-- ) {
+		int s = *in++;
+		*buf += s - prev;
+		prev = s;
+		++buf;
+	}
+	*buf -= *--in;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Blip_Buffer.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,248 @@
+
+// Buffer of sound samples into which band-limited waveforms can be synthesized
+// using Blip_Wave or Blip_Synth.
+
+// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef BLIP_BUFFER_H
+#define BLIP_BUFFER_H
+
+#include "blargg_common.h"
+
+class Blip_Reader;
+
+// Source time unit.
+typedef long blip_time_t;
+
+// Type of sample produced. Signed 16-bit format.
+typedef BOOST::int16_t blip_sample_t;
+
+// Make buffer as large as possible (currently about 65000 samples)
+const int blip_default_length = 0;
+
+class Blip_Buffer {
+public:
+	// Construct an empty buffer.
+	Blip_Buffer();
+	~Blip_Buffer();
+	
+	// Set output sample rate and buffer length in milliseconds (1/1000 sec),
+	// then clear buffer. If length is not specified, make as large as possible.
+	// If there is insufficient memory for the buffer, sets the buffer length
+	// to 0 and returns error string (or propagates exception if compiler supports it).
+	blargg_err_t sample_rate( long samples_per_sec, int msec_length = blip_default_length );
+	
+	// Length of buffer, in milliseconds
+	int length() const;
+	
+	// Current output sample rate
+	long sample_rate() const;
+	
+	// Number of source time units per second
+	void clock_rate( long );
+	long clock_rate() const;
+	
+	// Set frequency at which high-pass filter attenuation passes -3dB
+	void bass_freq( int frequency );
+	
+	// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
+	// false, just clear out any samples waiting rather than the entire buffer.
+	void clear( bool entire_buffer = true );
+	
+	// End current time frame of specified duration and make its samples available
+	// (along with any still-unread samples) for reading with read_samples(). Begin
+	// a new time frame at the end of the current frame. All transitions must have
+	// been added before 'time'.
+	void end_frame( blip_time_t time );
+	
+	// Number of samples available for reading with read_samples()
+	long samples_avail() const;
+	
+	// Read at most 'max_samples' out of buffer into 'dest', removing them from from
+	// the buffer. Return number of samples actually read and removed. If stereo is
+	// true, increment 'dest' one extra time after writing each sample, to allow
+	// easy interleving of two channels into a stereo output buffer.
+	long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false );
+	
+	// Remove 'count' samples from those waiting to be read
+	void remove_samples( long count );
+	
+	// Number of samples delay from synthesis to samples read out
+	int output_latency() const;
+	
+	
+	// Experimental external buffer mixing support
+	
+	// Number of raw samples that can be mixed within frame of specified duration
+	long count_samples( blip_time_t duration ) const;
+	
+	// Mix 'count' samples from 'buf' into buffer.
+	void mix_samples( const blip_sample_t* buf, long count );
+	
+	
+	// not documented yet
+	
+	void remove_silence( long count );
+	
+	typedef unsigned long resampled_time_t;
+	
+	resampled_time_t resampled_time( blip_time_t t ) const {
+		return t * resampled_time_t (factor_) + offset_;
+	}
+	
+	resampled_time_t resampled_duration( int t ) const {
+		return t * resampled_time_t (factor_);
+	}
+	
+private:
+	// noncopyable
+	Blip_Buffer( const Blip_Buffer& );
+	Blip_Buffer& operator = ( const Blip_Buffer& );
+
+	// Don't use the following members. They are public only for technical reasons.
+	public:
+		enum { widest_impulse_ = 24 };
+		typedef BOOST::uint16_t buf_t_;
+		
+		unsigned long factor_;
+		resampled_time_t offset_;
+		buf_t_* buffer_;
+		unsigned buffer_size_;
+	private:
+		long reader_accum;
+		int bass_shift;
+		long samples_per_sec;
+		long clocks_per_sec;
+		int bass_freq_;
+		int length_;
+		
+		enum { accum_fract = 15 }; // less than 16 to give extra sample range
+		enum { sample_offset = 0x7F7F }; // repeated byte allows memset to clear buffer
+		
+		friend class Blip_Reader;
+};
+
+// Low-pass equalization parameters (see notes.txt)
+class blip_eq_t {
+public:
+	blip_eq_t( double treble = 0 );
+	blip_eq_t( double treble, long cutoff, long sample_rate );
+private:
+	double treble;
+	long cutoff;
+	long sample_rate;
+	friend class Blip_Impulse_;
+};
+
+// not documented yet (see Multi_Buffer.cpp for an example of use)
+class Blip_Reader {
+	const Blip_Buffer::buf_t_* buf;
+	long accum;
+	#ifdef __MWERKS__
+	void operator = ( struct foobar ); // helps optimizer
+	#endif
+public:
+	// avoid anything which might cause optimizer to put object in memory
+	
+	int begin( Blip_Buffer& blip_buf ) {
+		buf = blip_buf.buffer_;
+		accum = blip_buf.reader_accum;
+		return blip_buf.bass_shift;
+	}
+	
+	int read() const {
+		return accum >> Blip_Buffer::accum_fract;
+	}
+	
+	void next( int bass_shift = 9 ) {
+		accum -= accum >> bass_shift;
+		accum += ((long) *buf++ - Blip_Buffer::sample_offset) << Blip_Buffer::accum_fract;
+	}
+	
+	void end( Blip_Buffer& blip_buf ) {
+		blip_buf.reader_accum = accum;
+	}
+};
+
+
+
+// End of public interface
+	
+#ifndef BLIP_BUFFER_ACCURACY
+	#define BLIP_BUFFER_ACCURACY 16
+#endif
+
+const int blip_res_bits_ = 5;
+
+typedef BOOST::uint32_t blip_pair_t_;
+
+class Blip_Impulse_ {
+	typedef BOOST::uint16_t imp_t;
+	
+	blip_eq_t eq;
+	double  volume_unit_;
+	imp_t*  impulses;
+	imp_t*  impulse;
+	int     width;
+	int     fine_bits;
+	int     res;
+	bool    generate;
+	
+	void fine_volume_unit();
+	void scale_impulse( int unit, imp_t* ) const;
+public:
+	Blip_Buffer*    buf;
+	BOOST::uint32_t offset;
+	
+	void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 );
+	void volume_unit( double );
+	void treble_eq( const blip_eq_t& );
+};
+
+inline blip_eq_t::blip_eq_t( double t ) :
+		treble( t ), cutoff( 0 ), sample_rate( 44100 ) {
+}
+
+inline blip_eq_t::blip_eq_t( double t, long c, long sr ) :
+		treble( t ), cutoff( c ), sample_rate( sr ) {
+}
+
+inline int Blip_Buffer::length() const {
+	return length_;
+}
+
+inline long Blip_Buffer::samples_avail() const {
+	return long (offset_ >> BLIP_BUFFER_ACCURACY);
+}
+
+inline long Blip_Buffer::sample_rate() const {
+	return samples_per_sec;
+}
+
+inline void Blip_Buffer::end_frame( blip_time_t t ) {
+	offset_ += t * factor_;
+	assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer",
+			samples_avail() <= buffer_size_ ));
+}
+
+inline void Blip_Buffer::remove_silence( long count ) {
+	assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available",
+			count <= samples_avail() ));
+	offset_ -= resampled_time_t (count) << BLIP_BUFFER_ACCURACY;
+}
+
+inline int Blip_Buffer::output_latency() const {
+	return widest_impulse_ / 2;
+}
+
+inline long Blip_Buffer::clock_rate() const {
+	return clocks_per_sec;
+}
+
+// MSVC6 fix
+typedef Blip_Buffer::resampled_time_t blip_resampled_time_t;
+
+#include "Blip_Synth.h"
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Blip_Synth.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,204 @@
+
+// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding
+// waveforms to a Blip_Buffer.
+
+// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef BLIP_SYNTH_H
+#define BLIP_SYNTH_H
+
+#ifndef BLIP_BUFFER_H
+	#include "Blip_Buffer.h"
+#endif
+
+// Quality level. Higher levels are slower, and worse in a few cases.
+// Use blip_good_quality as a starting point.
+const int blip_low_quality = 1;
+const int blip_med_quality = 2;
+const int blip_good_quality = 3;
+const int blip_high_quality = 4;
+
+// Blip_Synth is a transition waveform synthesizer which adds band-limited
+// offsets (transitions) into a Blip_Buffer. For a simpler interface, use
+// Blip_Wave (below).
+//
+// Range specifies the greatest expected offset that will occur. For a
+// waveform that goes between +amp and -amp, range should be amp * 2 (half
+// that if it only goes between +amp and 0). When range is large, a higher
+// accuracy scheme is used; to force this even when range is small, pass
+// the negative of range (i.e. -range).
+template<int quality,int range>
+class Blip_Synth {
+	BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 );
+	BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 );
+	enum {
+		abs_range = (range < 0) ? -range : range,
+		fine_mode = (range > 512 || range < 0),
+		width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_),
+		res = 1 << blip_res_bits_,
+		impulse_size = width / 2 * (fine_mode + 1),
+		base_impulses_size = width / 2 * (res / 2 + 1),
+		fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 :
+			abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 :
+			abs_range <= 2048 ? 7 : 8) : 0)
+	};
+	blip_pair_t_  impulses [impulse_size * res * 2 + base_impulses_size];
+	Blip_Impulse_ impulse;
+public:
+	Blip_Synth()                            { impulse.init( impulses, width, res, fine_bits ); }
+	
+	// Configure low-pass filter (see notes.txt). Not optimized for real-time control
+	void treble_eq( const blip_eq_t& eq )   { impulse.treble_eq( eq ); }
+	
+	// Set volume of a transition at amplitude 'range' by setting volume_unit
+	// to v / range
+	void volume( double v )                 { impulse.volume_unit( v * (1.0 / abs_range) ); }
+	
+	// Set base volume unit of transitions, where 1.0 is a full swing between the
+	// positive and negative extremes. Not optimized for real-time control.
+	void volume_unit( double unit )         { impulse.volume_unit( unit ); }
+	
+	// Default Blip_Buffer used for output when none is specified for a given call
+	Blip_Buffer* output() const             { return impulse.buf; }
+	void output( Blip_Buffer* b )           { impulse.buf = b; }
+	
+	// Add an amplitude offset (transition) with an amplitude of delta * volume_unit
+	// into the specified buffer (default buffer if none specified) at the
+	// specified source time. Amplitude can be positive or negative. To increase
+	// performance by inlining code at the call site, use offset_inline().
+	void offset( blip_time_t, int delta, Blip_Buffer* ) const;
+	
+	void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
+	void offset_resampled( blip_resampled_time_t t, int o ) const {
+		offset_resampled( t, o, impulse.buf );
+	}
+	void offset( blip_time_t t, int delta ) const {
+		offset( t, delta, impulse.buf );
+	}
+	void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const {
+		offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
+	}
+	void offset_inline( blip_time_t time, int delta ) const {
+		offset_inline( time, delta, impulse.buf );
+	}
+};
+
+// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer.
+// A wave is built from a series of delays and new amplitudes. This provides a
+// simpler interface than Blip_Synth.
+template<int quality,int range>
+class Blip_Wave {
+	Blip_Synth<quality,range> synth;
+	blip_time_t time_;
+	int last_amp;
+public:
+	// Start wave at time 0 and amplitude 0
+	Blip_Wave()                         : time_( 0 ), last_amp( 0 ) { }
+	
+	// See Blip_Synth for description
+	void volume( double v )             { synth.volume( v ); }
+	void volume_unit( double v )        { synth.volume_unit( v ); }
+	void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); }
+	Blip_Buffer* output() const         { return synth.output(); }
+	void output( Blip_Buffer* b )       { synth.output( b ); if ( !b ) time_ = last_amp = 0; }
+	
+	// Current time in frame
+	blip_time_t time() const            { return time_; }
+	void time( blip_time_t t )          { time_ = t; }
+	
+	// Current amplitude of wave
+	int amplitude() const               { return last_amp; }
+	void amplitude( int );
+	
+	// Move forward by 't' time units
+	void delay( blip_time_t t )         { time_ += t; }
+	
+	// End time frame of specified duration. Localize time to new frame.
+	void end_frame( blip_time_t duration ) {
+		assert(( "Blip_Wave::end_frame(): Wave hadn't yet been run for entire frame",
+				duration <= time_ ));
+		time_ -= duration;
+	}
+};
+
+
+
+// End of public interface
+	
+	template<int quality,int range>
+	void Blip_Wave<quality,range>::amplitude( int amp ) {
+		int delta = amp - last_amp;
+		last_amp = amp;
+		synth.offset_inline( time_, delta );
+	}
+	
+	template<int quality,int range>
+	inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
+			int delta, Blip_Buffer* blip_buf ) const
+	{
+		typedef blip_pair_t_ pair_t;
+		
+		unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1;
+		assert(( "Blip_Synth/Blip_wave: Went past end of buffer",
+				sample_index < blip_buf->buffer_size_ ));
+		enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 };
+		pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index];
+		
+		enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ };
+		enum { mask = res * 2 - 1 };
+		const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size];
+		
+		pair_t offset = impulse.offset * delta;
+		
+		if ( !fine_bits )
+		{
+			// normal mode
+			for ( int n = width / 4; n; --n )
+			{
+				pair_t t0 = buf [0] - offset;
+				pair_t t1 = buf [1] - offset;
+				
+				t0 += imp [0] * delta;
+				t1 += imp [1] * delta;
+				imp += 2;
+				
+				buf [0] = t0;
+				buf [1] = t1;
+				buf += 2;
+			}
+		}
+		else
+		{
+			// fine mode
+			enum { sub_range = 1 << fine_bits };
+			delta += sub_range / 2;
+			int delta2 = (delta & (sub_range - 1)) - sub_range / 2;
+			delta >>= fine_bits;
+			
+			for ( int n = width / 4; n; --n )
+			{
+				pair_t t0 = buf [0] - offset;
+				pair_t t1 = buf [1] - offset;
+				
+				t0 += imp [0] * delta2;
+				t0 += imp [1] * delta;
+				
+				t1 += imp [2] * delta2;
+				t1 += imp [3] * delta;
+				
+				imp += 4;
+				
+				buf [0] = t0;
+				buf [1] = t1;
+				buf += 2;
+			}
+		}
+	}
+	
+	template<int quality,int range>
+	void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const {
+		offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Classic_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,116 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Classic_Emu.h"
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Classic_Emu::Classic_Emu()
+{
+	buf = NULL;
+	std_buf = NULL;
+	set_equalizer( equalizer_t( -8.87, 8800 ) );
+}
+
+Classic_Emu::~Classic_Emu()
+{
+	delete std_buf;
+}
+
+void Classic_Emu::update_eq_()
+{
+	update_eq( blip_eq_t( equalizer_.treble, equalizer_.cutoff, buf->sample_rate() ) );
+	buf->bass_freq( equalizer_.bass );
+}
+
+void Classic_Emu::set_equalizer( const equalizer_t& eq )
+{
+	equalizer_ = eq;
+	if ( buf )
+		update_eq_();
+}
+	
+blargg_err_t Classic_Emu::init( long sample_rate )
+{
+	buf = NULL;
+	delete std_buf;
+	std_buf = NULL;
+	
+	Stereo_Buffer* sb = new Stereo_Buffer;
+	if ( !sb )
+		return "Out of memory";
+	std_buf = sb;
+	
+	BLARGG_RETURN_ERR( sb->sample_rate( sample_rate, 1000 / 20 ) );
+
+	buf = std_buf;
+	return blargg_success;
+}
+
+void Classic_Emu::mute_voices( int mask )
+{
+	require( buf ); // init() must have been called
+	
+	mute_mask_ = mask;
+	for ( int i = voice_count(); i--; )
+	{
+		if ( mask & (1 << i) ) {
+			set_voice( i, NULL );
+		}
+		else {
+			Multi_Buffer::channel_t ch = buf->channel( i );
+			set_voice( i, ch.center, ch.left, ch.right );
+		}
+	}
+}
+
+blargg_err_t Classic_Emu::setup_buffer( long clock_rate )
+{
+	require( buf ); // init() must have been called
+	
+	buf->clock_rate( clock_rate );
+	update_eq_();
+	return buf->set_channel_count( voice_count() );
+}
+
+void Classic_Emu::starting_track()
+{
+	require( buf ); // init() must have been called
+	
+	mute_voices( 0 );
+	buf->clear();
+}
+
+blargg_err_t Classic_Emu::play( long count, sample_t* out )
+{
+	require( buf ); // init() must have been called
+	
+	long remain = count;
+	while ( remain )
+	{
+		remain -= buf->read_samples( &out [count - remain], remain );
+		if ( remain )
+		{
+			bool added_stereo = false;
+			blip_time_t cyc = run( buf->length(), &added_stereo );
+			if ( !cyc )
+				return "Emulation error";
+			buf->end_frame( cyc, added_stereo );
+		}
+	}
+	return blargg_success;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Classic_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,69 @@
+
+// Classic game music emulator interface base class for emulators which use Blip_Buffer
+// for sound output.
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef CLASSIC_EMU_H
+#define CLASSIC_EMU_H
+
+#include "Music_Emu.h"
+class Blip_Buffer;
+class blip_eq_t;
+class Multi_Buffer;
+
+class Classic_Emu : public Music_Emu {
+public:
+	Classic_Emu();
+	~Classic_Emu();
+	
+	// Initialize emulator with specified sample rate. Sample output is in stereo.
+	virtual blargg_err_t init( long sample_rate );
+	
+	// Initialize emulator using custom output buffer
+	blargg_err_t init( Multi_Buffer* buf );
+	
+	// Frequency equalizer parameters (see notes.txt)
+	struct equalizer_t {
+		double treble; // treble level at 22kHz, in dB (-3.0dB = 0.50)
+		long cutoff;   // beginning of low-pass rolloff, in Hz
+		long bass;     // high-pass breakpoint, in Hz
+		equalizer_t( double treble_ = 0, long cutoff_ = 0, int bass_ = 33 ) :
+				treble( treble_ ), cutoff( cutoff_ ), bass( bass_ ) { }
+	};
+	
+	// Current frequency equalizater parameters
+	const equalizer_t& equalizer() const;
+	
+	// Set frequency equalizer parameters
+	void set_equalizer( const equalizer_t& );
+	
+	// See Music_Emu.h
+	void mute_voices( int );
+	blargg_err_t play( long, sample_t* );
+	
+	
+// End of public interface
+protected:
+	virtual void starting_track();
+	virtual blargg_err_t setup_buffer( long clock_rate );
+	virtual void set_voice( int index, Blip_Buffer* center,
+			Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ) = 0;
+	virtual long run( int msec, bool* added_stereo = NULL ) = 0;
+	virtual void update_eq( blip_eq_t const& ) = 0;
+private:
+	Multi_Buffer* buf;
+	Multi_Buffer* std_buf; // owned
+	equalizer_t equalizer_;
+	void update_eq_();
+};
+
+inline blargg_err_t Classic_Emu::init( Multi_Buffer* buf_ ) {
+	buf = buf_;
+	return blargg_success;
+}
+inline const Classic_Emu::equalizer_t& Classic_Emu::equalizer() const {
+	return equalizer_;
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Effects_Buffer.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,466 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Effects_Buffer.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+typedef long fixed_t;
+
+#define TO_FIXED( f )   fixed_t ((f) * (1L << 15) + 0.5)
+#define FMUL( x, y )    (((x) * (y)) >> 15)
+
+const unsigned echo_size = 4096;
+const unsigned echo_mask = echo_size - 1;
+BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2
+
+const unsigned reverb_size = 8192 * 2;
+const unsigned reverb_mask = reverb_size - 1;
+BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2
+
+Effects_Buffer::config_t::config_t()
+{
+	pan_1 = 0.0;
+	pan_2 = 0.0;
+	reverb_delay = 88;
+	reverb_level = 0.10;
+	echo_delay = 61;
+	echo_level = 0.12;
+	delay_variance = 18;
+	effects_enabled = false;
+}
+
+Effects_Buffer::Effects_Buffer()
+{
+	echo_buf = NULL;
+	echo_pos = 0;
+	
+	reverb_buf = NULL;
+	reverb_pos = 0;
+	
+	stereo_remain = 0;
+	effect_remain = 0;
+	effects_enabled = false;
+	config( config_t() );
+}
+
+Effects_Buffer::~Effects_Buffer()
+{
+	delete [] echo_buf;
+	delete [] reverb_buf;
+}
+
+blargg_err_t Effects_Buffer::sample_rate( long rate, int msec )
+{
+	if ( !echo_buf )
+	{
+		echo_buf = new blip_sample_t [echo_size];
+		if ( !echo_buf )
+			return "Out of memory";
+	}
+	
+	if ( !reverb_buf )
+	{
+		reverb_buf = new blip_sample_t [reverb_size];
+		if ( !reverb_buf )
+			return "Out of memory";
+	}
+	
+	for ( int i = 0; i < buf_count; i++ )
+		BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) );
+	
+	length_ = msec;
+	sample_rate_ = rate;
+	
+	config( config_ );
+	
+	return blargg_success;
+}
+
+void Effects_Buffer::clock_rate( long rate )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].clock_rate( rate );
+}
+
+void Effects_Buffer::bass_freq( int freq )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].bass_freq( freq );
+}
+
+void Effects_Buffer::clear()
+{
+	stereo_remain = 0;
+	effect_remain = 0;
+	memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) );
+	memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) );
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].clear();
+}
+
+inline int pin_range( int n, int max, int min = 0 )
+{
+	if ( n < min )
+		return min;
+	if ( n > max )
+		return max;
+	return n;
+}
+
+void Effects_Buffer::config( const config_t& cfg )
+{
+	// clear echo and reverb buffers
+	if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf ) {
+		memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) );
+		memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) );
+	}
+	
+	config_ = cfg;
+	
+	if ( config_.effects_enabled )
+	{
+		// convert to internal format
+		
+		chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 );
+		chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0];
+		
+		chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 );
+		chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0];
+		
+		chans.reverb_level = TO_FIXED( config_.reverb_level );
+		chans.echo_level = TO_FIXED( config_.echo_level );
+		
+		const int delay_offset = config_.delay_variance * sample_rate_ / (1000 * 2);
+		
+		const int reverb_sample_delay = config_.reverb_delay * sample_rate_ / 1000;
+		chans.reverb_delay_l = pin_range( reverb_size -
+				(reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 );
+		chans.reverb_delay_r = pin_range( reverb_size + 1 -
+				(reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 );
+		
+		const int echo_sample_delay = config_.echo_delay * sample_rate_ / 1000;
+		chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset),
+				echo_size - 1 );
+		chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset),
+				echo_size - 1 );
+		
+		// set up outputs
+		for ( unsigned i = 0; i < chan_count; i++ ) {
+			channel_t& o = channels [i];
+			if ( i < 2 ) {
+				o.center = &bufs [i];
+				o.left   = &bufs [3];
+				o.right  = &bufs [4];
+			}
+			else {
+				o.center = &bufs [2];
+				o.left   = &bufs [5];
+				o.right  = &bufs [6];
+			}
+		}
+		
+	}
+	else {
+		// set up outputs
+		for ( unsigned i = 0; i < chan_count; i++ ) {
+			channel_t& o = channels [i];
+			o.center = &bufs [0];
+			o.left   = &bufs [1];
+			o.right  = &bufs [2];
+		}
+	}
+}
+
+void Effects_Buffer::end_frame( blip_time_t clock_count, bool stereo )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].end_frame( clock_count );
+	
+	if ( stereo )
+		stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency();
+	
+	if ( effects_enabled || config_.effects_enabled )
+		effect_remain = bufs [0].samples_avail() + bufs [0].output_latency();
+	
+	effects_enabled = config_.effects_enabled;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
+{
+	require( total_samples % 2 == 0 ); // count must be even
+	
+	long remain = bufs [0].samples_avail();
+	if ( remain > (unsigned) total_samples / 2 )
+		remain = (unsigned) total_samples / 2;
+	total_samples = remain;
+	while ( remain )
+	{
+		int active_bufs = buf_count;
+		long count = remain;
+		
+		if ( effect_remain ) {
+			if ( count > effect_remain )
+				count = effect_remain;
+			
+			if ( stereo_remain ) {
+				mix_enhanced( out, count );
+			}
+			else {
+				mix_mono_enhanced( out, count );
+				active_bufs = 3;
+			}
+		}
+		else if ( stereo_remain ) {
+			mix_stereo( out, count );
+			active_bufs = 3; 
+		}
+		else {
+			mix_mono( out, count );
+			active_bufs = 1; 
+		}
+		
+		out += count * 2;
+		remain -= count;
+		
+		stereo_remain -= count;
+		if ( stereo_remain < 0 )
+			stereo_remain = 0;
+		
+		effect_remain -= count;
+		if ( effect_remain < 0 )
+			effect_remain = 0;
+		
+		for ( int i = 0; i < buf_count; i++ ) {
+			if ( i < active_bufs )
+				bufs [i].remove_samples( count );
+			else
+				bufs [i].remove_silence( count ); // keep time synchronized
+		}
+	}
+	
+	return total_samples * 2;
+}
+
+void Effects_Buffer::mix_mono( blip_sample_t* out, long count )
+{
+	Blip_Reader c;
+	int shift = c.begin( bufs [0] );
+	
+	// unrolled loop
+	for ( long n = count >> 1; n--; )
+	{
+		long cs0 = c.read();
+		c.next( shift );
+		
+		long cs1 = c.read();
+		c.next( shift );
+		
+		if ( (BOOST::int16_t) cs0 != cs0 )
+			cs0 = 0x7FFF - (cs0 >> 24);
+		((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16);
+		
+		if ( (BOOST::int16_t) cs1 != cs1 )
+			cs1 = 0x7FFF - (cs1 >> 24);
+		((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16);
+		out += 4;
+	}
+	
+	if ( count & 1 ) {
+		int s = c.read();
+		c.next( shift );
+		out [0] = s;
+		out [1] = s;
+		if ( (BOOST::int16_t) s != s ) {
+			s = 0x7FFF - (s >> 24);
+			out [0] = s;
+			out [1] = s;
+		}
+	}
+	
+	c.end( bufs [0] );
+}
+
+void Effects_Buffer::mix_stereo( blip_sample_t* out, long count )
+{
+	Blip_Reader l; l.begin( bufs [1] );
+	Blip_Reader r; r.begin( bufs [2] );
+	Blip_Reader c;
+	int shift = c.begin( bufs [0] );
+	
+	while ( count-- ) {
+		int cs = c.read();
+		c.next( shift );
+		int left = cs + l.read();
+		int right = cs + r.read();
+		l.next( shift );
+		r.next( shift );
+		
+		if ( (BOOST::int16_t) left != left )
+			left = 0x7FFF - (left >> 24);
+		
+		out [0] = left;
+		out [1] = right;
+		
+		out += 2;
+		
+		if ( (BOOST::int16_t) right != right )
+			out [-1] = 0x7FFF - (right >> 24);
+	}
+	
+	c.end( bufs [0] );
+	r.end( bufs [2] );
+	l.end( bufs [1] );
+}
+
+void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out, long count )
+{
+	Blip_Reader sq1; sq1.begin( bufs [0] );
+	Blip_Reader sq2; sq2.begin( bufs [1] );
+	Blip_Reader center;
+	int shift = center.begin( bufs [2] );
+	
+	int echo_pos = this->echo_pos;
+	int reverb_pos = this->reverb_pos;
+	
+	while ( count-- )
+	{
+		int sum1_s = sq1.read();
+		int sum2_s = sq2.read();
+		
+		sq1.next( shift );
+		sq2.next( shift );
+		
+		int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
+				FMUL( sum2_s, chans.pan_2_levels [0] ) +
+				reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
+		
+		int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
+				FMUL( sum2_s, chans.pan_2_levels [1] ) +
+				reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
+		
+		fixed_t reverb_level = chans.reverb_level;
+		reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level );
+		reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level );
+		reverb_pos = (reverb_pos + 2) & reverb_mask;
+		
+		int sum3_s = center.read();
+		center.next( shift );
+		
+		int left = new_reverb_l + sum3_s + FMUL( chans.echo_level,
+				echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
+		int right = new_reverb_r + sum3_s + FMUL( chans.echo_level,
+				echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
+		
+		echo_buf [echo_pos] = sum3_s;
+		echo_pos = (echo_pos + 1) & echo_mask;
+		
+		if ( (BOOST::int16_t) left != left )
+			left = 0x7FFF - (left >> 24);
+		
+		out [0] = left;
+		out [1] = right;
+		
+		out += 2;
+		
+		if ( (BOOST::int16_t) right != right )
+			out [-1] = 0x7FFF - (right >> 24);
+	}
+	this->reverb_pos = reverb_pos;
+	this->echo_pos = echo_pos;
+	
+	sq1.end( bufs [0] );
+	sq2.end( bufs [1] );
+	center.end( bufs [2] );
+}
+
+void Effects_Buffer::mix_enhanced( blip_sample_t* out, long count )
+{
+	Blip_Reader l1; l1.begin( bufs [3] );
+	Blip_Reader r1; r1.begin( bufs [4] );
+	Blip_Reader l2; l2.begin( bufs [5] );
+	Blip_Reader r2; r2.begin( bufs [6] );
+	Blip_Reader sq1; sq1.begin( bufs [0] );
+	Blip_Reader sq2; sq2.begin( bufs [1] );
+	Blip_Reader center;
+	int shift = center.begin( bufs [2] );
+	
+	int echo_pos = this->echo_pos;
+	int reverb_pos = this->reverb_pos;
+	
+	while ( count-- )
+	{
+		int sum1_s = sq1.read();
+		int sum2_s = sq2.read();
+		
+		sq1.next( shift );
+		sq2.next( shift );
+		
+		int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
+				FMUL( sum2_s, chans.pan_2_levels [0] ) + l1.read() +
+				reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
+		
+		int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
+				FMUL( sum2_s, chans.pan_2_levels [1] ) + r1.read() +
+				reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
+		
+		l1.next( shift );
+		r1.next( shift );
+		
+		fixed_t reverb_level = chans.reverb_level;
+		reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level );
+		reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level );
+		reverb_pos = (reverb_pos + 2) & reverb_mask;
+		
+		int sum3_s = center.read();
+		center.next( shift );
+		
+		int left = new_reverb_l + sum3_s + l2.read() + FMUL( chans.echo_level,
+				echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
+		int right = new_reverb_r + sum3_s + r2.read() + FMUL( chans.echo_level,
+				echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
+		
+		l2.next( shift );
+		r2.next( shift );
+		
+		echo_buf [echo_pos] = sum3_s;
+		echo_pos = (echo_pos + 1) & echo_mask;
+		
+		if ( (BOOST::int16_t) left != left )
+			left = 0x7FFF - (left >> 24);
+		
+		out [0] = left;
+		out [1] = right;
+		
+		out += 2;
+		
+		if ( (BOOST::int16_t) right != right )
+			out [-1] = 0x7FFF - (right >> 24);
+	}
+	this->reverb_pos = reverb_pos;
+	this->echo_pos = echo_pos;
+	
+	sq1.end( bufs [0] );
+	sq2.end( bufs [1] );
+	center.end( bufs [2] );
+	l1.end( bufs [3] );
+	r1.end( bufs [4] );
+	l2.end( bufs [5] );
+	r2.end( bufs [6] );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Effects_Buffer.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,91 @@
+
+// Multi-channel effects buffer with panning, echo and reverb effects
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef EFFECTS_BUFFER_H
+#define EFFECTS_BUFFER_H
+
+#include "Multi_Buffer.h"
+
+// Effects_Buffer uses several buffers and outputs stereo sample pairs.
+class Effects_Buffer : public Multi_Buffer {
+public:
+	Effects_Buffer();
+	~Effects_Buffer();
+	
+	// Channel  Effect    Center Pan
+	// ---------------------------------
+	//    0     reverb       pan_1
+	//    1     reverb       pan_2
+	//    2      echo         -
+	//    3      echo         -
+	//    4      echo         -
+	
+	// Channel configuration
+	struct config_t {
+		double pan_1;           // -1.0 = left, 0.0 = center, 1.0 = right
+		double pan_2;
+		double echo_delay;      // msec
+		double echo_level;      // 0.0 to 1.0
+		double reverb_delay;    // msec
+		double delay_variance;  // difference between left/right delays (msec)
+		double reverb_level;    // 0.0 to 1.0
+		bool effects_enabled;   // if false, use optimized simple mixer
+		config_t();
+	};
+	
+	// Set configuration of buffer
+	void config( const config_t& );
+	
+	// See Multi_Buffer.h
+	blargg_err_t sample_rate( long samples_per_sec, int msec );
+	void clock_rate( long );
+	void bass_freq( int );
+	void clear();
+	channel_t channel( int );
+	void end_frame( blip_time_t, bool was_stereo = true );
+	long read_samples( blip_sample_t*, long );
+	
+
+// End of public interface
+private:
+	typedef long fixed_t;
+	
+	enum { buf_count = 7 };
+	Blip_Buffer bufs [buf_count];
+	enum { chan_count = 5 };
+	channel_t channels [chan_count];
+	config_t config_;
+	long stereo_remain;
+	long effect_remain;
+	bool effects_enabled;
+	
+	blip_sample_t* reverb_buf;
+	blip_sample_t* echo_buf;
+	int reverb_pos;
+	int echo_pos;
+	
+	struct {
+		fixed_t pan_1_levels [2];
+		fixed_t pan_2_levels [2];
+		int echo_delay_l;
+		int echo_delay_r;
+		fixed_t echo_level;
+		int reverb_delay_l;
+		int reverb_delay_r;
+		fixed_t reverb_level;
+	} chans;
+	
+	void mix_mono( blip_sample_t*, long );
+	void mix_stereo( blip_sample_t*, long );
+	void mix_enhanced( blip_sample_t*, long );
+	void mix_mono_enhanced( blip_sample_t*, long );
+};
+
+	inline Effects_Buffer::channel_t Effects_Buffer::channel( int i ) {
+		return channels [i % chan_count];
+	}
+	
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Fir_Resampler.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,273 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Fir_Resampler.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+static const double pi = 3.1415926535897932384626433832795029L;
+
+const bool show_impulse = 0;
+
+class Dsf {
+	double rolloff;
+	double factor;
+public:
+	Dsf( double r ) : rolloff( r ) {
+		factor = 1.0;
+		//if ( rolloff < 1.0 )
+		//  factor = 1.0 / (*this)( 0 );
+	}
+	
+	double operator () ( double angle ) const
+	{
+		double const n_harm = 256;
+		angle /= n_harm;
+		double pow_a_n = pow( rolloff, n_harm );
+		double rescale = 1.0 / n_harm;
+		
+		double num = 1.0 - rolloff * cos( angle ) -
+				pow_a_n * cos( n_harm * angle ) +
+				pow_a_n * rolloff * cos( (n_harm - 1) * angle );
+		double den = 1 + rolloff * (rolloff - 2 * cos( angle ));
+		
+		return (num / den - 1) / n_harm * factor;
+	}
+};
+
+template<class T,class Sinc>
+void gen_sinc( int width, double offset, double spacing, int count, double scale, T* p,
+		const Sinc& sinc )
+{
+	double range = pi * (width / 2);
+	double step = pi * spacing;
+	double a = -step * (count / 2 - 1);
+	a -= offset * step;
+	
+	while ( count-- )
+	{
+		double w = a / range;
+		double y = 0.0;
+		if ( fabs( w ) < 1.0 )
+		{
+			y = cos( pi * w ) * 0.5 + 0.5;
+			y *= sinc( a );
+		}
+		
+		*p++ = y * scale;
+		a += step;
+	}
+}
+
+static double plain_sinc( double a ) {
+	return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a;
+}
+
+Fir_Resampler::Fir_Resampler()
+{
+	res = 1;
+	skip_bits = 0;
+	step = 2;
+	buf = NULL;
+	write_pos = NULL;
+	buf_size = 0;
+}
+
+Fir_Resampler::~Fir_Resampler() {
+	free( buf );
+}
+
+void Fir_Resampler::clear()
+{
+	imp = 0;
+	if ( buf ) {
+		write_pos = buf + latency;
+		memset( buf, 0, (write_pos - buf) * sizeof *buf );
+	}
+}
+
+blargg_err_t Fir_Resampler::buffer_size( int new_size )
+{
+	new_size += latency;
+	void* new_buf = realloc( buf, new_size * sizeof *buf );
+	if ( !new_buf )
+		return "Out of memory";
+	buf = (sample_t*) new_buf;
+	buf_size = new_size;
+	clear();
+	return blargg_success;
+}
+	
+double Fir_Resampler::time_ratio( double ratio, double rolloff, double volume )
+{
+	this->ratio = ratio;
+	
+	double fstep = 0.0;
+	{
+		double least_error = 2;
+		double pos = 0;
+		res = -1;
+		for ( int r = 1; r <= max_res; r++ ) {
+			pos += ratio;
+			double nearest = floor( pos + 0.5 );
+			double error = fabs( pos - nearest );
+			if ( error < least_error ) {
+				res = r;
+				fstep = nearest / res;
+				least_error = error;
+			}
+		}
+	}
+	
+	skip_bits = 0;
+	
+	step = 2 * (int) floor( fstep );
+	
+	ratio = fstep;
+	fstep = fmod( fstep, 1.0 );
+	
+	double filter = (ratio < 1.0) ? 1.0 : 1.0 / ratio;
+	double pos = 0.0;
+	Dsf dsf( rolloff );
+	for ( int i = 0; i < res; i++ )
+	{
+		if ( show_impulse )
+			printf( "pos = %f\n", pos );
+		
+		gen_sinc( int (width * filter + 1) & ~1, pos, filter, (int) width,
+				double (0x7fff * volume * filter), impulses [i], dsf );
+		
+		if ( show_impulse ) {
+			for ( int j = 0; j < width; j++ )
+				printf( "%d ", (int) impulses [i] [j] );
+			printf( "\n" );
+		}
+		
+		pos += fstep;
+		if ( pos >= 0.9999999 ) {
+			pos -= 1.0;
+			skip_bits |= 1 << i;
+		}
+	}
+	
+	if ( show_impulse ) {
+		printf( "skip = %X\n", skip_bits );
+		printf( "step = %d\n", step );
+	}
+	
+	clear();
+	
+	return ratio;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+int Fir_Resampler::read( sample_t* out_begin, int count )
+{
+	sample_t* out = out_begin;
+	const sample_t* in = buf;
+	sample_t* end_pos = write_pos;
+	unsigned long skip = skip_bits >> imp;
+	sample_t const* imp = impulses [this->imp];
+	int remain = res - this->imp;
+	int const step = this->step;
+	
+	count = (count >> 1) + 1;
+	
+	// to do: optimize loop to use a single counter rather than 'in' and 'count'
+	
+	if ( end_pos - in >= width * 2 )
+	{
+		end_pos -= width * 2;
+		do
+		{
+			count--;
+			
+			// accumulate in extended precision
+			long l = 0;
+			long r = 0;
+			
+			const sample_t* i = in;
+			if ( !count )
+				break;
+			
+			for ( int n = width / 2; n--; )
+			{
+				int pt0 = imp [0];
+				int pt1 = imp [1];
+				imp += 2;
+				l += (pt0 * i [0]) + (pt1 * i [2]);
+				r += (pt0 * i [1]) + (pt1 * i [3]);
+				i += 4;
+			}
+			
+			remain--;
+			
+			l >>= 15;
+			r >>= 15;
+			
+			in += step + ((skip * 2) & 2);
+			skip >>= 1;
+			
+			if ( !remain ) {
+				imp = impulses [0];
+				skip = skip_bits;
+				remain = res;
+			}
+			
+			out [0] = l;
+			out [1] = r;
+			out += 2;
+		}
+		while ( in <= end_pos );
+	}
+	
+	this->imp = res - remain;
+	
+	int left = write_pos - in;
+	write_pos = buf + left;
+	assert( unsigned (write_pos - buf) <= buf_size );
+	memmove( buf, in, left * sizeof *in );
+	
+	return out - out_begin;
+}
+
+int Fir_Resampler::skip_input( int count )
+{
+	int remain = write_pos - buf;
+	int avail = remain - width * 2;
+	if ( count > avail )
+		count = avail;
+	
+	remain -= count;
+	write_pos = buf + remain;
+	assert( unsigned (write_pos - buf) <= buf_size );
+	memmove( buf, buf + count, remain * sizeof *buf );
+	
+	return count;
+}
+
+/*
+int Fir_Resampler::skip( int count )
+{
+	count = int (count * ratio) & ~1;
+	count = skip_input( count );
+	return int (count / ratio) & ~1;
+}
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Fir_Resampler.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,76 @@
+
+// Finite Impulse Response (FIR) Resampler
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+
+#ifndef FIR_RESAMPLER_H
+#define FIR_RESAMPLER_H
+
+#include "blargg_common.h"
+
+class Fir_Resampler {
+	enum { width = 24 };
+	enum { latency = (width - 1) * 2 };
+public:
+	typedef short sample_t;
+	
+	Fir_Resampler();
+	~Fir_Resampler();
+	
+	// interface hasn't been stabilized yet
+	
+	// Set size of buffer. Return true if out of memory.
+	blargg_err_t buffer_size( int );
+	
+	// Set input/output resampling ratio and frequency rolloff. Return
+	// actual (rounded) ratio used.
+	double time_ratio( double ratio, double rolloff = 0.999, double volume = 1.0 );
+	
+	// Remove any buffered samples and clear buffer
+	void clear();
+	
+	// Pointer to buffer to write input samples to
+	sample_t* buffer() { return write_pos; }
+	
+	// Maximum number of samples that can be written to buffer at current position
+	int max_write() const { return buf_size - (write_pos - buf); }
+	
+	// Number of unread input samples
+	int written() const { return write_pos - buf - latency; }
+	
+	// Advance buffer position by 'count' samples. Call after writing 'count' samples
+	// to buffer().
+	void write( int count );
+	
+	// True if there are there aren't enough input samples to read at least one
+	// output sample.
+	bool empty() const { return (write_pos - buf) <= latency; }
+	
+	// Resample and output at most 'count' into 'buf', then remove input samples from
+	// buffer. Return number of samples written into 'buf'.
+	int read( sample_t* buf, int count );
+	
+	// Skip at most 'count' *input* samples. Return number of samples actually skipped.
+	int skip_input( int count );
+	
+private:
+	enum { max_res = 32 };
+	
+	sample_t* buf;
+	sample_t* write_pos;
+	double ratio;
+	int buf_size;
+	int res;
+	int imp;
+	unsigned long skip_bits;
+	int step;
+	sample_t impulses [max_res] [width];
+};
+
+	inline void Fir_Resampler::write( int count ) {
+		write_pos += count;
+		assert( unsigned (write_pos - buf) <= buf_size );
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Apu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,227 @@
+
+// Gb_Snd_Emu 0.1.3. http://www.slack.net/~ant/libs/
+
+#include "Gb_Apu.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Gb_Apu::Gb_Apu()
+{
+	square1.synth = &square_synth;
+	square2.synth = &square_synth;
+	square1.has_sweep = true;
+	
+	oscs [0] = &square1;
+	oscs [1] = &square2;
+	oscs [2] = &wave;
+	oscs [3] = &noise;
+	
+	volume( 1.0 );
+	reset();
+}
+
+Gb_Apu::~Gb_Apu()
+{
+}
+
+void Gb_Apu::treble_eq( const blip_eq_t& eq )
+{
+	square_synth.treble_eq( eq );
+	wave.synth.treble_eq( eq );
+	noise.synth.treble_eq( eq );
+}
+
+void Gb_Apu::volume( double vol )
+{
+	vol *= 0.60 / osc_count;
+	square_synth.volume( vol );
+	wave.synth.volume( vol );
+	noise.synth.volume( vol );
+}
+
+void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, center, left, right );
+}
+
+void Gb_Apu::reset()
+{
+	next_frame_time = 0;
+	last_time = 0;
+	frame_count = 0;
+	stereo_found = false;
+	
+	square1.reset();
+	square2.reset();
+	wave.reset();
+	noise.reset();
+	
+	memset( regs, 0, sizeof regs );
+}
+
+void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+	require( (unsigned) index < osc_count );
+	
+	Gb_Osc& osc = *oscs [index];
+	if ( center && !left && !right )
+	{
+		// mono
+		left = center;
+		right = center;
+	}
+	else
+	{
+		// must be silenced or stereo
+		require( (!left && !right) || (left && right) );
+	}
+	osc.outputs [1] = right;
+	osc.outputs [2] = left;
+	osc.outputs [3] = center;
+	osc.output = osc.outputs [osc.output_select];
+}
+
+void Gb_Apu::run_until( gb_time_t end_time )
+{
+	require( end_time >= last_time ); // end_time must not be before previous time
+	if ( end_time == last_time )
+		return;
+	
+	while ( true )
+	{
+		gb_time_t time = next_frame_time;
+		if ( time > end_time )
+			time = end_time;
+		
+		// run oscillators
+		for ( int i = 0; i < osc_count; ++i ) {
+			Gb_Osc& osc = *oscs [i];
+			if ( osc.output ) {
+				if ( osc.output != osc.outputs [3] )
+					stereo_found = true;
+				osc.run( last_time, time );
+			}
+		}
+		last_time = time;
+		
+		if ( time == end_time )
+			break;
+		
+		next_frame_time += 4194304 / 256; // 256 Hz
+		
+		// 256 Hz actions
+		square1.clock_length();
+		square2.clock_length();
+		wave.clock_length();
+		noise.clock_length();
+		
+		frame_count = (frame_count + 1) & 3;
+		if ( frame_count == 0 ) {
+			// 64 Hz actions
+			square1.clock_envelope();
+			square2.clock_envelope();
+			noise.clock_envelope();
+		}
+		
+		if ( frame_count & 1 )
+			square1.clock_sweep(); // 128 Hz action
+	}
+}
+
+bool Gb_Apu::end_frame( gb_time_t end_time )
+{
+	run_until( end_time );
+	
+	next_frame_time -= end_time;
+	assert( next_frame_time >= 0 );
+	last_time = 0;
+	
+	bool result = stereo_found;
+	stereo_found = false;
+	return result;
+}
+
+void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data )
+{
+	// function now takes actual address, i.e. 0xFFXX
+	require( start_addr <= addr && addr <= end_addr );
+	require( (unsigned) data <= 0xff );
+	
+	int reg = addr - start_addr;
+	if ( (unsigned) reg >= register_count )
+		return;
+	
+	run_until( time );
+	
+	regs [reg] = data;
+	
+	if ( addr < 0xff24 )
+	{
+		// oscillator
+		int index = reg / 5;
+		oscs [index]->write_register( reg - index * 5, data ); 
+	}
+	else if ( addr == 0xff25 || addr == 0xff26 )
+	{
+		int flags = (regs [0xff26 - start_addr] & 0x80) ? regs [0xff25 - start_addr] : 0;
+		
+		// left/right assignments
+		for ( int i = 0; i < osc_count; i++ )
+		{
+			Gb_Osc& osc = *oscs [i];
+			int bits = flags >> i;
+			Blip_Buffer* old_output = osc.output;
+			osc.output_select = ((bits >> 3) & 2) | (bits & 1);
+			osc.output = osc.outputs [osc.output_select];
+			if ( osc.output != old_output && osc.last_amp ) {
+				if ( old_output )
+					square_synth.offset( time, -osc.last_amp, old_output );
+				osc.last_amp = 0;
+			}
+		}
+	}
+	else if ( addr >= 0xff30 )
+	{
+		// separate samples now (simplifies oscillator)
+		int index = (addr & 0x0f) * 2;
+		wave.wave [index] = data >> 4;
+		wave.wave [index + 1] = data & 0x0f;
+	}
+}
+
+int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr )
+{
+	// function now takes actual address, i.e. 0xFFXX
+	require( start_addr <= addr && addr <= end_addr );
+	
+	run_until( time );
+	
+	int data = regs [addr - start_addr];
+	
+	if ( addr == 0xff26 ) { // status
+		data &= 0xf0;
+		for ( int i = 0; i < osc_count; i++ ) {
+			const Gb_Osc& osc = *oscs [i];
+			if ( osc.enabled && (osc.length || !osc.length_enabled) )
+				data |= 1 << i;
+		}
+	}
+	
+	return data;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Apu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,91 @@
+
+// Nintendo Game Boy PAPU sound chip emulator
+
+// Gb_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef GB_APU_H
+#define GB_APU_H
+
+typedef long gb_time_t;     // clock cycle count
+typedef unsigned gb_addr_t; // 16-bit address
+
+#include "Gb_Oscs.h"
+
+class Gb_Apu {
+public:
+	Gb_Apu();
+	~Gb_Apu();
+	
+	// Overall volume of all oscillators, where 1.0 is full volume.
+	void volume( double );
+	
+	// Treble equalization (see notes.txt).
+	void treble_eq( const blip_eq_t& );
+	
+	// Reset oscillators and internal state.
+	void reset();
+	
+	// Assign all oscillator outputs to specified buffer(s). If buffer
+	// is NULL, silence all oscillators.
+	void output( Blip_Buffer* mono );
+	void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
+	
+	// Assign oscillator output to buffer(s). Valid indicies are 0 to
+	// osc_count - 1, which refer to Square 1, Square 2, Wave, and
+	// Noise, respectively. If buffer is NULL, silence oscillator.
+	enum { osc_count = 4 };
+	void osc_output( int index, Blip_Buffer* mono );
+	void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
+	
+	// Reads and writes at addr must satisfy start_addr <= addr <= end_addr
+	enum { start_addr = 0xff10 };
+	enum { end_addr   = 0xff3f };
+	enum { register_count = end_addr - start_addr + 1 };
+	
+	// Write 'data' to address at specified time. Previous writes and reads
+	// within the current frame must not have specified a later time.
+	void write_register( gb_time_t, gb_addr_t, int data );
+	
+	// Read from address at specified time. Previous writes and reads within
+	// the current frame must not have specified a later time.
+	int read_register( gb_time_t, gb_addr_t );
+	
+	// Run all oscillators up to specified time, end current time frame, then
+	// start a new frame at time 0. Return true if any oscillators added
+	// sound to one of the left/right buffers, false if they only added
+	// to the center buffer.
+	bool end_frame( gb_time_t );
+	
+	static void begin_debug_log();
+private:
+	// noncopyable
+	Gb_Apu( const Gb_Apu& );
+	Gb_Apu& operator = ( const Gb_Apu& );
+	
+	Gb_Osc*     oscs [osc_count];
+	gb_time_t   next_frame_time;
+	gb_time_t   last_time;
+	int         frame_count;
+	bool        stereo_found;
+	
+	Gb_Square   square1;
+	Gb_Square   square2;
+	Gb_Wave     wave;
+	Gb_Noise    noise;
+	Gb_Square::Synth square_synth; // shared between squares
+	BOOST::uint8_t regs [register_count];
+	
+	void run_until( gb_time_t );
+	friend class Gb_Apu_Reflector;
+};
+
+	inline void Gb_Apu::output( Blip_Buffer* mono ) {
+		output( mono, NULL, NULL );
+	}
+	
+	inline void Gb_Apu::osc_output( int index, Blip_Buffer* mono ) {
+		osc_output( index, mono, NULL, NULL );
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Cpu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,1117 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Gb_Cpu.h"
+
+#include <string.h>
+#include <limits.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Common instructions:
+//
+// 365880   FA      LD  A,IND16
+// 355863   20      JR  NZ
+// 313655   21      LD  HL,IMM
+// 274580   28      JR  Z
+// 252878   FE      CMP IMM
+// 230541   7E      LD  A,(HL)
+// 226209   2A      LD A,(HL+)
+// 217467   CD      CALL
+// 212034   C9      RET
+// 208376   CB      CB prefix
+//
+//  27486   CB 7E   BIT 7,(HL)
+//  15925   CB 76   BIT 6,(HL)
+//  13035   CB 19   RR  C
+//  11557   CB 7F   BIT 7,A
+//  10898   CB 37   SWAP A
+//  10208   CB 66   BIT 4,(HL)
+
+static void write_unmapped( Gbs_Emu*, gb_addr_t, int )
+{
+	check( false );
+}
+
+static int read_unmapped( Gbs_Emu*, gb_addr_t )
+{
+	check( false );
+	return 0;
+}
+
+Gb_Cpu::Gb_Cpu()
+{
+	rst_base = 0;
+	callback_data = NULL;
+	data_writer [page_count] = write_unmapped;
+	data_reader [page_count] = read_unmapped;
+	reset();
+}
+
+void Gb_Cpu::reset( const void* unmapped_code_page )
+{
+	interrupts_enabled = false;
+	remain_ = 0;
+	
+	r.pc = 0;
+	r.sp = 0;
+	r.flags = 0;
+	r.a = 0;
+	r.b = 0;
+	r.c = 0;
+	r.d = 0;
+	r.e = 0;
+	r.h = 0;
+	r.l = 0;
+	
+	for ( int i = 0; i < page_count + 1; i++ )
+		code_map [i] = (const uint8_t*) unmapped_code_page;
+	
+	map_memory( 0, 0x10000, read_unmapped, write_unmapped );
+}
+
+void Gb_Cpu::map_code( gb_addr_t start, unsigned long size, const void* data )
+{
+	// start end end must fall on page bounadries
+	require( start % page_size == 0 && size % page_size == 0 );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		code_map [first_page + i] = (uint8_t*) data + i * page_size;
+}
+
+void Gb_Cpu::map_memory( gb_addr_t start, unsigned long size, reader_t read, writer_t write )
+{
+	// start end end must fall on page bounadries
+	require( start % page_size == 0 && size % page_size == 0 );
+	
+	if ( !read )
+		read = read_unmapped;
+	if ( !write )
+		write = write_unmapped;
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; ) {
+		data_reader [first_page + i] = read;
+		data_writer [first_page + i] = write;
+	}
+}
+
+// Note: 'addr' is evaulated more than once in the following macros, so it
+// must not contain side-effects.
+
+#define READ( addr )        (data_reader [(addr) >> page_bits]( callback_data, addr ))
+#define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data ))
+
+#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [(addr) & (page_size - 1)])
+#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
+
+int Gb_Cpu::read( gb_addr_t addr ) {
+	return READ( addr );
+}
+
+BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) {
+	return (uint8_t*) &READ_PROG( addr );
+}
+
+void Gb_Cpu::write( gb_addr_t addr, int data ) {
+	WRITE( addr, data );
+}
+
+#ifndef GB_CPU_GLUE_ONLY
+
+const unsigned z_flag = 0x80;
+const unsigned n_flag = 0x40;
+const unsigned h_flag = 0x20;
+const unsigned c_flag = 0x10;
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+Gb_Cpu::result_t Gb_Cpu::run( long cycle_count )
+{
+	const int cycles_per_instruction = 4;
+	
+	remain_ = cycle_count + cycles_per_instruction;
+	
+	Gb_Cpu::result_t result = result_cycles;
+	
+#if BLARGG_CPU_POWERPC
+	const reader_t* data_reader = this->data_reader; // cache
+	const writer_t* data_writer = this->data_writer; // cache
+#endif
+	
+	union {
+		struct {
+#if BLARGG_BIG_ENDIAN
+			uint8_t b, c, d, e, h, l, unused, a;
+#   define R8( n ) (r8_ [n]) 
+#elif BLARGG_LITTLE_ENDIAN
+			uint8_t c, b, e, d, l, h, a, unused;
+#   define R8( n ) (r8_ [(n) ^ 1]) 
+#else
+#   error "Byte order of CPU must be known."
+#endif
+		} rg; // registers
+		struct {
+			BOOST::uint16_t bc, de, hl, unused; // pairs
+		} rp;
+		uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
+		BOOST::uint16_t r16 [4]; // indexed pairs
+	};
+	BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 );
+	
+	rg.a = r.a;
+	rg.b = r.b;
+	rg.c = r.c;
+	rg.d = r.d;
+	rg.e = r.e;
+	rg.h = r.h;
+	rg.l = r.l;
+	unsigned pc = r.pc;
+	unsigned sp = r.sp;
+	unsigned flags = r.flags;
+	
+	goto loop;
+	
+	unsigned data;
+	
+jr_taken:
+	pc += (BOOST::int8_t) data;
+inc_pc_loop:
+	pc++;
+loop:
+	
+	check( (unsigned) pc < 0x10000 );
+	check( (unsigned) sp < 0x10000 );
+	check( (flags & ~0xf0) == 0 );
+	
+	// Read opcode and first operand. Optimize if processor's byte order is known
+	// and non-portable constructs are allowed.
+#if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
+	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	pc++;
+	unsigned op = data >> 8;
+	data = (uint8_t) data;
+
+#elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
+	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	pc++;
+	unsigned op = (uint8_t) data;
+	data >>= 8;
+
+#else
+	unsigned op = READ_PROG( pc );
+	pc++;
+	data = READ_PROG( pc );
+	
+#endif
+	
+	if ( (remain_ -= cycles_per_instruction) <= 0 )
+		goto stop;
+	
+	switch ( op )
+	{
+
+// Most Common
+
+	case 0x20: // JR NZ
+		if ( !(flags & z_flag) )
+			goto jr_taken;
+		goto inc_pc_loop;
+	
+	case 0x21: // LD HL,IMM (common)
+		rp.hl = READ_PROG16( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x28: // JR Z
+		if ( flags & z_flag )
+			goto jr_taken;
+		goto inc_pc_loop;
+	
+	{
+		unsigned temp;
+		
+	case 0xF0: // LD A,(0xff00+imm)
+		temp = data + 0xff00;
+		pc++;
+		goto ld_a_ind_comm;
+	
+	case 0xF2: // LD A,(0xff00+C)
+		temp = rg.c + 0xff00;
+		goto ld_a_ind_comm;
+	
+	case 0x0A: // LD A,(BC)
+		temp = rp.bc;
+		goto ld_a_ind_comm;
+	
+	case 0x3A: // LD A,(HL-)
+		temp = rp.hl;
+		rp.hl = temp - 1;
+		goto ld_a_ind_comm;
+	
+	case 0x1A: // LD A,(DE)
+		temp = rp.de;
+		goto ld_a_ind_comm;
+	
+	case 0x2A: // LD A,(HL+) (common)
+		temp = rp.hl;
+		rp.hl = temp + 1;
+		goto ld_a_ind_comm;
+		
+	case 0xFA: // LD A,IND16 (common)
+		temp = READ_PROG16( pc );
+		pc += 2;
+	ld_a_ind_comm:
+		rg.a = READ( temp );
+		goto loop;
+	}
+	
+	case 0xBE: // CMP (HL)
+		data = READ( rp.hl );
+		goto cmp_comm;
+	
+	case 0xB8: // CMP B
+	case 0xB9: // CMP C
+	case 0xBA: // CMP D
+	case 0xBB: // CMP E
+	case 0xBC: // CMP H
+	case 0xBD: // CMP L
+		data = R8( op & 7 );
+		goto cmp_comm;
+	
+	case 0xFE: // CMP IMM
+		pc++;
+	cmp_comm:
+		op = rg.a;
+		data = op - data;
+	sub_set_flags:
+		flags = ((op & 15) - (data & 15)) & h_flag;
+		flags |= (data >> 4) & c_flag;
+		flags |= n_flag;
+		if ( data & 0xff )
+			goto loop;
+		flags |= z_flag;
+		goto loop;
+
+	case 0x46: // LD B,(HL)
+	case 0x4E: // LD C,(HL)
+	case 0x56: // LD D,(HL)
+	case 0x5E: // LD E,(HL)
+	case 0x66: // LD H,(HL)
+	case 0x6E: // LD L,(HL)
+	case 0x7E: // LD A,(HL)
+		R8( (op >> 3) & 7 ) = READ( rp.hl );
+		goto loop;
+	
+	case 0xC4: // CNZ (next-most-common)
+		pc += 2;
+		if ( flags & z_flag )
+			goto loop;
+	call:
+		pc -= 2;
+	case 0xCD: // CALL (most-common)
+		data = pc + 2;
+		pc = READ_PROG16( pc );
+	push:
+		sp--;
+		WRITE( sp, data >> 8 );
+		sp--;
+		WRITE( sp, data & 0xff );
+		goto loop;
+	
+	case 0xC8: // RNZ (next-most-common)
+		if ( !(flags & z_flag) )
+			goto loop;
+	case 0xC9: // RET (most common)
+	ret:
+		pc = READ( sp );
+		pc += 0x100 * READ( sp + 1 );
+		sp += 2;
+		goto loop;
+	
+	case 0x00: // NOP
+	case 0x40: // LD B,B
+	case 0x49: // LD C,C
+	case 0x52: // LD D,D
+	case 0x5B: // LD E,E
+	case 0x64: // LD H,H
+	case 0x6D: // LD L,L
+	case 0x7F: // LD A,A
+		goto loop;
+	
+// CB Instructions
+
+	case 0xCB:
+		pc++;
+		// now data is the opcode
+		switch ( data ) {
+			
+		{
+			int temp;
+			
+		case 0x46: // BIT b,(HL)
+		case 0x4E:
+		case 0x56:
+		case 0x5E:
+		case 0x66:
+		case 0x6E:
+		case 0x76:
+		case 0x7E:
+			temp = READ( rp.hl );
+			goto bit_comm;
+		
+		case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r
+		case 0x44: case 0x45: case 0x47: case 0x48:
+		case 0x49: case 0x4A: case 0x4B: case 0x4C:
+		case 0x4D: case 0x4F: case 0x50: case 0x51:
+		case 0x52: case 0x53: case 0x54: case 0x55:
+		case 0x57: case 0x58: case 0x59: case 0x5A:
+		case 0x5B: case 0x5C: case 0x5D: case 0x5F:
+		case 0x60: case 0x61: case 0x62: case 0x63:
+		case 0x64: case 0x65: case 0x67: case 0x68:
+		case 0x69: case 0x6A: case 0x6B: case 0x6C:
+		case 0x6D: case 0x6F: case 0x70: case 0x71:
+		case 0x72: case 0x73: case 0x74: case 0x75:
+		case 0x77: case 0x78: case 0x79: case 0x7A:
+		case 0x7B: case 0x7C: case 0x7D: case 0x7F:
+			temp = R8( data & 7 );
+		bit_comm:
+			int bit = (~data >> 3) & 7;
+			flags &= ~n_flag;
+			flags |= h_flag | z_flag;
+			flags ^= (temp << bit) & z_flag;
+			goto loop;
+		}
+		
+		case 0x86: // RES b,(HL)
+		case 0x8E:
+		case 0x96:
+		case 0x9E:
+		case 0xA6:
+		case 0xAE:
+		case 0xB6:
+		case 0xBE:
+		case 0xC6: // SET b,(HL)
+		case 0xCE:
+		case 0xD6:
+		case 0xDE:
+		case 0xE6:
+		case 0xEE:
+		case 0xF6:
+		case 0xFE: {
+			int temp = READ( rp.hl );
+			int bit = 1 << ((data >> 3) & 7);
+			temp &= ~bit;
+			if ( !(data & 0x40) )
+				bit = 0;
+			WRITE( rp.hl, temp | bit );
+			goto loop;
+		}
+		
+		case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r
+		case 0xC4: case 0xC5: case 0xC7: case 0xC8:
+		case 0xC9: case 0xCA: case 0xCB: case 0xCC:
+		case 0xCD: case 0xCF: case 0xD0: case 0xD1:
+		case 0xD2: case 0xD3: case 0xD4: case 0xD5:
+		case 0xD7: case 0xD8: case 0xD9: case 0xDA:
+		case 0xDB: case 0xDC: case 0xDD: case 0xDF:
+		case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+		case 0xE4: case 0xE5: case 0xE7: case 0xE8:
+		case 0xE9: case 0xEA: case 0xEB: case 0xEC:
+		case 0xED: case 0xEF: case 0xF0: case 0xF1:
+		case 0xF2: case 0xF3: case 0xF4: case 0xF5:
+		case 0xF7: case 0xF8: case 0xF9: case 0xFA:
+		case 0xFB: case 0xFC: case 0xFD: case 0xFF:
+			R8( data & 7 ) |= 1 << ((data >> 3) & 7);
+			goto loop;
+
+		case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r
+		case 0x84: case 0x85: case 0x87: case 0x88:
+		case 0x89: case 0x8A: case 0x8B: case 0x8C:
+		case 0x8D: case 0x8F: case 0x90: case 0x91:
+		case 0x92: case 0x93: case 0x94: case 0x95:
+		case 0x97: case 0x98: case 0x99: case 0x9A:
+		case 0x9B: case 0x9C: case 0x9D: case 0x9F:
+		case 0xA0: case 0xA1: case 0xA2: case 0xA3:
+		case 0xA4: case 0xA5: case 0xA7: case 0xA8:
+		case 0xA9: case 0xAA: case 0xAB: case 0xAC:
+		case 0xAD: case 0xAF: case 0xB0: case 0xB1:
+		case 0xB2: case 0xB3: case 0xB4: case 0xB5:
+		case 0xB7: case 0xB8: case 0xB9: case 0xBA:
+		case 0xBB: case 0xBC: case 0xBD: case 0xBF:
+			R8( data & 7 ) &= ~(1 << ((data >> 3) & 7));
+			goto loop;
+		
+		{
+			int temp;
+		case 0x36: // SWAP (HL)
+			temp = READ( rp.hl );
+			goto swap_comm;
+		
+		case 0x30: // SWAP B
+		case 0x31: // SWAP C
+		case 0x32: // SWAP D
+		case 0x33: // SWAP E
+		case 0x34: // SWAP H
+		case 0x35: // SWAP L
+		case 0x37: // SWAP A
+			temp = R8( data & 7 );
+		swap_comm:
+			op = (temp >> 4) | (temp << 4);
+			flags = 0;
+			goto shift_comm;
+		}
+		
+// Shift/Rotate
+
+		case 0x06: // RLC (HL)
+		case 0x16: // RL (HL)
+		case 0x26: // SLA (HL)
+			op = READ( rp.hl );
+			goto rl_comm;
+		
+		case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A
+		case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A
+		case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A
+			op = R8( data & 7 );
+			goto rl_comm;
+		
+		case 0x3E: // SRL (HL)
+			data += 0x10; // bump up to 0x4n to avoid preserving sign bit
+		case 0x1E: // RR (HL)
+		case 0x0E: // RRC (HL)
+		case 0x2E: // SRA (HL)
+			op = READ( rp.hl );
+			goto rr_comm;
+		
+		case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A
+			data += 0x10; // bump up to 0x4n
+		case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A
+		case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A
+		case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A
+			op = R8( data & 7 );
+			goto rr_comm;
+		
+	} // CB op
+	assert( false ); // unhandled CB op
+
+	case 0x07: // RLCA
+	case 0x17: // RLA
+		data = op;
+		op = rg.a;
+	rl_comm:
+		op <<= 1;
+		op |= ((data & flags) >> 4) & 1; // RL and carry is set
+		flags = (op >> 4) & c_flag; // C = bit shifted out
+		if ( data < 0x10 ) // RLC
+			op |= op >> 8;
+		// SLA doesn't fill lower bit
+		goto shift_comm;
+	
+	case 0x0F: // RRCA
+	case 0x1F: // RRA
+		data = op;
+		op = rg.a;
+	rr_comm:
+		op |= (data & flags) << 4; // RR and carry is set
+		flags = (op << 4) & c_flag; // C = bit shifted out
+		if ( data < 0x10 ) // RRC
+			op |= op << 8;
+		op >>= 1;
+		if ( data & 0x20 ) // SRA propagates sign bit
+			op |= (op << 1) & 0x80;
+	shift_comm:
+		data &= 7;
+		if ( !(op & 0xff) )
+			flags |= z_flag;
+		if ( data == 6 )
+			goto write_hl_op_ff;
+		R8( data ) = op;
+		goto loop;
+
+// Load
+
+	case 0x70: // LD (HL),B
+	case 0x71: // LD (HL),C
+	case 0x72: // LD (HL),D
+	case 0x73: // LD (HL),E
+	case 0x74: // LD (HL),H
+	case 0x75: // LD (HL),L
+	case 0x77: // LD (HL),A
+		op = R8( op & 7 );
+	write_hl_op_ff:
+		WRITE( rp.hl, op & 0xff );
+		goto loop;
+
+	case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r
+	case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F:
+	case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57:
+	case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F:
+	case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67:
+	case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F:
+	case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D:
+		R8( (op >> 3) & 7 ) = R8( op & 7 );
+		goto loop;
+
+	case 0x08: // LD IND16,SP
+		data = READ_PROG16( pc );
+		pc += 2;
+		WRITE( data, sp&0xff );
+		data++;
+		WRITE( data, sp >> 8 );
+		goto loop;
+	
+	case 0xF9: // LD SP,HL
+		sp = rp.hl;
+		goto loop;
+
+	case 0x31: // LD SP,IMM
+		sp = READ_PROG16( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x01: // LD BC,IMM
+	case 0x11: // LD DE,IMM
+		r16 [op >> 4] = READ_PROG16( pc );
+		pc += 2;
+		goto loop;
+	
+	{
+		unsigned temp;
+	case 0xE0: // LD (0xff00+imm),A
+		temp = data + 0xff00;
+		pc++;
+		goto write_data_rg_a;
+	
+	case 0xE2: // LD (0xff00+C),A
+		temp = rg.c + 0xff00;
+		goto write_data_rg_a;
+
+	case 0x32: // LD (HL-),A
+		temp = rp.hl;
+		rp.hl = temp - 1;
+		goto write_data_rg_a;
+	
+	case 0x02: // LD (BC),A
+		temp = rp.bc;
+		goto write_data_rg_a;
+	
+	case 0x12: // LD (DE),A
+		temp = rp.de;
+		goto write_data_rg_a;
+	
+	case 0x22: // LD (HL+),A
+		temp = rp.hl;
+		rp.hl = temp + 1;
+		goto write_data_rg_a;
+		
+	case 0xEA: // LD IND16,A (common)
+		temp = READ_PROG16( pc );
+		pc += 2;
+	write_data_rg_a:
+		WRITE( temp, rg.a );
+		goto loop;
+	}
+	
+	case 0x06: // LD B,IMM
+		rg.b = data;
+		goto inc_pc_loop;
+	
+	case 0x0E: // LD C,IMM
+		rg.c = data;
+		goto inc_pc_loop;
+	
+	case 0x16: // LD D,IMM
+		rg.d = data;
+		goto inc_pc_loop;
+	
+	case 0x1E: // LD E,IMM
+		rg.e = data;
+		goto inc_pc_loop;
+	
+	case 0x26: // LD H,IMM
+		rg.h = data;
+		goto inc_pc_loop;
+	
+	case 0x2E: // LD L,IMM
+		rg.l = data;
+		goto inc_pc_loop;
+	
+	case 0x36: // LD (HL),IMM
+		WRITE( rp.hl, data );
+		goto inc_pc_loop;
+	
+	case 0x3E: // LD A,IMM
+		rg.a = data;
+		goto inc_pc_loop;
+
+// Increment/Decrement
+
+	case 0x03: // INC BC
+	case 0x13: // INC DE
+	case 0x23: // INC HL
+		r16 [op >> 4]++;
+		goto loop;
+	
+	case 0x33: // INC SP
+		sp++;
+		goto loop;
+
+	case 0x0B: // DEC BC
+	case 0x1B: // DEC DE
+	case 0x2B: // DEC HL
+		r16 [op >> 4]--;
+		goto loop;
+	
+	case 0x3B: // DEC SP
+		sp--;
+		goto loop;
+	
+	case 0x34: // INC (HL)
+		op = rp.hl;
+		data = READ( op );
+		data++;
+		WRITE( op, data & 0xff );
+		goto inc_comm;
+	
+	case 0x04: // INC B
+	case 0x0C: // INC C (common)
+	case 0x14: // INC D
+	case 0x1C: // INC E
+	case 0x24: // INC H
+	case 0x2C: // INC L
+	case 0x3C: // INC A
+		op = (op >> 3) & 7;
+		R8( op ) = data = R8( op ) + 1;
+	inc_comm:
+		flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag);
+		goto loop;
+	
+	case 0x35: // DEC (HL)
+		op = rp.hl;
+		data = READ( op );
+		data--;
+		WRITE( op, data & 0xff );
+		goto dec_comm;
+	
+	case 0x05: // DEC B
+	case 0x0D: // DEC C
+	case 0x15: // DEC D
+	case 0x1D: // DEC E
+	case 0x25: // DEC H
+	case 0x2D: // DEC L
+	case 0x3D: // DEC A
+		op = (op >> 3) & 7;
+		data = R8( op ) - 1;
+		R8( op ) = data;
+	dec_comm:
+		flags = (flags & c_flag) | n_flag | (((data & 15) + 0x31) & h_flag);
+		if ( data & 0xff )
+			goto loop;
+		flags |= z_flag;
+		goto loop;
+
+// Add 16-bit
+
+	{
+		unsigned long temp; // need more than 16 bits for carry
+		unsigned prev;
+		
+	case 0xF8: // LD HL,SP+imm
+		temp = BOOST::int8_t (data) & 0xffff; // sign-extend to 16 bits
+		pc++;
+		flags = 0;
+		temp += sp;
+		prev = sp;
+		goto add_16_hl;
+	
+	case 0xE8: // ADD SP,IMM
+		temp = BOOST::int8_t (data) & 0xffff; // sign-extend to 16 bits
+		pc++;
+		flags = 0;
+		temp += sp;
+		prev = sp;
+		sp = temp;
+		goto add_16_comm;
+
+	case 0x39: // ADD HL,SP
+		temp = sp;
+		goto add_hl_comm;
+	
+	case 0x09: // ADD HL,BC
+	case 0x19: // ADD HL,DE
+	case 0x29: // ADD HL,HL
+		temp = r16 [op >> 4];
+	add_hl_comm:
+		prev = rp.hl;
+		temp += prev;
+		flags &= z_flag;
+	add_16_hl:
+		rp.hl = temp;
+	add_16_comm:
+		flags |= (temp >> 12) & c_flag;
+		flags |= (((temp & 0x0fff) - (prev & 0x0fff)) >> 7) & h_flag;
+		goto loop;
+	}
+	
+	case 0x86: // ADD (HL)
+		data = READ( rp.hl );
+		goto add_comm;
+	
+	case 0x80: // ADD B
+	case 0x81: // ADD C
+	case 0x82: // ADD D
+	case 0x83: // ADD E
+	case 0x84: // ADD H
+	case 0x85: // ADD L
+	case 0x87: // ADD A
+		data = R8( op & 7 );
+		goto add_comm;
+	
+	case 0xC6: // ADD IMM
+		pc++;
+	add_comm:
+		flags = rg.a;
+		data += flags;
+		flags = ((data & 15) - (flags & 15)) & h_flag;
+		flags |= (data >> 4) & c_flag;
+		rg.a = data;
+		if ( data & 0xff )
+			goto loop;
+		flags |= z_flag;
+		goto loop;
+
+// Add/Subtract
+
+	case 0x8E: // ADC (HL)
+		data = READ( rp.hl );
+		goto adc_comm;
+	
+	case 0x88: // ADC B
+	case 0x89: // ADC C
+	case 0x8A: // ADC D
+	case 0x8B: // ADC E
+	case 0x8C: // ADC H
+	case 0x8D: // ADC L
+	case 0x8F: // ADC A
+		data = R8( op & 7 );
+		goto adc_comm;
+	
+	case 0xCE: // ADC IMM
+		pc++;
+	adc_comm:
+		data += (flags >> 4) & 1;
+		data &= 0xff; // to do: does carry get set when sum + carry = 0x100?
+		goto add_comm;
+
+	case 0x96: // SUB (HL)
+		data = READ( rp.hl );
+		goto sub_comm;
+	
+	case 0x90: // SUB B
+	case 0x91: // SUB C
+	case 0x92: // SUB D
+	case 0x93: // SUB E
+	case 0x94: // SUB H
+	case 0x95: // SUB L
+	case 0x97: // SUB A
+		data = R8( op & 7 );
+		goto sub_comm;
+	
+	case 0xD6: // SUB IMM
+		pc++;
+	sub_comm:
+		op = rg.a;
+		data = op - data;
+		rg.a = data;
+		goto sub_set_flags;
+
+	case 0x9E: // SBC (HL)
+		data = READ( rp.hl );
+		goto sbc_comm;
+	
+	case 0x98: // SBC B
+	case 0x99: // SBC C
+	case 0x9A: // SBC D
+	case 0x9B: // SBC E
+	case 0x9C: // SBC H
+	case 0x9D: // SBC L
+	case 0x9F: // SBC A
+		data = R8( op & 7 );
+		goto sbc_comm;
+	
+	case 0xDE: // SBC IMM
+		pc++;
+	sbc_comm:
+		data += (flags >> 4) & 1;
+		data &= 0xff; // to do: does carry get set when sum + carry = 0x100?
+		goto sub_comm;
+
+// Logical
+
+	case 0xA0: // AND B
+	case 0xA1: // AND C
+	case 0xA2: // AND D
+	case 0xA3: // AND E
+	case 0xA4: // AND H
+	case 0xA5: // AND L
+		data = R8( op & 7 );
+		goto and_comm;
+	
+	case 0xA6: // AND (HL)
+		data = READ( rp.hl );
+		pc--;
+	case 0xE6: // AND IMM
+		pc++;
+	and_comm:
+		rg.a &= data;
+	case 0xA7: // AND A
+		flags = h_flag | (((rg.a - 1) >> 1) & z_flag);
+		goto loop;
+
+	case 0xB0: // OR B
+	case 0xB1: // OR C
+	case 0xB2: // OR D
+	case 0xB3: // OR E
+	case 0xB4: // OR H
+	case 0xB5: // OR L
+		data = R8( op & 7 );
+		goto or_comm;
+	
+	case 0xB6: // OR (HL)
+		data = READ( rp.hl );
+		pc--;
+	case 0xF6: // OR IMM
+		pc++;
+	or_comm:
+		rg.a |= data;
+	case 0xB7: // OR A
+		flags = ((rg.a - 1) >> 1) & z_flag;
+		goto loop;
+
+	case 0xA8: // XOR B
+	case 0xA9: // XOR C
+	case 0xAA: // XOR D
+	case 0xAB: // XOR E
+	case 0xAC: // XOR H
+	case 0xAD: // XOR L
+		data = R8( op & 7 );
+		goto xor_comm;
+	
+	case 0xAE: // XOR (HL)
+		data = READ( rp.hl );
+		pc--;
+	case 0xEE: // XOR IMM
+		pc++;
+	xor_comm:
+		data ^= rg.a;
+		rg.a = data;
+		data--;
+		flags = (data >> 1) & z_flag;
+		goto loop;
+	
+	case 0xAF: // XOR A
+		rg.a = 0;
+		flags = z_flag;
+		goto loop;
+
+// Stack
+
+	case 0xC1: // POP BC
+	case 0xD1: // POP DE
+	case 0xE1:{// POP HL (common)
+		int temp = READ( sp );
+		r16 [(op >> 4) & 3] = temp + 0x100 * READ( sp + 1 );
+		sp += 2;
+		goto loop;
+	}
+	
+	case 0xF1: // POP FA
+		rg.a = READ( sp );
+		flags = READ( sp + 1 ) & 0xf0;
+		sp += 2;
+		goto loop;
+
+	case 0xC5: // PUSH BC
+		data = rp.bc;
+		goto push;
+	
+	case 0xD5: // PUSH DE
+		data = rp.de;
+		goto push;
+	
+	case 0xE5: // PUSH HL
+		data = rp.hl;
+		goto push;
+	
+	case 0xF5: // PUSH FA
+		data = (flags << 8) | rg.a;
+		goto push;
+
+// Flow control
+
+	case 0xC7: case 0xCF: case 0xD7: case 0xDF:  // RST
+	case 0xE7: case 0xEF: case 0xF7: case 0xFF:
+		data = pc;
+		pc = (op & 0x38) + rst_base;
+		goto push;
+
+	case 0xCC: // CZ
+		pc += 2;
+		if ( flags & z_flag )
+			goto call;
+		goto loop;
+	
+	case 0xD4: // CNC
+		pc += 2;
+		if ( !(flags & c_flag) )
+			goto call;
+		goto loop;
+	
+	case 0xDC: // CC
+		pc += 2;
+		if ( flags & c_flag )
+			goto call;
+		goto loop;
+
+	case 0xD9: // RETI
+		Gb_Cpu::interrupts_enabled = 1;
+		goto ret;
+	
+	case 0xC0: // RZ
+		if ( !(flags & z_flag) )
+			goto ret;
+		goto loop;
+	
+	case 0xD0: // RNC
+		if ( !(flags & c_flag) )
+			goto ret;
+		goto loop;
+	
+	case 0xD8: // RC
+		if ( flags & c_flag )
+			goto ret;
+		goto loop;
+
+	case 0x18: // JR
+		goto jr_taken;
+	
+	case 0x30: // JR NC
+		if ( !(flags & c_flag) )
+			goto jr_taken;
+		goto inc_pc_loop;
+	
+	case 0x38: // JR C
+		if ( flags & c_flag )
+			goto jr_taken;
+		goto inc_pc_loop;
+	
+	case 0xE9: // JP_HL
+		pc = rp.hl;
+		goto loop;
+
+	case 0xC3: // JP (next-most-common)
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+	case 0xC2: // JP NZ
+		pc += 2;
+		if ( !(flags & z_flag) )
+			goto jp_taken;
+		goto loop;
+	
+	case 0xCA: // JP Z (most common)
+		pc += 2;
+		if ( !(flags & z_flag) )
+			goto loop;
+	jp_taken:
+		pc -= 2;
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+	case 0xD2: // JP NC
+		pc += 2;
+		if ( !(flags & c_flag) )
+			goto jp_taken;
+		goto loop;
+	
+	case 0xDA: // JP C
+		pc += 2;
+		if ( flags & c_flag )
+			goto jp_taken;
+		goto loop;
+
+// Flags
+
+	case 0x2F: // CPL
+		rg.a = ~rg.a;
+		flags |= n_flag | h_flag;
+		goto loop;
+
+	case 0x3F: // CCF
+		flags = (flags ^ c_flag) & ~(n_flag | h_flag);
+		goto loop;
+
+	case 0x37: // SCF
+		flags = (flags | c_flag) & ~(n_flag | h_flag);
+		goto loop;
+
+	case 0xF3: // DI
+		interrupts_enabled = 0;
+		goto loop;
+
+	case 0xFB: // EI
+		interrupts_enabled = 1;
+		goto loop;
+
+// Special
+
+	case 0xDD: case 0xD3: case 0xDB: case 0xE3: case 0xE4: // ?
+	case 0xEB: case 0xEC: case 0xF4: case 0xFD: case 0xFC:
+	case 0x10: // STOP
+	case 0x27: // DAA (I'll have to implement this eventually...)
+	case 0xBF:
+	case 0xED: // Z80 prefix
+		result = Gb_Cpu::result_badop;
+		goto stop;
+	
+	case 0x76: // HALT
+		result = Gb_Cpu::result_halt;
+		goto stop;
+	}
+	
+	assert( false ); // all opcodes should end with a goto
+	
+stop:
+	pc--;
+	
+	// copy state back
+	r.pc = pc;
+	r.sp = sp;
+	r.flags = flags;
+	r.a = rg.a;
+	r.b = rg.b;
+	r.c = rg.c;
+	r.d = rg.d;
+	r.e = rg.e;
+	r.h = rg.h;
+	r.l = rg.l;
+	
+	return result;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Cpu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,97 @@
+
+// Nintendo Game Boy CPU emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef GB_CPU_H
+#define GB_CPU_H
+
+#include "blargg_common.h"
+
+typedef unsigned gb_addr_t; // 16-bit address
+
+class Gbs_Emu;
+
+// Game Boy CPU emulator. Currently treats every instruction as taking 4 cycles.
+class Gb_Cpu {
+	typedef BOOST::uint8_t uint8_t;
+	enum { page_bits = 8 };
+	enum { page_count = 0x10000 >> page_bits };
+	const uint8_t* code_map [page_count + 1];
+	long remain_;
+public:
+	Gb_Cpu();
+	
+	// Set all registers to 0, unmap all memory, and map all code pages
+	// to unmapped_page.
+	void reset( const void* unmapped_page = NULL );
+	
+	// Memory read/write function types. Memory reader return value must be 0 to 255.
+	Gbs_Emu* callback_data; // passed to memory read/write functions
+	typedef int (*reader_t)( Gbs_Emu* callback_data, gb_addr_t );
+	typedef void (*writer_t)( Gbs_Emu* callback_data, gb_addr_t, int );
+	
+	// Memory mapping functions take a block of memory of specified 'start' address
+	// and 'size' in bytes. Both start address and size must be a multiple of page_size.
+	enum { page_size = 1L << page_bits };
+	
+	// Map code memory to 'code' (memory accessed via the program counter)
+	void map_code( gb_addr_t start, unsigned long size, const void* code );
+	
+	// Map data memory to read and write functions
+	void map_memory( gb_addr_t start, unsigned long size, reader_t, writer_t );
+	
+	// Access memory as the emulated CPU does.
+	int  read( gb_addr_t );
+	void write( gb_addr_t, int data );
+	uint8_t* get_code( gb_addr_t ); // for use in a debugger
+	
+	// Game Boy Z80 registers. *Not* kept updated during a call to run().
+	struct registers_t {
+		BOOST::uint16_t pc;
+		BOOST::uint16_t sp;
+		uint8_t flags;
+		uint8_t a;
+		uint8_t b;
+		uint8_t c;
+		uint8_t d;
+		uint8_t e;
+		uint8_t h;
+		uint8_t l;
+	} r;
+	
+	// Interrupt enable flag set by EI and cleared by DI.
+	bool interrupts_enabled;
+	
+	// Base address for RST vectors (normally 0).
+	gb_addr_t rst_base;
+	
+	// Reasons that run() returns
+	enum result_t {
+		result_cycles,      // Requested number of cycles (or more) were executed
+		result_halt,        // PC is at HALT instruction
+		result_badop        // PC is at bad (unimplemented) instruction
+	};
+	
+	// Run CPU for at least 'count' cycles, or until one of the above conditions
+	// arises. Return reason for stopping.
+	result_t run( long count );
+	
+	// Number of clock cycles remaining for current run() call.
+	long remain() const;
+	
+private:
+	// noncopyable
+	Gb_Cpu( const Gb_Cpu& );
+	Gb_Cpu& operator = ( const Gb_Cpu& );
+	
+	reader_t data_reader [page_count + 1]; // extra entry to catch overflow addresses
+	writer_t data_writer [page_count + 1];
+};
+
+	inline long Gb_Cpu::remain() const {
+		return remain_;
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Oscs.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,404 @@
+
+// Gb_Snd_Emu 0.1.3. http://www.slack.net/~ant/libs/
+
+#include "Gb_Apu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+const int trigger = 0x80;
+
+// Gb_Osc
+
+Gb_Osc::Gb_Osc()
+{
+	output = NULL;
+	outputs [0] = NULL;
+	outputs [1] = NULL;
+	outputs [2] = NULL;
+	outputs [3] = NULL;
+}
+
+void Gb_Osc::reset()
+{
+	delay = 0;
+	last_amp = 0;
+	period = 2048;
+	volume = 0;
+	frequency = 0;
+	length = 0;
+	enabled = false;
+	length_enabled = false;
+	output_select = 3;
+	output = outputs [output_select];
+}
+
+void Gb_Osc::clock_length()
+{
+	if ( length_enabled && length )
+		--length;
+}
+
+void Gb_Osc::write_register( int reg, int value )
+{
+	if ( reg == 4 )
+		length_enabled = value & 0x40;
+}
+
+// Gb_Env
+
+void Gb_Env::reset()
+{
+	env_period = 0;
+	env_dir = 0;
+	env_delay = 0;
+	new_env_period = 0;
+	new_env_dir = 0;
+	new_volume = 0;
+	Gb_Osc::reset();
+}
+
+Gb_Env::Gb_Env() {
+}
+
+void Gb_Env::clock_envelope()
+{
+	if ( env_delay && !--env_delay ) {
+		env_delay = env_period;
+		if ( env_dir ) {
+			if ( volume < 15 )
+				++volume;
+		}
+		else if ( volume > 0 ) {
+			--volume;
+		}
+	}
+}
+
+void Gb_Env::write_register( int reg, int value )
+{
+	if ( reg == 2 ) {
+		new_env_period = value & 7;
+		new_env_dir = value & 8;
+		new_volume = value >> 4;
+		if ( value == 0 && volume )
+			// to do: find correct behavior
+			volume = 0;
+		//enabled = new_volume != 0;
+		enabled = true;
+	}
+	else if ( reg == 4 && (value & trigger) ) {
+		env_period = new_env_period;
+		env_delay = new_env_period;
+		env_dir = new_env_dir;
+		volume = new_volume;
+	}
+	Gb_Osc::write_register( reg, value );
+}
+
+// Gb_Square
+
+void Gb_Square::reset()
+{
+	phase = 1;
+	duty = 1;
+	
+	sweep_period = 0;
+	sweep_delay = 0;
+	sweep_shift = 0;
+	sweep_dir = 0;
+	sweep_freq = 0;
+	
+	Gb_Env::reset();
+}
+
+Gb_Square::Gb_Square()
+{
+	has_sweep = false;
+}
+
+void Gb_Square::clock_sweep()
+{
+	if ( sweep_period && sweep_delay && !--sweep_delay ) {
+		sweep_delay = sweep_period;
+		frequency = sweep_freq;
+		period = (2048 - frequency) * 4;
+		
+		int offset = sweep_freq >> sweep_shift;
+		if ( sweep_dir )
+			offset = -offset;
+		sweep_freq += offset;
+		if ( sweep_freq < 0 || sweep_freq >= 2048 ) {
+			sweep_delay = 0;
+			sweep_freq = 2048; // stop sound output
+		}
+	}
+}
+
+void Gb_Square::write_register( int reg, int value )
+{
+	switch ( reg ) {
+		case 0:
+			sweep_period = (value >> 4) & 3;
+			sweep_shift = value & 7;
+			sweep_dir = value & 0x08;
+			break;
+		
+		case 1:
+			length = 64 - (value & 0x3f);
+			duty = (value >> 5) & 6; // duty = { 1, 2, 4, 6 }
+			if ( !duty )
+				duty = 1;
+			break;
+		
+		case 3:
+			frequency = (frequency & ~0xFF) + value;
+			break;
+		
+		case 4:
+			frequency = (value & 7) * 0x100 + (frequency & 0xFF);
+			if ( value & trigger ) {
+				sweep_freq = frequency;
+				if ( has_sweep && sweep_period && sweep_shift ) {
+					sweep_delay = 1;
+					clock_sweep();
+					sweep_delay = sweep_period;
+				}
+				enabled = true;
+			}
+			break;
+	}
+	
+	period = (2048 - frequency) * 4;
+	
+	Gb_Env::write_register( reg, value );
+}
+
+void Gb_Square::run( gb_time_t time, gb_time_t end_time )
+{
+	if ( !enabled || (!length && length_enabled) || !volume || sweep_freq == 2048 ) {
+		if ( last_amp ) {
+			synth->offset( time, -last_amp, output );
+			last_amp = 0;
+		}
+		delay = 0;
+	}
+	else
+	{
+		int amp = (phase < duty) ? volume : -volume;
+		if ( amp != last_amp ) {
+			synth->offset( time, amp - last_amp, output );
+			last_amp = amp;
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			Blip_Buffer* const output = this->output;
+			const int duty = this->duty;
+			int phase = this->phase;
+			amp *= 2;
+			do {
+				phase = (phase + 1) & 7;
+				if ( phase == 0 || phase == duty ) {
+					amp = -amp;
+					synth->offset_inline( time, amp, output );
+				}
+				time += period;
+			}
+			while ( time < end_time );
+			
+			this->phase = phase;
+			last_amp = amp >> 1;
+		}
+		delay = time - end_time;
+	}
+}
+
+
+// Gb_Wave
+
+void Gb_Wave::reset()
+{
+	volume_shift = 0;
+	wave_pos = 0;
+	memset( wave, 0, sizeof wave );
+	Gb_Osc::reset();
+}
+
+Gb_Wave::Gb_Wave() {
+}
+
+void Gb_Wave::write_register( int reg, int value )
+{
+	switch ( reg ) {
+		case 0:
+			enabled = value & 0x80;
+			break;
+		
+		case 1:
+			length = 256 - value;
+			break;
+		
+		case 2:
+			volume = ((value >> 5) & 3);
+			volume_shift = (volume - 1) & 7; // silence = 7
+			break;
+		
+		case 3:
+			frequency = (frequency & ~0xFF) + value;
+			break;
+		
+		case 4:
+			frequency = (value & 7) * 0x100 + (frequency & 0xFF);
+			//if ( value & trigger )
+			//  wave_pos = 0;
+			break;
+		
+	}
+	
+	period = (2048 - frequency) * 2;
+	
+	Gb_Osc::write_register( reg, value );
+}
+
+void Gb_Wave::run( gb_time_t time, gb_time_t end_time )
+{
+	if ( !enabled || (!length && length_enabled) || !volume ) {
+		if ( last_amp ) {
+			synth.offset( time, -last_amp, output );
+			last_amp = 0;
+		}
+		delay = 0;
+	}
+	else
+	{
+		// wave data or shift may have changed
+		int diff = (wave [wave_pos] >> volume_shift) * 2 - last_amp;
+		if ( diff ) {
+			last_amp += diff;
+			synth.offset( time, diff, output );
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+		 	unsigned wave_pos = this->wave_pos;
+		 	
+			do {
+				wave_pos = (wave_pos + 1) % wave_size;
+				int amp = (wave [wave_pos] >> volume_shift) * 2;
+				int diff = amp - last_amp;
+				if ( diff ) {
+					last_amp = amp;
+					synth.offset_inline( time, diff, output );
+				}
+				time += period;
+			}
+			while ( time < end_time );
+			
+			this->wave_pos = wave_pos;
+		}
+		delay = time - end_time;
+	}
+}
+
+
+// Gb_Noise
+
+void Gb_Noise::reset()
+{
+	bits = 1;
+	tap = 14;
+	Gb_Env::reset();
+}
+
+Gb_Noise::Gb_Noise() {
+}
+
+void Gb_Noise::write_register( int reg, int value )
+{
+	if ( reg == 1 ) {
+		length = 64 - (value & 0x3f);
+	}
+	else if ( reg == 3 ) {
+		tap = 14 - (value & 8);
+		// noise formula and frequency tested against Metroid 2 and Zelda LA
+		int divisor = (value & 7) * 16;
+		if ( !divisor )
+			divisor = 8;
+		period = divisor << (value >> 4);
+	}
+	else if ( reg == 4 && value & trigger ) {
+		bits = ~0u;
+	}
+	
+	Gb_Env::write_register( reg, value );
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Gb_Noise::run( gb_time_t time, gb_time_t end_time )
+{
+	if ( !enabled || (!length && length_enabled) || !volume ) {
+		if ( last_amp ) {
+			synth.offset( time, -last_amp, output );
+			last_amp = 0;
+		}
+		delay = 0;
+	}
+	else
+	{
+		int amp = bits & 1 ? -volume : volume;
+		if ( amp != last_amp ) {
+			synth.offset( time, amp - last_amp, output );
+			last_amp = amp;
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			Blip_Buffer* const output = this->output;
+			// keep parallel resampled time to eliminate multiplication in the loop
+			const Blip_Buffer::resampled_time_t resampled_period =
+					output->resampled_duration( period );
+			Blip_Buffer::resampled_time_t resampled_time = output->resampled_time( time );
+			const unsigned mask = ~(1u << tap);
+			unsigned bits = this->bits;
+			amp *= 2;
+			
+			do {
+				unsigned feedback = bits;
+				bits >>= 1;
+				feedback = 1 & (feedback ^ bits);
+				time += period;
+				bits = (feedback << tap) | (bits & mask);
+				// feedback just happens to be true only when the level needs to change
+				// (the previous and current bits are different)
+				if ( feedback ) {
+					amp = -amp;
+					synth.offset_resampled( resampled_time, amp, output );
+				}
+				resampled_time += resampled_period;
+			}
+			while ( time < end_time );
+			
+			this->bits = bits;
+			last_amp = amp >> 1;
+		}
+		delay = time - end_time;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Oscs.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,97 @@
+
+// Private oscillators used by Gb_Apu
+
+// Gb_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef GB_OSCS_H
+#define GB_OSCS_H
+
+#include "Blip_Buffer.h"
+
+struct Gb_Osc {
+	Blip_Buffer* outputs [4]; // NULL, right, left, center
+	Blip_Buffer* output;
+	int output_select;
+	
+	int delay;
+	int last_amp;
+	int period;
+	int volume;
+	int frequency;
+	int length;
+	bool enabled;
+	bool length_enabled;
+	
+	Gb_Osc();
+	
+	void clock_length();
+	void reset();
+	virtual void run( gb_time_t begin, gb_time_t end ) = 0;
+	virtual void write_register( int reg, int value );
+};
+
+struct Gb_Env : Gb_Osc {
+	int env_period;
+	int env_dir;
+	int env_delay;
+	int new_env_period;
+	int new_env_dir;
+	int new_volume;
+	
+	Gb_Env();
+	void reset();
+	void clock_envelope();
+	void write_register( int, int );
+};
+
+struct Gb_Square : Gb_Env {
+	int phase;
+	int duty;
+	
+	int sweep_period;
+	int sweep_delay;
+	int sweep_shift;
+	int sweep_dir;
+	int sweep_freq;
+	bool has_sweep;
+	
+	typedef Blip_Synth<blip_good_quality,15 * 2> Synth;
+	const Synth* synth;
+	
+	Gb_Square();
+	void reset();
+	void run( gb_time_t, gb_time_t );
+	void write_register( int, int );
+	void clock_sweep();
+};
+
+struct Gb_Wave : Gb_Osc {
+	int volume_shift;
+	unsigned wave_pos;
+	enum { wave_size = 32 };
+	BOOST::uint8_t wave [wave_size];
+	
+	typedef Blip_Synth<blip_med_quality,15 * 2> Synth;
+	Synth synth;
+	
+	Gb_Wave();
+	void reset();
+	void run( gb_time_t, gb_time_t );
+	void write_register( int, int );
+};
+
+struct Gb_Noise : Gb_Env {
+	unsigned bits;
+	int tap;
+	
+	typedef Blip_Synth<blip_med_quality,15 * 2> Synth;
+	Synth synth;
+	
+	Gb_Noise();
+	void reset();
+	void run( gb_time_t, gb_time_t );
+	void write_register( int, int );
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gbs_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,352 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Gbs_Emu.h"
+
+#include <string.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+const long clock_rate = 4194304;
+const long bank_size = 0x4000;
+const gb_addr_t ram_addr = 0xa000;
+const gb_addr_t halt_addr = 0x9eff;
+static BOOST::uint8_t unmapped_code [Gb_Cpu::page_size];
+
+// RAM
+
+int Gbs_Emu::read_ram( Gbs_Emu* emu, gb_addr_t addr )
+{
+	return emu->ram [addr - ram_addr];
+}
+
+void Gbs_Emu::write_ram( Gbs_Emu* emu, gb_addr_t addr, int data )
+{
+	emu->ram [addr - ram_addr] = data;
+}
+
+// Unmapped
+
+int Gbs_Emu::read_unmapped( Gbs_Emu*, gb_addr_t addr )
+{
+	dprintf( "Read from unmapped memory $%.4x\n", (unsigned) addr );
+	return 0xff; // open bus value (probably due to pull-up resistors)
+}
+
+void Gbs_Emu::write_unmapped( Gbs_Emu*, gb_addr_t addr, int )
+{
+	dprintf( "Wrote to unmapped memory $%.4x\n", (unsigned) addr );
+}
+
+// ROM
+
+int Gbs_Emu::read_rom( Gbs_Emu* emu, gb_addr_t addr )
+{
+	return emu->rom [addr];
+}
+
+int Gbs_Emu::read_bank( Gbs_Emu* emu, gb_addr_t addr )
+{
+	return emu->rom_bank [addr & (bank_size - 1)];
+}
+
+void Gbs_Emu::set_bank( int n )
+{
+	if ( n >= bank_count ) {
+		n = 0;
+		dprintf( "Set to non-existent bank %d\n", (int) n );
+	}
+	if ( n == 0 && bank_count > 1 )
+		dprintf( "Selected ROM bank 0\n" );
+	rom_bank = &rom [n * bank_size];
+	cpu.map_code( bank_size, bank_size, rom_bank );
+}
+
+void Gbs_Emu::write_rom( Gbs_Emu* emu, gb_addr_t addr, int data )
+{
+	if ( unsigned (addr - 0x2000) < 0x2000 )
+		emu->set_bank( data & 0x1f );
+}
+
+// I/O: Timer, APU
+
+void Gbs_Emu::set_timer( int modulo, int rate )
+{
+	if ( timer_mode )
+		play_period = gb_time_t (256 - modulo) << (((rate - 1) & 3) * 2 + 4 - double_speed);
+}
+
+inline gb_time_t Gbs_Emu::clock() const
+{
+	return cpu_time - cpu.remain();
+}
+	
+int Gbs_Emu::read_io( Gbs_Emu* emu, gb_addr_t addr )
+{
+	// hi_page is accessed most
+	if ( addr >= 0xff80 )
+		return emu->hi_page [addr & 0xff];
+	
+	if ( unsigned (addr - Gb_Apu::start_addr) <= Gb_Apu::register_count )
+		return emu->apu.read_register( emu->clock(), addr );
+	
+	if ( addr == 0xff00 )
+		return 0; // joypad
+	
+	dprintf( "Unhandled I/O read 0x%4x\n", (unsigned) addr );
+	
+	return 0xff;
+}
+
+void Gbs_Emu::write_io( Gbs_Emu* emu, gb_addr_t addr, int data )
+{
+	// apu is accessed most
+	if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) {
+		emu->apu.write_register( emu->clock(), addr, data );
+	}
+	else {
+		emu->hi_page [addr & 0xff] = data;
+		
+		if ( addr == 0xff06 || addr == 0xff07 )
+			emu->set_timer( emu->hi_page [6], emu->hi_page [7] );
+		
+		if ( addr == 0xffff )
+			dprintf( "Wrote interrupt mask\n" );
+	}
+}
+
+Gbs_Emu::Gbs_Emu( double gain )
+{
+	rom = NULL;
+	
+	apu.volume( gain );
+	
+	// to do: decide on equalization parameters
+	set_equalizer( equalizer_t( -32, 8000, 90 ) );
+	
+ 	// unmapped code is all HALT instructions
+	memset( unmapped_code, 0x76, sizeof unmapped_code );
+	
+	// cpu
+	cpu.callback_data = this;
+	cpu.reset( unmapped_code );
+	cpu.map_memory( 0x0000, 0x4000, read_rom, write_rom );
+	cpu.map_memory( 0x4000, 0x4000, read_bank, write_rom );
+	cpu.map_memory( 0x8000, 0x8000, read_unmapped, write_unmapped );
+	cpu.map_memory( ram_addr, 0x4000, read_ram, write_ram );
+	cpu.map_code(   ram_addr, 0x4000, ram );
+	cpu.map_code(   0xff00, 0x0100, hi_page );
+	cpu.map_memory( 0xff00, 0x0100, read_io, write_io );
+}
+
+Gbs_Emu::~Gbs_Emu()
+{
+	unload();
+}
+
+void Gbs_Emu::unload()
+{
+	delete [] rom;
+	rom = NULL;
+	cpu.r.pc = halt_addr;
+}
+
+void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+	apu.osc_output( i, c, l, r );
+}
+
+void Gbs_Emu::update_eq( blip_eq_t const& eq )
+{
+	apu.treble_eq( eq );
+}
+
+blargg_err_t Gbs_Emu::load( const header_t& h, Emu_Reader& in )
+{
+	unload();
+	
+	// check compatibility
+	if ( 0 != memcmp( h.tag, "GBS", 3 ) )
+		return "Not a GBS file";
+	if ( h.vers != 1 )
+		return "Unsupported GBS format";
+	
+	// gather relevant fields
+	load_addr = get_le16( h.load_addr );
+	init_addr = get_le16( h.init_addr );
+	play_addr = get_le16( h.play_addr );
+	stack_ptr = get_le16( h.stack_ptr );
+	double_speed = (h.timer_mode & 0x80) != 0;
+	timer_modulo_init = h.timer_modulo;
+	timer_mode = h.timer_mode;
+	if ( !(timer_mode & 0x04) )
+		timer_mode = 0; // using vbl
+	
+	#ifndef NDEBUG
+	{
+		if ( h.timer_mode & 0x78 )
+			dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) h.timer_mode );
+		
+		if ( load_addr < 0x400 || load_addr >= 0x8000 ||
+				init_addr < 0x400 || init_addr >= 0x8000 ||
+				play_addr < 0x400 || play_addr >= 0x8000 )
+			dprintf( "Load/init/play address violates GBS spec.\n" );
+	}
+	#endif
+	
+	// rom
+	bank_count = (load_addr + in.remain() + bank_size - 1) / bank_size;
+	long rom_size = bank_count * bank_size;
+	rom = new BOOST::uint8_t [rom_size];
+	if ( !rom )
+		return "Out of memory";
+	memset( rom, 0, rom_size );
+	blargg_err_t err = in.read( &rom [load_addr], in.remain() );
+	if ( err ) {
+		unload();
+		return err;
+	}
+	
+	// cpu
+	cpu.rst_base = load_addr;
+	cpu.map_code( 0x0000, 0x4000, rom );
+	
+	voice_count_ = Gb_Apu::osc_count;
+	track_count_ = h.track_count;
+	
+	return setup_buffer( clock_rate );
+}
+
+const char** Gbs_Emu::voice_names() const
+{
+	static const char* names [] = { "Square 1", "Square 2", "Wave", "Noise" };
+	return names;
+}
+
+// Emulation
+
+static const BOOST::uint8_t sound_data [Gb_Apu::register_count] = {
+	0x80, 0xbf, 0x00, 0x00, 0xbf, // square 1
+	0x00, 0x3f, 0x00, 0x00, 0xbf, // square 2
+	0x7f, 0xff, 0x9f, 0x00, 0xbf, // wave
+	0x00, 0xff, 0x00, 0x00, 0xbf, // noise
+	
+	0x77, 0xf3, 0xf1, // vin/volume, status, power mode
+	
+	0, 0, 0, 0, 0, 0, 0, 0, 0, // unused
+	
+	0xac, 0xdd, 0xda, 0x48, 0x36, 0x02, 0xcf, 0x16, // waveform data
+	0x2c, 0x04, 0xe5, 0x2c, 0xac, 0xdd, 0xda, 0x48
+};
+
+void Gbs_Emu::cpu_jsr( gb_addr_t addr )
+{
+	cpu.write( --cpu.r.sp, cpu.r.pc >> 8 );
+	cpu.write( --cpu.r.sp, cpu.r.pc&0xff );
+	cpu.r.pc = addr;
+}
+
+blargg_err_t Gbs_Emu::start_track( int track_index )
+{
+	require( rom ); // file must be loaded
+	require( (unsigned) track_index < track_count() );
+	
+	starting_track();
+	
+	apu.reset();
+	
+	memset( ram, 0, sizeof ram );
+	memset( hi_page, 0, sizeof hi_page );
+	
+	// configure hardware
+	set_bank( bank_count > 1 );
+	for ( int i = 0; i < sizeof sound_data; i++ )
+		apu.write_register( 0, i + apu.start_addr, sound_data [i] );
+	play_period = 70224; // 59.73 Hz
+	set_timer( timer_modulo_init, timer_mode ); // ignored if using vbl
+	next_play = play_period;
+	
+	// set up init call
+	cpu.r.a = track_index;
+	cpu.r.b = 0;
+	cpu.r.c = 0;
+	cpu.r.d = 0;
+	cpu.r.e = 0;
+	cpu.r.h = 0;
+	cpu.r.l = 0;
+	cpu.r.flags = 0;
+	cpu.r.pc = halt_addr;
+	cpu.r.sp = stack_ptr;
+	cpu_jsr( init_addr );
+	
+	return blargg_success;
+}
+
+blip_time_t Gbs_Emu::run( int msec, bool* added_stereo )
+{
+	require( rom ); // file must be loaded
+	
+	gb_time_t duration = clock_rate * (1.0 / 1000.0) * msec;
+	cpu_time = 0;
+	while ( cpu_time < duration )
+	{
+		// check for idle cpu
+		if ( cpu.r.pc == halt_addr )
+		{
+			if ( next_play > duration ) {
+				cpu_time = duration;
+				break;
+			}
+			
+			if ( cpu_time < next_play )
+				cpu_time = next_play;
+			next_play += play_period;
+			cpu_jsr( play_addr );
+		}
+		
+		long count = duration - cpu_time;
+		cpu_time = duration;
+		Gb_Cpu::result_t result = cpu.run( count );
+		cpu_time -= cpu.remain();
+		
+		if ( (result == Gb_Cpu::result_halt && cpu.r.pc != halt_addr) ||
+				result == Gb_Cpu::result_badop )
+		{
+			if ( result == Gb_Cpu::result_halt && cpu.r.pc < cpu.page_size )
+			{
+				dprintf( "PC wrapped around\n" );
+			}
+			else
+			{
+				dprintf( "Bad opcode $%.2x at $%.4x\n",
+						(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
+				return 0; // error
+			}
+		}
+	}
+	
+	// end time frame
+	
+	next_play -= cpu_time;
+	if ( next_play < 0 ) // could go negative if routine is taking too long to return
+		next_play = 0;
+	
+	if ( apu.end_frame( cpu_time ) && added_stereo )
+		*added_stereo = true;
+	
+	return cpu_time;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gbs_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,95 @@
+
+// Game Boy GBS-format game music file emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef GBS_EMU_H
+#define GBS_EMU_H
+
+#include "Classic_Emu.h"
+#include "Gb_Apu.h"
+#include "Gb_Cpu.h"
+
+class Gbs_Emu : public Classic_Emu {
+public:
+	// Sets internal gain, where 1.0 results in almost no clamping. Default gain
+	// roughly matches volume of other emulators.
+	Gbs_Emu( double gain = 1.3 );
+	~Gbs_Emu();
+	
+	struct header_t {
+		char tag [3];
+		byte vers;
+		byte track_count;
+		byte first_track;
+		byte load_addr [2];
+		byte init_addr [2];
+		byte play_addr [2];
+		byte stack_ptr [2];
+		byte timer_modulo;
+		byte timer_mode;
+		char game [32];
+		char author [32];
+		char copyright [32];
+		
+		enum { song = 0 }; // no song titles
+	};
+	BOOST_STATIC_ASSERT( sizeof (header_t) == 112 );
+	
+	// Load GBS, given its header and reader for remaining data
+	blargg_err_t load( const header_t&, Emu_Reader& );
+	
+	const char** voice_names() const;
+	blargg_err_t start_track( int );
+	
+
+// End of public interface
+protected:
+	void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+	void update_eq( blip_eq_t const& );
+	blip_time_t run( int, bool* );
+private:
+	// rom
+	const byte* rom_bank;
+	byte* rom;
+	void unload();
+	int bank_count;
+	void set_bank( int );
+	static void write_rom( Gbs_Emu*, gb_addr_t, int );
+	static int read_rom( Gbs_Emu*, gb_addr_t );
+	static int read_bank( Gbs_Emu*, gb_addr_t );
+	
+	// state
+	gb_addr_t load_addr;
+	gb_addr_t init_addr;
+	gb_addr_t play_addr;
+	gb_addr_t stack_ptr;
+	int timer_modulo_init;
+	int timer_mode;
+	
+	// timer
+	gb_time_t cpu_time;
+	gb_time_t play_period;
+	gb_time_t next_play;
+	int double_speed;
+	
+	// hardware
+	Gb_Apu apu;
+	byte hi_page [0x100];
+	void set_timer( int tma, int tmc );
+	static int read_io( Gbs_Emu*, gb_addr_t );
+	static void write_io( Gbs_Emu*, gb_addr_t, int );
+	static int read_unmapped( Gbs_Emu*, gb_addr_t );
+	static void write_unmapped( Gbs_Emu*, gb_addr_t, int );
+	
+	// cpu and ram
+	Gb_Cpu cpu;
+	void cpu_jsr( gb_addr_t );
+	gb_time_t clock() const;
+	byte ram [0x4000];
+	static int read_ram( Gbs_Emu*, gb_addr_t );
+	static void write_ram( Gbs_Emu*, gb_addr_t, int );
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gym_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,441 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Gym_Emu.h"
+
+#include "ym2612.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+const long base_clock = 53700300;
+const long clock_rate = base_clock / 15;
+
+Gym_Emu::Gym_Emu()
+{
+	data = NULL;
+	pos = NULL;
+	mem = NULL;
+	pairs_per_frame = 0;
+}
+
+Gym_Emu::~Gym_Emu()
+{
+	unload();
+}
+
+void Gym_Emu::unload()
+{
+	delete [] mem;
+	mem = NULL;
+	data = NULL;
+	pos = NULL;
+	track_ended_ = false;
+}
+
+blargg_err_t Gym_Emu::init( long sample_rate, double gain, double oversample_ )
+{
+	require( oversample_ <= 4.0 ); 
+	
+	blip_eq_t eq( -32, 8000, sample_rate );
+	apu.treble_eq( eq );
+	apu.volume( 0.27 * gain );
+	dac_synth.treble_eq( eq );
+	dac_synth.volume( 0.25 * gain );
+	oversample = resampler.time_ratio( oversample_, 0.990, gain );
+
+	pairs_per_frame = sample_rate / 60;
+	oversamples_per_frame = int (pairs_per_frame * oversample) * 2 + 2;
+	clocks_per_sample = (double) clock_rate / sample_rate;
+	
+	BLARGG_RETURN_ERR( resampler.buffer_size( oversamples_per_frame + 256 ) );
+	
+	BLARGG_RETURN_ERR( blip_buf.sample_rate( sample_rate, 1000 / 30 ) );
+	
+	BLARGG_RETURN_ERR( fm.set_rate( sample_rate * oversample, base_clock / 7 ) );
+	
+	blip_buf.clock_rate( clock_rate );
+	
+	return blargg_success;
+}
+
+void Gym_Emu::mute_voices( int mask )
+{
+	fm.mute_voices( mask );
+	dac_disabled = mask & 0x40;
+	apu.output( (mask & 0x80) ? NULL : &blip_buf );
+}
+
+const char** Gym_Emu::voice_names() const
+{
+	static const char* names [] = {
+		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "SN76489"
+	};
+	return names;
+}
+
+static blargg_err_t check_header( const Gym_Emu::header_t& h, int* data_offset = NULL )
+{
+	if ( memcmp( h.tag, "GYMX", 4 ) == 0 )
+	{
+		if ( memcmp( h.packed, "\0\0\0\0", 4 ) != 0 )
+			return "Packed GYM file not supported";
+		
+		if ( data_offset )
+			*data_offset = sizeof h;
+	}
+	else if ( h.tag [0] != 0 && h.tag [0] != 1 ) {
+		// not a headerless GYM
+		// to do: more thorough check, or just require a damn header
+		return "Not a GYM file";
+	}
+	
+	return blargg_success;
+}
+
+blargg_err_t Gym_Emu::load_( const void* file, long data_offset, long file_size )
+{
+	require( pairs_per_frame );
+	
+	data = (const byte*) file + data_offset;
+	data_end = (const byte*) file + file_size;
+	
+	loop_begin = NULL;
+	loop_offset = 0;
+	if ( data_offset )
+	{
+		const header_t& h = *(header_t*) file;
+		loop_offset =
+				h.loop [3] * 0x1000000L +
+				h.loop [2] * 0x10000L +
+				h.loop [1] * 0x100L +
+				h.loop [0];
+	}
+	
+	track_count_ = 1;
+	voice_count_ = 8;
+	mute_voices( 0 );
+	
+	return blargg_success;
+}
+
+blargg_err_t Gym_Emu::load( const void* file, long file_size )
+{
+	unload();
+	
+	if ( file_size < sizeof (header_t) )
+		return "Not a GYM file";
+	
+	int data_offset = 0;
+	BLARGG_RETURN_ERR( check_header( *(header_t*) file, &data_offset ) );
+	
+	return load_( file, data_offset, file_size );
+}
+
+blargg_err_t Gym_Emu::load( const header_t& h, Emu_Reader& in )
+{
+	unload();
+	
+	int data_offset = 0;
+	BLARGG_RETURN_ERR( check_header( h, &data_offset ) );
+	
+	long file_size = sizeof h + in.remain();
+	mem = new byte [file_size];
+	if ( !mem )
+		return "Out of memory";
+	memcpy( mem, &h, sizeof h );
+	BLARGG_RETURN_ERR( in.read( mem + sizeof h, file_size - sizeof h ) );
+	
+	return load_( mem, data_offset, file_size );
+}
+
+int Gym_Emu::track_length() const
+{
+	if ( loop_offset || loop_begin )
+		return 0;
+	
+	long time = 0; // 1/60 sec frames
+	const byte* p = data;
+	while ( p < data_end )
+	{
+		switch ( *p++ )
+		{
+			case 0:
+				time++;
+				break;
+			
+			case 1:
+			case 2:
+				++p;
+			case 3:
+				++p;
+				break;
+			
+			default:
+				dprintf( "Bad command: %02X\n", (int) p [-1] );
+				break;
+		}
+	}
+	
+	return (time + 30 + 59) / 60;
+}
+
+blargg_err_t Gym_Emu::start_track( int )
+{
+	require( data );
+	
+	pos = &data [0];
+	extra_pos = 0;
+	loop_remain = loop_offset;
+	
+	prev_dac_count = 0;
+	dac_enabled = false;
+	last_dac = -1;
+	
+	fm.reset();
+	apu.reset();
+	blip_buf.clear( false );
+	resampler.clear();
+	
+	track_ended_ = false;
+	
+	return blargg_success;
+}
+
+void Gym_Emu::play_frame( sample_t* out )
+{
+	parse_frame();
+	
+	// run SMS APU and buffer
+	blip_time_t clock_count = (pairs_per_frame + 1 - blip_buf.samples_avail()) *
+			clocks_per_sample;
+	apu.end_frame( clock_count );
+	blip_buf.end_frame( clock_count );
+	assert( unsigned (blip_buf.samples_avail() - pairs_per_frame) <= 4 );
+	
+	// run fm
+	const int sample_count = oversamples_per_frame - resampler.written();
+	sample_t* buf = resampler.buffer();
+	memset( buf, 0, sample_count * sizeof *buf );
+	fm.run( buf, sample_count );
+	resampler.write( sample_count );
+	int count = resampler.read( sample_buf, pairs_per_frame * 2 );
+	assert( count <= sample_buf_size );
+	assert( unsigned (count - pairs_per_frame * 2) < 32 );
+	
+	// mix outputs
+	mix_samples( out );
+	blip_buf.remove_samples( pairs_per_frame );
+}
+
+blargg_err_t Gym_Emu::play( long count, sample_t* out )
+{
+	require( pos );
+	
+	const int samples_per_frame = pairs_per_frame * 2;
+	
+	// empty extra buffer
+	if ( extra_pos ) {
+		int n = samples_per_frame - extra_pos;
+		if ( n > count )
+			n = count;
+		memcpy( out, sample_buf + extra_pos, n * sizeof *out );
+		out += n;
+		count -= n;
+		extra_pos = (extra_pos + n) % samples_per_frame;
+	}
+	
+	// entire frames
+	while ( count >= samples_per_frame ) {
+		play_frame( out );
+		out += samples_per_frame;
+		count -= samples_per_frame;
+	}
+	
+	// extra
+	if ( count ) {
+		play_frame( sample_buf );
+		extra_pos = count;
+		memcpy( out, sample_buf, count * sizeof *out );
+		out += count;
+	}
+	
+	return blargg_success;
+}
+
+blargg_err_t Gym_Emu::skip( long count )
+{
+	// to do: figure out why total muting generated access violation on MorphOS
+	const int buf_size = 1024;
+	sample_t buf [buf_size];
+	
+	while ( count )
+	{
+		int n = buf_size;
+		if ( n > count )
+			n = count;
+		count -= n;
+		BLARGG_RETURN_ERR( play( n, buf ) );
+	}
+	
+	return blargg_success;
+}
+
+void Gym_Emu::run_dac( int dac_count )
+{
+	if ( !dac_disabled )
+	{
+		// Guess beginning and end of sample and adjust rate and buffer position accordingly.
+		
+		// count dac samples in next frame
+		int next_dac_count = 0;
+		const byte* p = this->pos;
+		int cmd;
+		while ( (cmd = *p++) != 0 ) {
+			int data = *p++;
+			if ( cmd <= 2 )
+				++p;
+			if ( cmd == 1 && data == 0x2A )
+				next_dac_count++;
+		}
+		
+		// adjust
+		int rate_count = dac_count;
+		int start = 0;
+		if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) {
+			rate_count = next_dac_count;
+			start = next_dac_count - dac_count;
+		}
+		else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) {
+			rate_count = prev_dac_count;
+		}
+		
+		// Evenly space samples within buffer section being used
+		Blip_Buffer::resampled_time_t period =
+				blip_buf.resampled_duration( clock_rate / 60 ) / rate_count;
+		
+		Blip_Buffer::resampled_time_t time = blip_buf.resampled_time( 0 ) +
+				period * start + (period >> 1);
+		
+		int last_dac = this->last_dac;
+		if ( last_dac < 0 )
+			last_dac = dac_buf [0];
+		
+		for ( int i = 0; i < dac_count; i++ )
+		{
+			int diff = dac_buf [i] - last_dac;
+			last_dac += diff;
+			dac_synth.offset_resampled( time, diff, &blip_buf );
+			time += period;
+		}
+		this->last_dac = last_dac;
+	}
+	
+	int const step = 6 * oversample;
+	int remain = pairs_per_frame * oversample;
+	while ( remain ) {
+		int n = step;
+		if ( n > remain )
+			n = remain;
+		remain -= n;
+		fm.run_timer( n );
+	}
+}
+
+void Gym_Emu::parse_frame()
+{
+	if ( track_ended_ )
+		return;
+	
+	int dac_count = 0;
+	
+	const byte* pos = this->pos;
+	if ( loop_remain && !--loop_remain )
+		loop_begin = pos; // find loop on first time through sequence
+	int cmd;
+	while ( (cmd = *pos++) != 0 )
+	{
+		int data = *pos++;
+		if ( cmd == 1 ) {
+			int data2 = *pos++;
+			if ( data == 0x2A ) {
+				if ( dac_count < sizeof dac_buf ) {
+					dac_buf [dac_count] = data2;
+					dac_count += dac_enabled;
+				}
+			}
+			else {
+				if ( data == 0x2B )
+					dac_enabled = (data2 & 0x80) != 0;
+				
+				fm.write( 0, data );
+				fm.write( 1, data2 );
+			}
+		}
+		else if ( cmd == 2 ) {
+			fm.write( 2, data );
+			fm.write( 3, *pos++ );
+		}
+		else if ( cmd == 3 ) {
+			apu.write_data( 0, data );
+		}
+		else {
+			dprintf( "Bad command: %02X\n", (int) cmd );
+			--pos; // put data back
+		}
+	}
+	// loop
+	if ( pos >= data_end ) {
+		if ( loop_begin )
+			pos = loop_begin;
+		else
+			track_ended_ = true;
+	}
+	this->pos = pos;
+	
+	// dac
+	if ( dac_count )
+		run_dac( dac_count );
+	prev_dac_count = dac_count;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Gym_Emu::mix_samples( sample_t* out )
+{
+	// Mix one frame of Blip_Buffer (SMS APU and PCM) and resampled YM audio
+	Blip_Reader sn;
+	int bass = sn.begin( blip_buf );
+	const sample_t* ym = sample_buf;
+	
+	for ( int n = pairs_per_frame; n--; )
+	{
+		int s = sn.read();
+		long l = ym [0] * 2 + s;
+		sn.next( bass );
+		if ( (BOOST::int16_t) l != l )
+			l = 0x7FFF - (l >> 24);
+		long r = ym [1] * 2 + s;
+		ym += 2;
+		out [0] = l;
+		out [1] = r;
+		out += 2;
+		if ( (BOOST::int16_t) r != r )
+			out [-1] = 0x7FFF - (r >> 24);
+	}
+	
+	sn.end( blip_buf );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gym_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,101 @@
+
+// Sega Genesis GYM music file emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
+
+#ifndef GYM_EMU_H
+#define GYM_EMU_H
+
+#include "Fir_Resampler.h"
+#include "Blip_Buffer.h"
+#include "Music_Emu.h"
+#include "Sms_Apu.h"
+#include "ym2612.h"
+
+class Gym_Emu : public Music_Emu {
+public:
+	Gym_Emu();
+	~Gym_Emu();
+	
+	// Initialize emulator with given sample rate, gain, and oversample. A gain of 1.0
+	// results in almost no clamping. Default gain roughly matches volume of other emulators.
+	// The FM chip is synthesized at an increased rate governed by the oversample factor,
+	// where 1.0 results in no oversampling and > 1.0 results in oversampling.
+	blargg_err_t init( long sample_rate, double gain = 1.5, double oversample = 5 / 3.0 );
+	
+	struct header_t {
+	    char tag [4];
+	    char song [32];
+	    char game [32];
+	    char copyright [32];
+	    char emulator [32];
+	    char dumper [32];
+	    char comment [256];
+	    byte loop [4];
+	    byte packed [4];
+	    
+	    enum { track_count = 1 }; // one track per file
+		enum { author = 0 }; // no author field
+	};
+	BOOST_STATIC_ASSERT( sizeof (header_t) == 428 );
+	
+	// Load GYM, given its header and reader for remaining data
+	blargg_err_t load( const header_t&, Emu_Reader& );
+	
+	// Load GYM, given pointer to complete file data. Keeps reference
+	// to data, but doesn't free it.
+	blargg_err_t load( const void*, long size );
+	
+	// Length of track, in seconds (0 if looped)
+	int track_length() const;
+	
+	void mute_voices( int );
+	blargg_err_t start_track( int );
+	blargg_err_t play( long count, sample_t* );
+	const char** voice_names() const;
+	blargg_err_t skip( long count );
+	
+// End of public interface
+private:
+	// sequence data begin, loop begin, current position, end
+	const byte* data;
+	const byte* loop_begin;
+	const byte* pos;
+	const byte* data_end;
+	long loop_offset;
+	long loop_remain; // frames remaining until loop beginning has been located
+	byte* mem;
+	blargg_err_t load_( const void* file, long data_offset, long file_size );
+	
+	// frames
+	double oversample;
+	double clocks_per_sample;
+	int pairs_per_frame;
+	int oversamples_per_frame;
+	void parse_frame();
+	void play_frame( sample_t* );
+	void mix_samples( sample_t* );
+	
+	// dac (pcm)
+	int last_dac;
+	int prev_dac_count;
+	bool dac_enabled;
+	bool dac_disabled;
+	void run_dac( int );
+	
+	// sound
+	int extra_pos; // extra samples remaining from last read
+	Blip_Buffer blip_buf;
+	YM2612_Emu fm;
+	Blip_Synth<blip_med_quality,256> dac_synth;
+	Sms_Apu apu;
+	Fir_Resampler resampler;
+	byte dac_buf [1024];
+	enum { sample_buf_size = 4096 };
+	sample_t sample_buf [sample_buf_size];
+	
+	void unload();
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Makefile.am	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,37 @@
+noinst_LTLIBRARIES = libconsole.la
+
+libdir = $(plugin_dir)/$(INPUT_PLUGIN_DIR)
+
+libconsole_la_SOURCES = \
+	Blip_Buffer.cpp		\
+	Classic_Emu.cpp		\
+	Effects_Buffer.cpp	\
+	Fir_Resampler.cpp	\
+	Gb_Abu.cpp		\
+	Gb_Cpu.cpp		\
+	Gb_Oscs.cpp		\
+	Gbs_Emu.cpp		\
+	Gym_Emu.cpp		\
+	Multi_Buffer.cpp	\
+	Music_Emu.cpp		\
+	Nes_Apu.cpp		\
+	Nes_Cpu.cpp		\
+	Nes_Namco.cpp		\
+	Nes_Oscs.cpp		\
+	Nes_Vrc6.cpp		\
+	Nsf_Emu.cpp		\
+	Panning_Buffer.cpp	\
+	Sms_Apu.cpp		\
+	Snes_Spc.cpp		\
+	Spc_Cpu.cpp		\
+	Spc_Dsp.cpp		\
+	Spc_Emu.cpp		\
+	Vgm_Emu.cpp		\
+	abstract_file.cpp	\
+	ym2612.cpp		\
+	Audacious_Driver.cpp
+
+libconsole_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+libconsole_la_LIBADD = $(GTK_LIBS) $(top_builddir)/libaudacious/libaudacious.la
+INCLUDES = $(GTK_CFLAGS) $(ARCH_DEFINES) -I$(top_srcdir)/intl -I$(top_srcdir) -Iboost/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Multi_Buffer.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,205 @@
+
+// Blip_Buffer 0.3.3. http://www.slack.net/~ant/libs/
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Multi_Buffer::Multi_Buffer()
+{
+	length_ = 0;
+	sample_rate_ = 0;
+}
+
+blargg_err_t Multi_Buffer::set_channel_count( int )
+{
+	return blargg_success;
+}
+
+Mono_Buffer::Mono_Buffer()
+{
+}
+
+Mono_Buffer::~Mono_Buffer()
+{
+}
+
+blargg_err_t Mono_Buffer::sample_rate( long rate, int msec )
+{
+	BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) );
+	length_ = buf.length();
+	sample_rate_ = buf.sample_rate();
+	return blargg_success;
+}
+
+Mono_Buffer::channel_t Mono_Buffer::channel( int index )
+{
+	channel_t ch;
+	ch.center = &buf;
+	ch.left   = &buf;
+	ch.right  = &buf;
+	return ch;
+}
+
+void Mono_Buffer::end_frame( blip_time_t t, bool )
+{
+	buf.end_frame( t );
+}
+
+Stereo_Buffer::Stereo_Buffer()
+{
+	chan.center = &bufs [0];
+	chan.left = &bufs [1];
+	chan.right = &bufs [2];
+}
+
+Stereo_Buffer::~Stereo_Buffer()
+{
+}
+
+blargg_err_t Stereo_Buffer::sample_rate( long rate, int msec )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) );
+	length_ = msec;
+	sample_rate_ = rate;
+	return blargg_success;
+}
+
+void Stereo_Buffer::clock_rate( long rate )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].clock_rate( rate );
+}
+
+void Stereo_Buffer::bass_freq( int bass )
+{
+	for ( unsigned i = 0; i < buf_count; i++ )
+		bufs [i].bass_freq( bass );
+}
+
+void Stereo_Buffer::clear()
+{
+	stereo_added = false;
+	was_stereo = false;
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].clear();
+}
+
+void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo )
+{
+	for ( unsigned i = 0; i < buf_count; i++ )
+		bufs [i].end_frame( clock_count );
+	
+	stereo_added |= stereo;
+}
+
+long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
+{
+	require( !(count & 1) ); // count must be even
+	count = (unsigned) count / 2;
+	
+	long avail = bufs [0].samples_avail();
+	if ( count > avail )
+		count = avail;
+	if ( count )
+	{
+		if ( stereo_added || was_stereo )
+		{
+			mix_stereo( out, count );
+			
+			bufs [0].remove_samples( count );
+			bufs [1].remove_samples( count );
+			bufs [2].remove_samples( count );
+		}
+		else
+		{
+			mix_mono( out, count );
+			
+			bufs [0].remove_samples( count );
+			
+			bufs [1].remove_silence( count );
+			bufs [2].remove_silence( count );
+		}
+		
+		// to do: this might miss opportunities for optimization
+		if ( !bufs [0].samples_avail() ) {
+			was_stereo = stereo_added;
+			stereo_added = false;
+		}
+	}
+	
+	return count * 2;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count )
+{
+	Blip_Reader left; 
+	Blip_Reader right; 
+	Blip_Reader center;
+	
+	left.begin( bufs [1] );
+	right.begin( bufs [2] );
+	int bass = center.begin( bufs [0] );
+	
+	while ( count-- )
+	{
+		int c = center.read();
+		long l = c + left.read();
+		long r = c + right.read();
+		center.next( bass );
+		out [0] = l;
+		out [1] = r;
+		out += 2;
+		
+		if ( (BOOST::int16_t) l != l )
+			out [-2] = 0x7FFF - (l >> 24);
+		
+		left.next( bass );
+		right.next( bass );
+		
+		if ( (BOOST::int16_t) r != r )
+			out [-1] = 0x7FFF - (r >> 24);
+	}
+	
+	center.end( bufs [0] );
+	right.end( bufs [2] );
+	left.end( bufs [1] );
+}
+
+void Stereo_Buffer::mix_mono( blip_sample_t* out, long count )
+{
+	Blip_Reader in;
+	int bass = in.begin( bufs [0] );
+	
+	while ( count-- )
+	{
+		long s = in.read();
+		in.next( bass );
+		out [0] = s;
+		out [1] = s;
+		out += 2;
+		
+		if ( (BOOST::int16_t) s != s ) {
+			s = 0x7FFF - (s >> 24);
+			out [-2] = s;
+			out [-1] = s;
+		}
+	}
+	
+	in.end( bufs [0] );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Multi_Buffer.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,157 @@
+
+// Multi-channel sound buffer interface, and basic mono and stereo buffers
+
+// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef MULTI_BUFFER_H
+#define MULTI_BUFFER_H
+
+#include "Blip_Buffer.h"
+
+// Multi_Buffer is an interface to one or more Blip_Buffers mapped to one or
+// more channels consisting of left, center, and right buffers.
+class Multi_Buffer {
+public:
+	Multi_Buffer();
+	virtual ~Multi_Buffer() { }
+	
+	// Set the number of channels available
+	virtual blargg_err_t set_channel_count( int );
+	
+	// Get indexed channel, from 0 to channel count - 1
+	struct channel_t {
+		Blip_Buffer* center;
+		Blip_Buffer* left;
+		Blip_Buffer* right;
+	};
+	virtual channel_t channel( int index ) = 0;
+	
+	// See Blip_Buffer.h
+	//virtual blargg_err_t sample_rate( long rate, int msec = 0 );
+	virtual void clock_rate( long ) = 0;
+	virtual void bass_freq( int ) = 0;
+	virtual void clear() = 0;
+	long sample_rate() const;
+	
+	// Length of buffer, in milliseconds
+	int length() const;
+	
+	// See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo'
+	// if nothing was added to the left and right buffers of any channel for
+	// this time frame.
+	virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0;
+	
+	// See Blip_Buffer.h
+	virtual long read_samples( blip_sample_t*, long ) = 0;
+	
+protected:
+	// Derived classes must set these to appropriate values, which are returned
+	// by the corresponding public function.
+	long sample_rate_;
+	int length_;
+private:
+	// noncopyable
+	Multi_Buffer( const Multi_Buffer& );
+	Multi_Buffer& operator = ( const Multi_Buffer& );
+};
+
+// Mono_Buffer uses a single buffer and outputs mono samples.
+class Mono_Buffer : public Multi_Buffer {
+	Blip_Buffer buf;
+public:
+	Mono_Buffer();
+	~Mono_Buffer();
+	
+	// Buffer used for all channels
+	Blip_Buffer* center();
+	
+	// See Multi_Buffer
+	blargg_err_t sample_rate( long rate, int msec = blip_default_length );
+	void clock_rate( long );
+	void bass_freq( int );
+	void clear();
+	channel_t channel( int );
+	void end_frame( blip_time_t, bool unused = true );
+	long read_samples( blip_sample_t*, long );
+};
+
+// Stereo_Buffer uses three buffers (one for center) and outputs stereo sample pairs.
+class Stereo_Buffer : public Multi_Buffer {
+public:
+	Stereo_Buffer();
+	~Stereo_Buffer();
+	
+	// Buffers used for all channels
+	Blip_Buffer* center();
+	Blip_Buffer* left();
+	Blip_Buffer* right();
+	
+	// See Multi_Buffer
+	blargg_err_t sample_rate( long, int msec = blip_default_length );
+	void clock_rate( long );
+	void bass_freq( int );
+	void clear();
+	channel_t channel( int index );
+	void end_frame( blip_time_t, bool added_stereo = true );
+	long read_samples( blip_sample_t*, long );
+	
+private:
+	enum { buf_count = 3 };
+	Blip_Buffer bufs [buf_count];
+	channel_t chan;
+	bool stereo_added;
+	bool was_stereo;
+	
+	void mix_stereo( blip_sample_t*, long );
+	void mix_mono( blip_sample_t*, long );
+};
+
+
+// End of public interface
+
+inline Blip_Buffer* Stereo_Buffer::left() {
+	return &bufs [1];
+}
+
+inline Blip_Buffer* Stereo_Buffer::center() {
+	return &bufs [0];
+}
+
+inline Blip_Buffer* Stereo_Buffer::right() {
+	return &bufs [2];
+}
+
+inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) {
+	return chan;
+}
+
+inline long Multi_Buffer::sample_rate() const {
+	return sample_rate_;
+}
+
+inline int Multi_Buffer::length() const {
+	return length_;
+}
+
+inline Blip_Buffer* Mono_Buffer::center() {
+	return &buf;
+}
+
+inline void Mono_Buffer::clock_rate( long rate ) {
+	buf.clock_rate( rate );
+}
+
+inline void Mono_Buffer::clear() {
+	buf.clear();
+}
+
+inline void Mono_Buffer::bass_freq( int freq ) {
+	buf.bass_freq( freq );
+}
+
+inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) {
+	return buf.read_samples( p, s );
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Music_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,77 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Music_Emu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Music_Emu::Music_Emu()
+{
+	mute_mask_ = 0;
+	track_count_ = 0;
+	voice_count_ = 0;
+	track_ended_ = false;
+}
+
+Music_Emu::~Music_Emu()
+{
+}
+
+void Music_Emu::mute_voices( int )
+{
+	// empty
+}
+
+blargg_err_t Music_Emu::skip( long count )
+{
+	const int buf_size = 1024;
+	sample_t buf [buf_size];
+	
+	const long threshold = 30000;
+	if ( count > threshold )
+	{
+		int saved_mute = mute_mask_;
+		mute_voices( ~0 );
+		
+		while ( count > threshold / 2 ) {
+			BLARGG_RETURN_ERR( play( buf_size, buf ) );
+			count -= buf_size;
+		}
+		
+		mute_voices( saved_mute );
+	}
+	
+	while ( count )
+	{
+		int n = buf_size;
+		if ( n > count )
+			n = count;
+		count -= n;
+		BLARGG_RETURN_ERR( play( n, buf ) );
+	}
+	
+	return blargg_success;
+}
+
+const char** Music_Emu::voice_names() const
+{
+	static const char* names [] = {
+		"Voice 1", "Voice 2", "Voice 3", "Voice 4",
+		"Voice 5", "Voice 6", "Voice 7", "Voice 8"
+	};
+	return names;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Music_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,66 @@
+
+// Game music emulator interface base class
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef MUSIC_EMU_H
+#define MUSIC_EMU_H
+
+#include "blargg_common.h"
+
+#include "abstract_file.h"
+typedef Data_Reader Emu_Reader; // File reader base class
+typedef Std_File_Reader Emu_Std_Reader; // Read from standard file
+typedef Mem_File_Reader Emu_Mem_Reader; // Read from block of memory
+
+class Music_Emu {
+public:
+	Music_Emu();
+	virtual ~Music_Emu();
+	
+	// Number of voices used by currently loaded file
+	int voice_count() const;
+	
+	// Names of voices
+	virtual const char** voice_names() const;
+	
+	// Number of tracks. Zero if file hasn't been loaded yet.
+	int track_count() const;
+	
+	// Start a track, where 0 is the first track. Might un-mute any muted voices.
+	virtual blargg_err_t start_track( int ) = 0;
+	
+	// Mute voice n if bit n (1 << n) of mask is set
+	virtual void mute_voices( int mask );
+	
+	// Generate 'count' samples info 'buf'
+	typedef short sample_t;
+	virtual blargg_err_t play( long count, sample_t* buf ) = 0;
+	
+	// Skip 'count' samples
+	virtual blargg_err_t skip( long count );
+	
+	// True if a track was started and has since ended. Currently only dumped
+	// format tracks (VGM, GYM) without loop points have an ending.
+	bool track_ended() const;
+	
+	
+// End of public interface
+protected:
+	typedef BOOST::uint8_t byte; // used often
+	int track_count_;
+	int voice_count_;
+	int mute_mask_;
+	bool track_ended_;
+private:
+	// noncopyable
+	Music_Emu( const Music_Emu& );
+	Music_Emu& operator = ( const Music_Emu& );
+};
+
+inline int Music_Emu::voice_count() const   { return voice_count_; }
+inline int Music_Emu::track_count() const   { return track_count_; }
+inline bool Music_Emu::track_ended() const  { return track_ended_; }
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Apu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,329 @@
+
+// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
+
+#include "Nes_Apu.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Nes_Apu::Nes_Apu()
+{
+	dmc.apu = this;
+	dmc.rom_reader = NULL;
+	square1.synth = &square_synth;
+	square2.synth = &square_synth;
+	irq_notifier_ = NULL;
+	
+	oscs [0] = &square1;
+	oscs [1] = &square2;
+	oscs [2] = &triangle;
+	oscs [3] = &noise;
+	oscs [4] = &dmc;
+	
+	output( NULL );
+	volume( 1.0 );
+	reset( false );
+}
+
+Nes_Apu::~Nes_Apu()
+{
+}
+
+void Nes_Apu::treble_eq( const blip_eq_t& eq )
+{
+	square_synth.treble_eq( eq );
+	triangle.synth.treble_eq( eq );
+	noise.synth.treble_eq( eq );
+	dmc.synth.treble_eq( eq );
+}
+
+void Nes_Apu::volume( double v, bool nonlinear )
+{
+	dmc.nonlinear = nonlinear;
+	if ( nonlinear )
+	{
+		square_synth.volume( 0.25751258 * 0.25 * v );
+		
+		const double tnd = 1.0 / 202 * 0.48;
+		triangle.synth.volume_unit( 3 * tnd );
+		noise.synth.volume_unit( 2 * tnd );
+		dmc.synth.volume_unit( tnd );
+	}
+	else
+	{
+		square_synth.volume( 0.1128 * v );
+		triangle.synth.volume( 0.12765 * v );
+		noise.synth.volume( 0.0741 * v );
+		dmc.synth.volume( 0.42545 * v );
+	}
+}
+
+void Nes_Apu::output( Blip_Buffer* buffer )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, buffer );
+}
+
+void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
+{
+	// to do: time pal frame periods exactly
+	frame_period = pal_mode ? 8314 : 7458;
+	dmc.pal_mode = pal_mode;
+	
+	square1.reset();
+	square2.reset();
+	triangle.reset();
+	noise.reset();
+	dmc.reset();
+	
+	last_time = 0;
+	osc_enables = 0;
+	irq_flag = false;
+	earliest_irq_ = no_irq;
+	frame_delay = 1;
+	write_register( 0, 0x4017, 0x00 );
+	write_register( 0, 0x4015, 0x00 );
+	
+	for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ )
+		write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
+	
+	dmc.dac = initial_dmc_dac;
+	if ( !dmc.nonlinear )
+		dmc.last_amp = initial_dmc_dac; // prevent output transition
+}
+
+void Nes_Apu::irq_changed()
+{
+	nes_time_t new_irq = dmc.next_irq;
+	if ( dmc.irq_flag | irq_flag ) {
+		new_irq = 0;
+	}
+	else if ( new_irq > next_irq ) {
+		new_irq = next_irq;
+	}
+	
+	if ( new_irq != earliest_irq_ ) {
+		earliest_irq_ = new_irq;
+		if ( irq_notifier_ )
+			irq_notifier_( irq_data );
+	}
+}
+
+// frames
+
+void Nes_Apu::run_until( nes_time_t end_time )
+{
+	require( end_time >= last_time );
+	
+	if ( end_time == last_time )
+		return;
+	
+	while ( true )
+	{
+		// earlier of next frame time or end time
+		nes_time_t time = last_time + frame_delay;
+		if ( time > end_time )
+			time = end_time;
+		frame_delay -= time - last_time;
+		
+		// run oscs to present
+		square1.run( last_time, time );
+		square2.run( last_time, time );
+		triangle.run( last_time, time );
+		noise.run( last_time, time );
+		dmc.run( last_time, time );
+		last_time = time;
+		
+		if ( time == end_time )
+			break; // no more frames to run
+		
+		// take frame-specific actions
+		frame_delay = frame_period;
+		switch ( frame++ )
+		{
+			case 0:
+				// set interrupt in mode 0
+				if ( !(frame_mode & 0xc0) ) {
+		 			irq_flag = true;
+		 			next_irq = time + frame_period * 4 + 1;
+		 		}
+		 		// fall through
+		 	case 2:
+		 		// clock length and sweep on frames 0 and 2
+				square1.clock_length( 0x20 );
+				square2.clock_length( 0x20 );
+				noise.clock_length( 0x20 );
+				triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
+				
+				square1.clock_sweep( -1 );
+				square2.clock_sweep( 0 );
+		 		break;
+		 	
+			case 1:
+				// frame 1 is slightly shorter
+				frame_delay -= 2;
+				break;
+			
+		 	case 3:
+		 		frame = 0;
+		 		
+		 		// frame 3 is almost twice as long in mode 1
+		 		if ( frame_mode & 0x80 )
+					frame_delay += frame_period - 6;
+				break;
+		}
+		
+		// clock envelopes and linear counter every frame
+		triangle.clock_linear_counter();
+		square1.clock_envelope();
+		square2.clock_envelope();
+		noise.clock_envelope();
+	}
+}
+
+void Nes_Apu::end_frame( nes_time_t end_time )
+{
+	run_until( end_time );
+	
+	// make times relative to new frame
+	last_time = 0;
+	if ( next_irq != no_irq ) {
+		next_irq -= end_time;
+		assert( next_irq >= 0 );
+	}
+	if ( dmc.next_irq != no_irq ) {
+		dmc.next_irq -= end_time;
+		assert( dmc.next_irq >= 0 );
+	}
+	if ( earliest_irq_ != no_irq ) {
+		earliest_irq_ -= end_time;
+		if ( earliest_irq_ < 0 )
+			earliest_irq_ = 0;
+	}
+}
+
+// registers
+
+static const unsigned char length_table [0x20] = {
+	0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
+	0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 
+	0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
+	0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
+};
+
+void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
+{
+	require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
+	require( (unsigned) data <= 0xff );
+	
+	// Ignore addresses outside range
+	if ( addr < start_addr || end_addr < addr )
+		return;
+	
+	run_until( time );
+	
+	if ( addr < 0x4014 )
+	{
+		// Write to channel
+		int osc_index = (addr - start_addr) >> 2;
+		Nes_Osc* osc = oscs [osc_index];
+		
+		int reg = addr & 3;
+		osc->regs [reg] = data;
+		osc->reg_written [reg] = true;
+		
+		if ( osc_index == 4 )
+		{
+			// handle DMC specially
+			dmc.write_register( reg, data );
+		}
+		else if ( reg == 3 )
+		{
+			// load length counter
+			if ( (osc_enables >> osc_index) & 1 )
+				osc->length_counter = length_table [(data >> 3) & 0x1f];
+			
+			// reset square phase
+			if ( osc_index < 2 )
+				((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1;
+		}
+	}
+	else if ( addr == 0x4015 )
+	{
+		// Channel enables
+		for ( int i = osc_count; i--; )
+			if ( !((data >> i) & 1) )
+				oscs [i]->length_counter = 0;
+		
+		bool recalc_irq = dmc.irq_flag;
+		dmc.irq_flag = false;
+		
+		int old_enables = osc_enables;
+		osc_enables = data;
+		if ( !(data & 0x10) ) {
+			dmc.next_irq = no_irq;
+			recalc_irq = true;
+		}
+		else if ( !(old_enables & 0x10) ) {
+			dmc.start(); // dmc just enabled
+		}
+		
+		if ( recalc_irq )
+			irq_changed();
+	}
+	else if ( addr == 0x4017 )
+	{
+		// Frame mode
+		frame_mode = data;
+		
+		bool irq_enabled = !(data & 0x40);
+		irq_flag &= irq_enabled;
+		next_irq = no_irq;
+		
+		// mode 1
+		frame_delay = (frame_delay & 1);
+		frame = 0;
+		
+		if ( !(data & 0x80) )
+		{
+			// mode 0
+			frame = 1;
+			frame_delay += frame_period;
+			if ( irq_enabled )
+				next_irq = time + frame_delay + frame_period * 3;
+		}
+		
+		irq_changed();
+	}
+}
+
+int Nes_Apu::read_status( nes_time_t time )
+{
+	run_until( time - 1 );
+	
+	int result = (dmc.irq_flag << 7) | (irq_flag << 6);
+	
+	for ( int i = 0; i < osc_count; i++ )
+		if ( oscs [i]->length_counter )
+			result |= 1 << i;
+	
+	run_until( time );
+	
+	if ( irq_flag ) {
+		irq_flag = false;
+		irq_changed();
+	}
+	
+	return result;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Apu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,155 @@
+
+// NES 2A03 APU sound chip emulator
+
+// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef NES_APU_H
+#define NES_APU_H
+
+typedef long nes_time_t;     // CPU clock cycle count
+typedef unsigned nes_addr_t; // 16-bit memory address
+
+#include "Nes_Oscs.h"
+
+class Tagged_Data;
+
+class Nes_Apu {
+public:
+	Nes_Apu();
+	~Nes_Apu();
+	
+// Initialization
+
+	// Reset internal frame counter, registers, and all oscillators.
+	// Use PAL timing if pal_timing is true, otherwise use NTSC timing.
+	// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
+	// any audible click.
+	void reset( bool pal_timing = false, int initial_dmc_dac = 0 );
+	
+	// Set memory reader callback used by DMC oscillator to fetch samples.
+	//  When callback is invoked,  'user_data' is passed unchanged as the
+	// first parameter.
+	void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL );
+	
+	// Set IRQ time callback that is invoked when the time of earliest IRQ
+	// may have changed, or NULL to disable. When callback is invoked,
+	// 'user_data' is passed unchanged as the first parameter.
+	void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );
+	
+	// Reflect complete state between tagged data container.
+	void reflect_state( Tagged_Data& );
+	
+// Sound output
+	
+	// Set overall volume (default is 1.0). Set nonlinear to true only when
+	// using special Nes_Nonlinearizer on output.
+	void volume( double, bool nonlinear = false );
+	
+	// Set treble equalization (see notes.txt).
+	void treble_eq( const blip_eq_t& );
+	
+	// Set sound output of all oscillators to buffer. If buffer is NULL, no
+	// sound is generated and emulation accuracy is reduced.
+	void output( Blip_Buffer* );
+	
+	// Set sound output of specific oscillator to buffer. If buffer is NULL,
+	// the specified oscillator is muted and emulation accuracy is reduced.
+	// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
+	// 2) Triangle, 3) Noise, 4) DMC.
+	enum { osc_count = 5 };
+	void osc_output( int index, Blip_Buffer* buffer );
+	
+// Emulation
+
+	// All time values are the number of CPU clock cycles relative to the
+	// beginning of the current time frame.
+
+	// Emulate memory write at specified time. Address must be in the range
+	// start_addr to end_addr.
+	enum { start_addr = 0x4000 };
+	enum { end_addr   = 0x4017 };
+	void write_register( nes_time_t, nes_addr_t, int data );
+	
+	// Emulate memory read of the status register at specified time.
+	enum { status_addr = 0x4015 };
+	int read_status( nes_time_t );
+	
+	// Get time that APU-generated IRQ will occur if no further register reads
+	// or writes occur. If IRQ is already pending, returns irq_waiting. If no
+	// IRQ will occur, returns no_irq.
+	enum { no_irq = LONG_MAX / 2 + 1 };
+	enum { irq_waiting = 0 };
+	nes_time_t earliest_irq() const;
+	
+	// Run APU until specified time, so that any DMC memory reads can be
+	// accounted for (i.e. inserting CPU wait states).
+	void run_until( nes_time_t );
+	
+	// Count number of DMC reads that would occur if 'run_until( t )' were executed.
+	// If last_read is not NULL, set *last_read to the earliest time that
+	// 'count_dmc_reads( time )' would result in the same result.
+	int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const;
+	
+	// Run all oscillators up to specified time, end current time frame, then
+	// start a new time frame at time 0. Time frames have no effect on emulation
+	// and each can be whatever length is convenient.
+	void end_frame( nes_time_t );
+	
+// End of public interface.
+
+private:
+	// noncopyable
+	Nes_Apu( const Nes_Apu& );
+	Nes_Apu& operator = ( const Nes_Apu& );
+	
+	Nes_Osc*            oscs [osc_count];
+	Nes_Square          square1;
+	Nes_Square          square2;
+	Nes_Noise           noise;
+	Nes_Triangle        triangle;
+	Nes_Dmc             dmc;
+	
+	nes_time_t last_time; // has been run until this time in current frame
+	nes_time_t earliest_irq_;
+	nes_time_t next_irq;
+	int frame_period;
+	int frame_delay; // cycles until frame counter runs next
+	int frame; // current frame (0-3)
+	int osc_enables;
+	int frame_mode;
+	bool irq_flag;
+	void (*irq_notifier_)( void* user_data );
+	void* irq_data;
+	Nes_Square::Synth square_synth; // shared by squares
+	
+	void irq_changed();
+	void state_restored();
+	
+	friend struct Nes_Dmc;
+};
+
+	inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) {
+		assert(( "Nes_Apu::osc_output(): Index out of range", 0 <= osc && osc < osc_count ));
+		oscs [osc]->output = buf;
+	}
+
+	inline nes_time_t Nes_Apu::earliest_irq() const {
+		return earliest_irq_;
+	}
+
+	inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) {
+		dmc.rom_reader_data = user_data;
+		dmc.rom_reader = func;
+	}
+
+	inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) {
+		irq_notifier_ = func;
+		irq_data = user_data;
+	}
+	
+	inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const {
+		return dmc.count_reads( time, last_read );
+	}
+	
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Cpu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,915 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Nes_Cpu.h"
+
+#include <limits.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Common instructions:
+// 7.40%  D0    BNE
+// 6.05%  F0    BEQ
+// 5.46%  BD    LDA abs,X
+// 4.44%  C8    INY
+// 4.32%  85    STA zp
+// 4.29%  C9    CMP imm
+// 3.74%  20    JSR
+// 3.66%  60    RTS
+// 3.23%  8D    STA abs
+// 3.02%  AD    LDA abs
+
+#ifndef NES_CPU_FIXED_CYCLES
+	#define NES_CPU_FIXED_CYCLES 0
+#endif
+
+static void write_unmapped( Nsf_Emu*, nes_addr_t, int )
+{
+	check( false );
+}
+
+static int read_unmapped( Nsf_Emu*, nes_addr_t )
+{
+	check( false );
+	return 0;
+}
+
+Nes_Cpu::Nes_Cpu()
+{
+	callback_data = NULL;
+	data_writer [page_count] = write_unmapped;
+	data_reader [page_count] = read_unmapped;
+	reset();
+}
+
+void Nes_Cpu::reset( const void* unmapped_code_page )
+{
+	r.status = 0;
+	r.sp = 0;
+	r.pc = 0;
+	r.a = 0;
+	r.x = 0;
+	r.y = 0;
+	
+	cycle_count = 0;
+	base_time = 0;
+	#if NES_CPU_IRQ_SUPPORT
+		cycle_limit = 0;
+	#endif
+	
+	for ( int i = 0; i < page_count + 1; i++ )
+		code_map [i] = (uint8_t*) unmapped_code_page;
+	
+	map_memory( 0, 0x10000, read_unmapped, write_unmapped );
+}
+
+void Nes_Cpu::map_code( nes_addr_t start, unsigned long size, const void* data )
+{
+	// start end end must fall on page bounadries
+	require( start % page_size == 0 && size % page_size == 0 );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		code_map [first_page + i] = (uint8_t*) data + i * page_size;
+}
+
+void Nes_Cpu::map_memory( nes_addr_t start, unsigned long size, reader_t read, writer_t write )
+{
+	// start end end must fall on page bounadries
+	require( start % page_size == 0 && size % page_size == 0 );
+	
+	if ( !read )
+		read = read_unmapped;
+	if ( !write )
+		write = write_unmapped;
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; ) {
+		data_reader [first_page + i] = read;
+		data_writer [first_page + i] = write;
+	}
+}
+
+// Note: 'addr' is evaulated more than once in some of the macros, so it
+// must not contain side-effects.
+
+#define LOW_MEM( a )        (low_mem [int (a)])
+
+#define READ( addr )        (data_reader [(addr) >> page_bits]( callback_data, addr ))
+#define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data ))
+
+#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [(addr) & (page_size - 1)])
+#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
+
+#define PUSH( v )   (*--sp = (v))
+#define POP()       (*sp++)
+
+int Nes_Cpu::read( nes_addr_t addr ) {
+	return READ( addr );
+}
+	
+void Nes_Cpu::write( nes_addr_t addr, int value ) {
+	WRITE( addr, value );
+}
+
+#ifndef NES_CPU_GLUE_ONLY
+
+static const unsigned char cycle_table [256] = {                             
+	7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
+	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
+	6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
+	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
+	6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
+	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
+	6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
+	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
+	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
+	2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
+	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
+	2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
+	2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
+	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
+	2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
+	2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7
+};
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+// on machines with relatively few registers:
+// use local temporaries (if no function calls) over all other variables
+// these are most likely to be in registers, so use them over others if possible:
+// pc
+// nz (so calculate with nz first, then assign to other variable if necessary)
+// data
+// this
+Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end )
+{
+	#if NES_CPU_IRQ_SUPPORT
+		end_time( end );
+	#else
+		cycle_count -= (end - base_time);
+		base_time = end;
+	#endif
+	
+	result_t result = result_cycles;
+	
+#if BLARGG_CPU_POWERPC
+	// cache commonly-used values in registers
+	long cycle_count = this->cycle_count;
+	writer_t* const data_writer = this->data_writer;
+	reader_t* const data_reader = this->data_reader;
+	uint8_t* const low_mem = this->low_mem;
+#endif
+	
+	// stack pointer is kept one greater than usual 6502 stack pointer
+	#define SET_SP( v )     (sp = low_mem + 0x101 + (v))
+	#define GET_SP()        (sp - 0x101 - low_mem)
+	uint8_t* sp;
+	SET_SP( r.sp );
+	
+	// registers
+	unsigned pc = r.pc;
+	int a = r.a;
+	int x = r.x;
+	int y = r.y;
+	
+	// status flags
+	
+	#define IS_NEG (int ((nz + 0x800) | (nz << (CHAR_BIT * sizeof (int) - 8))) < 0)
+	
+	const int st_n = 0x80;
+	const int st_v = 0x40;
+	const int st_r = 0x20;
+	const int st_b = 0x10;
+	const int st_d = 0x08;
+	const int st_i = 0x04;
+	const int st_z = 0x02;
+	const int st_c = 0x01;
+	
+	#define CALC_STATUS( out ) do {                 \
+		out = status & ~(st_n | st_z | st_c);       \
+		out |= (c >> 8) & st_c;                     \
+		if ( IS_NEG ) out |= st_n;                  \
+		if ( (nz & 0xFF) == 0 ) out |= st_z;        \
+	} while ( false )       
+
+	#define SET_STATUS( in ) do {                           \
+		status = in & ~(st_n | st_z | st_c | st_b | st_r);  \
+		c = in << 8;                                        \
+		nz = in << 4;                                       \
+		nz &= 0x820;                                        \
+		nz ^= ~0xDF;                                        \
+	} while ( false )
+	
+	uint8_t status;
+	int c;  // store C as 'c' & 0x100.
+	int nz; // store Z as 'nz' & 0xFF == 0. see above for encoding of N.
+	{
+		int temp = r.status;
+		SET_STATUS( temp );
+	}
+
+	goto loop;
+	
+	unsigned data;
+	
+branch_taken: {
+	unsigned old_pc = pc;
+	pc += (BOOST::int8_t) data;
+	if ( !NES_CPU_FIXED_CYCLES )
+		cycle_count += 1 + (((old_pc ^ (pc + 1)) >> 8) & 1);
+}
+inc_pc_loop:            
+	pc++;
+loop:
+
+	check( (unsigned) pc < 0x10000 );
+	check( (unsigned) GET_SP() < 0x100 );
+	check( (unsigned) a < 0x100 );
+	check( (unsigned) x < 0x100 );
+	check( (unsigned) y < 0x100 );
+	
+	// Read opcode and first operand. Optimize if processor's byte order is known
+	// and non-portable constructs are allowed.
+#if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
+	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	pc++;
+	unsigned opcode = data >> 8;
+	data = (uint8_t) data;
+
+#elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
+	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	pc++;
+	unsigned opcode = (uint8_t) data;
+	data >>= 8;
+
+#else
+	unsigned opcode = READ_PROG( pc );
+	pc++;
+	data = READ_PROG( pc );
+	
+#endif
+
+	if ( cycle_count >= cycle_limit )
+		goto stop;
+	
+	cycle_count += NES_CPU_FIXED_CYCLES ? NES_CPU_FIXED_CYCLES : cycle_table [opcode];
+	
+	#if BLARGG_CPU_POWERPC
+		this->cycle_count = cycle_count;
+	#endif
+	
+	switch ( opcode )
+	{
+#define ADD_PAGE        (pc++, data += 0x100 * READ_PROG( pc ));
+#define GET_ADDR()      READ_PROG16( pc )
+
+#if NES_CPU_FIXED_CYCLES
+	#define HANDLE_PAGE_CROSSING( lsb )
+#else
+	#define HANDLE_PAGE_CROSSING( lsb ) cycle_count += (lsb) >> 8;
+#endif
+
+#define INC_DEC_XY( reg, n )        \
+	reg = uint8_t (nz = reg + n);   \
+	goto loop;
+
+#define IND_Y {                                                 \
+		int temp = LOW_MEM( data ) + y;                         \
+		data = temp + 0x100 * LOW_MEM( uint8_t (data + 1) );    \
+		HANDLE_PAGE_CROSSING( temp );                           \
+	}
+	
+#define IND_X {                                                 \
+		int temp = data + x;                                    \
+		data = LOW_MEM( uint8_t (temp) ) + 0x100 * LOW_MEM( uint8_t (temp + 1) ); \
+	}
+	
+#define ARITH_ADDR_MODES( op )          \
+case op - 0x04: /* (ind,x) */           \
+	IND_X                               \
+	goto ptr##op;                       \
+case op + 0x0C: /* (ind),y */           \
+	IND_Y                               \
+	goto ptr##op;                       \
+case op + 0x10: /* zp,X */              \
+	data = uint8_t (data + x);          \
+case op + 0x00: /* zp */                \
+	data = LOW_MEM( data );             \
+	goto imm##op;                       \
+case op + 0x14: /* abs,Y */             \
+	data += y;                          \
+	goto ind##op;                       \
+case op + 0x18: /* abs,X */             \
+	data += x;                          \
+ind##op:                                \
+	HANDLE_PAGE_CROSSING( data );       \
+case op + 0x08: /* abs */               \
+	ADD_PAGE                            \
+ptr##op:                                \
+	data = READ( data );                \
+case op + 0x04: /* imm */               \
+imm##op:                                \
+
+// Often-Used
+
+	case 0xD0: // BNE
+		if ( (uint8_t) nz )
+			goto branch_taken;
+		goto inc_pc_loop;
+	
+	case 0xF0: // BEQ
+		if ( !(uint8_t) nz )
+			goto branch_taken;
+		goto inc_pc_loop;
+	
+	case 0xBD: // LDA abs,X
+		data += x;
+	lda_ind_common:
+		HANDLE_PAGE_CROSSING( data );
+	case 0xAD: // LDA abs
+		ADD_PAGE
+	lda_ptr:
+		nz = a = READ( data );
+		goto inc_pc_loop;
+
+	case 0xC8: INC_DEC_XY( y, 1 )  // INY
+
+	case 0x95: // STA zp,x
+		data = uint8_t (data + x);
+	case 0x85: // STA zp
+		LOW_MEM( data ) = a;
+		goto inc_pc_loop;
+	
+	ARITH_ADDR_MODES( 0xC5 ) // CMP
+		nz = a - data;
+	compare_common:
+		c = ~nz;
+		goto inc_pc_loop;
+	
+	case 0x4C: // JMP abs
+		pc++;
+		goto jmp_common;
+	
+	case 0x20: // JSR
+		pc++;
+		PUSH( pc >> 8 );
+		PUSH( pc );
+	jmp_common:
+		pc = data + 0x100 * READ_PROG( pc );
+		goto loop;
+	
+	case 0x60: // RTS
+		// often-used instruction
+		pc = POP();
+		pc += 0x100 * POP();
+		goto inc_pc_loop;
+
+	case 0x9D: // STA abs,X
+		data += x;
+	sta_ind_common:
+		HANDLE_PAGE_CROSSING( data );
+	case 0x8D: // STA abs
+		ADD_PAGE
+	sta_ptr:
+		WRITE( data, a );
+		goto inc_pc_loop;
+	
+	case 0xB5: // LDA zp,x
+		data = uint8_t (data + x);
+	case 0xA5: // LDA zp
+		data = LOW_MEM( data );
+	case 0xA9: // LDA #imm
+		a = nz = data;
+		goto inc_pc_loop;
+
+	case 0xA8: // TAY
+		y = a;
+	case 0x98: // TYA
+		a = nz = y;
+		goto loop;
+	
+// Branch
+
+#define BRANCH( cond )      \
+	if ( cond )             \
+		goto branch_taken;  \
+	goto inc_pc_loop;
+
+	case 0x50: // BVC
+		BRANCH( !(status & st_v) )
+	
+	case 0x70: // BVS
+		BRANCH( status & st_v )
+	
+	case 0xB0: // BCS
+		BRANCH( c & 0x100 )
+	
+	case 0x90: // BCC
+		BRANCH( !(c & 0x100) )
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG )
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
+	
+// Load/store
+	
+	case 0x94: // STY zp,x
+		data = uint8_t (data + x);
+	case 0x84: // STY zp
+		LOW_MEM( data ) = y;
+		goto inc_pc_loop;
+	
+	case 0x96: // STX zp,y
+		data = uint8_t (data + y);
+	case 0x86: // STX zp
+		LOW_MEM( data ) = x;
+		goto inc_pc_loop;
+	
+	case 0xB6: // LDX zp,y
+		data = uint8_t (data + y);
+	case 0xA6: // LDX zp
+		data = LOW_MEM( data );
+	case 0xA2: // LDX imm
+		x = data;
+		nz = data;
+		goto inc_pc_loop;
+	
+	case 0xB4: // LDY zp,x
+		data = uint8_t (data + x);
+	case 0xA4: // LDY zp
+		data = LOW_MEM( data );
+	case 0xA0: // LDY imm
+		y = data;
+		nz = data;
+		goto inc_pc_loop;
+	
+	case 0xB1: // LDA (ind),Y
+		IND_Y
+		goto lda_ptr;
+	
+	case 0xA1: // LDA (ind,X)
+		IND_X
+		goto lda_ptr;
+	
+	case 0xB9: // LDA abs,Y
+		data += y;
+		goto lda_ind_common;
+	
+	case 0x91: // STA (ind),Y
+		IND_Y
+		goto sta_ptr;
+	
+	case 0x81: // STA (ind,X)
+		IND_X
+		goto sta_ptr;
+	
+	case 0x99: // STA abs,Y
+		data += y;
+		goto sta_ind_common;
+	
+	case 0xBC: // LDY abs,X
+		data += x;
+		HANDLE_PAGE_CROSSING( data );
+	case 0xAC:{// LDY abs
+		pc++;
+		unsigned addr = data + 0x100 * READ_PROG( pc );
+		y = nz = READ( addr );
+		goto inc_pc_loop;
+	}
+	
+	case 0xBE: // LDX abs,y
+		data += y;
+		HANDLE_PAGE_CROSSING( data );
+	case 0xAE:{// LDX abs
+		pc++;
+		unsigned addr = data + 0x100 * READ_PROG( pc );
+		x = nz = READ( addr );
+		goto inc_pc_loop;
+	}
+	
+	{
+		int temp;
+	case 0x8C: // STY abs
+		temp = y;
+		goto store_abs;
+	
+	case 0x8E: // STX abs
+		temp = x;
+	store_abs:
+		int addr = GET_ADDR();
+		WRITE( addr, temp );
+		pc += 2;
+		goto loop;
+	}
+
+// Compare
+
+	case 0xEC:{// CPX abs
+		unsigned addr = GET_ADDR();
+		pc++;
+		data = READ( addr );
+		goto cpx_data;
+	}
+	
+	case 0xE4: // CPX zp
+		data = LOW_MEM( data );
+	case 0xE0: // CPX imm
+	cpx_data:
+		nz = x - data;
+		goto compare_common;
+	
+	case 0xCC:{// CPY abs
+		unsigned addr = GET_ADDR();
+		pc++;
+		data = READ( addr );
+		goto cpy_data;
+	}
+	
+	case 0xC4: // CPY zp
+		data = LOW_MEM( data );
+	case 0xC0: // CPY imm
+	cpy_data:
+		nz = y - data;
+		goto compare_common;
+	
+// Logical
+
+	ARITH_ADDR_MODES( 0x25 ) // AND
+		nz = (a &= data);
+		goto inc_pc_loop;
+	
+	ARITH_ADDR_MODES( 0x45 ) // EOR
+		nz = (a ^= data);
+		goto inc_pc_loop;
+	
+	ARITH_ADDR_MODES( 0x05 ) // ORA
+		nz = (a |= data);
+		goto inc_pc_loop;
+	
+// Add/Subtract
+
+	ARITH_ADDR_MODES( 0x65 ) // ADC
+	adc_imm: {
+		int carry = (c >> 8) & 1;
+		int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
+		c = nz = a + data + carry;
+		a = (uint8_t) nz;
+		status = (status & ~st_v) | ((ov >> 2) & 0x40);
+		goto inc_pc_loop;
+	}
+	
+	ARITH_ADDR_MODES( 0xE5 ) // SBC
+	case 0xEB: // unofficial equivalent
+		data ^= 0xFF;
+		goto adc_imm;
+	
+	case 0x2C:{// BIT abs
+		unsigned addr = GET_ADDR();
+		pc++;
+		nz = READ( addr );
+		goto bit_common;
+	}
+	
+	case 0x24: // BIT zp
+		nz = LOW_MEM( data );
+	bit_common:
+		status = (status & ~st_v) | (nz & st_v);
+		if ( !(a & nz) ) // use special encoding since N and Z might both be set
+			nz = ((nz & 0x80) << 4) ^ ~0xFF;
+		goto inc_pc_loop;
+		
+// Shift/Rotate
+
+	case 0x4A: // LSR A
+		c = 0;
+	case 0x6A: // ROR A
+		nz = (c >> 1) & 0x80; // could use bit insert macro here
+		c = a << 8;
+		nz |= a >> 1;
+		a = nz;
+		goto loop;
+
+	case 0x0A: // ASL A
+		nz = a << 1;
+		c = nz;
+		a = (uint8_t) nz;
+		goto loop;
+
+	case 0x2A: { // ROL A
+		nz = a << 1;
+		int temp = (c >> 8) & 1;
+		c = nz;
+		nz |= temp;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+	case 0x3E: // ROL abs,X
+		data += x;
+		goto rol_abs;
+	
+	case 0x1E: // ASL abs,X
+		data += x;
+	case 0x0E: // ASL abs
+		c = 0;
+	case 0x2E: // ROL abs
+	rol_abs:
+		HANDLE_PAGE_CROSSING( data );
+		ADD_PAGE
+		nz = (c >> 8) & 1;
+		nz |= (c = READ( data ) << 1);
+	rotate_common:
+		WRITE( data, (uint8_t) nz );
+		goto inc_pc_loop;
+	
+	case 0x7E: // ROR abs,X
+		data += x;
+		goto ror_abs;
+	
+	case 0x5E: // LSR abs,X
+		data += x;
+	case 0x4E: // LSR abs
+		c = 0;
+	case 0x6E: // ROR abs
+	ror_abs: {
+		HANDLE_PAGE_CROSSING( data );
+		ADD_PAGE
+		int temp = READ( data );
+		nz = ((c >> 1) & 0x80) | (temp >> 1);
+		c = temp << 8;
+		goto rotate_common;
+	}
+	
+	case 0x76: // ROR zp,x
+		data = uint8_t (data + x);
+		goto ror_zp;
+	
+	case 0x56: // LSR zp,x
+		data = uint8_t (data + x);
+	case 0x46: // LSR zp
+		c = 0;
+	case 0x66: // ROR zp
+	ror_zp: {
+		int temp = LOW_MEM( data );
+		nz = ((c >> 1) & 0x80) | (temp >> 1);
+		c = temp << 8;
+		goto write_nz_zp;
+	}
+	
+	case 0x36: // ROL zp,x
+		data = uint8_t (data + x);
+		goto rol_zp;
+	
+	case 0x16: // ASL zp,x
+		data = uint8_t (data + x);
+	case 0x06: // ASL zp
+		c = 0;
+	case 0x26: // ROL zp
+	rol_zp:
+		nz = (c >> 8) & 1;
+		nz |= (c = LOW_MEM( data ) << 1);
+		goto write_nz_zp;
+	
+// Increment/Decrement
+
+	case 0xE8: INC_DEC_XY( x, 1 )  // INX
+	case 0xCA: INC_DEC_XY( x, -1 ) // DEX
+	case 0x88: INC_DEC_XY( y, -1 ) // DEY
+	
+	case 0xF6: // INC zp,x
+		data = uint8_t (data + x);
+	case 0xE6: // INC zp
+		nz = 1;
+		goto add_nz_zp;
+	
+	case 0xD6: // DEC zp,x
+		data = uint8_t (data + x);
+	case 0xC6: // DEC zp
+		nz = -1;
+	add_nz_zp:
+		nz += LOW_MEM( data );
+	write_nz_zp:
+		LOW_MEM( data ) = nz;
+		goto inc_pc_loop;
+	
+	case 0xFE: // INC abs,x
+		HANDLE_PAGE_CROSSING( data + x );
+		data = x + GET_ADDR();
+		goto inc_ptr;
+	
+	case 0xEE: // INC abs
+		data = GET_ADDR();
+	inc_ptr:
+		nz = 1;
+		goto inc_common;
+	
+	case 0xDE: // DEC abs,x
+		HANDLE_PAGE_CROSSING( data + x );
+		data = x + GET_ADDR();
+		goto dec_ptr;
+	
+	case 0xCE: // DEC abs
+		data = GET_ADDR();
+	dec_ptr:
+		nz = -1;
+	inc_common:
+		nz += READ( data );
+		pc += 2;
+		WRITE( data, (uint8_t) nz );
+		goto loop;
+		
+// Transfer
+
+	case 0xAA: // TAX
+		x = a;
+	case 0x8A: // TXA
+		a = nz = x;
+		goto loop;
+
+	case 0x9A: // TXS
+		SET_SP( x ); // verified (no flag change)
+		goto loop;
+	
+	case 0xBA: // TSX
+		x = nz = GET_SP();
+		goto loop;
+	
+// Stack
+	
+	case 0x48: // PHA
+		PUSH( a ); // verified
+		goto loop;
+		
+	case 0x68: // PLA
+		a = nz = POP();
+		goto loop;
+		
+	{
+		int temp;
+	case 0x40: // RTI
+		temp = POP();
+		pc = POP();
+		pc |= POP() << 8;
+		goto set_status;
+	case 0x28: // PLP
+		temp = POP();
+	set_status:
+		#if !NES_CPU_IRQ_SUPPORT
+			SET_STATUS( temp );
+			goto loop;
+		#endif
+		data = status & st_i;
+		SET_STATUS( temp );
+		if ( !(data & ~status) )
+			goto loop;
+		result = result_cli; // I flag was just cleared
+		goto end;
+	}
+	
+	case 0x08: { // PHP
+		int temp;
+		CALC_STATUS( temp );
+		temp |= st_b | st_r;
+		PUSH( temp );
+		goto loop;
+	}
+	
+	case 0x6C: // JMP (ind)
+		data = GET_ADDR();
+		pc = READ( data );
+		data++;
+		pc |= READ( data ) << 8;
+		goto loop;
+	
+	case 0x00: { // BRK
+		pc += 2; // verified
+		PUSH( pc >> 8 );
+		PUSH( pc );
+		status |= st_i;
+		int temp;
+		CALC_STATUS( temp );
+		PUSH( temp | st_b | st_r );
+		pc = READ_PROG16( 0xFFFE );
+		goto loop;
+	}
+	
+// Flags
+
+	case 0x38: // SEC
+		c = ~0;
+		goto loop;
+	
+	case 0x18: // CLC
+		c = 0;
+		goto loop;
+		
+	case 0xB8: // CLV
+		status &= ~st_v;
+		goto loop;
+	
+	case 0xD8: // CLD
+		status &= ~st_d;
+		goto loop;
+	
+	case 0xF8: // SED
+		status |= st_d;
+		goto loop;
+	
+	case 0x58: // CLI
+		#if !NES_CPU_IRQ_SUPPORT
+			status &= ~st_i;
+			goto loop;
+		#endif
+		if ( !(status & st_i) )
+			goto loop;
+		status &= ~st_i;
+		result = result_cli;
+		goto end;
+		
+	case 0x78: // SEI
+		status |= st_i;
+		goto loop;
+
+// undocumented
+
+	case 0x0C: case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: // SKW
+		pc++;
+	case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB
+	case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
+		goto inc_pc_loop;
+
+	case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP
+		goto loop;
+
+// unimplemented
+	
+	case 0x9B: // TAS
+	case 0x9C: // SAY
+	case 0x9E: // XAS
+	case 0x93: // AXA
+	case 0x9F: // AXA
+	case 0x0B: // ANC
+	case 0x2B: // ANC
+	case 0xBB: // LAS
+	case 0x4B: // ALR
+	case 0x6B: // AAR
+	case 0x8B: // XAA
+	case 0xAB: // OAL
+	case 0xCB: // SAX
+	case 0x02: case 0x12: case 0x22: case 0x32: // ?
+	case 0x42: case 0x52: case 0x62: case 0x72:
+	case 0x92: case 0xB2: case 0xD2: case 0xF2:
+	case 0x83: case 0x87: case 0x8F: case 0x97: // AXS
+	case 0xA3: case 0xA7: case 0xAF: case 0xB3: case 0xB7: case 0xBF: // LAX
+	case 0xE3: case 0xE7: case 0xEF: case 0xF3: case 0xF7: case 0xFB: case 0xFF: // INS
+	case 0xC3: case 0xC7: case 0xCF: case 0xD3: case 0xD7: case 0xDB: case 0xDF: // DCM
+	case 0x63: case 0x67: case 0x6F: case 0x73: case 0x77: case 0x7B: case 0x7F: // RRA
+	case 0x43: case 0x47: case 0x4F: case 0x53: case 0x57: case 0x5B: case 0x5F: // LSE
+	case 0x23: case 0x27: case 0x2F: case 0x33: case 0x37: case 0x3B: case 0x3F: // RLA
+	case 0x03: case 0x07: case 0x0F: case 0x13: case 0x17: case 0x1B: case 0x1F: // ASO
+		result = result_badop;
+		goto stop;
+	}
+	assert( false );
+	
+stop:
+	pc--;
+end:
+	
+	{
+		int temp;
+		CALC_STATUS( temp );
+		r.status = temp;
+	}
+	
+	base_time += cycle_count;
+	#if NES_CPU_IRQ_SUPPORT
+		this->cycle_limit -= cycle_count;
+	#endif
+	this->cycle_count = 0;
+	r.pc = pc;
+	r.sp = GET_SP();
+	r.a = a;
+	r.x = x;
+	r.y = y;
+	
+	return result;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Cpu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,123 @@
+
+// Nintendo Entertainment System (NES) 6502 CPU emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef NES_CPU_H
+#define NES_CPU_H
+
+#include "blargg_common.h"
+
+typedef long nes_time_t;     // clock cycle count
+typedef unsigned nes_addr_t; // 16-bit address
+
+class Nsf_Emu;
+
+#ifndef NES_CPU_IRQ_SUPPORT
+	#define NES_CPU_IRQ_SUPPORT 0
+#endif
+
+class Nes_Cpu {
+	typedef BOOST::uint8_t uint8_t;
+	enum { page_bits = 11 };
+	enum { page_count = 0x10000 >> page_bits };
+	const uint8_t* code_map [page_count + 1];
+public:
+	Nes_Cpu();
+	
+	// Clear registers, unmap memory, and map code pages to unmapped_page.
+	void reset( const void* unmapped_page = NULL );
+	
+	// Memory read/write function types. Reader must return value from 0 to 255.
+	Nsf_Emu* callback_data;
+	typedef int  (*reader_t)( Nsf_Emu* callback_data, nes_addr_t );
+	typedef void (*writer_t)( Nsf_Emu* callback_data, nes_addr_t, int value );
+	
+	// Memory mapping functions take a block of memory of specified 'start' address
+	// and 'size' in bytes. Both start address and size must be a multiple of page_size.
+	enum { page_size = 1L << page_bits };
+	
+	// Map code memory to 'code' (memory accessed via the program counter)
+	void map_code( nes_addr_t start, unsigned long size, const void* code );
+	
+	// Map data memory to read and write functions
+	void map_memory( nes_addr_t start, unsigned long size, reader_t, writer_t );
+	
+	// Access memory as the emulated CPU does.
+	int  read( nes_addr_t );
+	void write( nes_addr_t, int value );
+	uint8_t* get_code( nes_addr_t );
+	
+	// NES 6502 registers. *Not* kept updated during a call to run().
+	struct registers_t {
+		BOOST::uint16_t pc;
+		uint8_t a;
+		uint8_t x;
+		uint8_t y;
+		uint8_t status;
+		uint8_t sp;
+	} r;
+	
+	// Reasons that run() returns
+	enum result_t {
+		result_cycles,  // Requested number of cycles (or more) were executed
+		result_cli,     // I flag cleared
+		result_badop    // unimplemented/illegal instruction
+	};
+	
+	// Run CPU to or after end_time, or until a stop reason from above
+	// is encountered. Return the reason for stopping.
+	result_t run( nes_time_t end_time );
+	
+	nes_time_t time() const             { return base_time + cycle_count; }
+	void time( nes_time_t t );
+	nes_time_t end_time() const         { return base_time + cycle_limit; }
+	void end_frame( nes_time_t );
+#if NES_CPU_IRQ_SUPPORT
+	void end_time( nes_time_t t )       { cycle_limit = t - base_time; }
+#endif
+	
+private:
+	// noncopyable
+	Nes_Cpu( const Nes_Cpu& );
+	Nes_Cpu& operator = ( const Nes_Cpu& );
+	
+	nes_time_t cycle_count;
+	nes_time_t base_time;
+#if NES_CPU_IRQ_SUPPORT
+	nes_time_t cycle_limit;
+#else
+	enum { cycle_limit = 0 };
+#endif
+	
+	reader_t data_reader [page_count + 1]; // extra entry to catch address overflow
+	writer_t data_writer [page_count + 1];
+
+public:
+	// low_mem is a full page size so it can be mapped with code_map
+	uint8_t low_mem [page_size > 0x800 ? page_size : 0x800];
+};
+
+	inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr ) {
+		return (uint8_t*) &code_map [(addr) >> page_bits] [(addr) & (page_size - 1)];
+	}
+	
+#if NES_CPU_IRQ_SUPPORT
+	inline void Nes_Cpu::time( nes_time_t t ) {
+		t -= time();
+		cycle_limit -= t;
+		base_time += t;
+	}
+#else
+	inline void Nes_Cpu::time( nes_time_t t ) {
+		cycle_count = t - base_time;
+	}
+#endif
+	
+	inline void Nes_Cpu::end_frame( nes_time_t end_time ) {
+		base_time -= end_time;
+		assert( time() >= 0 );
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Namco.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,146 @@
+
+// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
+
+#include "Nes_Namco.h"
+
+#include "Tagged_Data.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Nes_Namco::Nes_Namco()
+{
+	output( NULL );
+	volume( 1.0 );
+	reset();
+}
+
+Nes_Namco::~Nes_Namco() {
+}
+
+void Nes_Namco::reset()
+{
+	addr_reg = 0;
+	
+	int i;
+	for ( i = 0; i < reg_count; i++ )
+		reg [i] = 0;
+	
+	for ( i = 0; i < osc_count; i++ ) {
+		Namco_Osc& osc = oscs [i];
+		osc.delay = 0;
+		osc.last_amp = 0;
+		osc.wave_pos = 0;
+	}
+}
+
+void Nes_Namco::output( Blip_Buffer* buf )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, buf );
+}
+
+BOOST::uint8_t& Nes_Namco::access()
+{
+	int addr = addr_reg & 0x7f;
+	if ( addr_reg & 0x80 )
+		addr_reg = (addr + 1) | 0x80;
+	return reg [addr];
+}
+
+void Nes_Namco::reflect_state( Tagged_Data& data )
+{
+	reflect_int16( data, 'ADDR', &addr_reg );
+	
+	static const char hex [17] = "0123456789ABCDEF";
+	int i;
+	for ( i = 0; i < reg_count; i++ )
+		reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], &reg [i] );
+	
+	for ( i = 0; i < osc_count; i++ ) {
+		reflect_int32( data, 'DLY0' + i, &oscs [i].delay );
+		reflect_int16( data, 'POS0' + i, &oscs [i].wave_pos );
+	}
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Nes_Namco::run_until( nes_time_t nes_end_time )
+{
+	int active_oscs = ((reg [0x7f] >> 4) & 7) + 1;
+	for ( int i = osc_count - active_oscs; i < osc_count; i++ )
+	{
+		Namco_Osc& osc = oscs [i];
+		Blip_Buffer* output = osc.output;
+		if ( !output )
+			continue;
+		
+		Blip_Buffer::resampled_time_t time =
+				output->resampled_time( last_time ) + osc.delay;
+		Blip_Buffer::resampled_time_t end_time = output->resampled_time( nes_end_time );
+		osc.delay = 0;
+		if ( time < end_time )
+		{
+			const BOOST::uint8_t* osc_reg = &reg [i * 8 + 0x40];
+			if ( !(osc_reg [4] & 0xe0) )
+				continue;
+			
+			int volume = osc_reg [7] & 15;
+			if ( !volume )
+				continue;
+			
+			long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
+			if ( !freq )
+				continue;
+			Blip_Buffer::resampled_time_t period =
+					output->resampled_duration( 983040 ) / freq * active_oscs;
+			
+			int wave_size = (8 - ((osc_reg [4] >> 2) & 7)) * 4;
+			if ( !wave_size )
+				continue;
+			
+			int last_amp = osc.last_amp;
+			int wave_pos = osc.wave_pos;
+			
+			do {
+				// read wave sample
+				int addr = wave_pos + osc_reg [6];
+				int sample = reg [addr >> 1];
+				wave_pos++;
+				if ( addr & 1 )
+					sample >>= 4;
+				sample = (sample & 15) * volume;
+				
+				// output impulse if amplitude changed
+				int delta = sample - last_amp;
+				if ( delta ) {
+					last_amp = sample;
+					synth.offset_resampled( time, delta, output );
+				}
+				
+				// next sample
+				time += period;
+				if ( wave_pos >= wave_size )
+					wave_pos = 0;
+			}
+			while ( time < end_time );
+			
+			osc.wave_pos = wave_pos;
+			osc.last_amp = last_amp;
+		}
+		osc.delay = time - end_time;
+	}
+	
+	last_time = nes_end_time;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Namco.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,99 @@
+
+// Namco 106 sound chip emulator
+
+// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef NES_NAMCO_H
+#define NES_NAMCO_H
+
+#include "Nes_Apu.h"
+
+class Tagged_Data;
+
+class Nes_Namco {
+public:
+	Nes_Namco();
+	~Nes_Namco();
+	
+	// See Nes_Apu.h for reference.
+	
+	void volume( double );
+	void treble_eq( const blip_eq_t& );
+	void output( Blip_Buffer* );
+	enum { osc_count = 8 };
+	void osc_output( int index, Blip_Buffer* );
+	void reset();
+	
+	// Read/write data register is at $4800
+	enum { data_reg_addr = 0x4800 };
+	void write_data( nes_time_t, int );
+	int read_data();
+	
+	// Write-only address register is at $F800
+	enum { addr_reg_addr = 0xF800 };
+	void write_addr( int );
+	
+	void end_frame( nes_time_t );
+	void reflect_state( Tagged_Data& );
+	
+// End of public interface
+
+private:
+	// noncopyable
+	Nes_Namco( const Nes_Namco& );
+	Nes_Namco& operator = ( const Nes_Namco& );
+	
+	struct Namco_Osc {
+		long delay;
+		Blip_Buffer* output;
+		short last_amp;
+		short wave_pos;
+	};
+	
+	Namco_Osc oscs [osc_count];
+	
+	nes_time_t last_time;
+	int addr_reg;
+	
+	enum { reg_count = 0x80 };
+	BOOST::uint8_t reg [reg_count];
+	Blip_Synth<blip_good_quality,15> synth;
+	
+	BOOST::uint8_t& access();
+	void run_until( nes_time_t );
+};
+
+	inline void Nes_Namco::volume( double v ) {
+		synth.volume( 0.10 / osc_count * v );
+	}
+
+	inline void Nes_Namco::treble_eq( const blip_eq_t& eq ) {
+		synth.treble_eq( eq );
+	}
+
+	inline void Nes_Namco::osc_output( int i, Blip_Buffer* buf ) {
+		assert( (unsigned) i < osc_count );
+		oscs [i].output = buf;
+	}
+
+	inline void Nes_Namco::write_addr( int v ) {
+		addr_reg = v;
+	}
+
+	inline int Nes_Namco::read_data() {
+		return access();
+	}
+
+	inline void Nes_Namco::write_data( nes_time_t time, int data ) {
+		run_until( time );
+		access() = data;
+	}
+
+	inline void Nes_Namco::end_frame( nes_time_t time ) {
+		run_until( time );
+		last_time -= time;
+		assert( last_time >= 0 );
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Oscs.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,485 @@
+
+// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
+
+#include "Nes_Apu.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Nes_Osc
+
+void Nes_Osc::clock_length( int halt_mask )
+{
+	if ( length_counter && !(regs [0] & halt_mask) )
+		length_counter--;
+}
+
+void Nes_Envelope::clock_envelope()
+{
+	int period = regs [0] & 15;
+	if ( reg_written [3] ) {
+		reg_written [3] = false;
+		env_delay = period;
+		envelope = 15;
+	}
+	else if ( --env_delay < 0 ) {
+		env_delay = period;
+		if ( envelope | (regs [0] & 0x20) )
+			envelope = (envelope - 1) & 15;
+	}
+}
+
+int Nes_Envelope::volume() const
+{
+	return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope;
+}
+
+// Nes_Square
+
+void Nes_Square::clock_sweep( int negative_adjust )
+{
+	int sweep = regs [1];
+	
+	if ( --sweep_delay < 0 )
+	{
+		reg_written [1] = true;
+		
+		int period = this->period();
+		int shift = sweep & shift_mask;
+		if ( shift && (sweep & 0x80) && period >= 8 )
+		{
+			int offset = period >> shift;
+			
+			if ( sweep & negate_flag )
+				offset = negative_adjust - offset;
+			
+			if ( period + offset < 0x800 )
+			{
+				period += offset;
+				// rewrite period
+				regs [2] = period & 0xff;
+				regs [3] = (regs [3] & ~7) | ((period >> 8) & 7);
+			}
+		}
+	}
+	
+	if ( reg_written [1] ) {
+		reg_written [1] = false;
+		sweep_delay = (sweep >> 4) & 7;
+	}
+}
+
+void Nes_Square::run( nes_time_t time, nes_time_t end_time )
+{
+	if ( !output )
+		return;
+	
+	const int volume = this->volume();
+	const int period = this->period();
+	int offset = period >> (regs [1] & shift_mask);
+	if ( regs [1] & negate_flag )
+		offset = 0;
+	
+	const int timer_period = (period + 1) * 2;
+	if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
+	{
+		if ( last_amp ) {
+			synth->offset( time, -last_amp, output );
+			last_amp = 0;
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			// maintain proper phase
+			int count = (end_time - time + timer_period - 1) / timer_period;
+			phase = (phase + count) & (phase_range - 1);
+			time += (long) count * timer_period;
+		}
+	}
+	else
+	{
+		// handle duty select
+		int duty_select = (regs [0] >> 6) & 3;
+		int duty = 1 << duty_select; // 1, 2, 4, 2
+		int amp = 0;
+		if ( duty_select == 3 ) {
+			duty = 2; // negated 25%
+			amp = volume;
+		}
+		if ( phase < duty )
+			amp ^= volume;
+		
+		int delta = update_amp( amp );
+		if ( delta )
+			synth->offset( time, delta, output );
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			Blip_Buffer* const output = this->output;
+			const Synth* synth = this->synth;
+			int delta = amp * 2 - volume;
+			int phase = this->phase;
+			
+			do {
+				phase = (phase + 1) & (phase_range - 1);
+				if ( phase == 0 || phase == duty ) {
+					delta = -delta;
+					synth->offset_inline( time, delta, output );
+				}
+				time += timer_period;
+			}
+			while ( time < end_time );
+			
+			last_amp = (delta + volume) >> 1;
+			this->phase = phase;
+		}
+	}
+	
+	delay = time - end_time;
+}
+
+// Nes_Triangle
+
+void Nes_Triangle::clock_linear_counter()
+{
+	if ( reg_written [3] )
+		linear_counter = regs [0] & 0x7f;
+	else if ( linear_counter )
+		linear_counter--;
+	
+	if ( !(regs [0] & 0x80) )
+		reg_written [3] = false;
+}
+
+void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
+{
+	if ( !output )
+		return;
+	
+	// to do: track phase when period < 3
+	// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
+	
+	time += delay;
+	const int timer_period = period() + 1;
+	if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 )
+	{
+		time = end_time;
+	}
+	else if ( time < end_time )
+	{
+		Blip_Buffer* const output = this->output;
+		
+		int phase = this->phase;
+		int volume = 1;
+		if ( phase > phase_range ) {
+			phase -= phase_range;
+			volume = -volume;
+		}
+		
+		do {
+			if ( --phase == 0 ) {
+				phase = phase_range;
+				volume = -volume;
+			}
+			else {
+				synth.offset_inline( time, volume, output );
+			}
+			
+			time += timer_period;
+		}
+		while ( time < end_time );
+		
+		if ( volume < 0 )
+			phase += phase_range;
+		this->phase = phase;
+ 	}
+	delay = time - end_time;
+}
+
+// Nes_Dmc
+
+void Nes_Dmc::reset()
+{
+	address = 0;
+	dac = 0;
+	buf = 0;
+	bits_remain = 1;
+	bits = 0;
+	buf_empty = true;
+	silence = true;
+	next_irq = Nes_Apu::no_irq;
+	irq_flag = false;
+	irq_enabled = false;
+	
+	Nes_Osc::reset();
+	period = 0x036;
+}
+
+void Nes_Dmc::recalc_irq()
+{
+	nes_time_t irq = Nes_Apu::no_irq;
+	if ( irq_enabled && length_counter )
+		irq = apu->last_time + delay +
+				((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1;
+	if ( irq != next_irq ) {
+		next_irq = irq;
+		apu->irq_changed();
+	}
+}
+
+int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const
+{
+	if ( last_read )
+		*last_read = time;
+	
+	if ( length_counter == 0 )
+		return 0; // not reading
+	
+	long first_read = apu->last_time + delay + long (bits_remain - 1) * period;
+	long avail = time - first_read;
+	if ( avail <= 0 )
+		return 0;
+	
+	int count = (avail - 1) / (period * 8) + 1;
+	if ( !(regs [0] & loop_flag) && count > length_counter )
+		count = length_counter;
+	
+	if ( last_read ) {
+		*last_read = first_read + (count - 1) * (period * 8) + 1;
+		assert( *last_read <= time );
+		assert( count == count_reads( *last_read, NULL ) );
+		assert( count - 1 == count_reads( *last_read - 1, NULL ) );
+	}
+	
+	return count;
+}
+
+static const short dmc_period_table [2] [16] = {
+	0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC
+	0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036,
+	
+	0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested)
+	0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032  // to do: verify PAL periods
+};
+
+inline void Nes_Dmc::reload_sample()
+{
+	address = 0x4000 + regs [2] * 0x40;
+	length_counter = regs [3] * 0x10 + 1;
+}
+
+static const unsigned char dac_table [128] = {
+	 0,  0,  1,  2,  2,  3,  3,  4,  5,  5,  6,  7,  7,  8,  8,  9,
+	10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17, 18, 18,
+	19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26,
+	27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 32, 33, 33, 34,
+	34, 35, 35, 35, 36, 36, 37, 37, 38, 38, 38, 39, 39, 40, 40, 40,
+	41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 47,
+	47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 52, 52,
+	52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57
+};
+
+void Nes_Dmc::write_register( int addr, int data )
+{
+	if ( addr == 0 ) {
+		period = dmc_period_table [pal_mode] [data & 15];
+		irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled
+		irq_flag &= irq_enabled;
+		recalc_irq();
+	}
+	else if ( addr == 1 )
+	{
+		if ( !nonlinear )
+		{
+			// adjust last_amp so that "pop" amplitude will be properly non-linear
+			// with respect to change in dac
+			int old_amp = dac_table [dac];
+			dac = data & 0x7F;
+			int diff = dac_table [dac] - old_amp;
+			last_amp = dac - diff;
+		}
+		
+		dac = data & 0x7F;
+	}
+}
+
+void Nes_Dmc::start()
+{
+	reload_sample();
+	fill_buffer();
+	recalc_irq();
+}
+
+void Nes_Dmc::fill_buffer()
+{
+	if ( buf_empty && length_counter )
+	{
+		require( rom_reader ); // dmc_reader must be set
+		buf = rom_reader( rom_reader_data, 0x8000u + address );
+		address = (address + 1) & 0x7FFF;
+		buf_empty = false;
+		if ( --length_counter == 0 )
+		{
+			if ( regs [0] & loop_flag ) {
+				reload_sample();
+			}
+			else {
+				apu->osc_enables &= ~0x10;
+				irq_flag = irq_enabled;
+				next_irq = Nes_Apu::no_irq;
+				apu->irq_changed();
+			}
+		}
+	}
+}
+
+void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
+{
+	if ( !output )
+		return;
+	
+	int delta = update_amp( dac );
+	if ( delta )
+		synth.offset( time, delta, output );
+	
+	time += delay;
+	if ( time < end_time )
+	{
+		int bits_remain = this->bits_remain;
+		if ( silence && buf_empty )
+		{
+			int count = (end_time - time + period - 1) / period;
+			bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1;
+			time += count * period;
+		}
+		else
+		{
+			Blip_Buffer* const output = this->output;
+			const int period = this->period;
+			int bits = this->bits;
+			int dac = this->dac;
+			
+			do
+			{
+				if ( !silence )
+				{
+					const int step = (bits & 1) * 4 - 2;
+					bits >>= 1;
+					if ( unsigned (dac + step) <= 0x7F ) {
+						dac += step;
+						synth.offset_inline( time, step, output );
+					}
+				}
+				
+				time += period;
+				
+				if ( --bits_remain == 0 )
+				{
+					bits_remain = 8;
+					if ( buf_empty ) {
+						silence = true;
+					}
+					else {
+						silence = false;
+						bits = buf;
+						buf_empty = true;
+						fill_buffer();
+					}
+				}
+			}
+			while ( time < end_time );
+			
+			this->dac = dac;
+			this->last_amp = dac;
+			this->bits = bits;
+		}
+		this->bits_remain = bits_remain;
+	}
+	delay = time - end_time;
+}
+
+// Nes_Noise
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+static const short noise_period_table [16] = {
+	0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0,
+	0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4
+};
+
+void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
+{
+	if ( !output )
+		return;
+	
+	const int volume = this->volume();
+	int amp = (noise & 1) ? volume : 0;
+	int delta = update_amp( amp );
+	if ( delta )
+		synth.offset( time, delta, output );
+	
+	time += delay;
+	if ( time < end_time )
+	{
+		const int mode_flag = 0x80;
+		
+		int period = noise_period_table [regs [2] & 15];
+		if ( !volume )
+		{
+			// round to next multiple of period
+			time += (end_time - time + period - 1) / period * period;
+			
+			// approximate noise cycling while muted, by shuffling up noise register a bit
+			// to do: precise muted noise cycling?
+			if ( !(regs [2] & mode_flag) ) {
+				int feedback = (noise << 13) ^ (noise << 14);
+				noise = (feedback & 0x4000) | (noise >> 1);
+			}
+		}
+		else
+		{
+			Blip_Buffer* const output = this->output;
+			
+			// using resampled time avoids conversion in synth.offset()
+			Blip_Buffer::resampled_time_t rperiod = output->resampled_duration( period );
+			Blip_Buffer::resampled_time_t rtime = output->resampled_time( time );
+			
+			int noise = this->noise;
+			int delta = amp * 2 - volume;
+			const int tap = (regs [2] & mode_flag ? 8 : 13);
+			
+			do {
+				int feedback = (noise << tap) ^ (noise << 14);
+				time += period;
+				
+				if ( (noise + 1) & 2 ) {
+					// bits 0 and 1 of noise differ
+					delta = -delta;
+					synth.offset_resampled( rtime, delta, output );
+				}
+				
+				rtime += rperiod;
+				noise = (feedback & 0x4000) | (noise >> 1);
+			}
+			while ( time < end_time );
+			
+			last_amp = (delta + volume) >> 1;
+			this->noise = noise;
+		}
+	}
+	
+	delay = time - end_time;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Oscs.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,141 @@
+
+// Private oscillators used by Nes_Apu
+
+// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef NES_OSCS_H
+#define NES_OSCS_H
+
+#include "Blip_Buffer.h"
+
+class Nes_Apu;
+
+struct Nes_Osc
+{
+	unsigned char regs [4];
+	bool reg_written [4];
+	Blip_Buffer* output;
+	int length_counter;// length counter (0 if unused by oscillator)
+	int delay;      // delay until next (potential) transition
+	int last_amp;   // last amplitude oscillator was outputting
+	
+	void clock_length( int halt_mask );
+	int period() const {
+		return (regs [3] & 7) * 0x100 + (regs [2] & 0xff);
+	}
+	void reset() {
+		delay = 0;
+		last_amp = 0;
+	}
+	int update_amp( int amp ) {
+		int delta = amp - last_amp;
+		last_amp = amp;
+		return delta;
+	}
+};
+
+struct Nes_Envelope : Nes_Osc
+{
+	int envelope;
+	int env_delay;
+	
+	void clock_envelope();
+	int volume() const;
+	void reset() {
+		envelope = 0;
+		env_delay = 0;
+		Nes_Osc::reset();
+	}
+};
+
+// Nes_Square
+struct Nes_Square : Nes_Envelope
+{
+	enum { negate_flag = 0x08 };
+	enum { shift_mask = 0x07 };
+	enum { phase_range = 8 };
+	int phase;
+	int sweep_delay;
+	
+	typedef Blip_Synth<blip_good_quality,15> Synth;
+	const Synth* synth; // shared between squares
+	
+	void clock_sweep( int adjust );
+	void run( nes_time_t, nes_time_t );
+	void reset() {
+		sweep_delay = 0;
+		Nes_Envelope::reset();
+	}
+};
+
+// Nes_Triangle
+struct Nes_Triangle : Nes_Osc
+{
+	enum { phase_range = 16 };
+	int phase;
+	int linear_counter;
+	Blip_Synth<blip_good_quality,15> synth;
+	
+	void run( nes_time_t, nes_time_t );
+	void clock_linear_counter();
+	void reset() {
+		linear_counter = 0;
+		phase = phase_range;
+		Nes_Osc::reset();
+	}
+};
+
+// Nes_Noise
+struct Nes_Noise : Nes_Envelope
+{
+	int noise;
+	Blip_Synth<blip_med_quality,15> synth;
+	
+	void run( nes_time_t, nes_time_t );
+	void reset() {
+		noise = 1 << 14;
+		Nes_Envelope::reset();
+	}
+};
+
+// Nes_Dmc
+struct Nes_Dmc : Nes_Osc
+{
+	int address;    // address of next byte to read
+	int period;
+	//int length_counter; // bytes remaining to play (already defined in Nes_Osc)
+	int buf;
+	int bits_remain;
+	int bits;
+	bool buf_empty;
+	bool silence;
+	
+	enum { loop_flag = 0x40 };
+	
+	int dac;
+	
+	nes_time_t next_irq;
+	bool irq_enabled;
+	bool irq_flag;
+	bool pal_mode;
+	bool nonlinear;
+	
+	int (*rom_reader)( void*, nes_addr_t ); // needs to be initialized to rom read function
+	void* rom_reader_data;
+	
+	Nes_Apu* apu;
+	
+	Blip_Synth<blip_med_quality,127> synth;
+	
+	void start();
+	void write_register( int, int );
+	void run( nes_time_t, nes_time_t );
+	void recalc_irq();
+	void fill_buffer();
+	void reload_sample();
+	void reset();
+	int count_reads( nes_time_t, nes_time_t* ) const;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Vrc6.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,210 @@
+
+// Nes_Snd_Emu 0.1.6. http://www.slack.net/~ant/libs/
+
+#include "Nes_Vrc6.h"
+
+#include "Tagged_Data.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Nes_Vrc6::Nes_Vrc6()
+{
+	output( NULL );
+	volume( 1.0 );
+	reset();
+}
+
+void Nes_Vrc6::reset()
+{
+	last_time = 0;
+	for ( int i = 0; i < osc_count; i++ ) {
+		Vrc6_Osc& osc = oscs [i];
+		for ( int j = 0; j < reg_count; j++ )
+			osc.regs [j] = 0;
+		osc.delay = 0;
+		osc.last_amp = 0;
+		osc.phase = 1;
+		osc.amp = 0;
+	}
+}
+
+Nes_Vrc6::~Nes_Vrc6() {
+}
+
+void Nes_Vrc6::volume( double v )
+{
+	v *= 0.0967 * 2;
+	saw_synth.volume( v );
+	square_synth.volume( v * 0.5 );
+}
+
+void Nes_Vrc6::treble_eq( const blip_eq_t& eq )
+{
+	saw_synth.treble_eq( eq );
+	square_synth.treble_eq( eq );
+}
+
+void Nes_Vrc6::output( Blip_Buffer* buf )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, buf );
+}
+
+void Nes_Vrc6::run_until( nes_time_t time )
+{
+	run_square( oscs [0], time );
+	run_square( oscs [1], time );
+	run_saw( time );
+	last_time = time;
+}
+
+void Nes_Vrc6::write_osc( nes_time_t time, int osc_index, int reg, int data )
+{
+	require( (unsigned) osc_index < osc_count );
+	require( (unsigned) reg < reg_count );
+	
+	run_until( time );
+	oscs [osc_index].regs [reg] = data;
+	
+	// to do: remove? this messed up volume envelope in Akumajou Densetsu track 22
+	//if ( osc_index == 2 && reg == 2 )
+	//  oscs [2].amp = 0;
+}
+
+void Nes_Vrc6::end_frame( nes_time_t time )
+{
+	run_until( time );
+	last_time -= time;
+	assert( last_time >= 0 );
+}
+
+void Nes_Vrc6::reflect_state( Tagged_Data& data )
+{
+	for ( int i = 0; i < osc_count; i++ )
+	{
+		Tagged_Data odata( data, 'cCH0' + i );
+		Vrc6_Osc& osc = oscs [i];
+		for ( int r = 0; r < reg_count; r++ )
+			reflect_int16( odata, 'REG0' + r, &osc.regs [r] );
+		reflect_int16( odata, 'DELY', &osc.delay );
+		reflect_int16( odata, 'PHAS', &osc.phase );
+		if ( i == 2 )
+			reflect_int16( odata, 'AMPL', &osc.amp );
+	}
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Nes_Vrc6::run_square( Vrc6_Osc& osc, nes_time_t end_time )
+{
+	Blip_Buffer* output = osc.output;
+	if ( !output )
+		return;
+	
+	int volume = osc.regs [0] & 15;
+	if ( !(osc.regs [2] & 0x80) )
+		volume = 0;
+	
+	int gate = osc.regs [0] & 0x80;
+	int duty = ((osc.regs [0] >> 4) & 7) + 1;
+	int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
+	nes_time_t time = last_time;
+	if ( delta ) {
+		osc.last_amp += delta;
+		square_synth.offset( time, delta, output );
+	}
+	
+	time += osc.delay;
+	osc.delay = 0;
+	int period = osc.period();
+	if ( volume && !gate && period > 4 )
+	{
+		if ( time < end_time )
+		{
+			int phase = osc.phase;
+			
+			do {
+				phase++;
+				if ( phase == 16 ) {
+					phase = 0;
+					osc.last_amp = volume;
+					square_synth.offset( time, volume, output );
+				}
+				if ( phase == duty ) {
+					osc.last_amp = 0;
+					square_synth.offset( time, -volume, output );
+				}
+				time += period;
+			}
+			while ( time < end_time );
+			
+			osc.phase = phase;
+		}
+		osc.delay = time - end_time;
+	}
+}
+
+void Nes_Vrc6::run_saw( nes_time_t end_time )
+{
+	Vrc6_Osc& osc = oscs [2];
+	Blip_Buffer* output = osc.output;
+	if ( !output )
+		return;
+	
+	int amp = osc.amp;
+	int amp_step = osc.regs [0] & 0x3F;
+	nes_time_t time = last_time;
+	int last_amp = osc.last_amp;
+	if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) )
+	{
+		osc.delay = 0;
+		int delta = (amp >> 3) - last_amp;
+		last_amp = amp >> 3;
+		saw_synth.offset( time, delta, output );
+	}
+	else
+	{
+		time += osc.delay;
+		if ( time < end_time )
+		{
+			int period = osc.period() * 2;
+			int phase = osc.phase;
+			
+			do {
+				if ( --phase == 0 ) {
+					phase = 7;
+					amp = 0;
+				}
+				
+				int delta = (amp >> 3) - last_amp;
+				if ( delta ) {
+					last_amp = amp >> 3;
+					saw_synth.offset( time, delta, output );
+				}
+				
+				time += period;
+				amp = (amp + amp_step) & 0xFF;
+			}
+			while ( time < end_time );
+			
+			osc.phase = phase;
+			osc.amp = amp;
+		}
+		
+		osc.delay = time - end_time;
+	}
+	
+	osc.last_amp = last_amp;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Vrc6.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,75 @@
+
+// Konami VRC6 sound chip emulator
+
+// Nes_Snd_Emu 0.1.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef NES_VRC6_H
+#define NES_VRC6_H
+
+#include "Nes_Apu.h"
+
+class Tagged_Data;
+
+class Nes_Vrc6 {
+public:
+	Nes_Vrc6();
+	~Nes_Vrc6();
+	
+	// See Nes_Apu.h for reference.
+	
+	void volume( double );
+	void treble_eq( const blip_eq_t& );
+	void output( Blip_Buffer* );
+	enum { osc_count = 3 };
+	void osc_output( int index, Blip_Buffer* );
+	void reset();
+	
+	// Oscillator 0 write-only registers are at $9000-$9002
+	// Oscillator 1 write-only registers are at $A000-$A002
+	// Oscillator 2 write-only registers are at $B000-$B002
+	enum { reg_count = 3 };
+	enum { base_addr = 0x9000 };
+	enum { addr_step = 0x1000 };
+	void write_osc( nes_time_t, int osc, int reg, int data );
+	
+	void end_frame( nes_time_t );
+	void reflect_state( Tagged_Data& );
+	
+// End of public interface
+
+private:
+	// noncopyable
+	Nes_Vrc6( const Nes_Vrc6& );
+	Nes_Vrc6& operator = ( const Nes_Vrc6& );
+	
+	struct Vrc6_Osc {
+		BOOST::uint8_t regs [3];
+		Blip_Buffer* output;
+		int delay;
+		int last_amp;
+		int phase;
+		int amp; // only used by saw
+		
+		int period() const {
+			return (regs [2] & 0x0f) * 0x100L + regs [1] + 1;
+		}
+	};
+	
+	Vrc6_Osc oscs [osc_count];
+	nes_time_t last_time;
+	
+	Blip_Synth<blip_med_quality,31> saw_synth;
+	Blip_Synth<blip_good_quality,15> square_synth;
+	
+	void run_until( nes_time_t );
+	void run_square( Vrc6_Osc& osc, nes_time_t );
+	void run_saw( nes_time_t );
+};
+
+	inline void Nes_Vrc6::osc_output( int i, Blip_Buffer* buf ) {
+		assert( (unsigned) i < osc_count );
+		oscs [i].output = buf;
+	}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nsf_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,473 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Nsf_Emu.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+const unsigned low_mem_size = 0x800;
+const unsigned page_size = 0x1000;
+const long ram_size = 0x10000;
+const nes_addr_t rom_begin = 0x8000;
+const nes_addr_t bank_select_addr = 0x5ff8;
+const nes_addr_t exram_addr = bank_select_addr - (bank_select_addr % Nes_Cpu::page_size);
+const int master_clock_divisor = 12;
+
+const int vrc6_flag = 0x01;
+const int namco_flag = 0x10;
+
+// ROM
+
+int Nsf_Emu::read_code( Nsf_Emu* emu, nes_addr_t addr )
+{
+	return *emu->cpu.get_code( addr );
+}
+
+void Nsf_Emu::write_exram( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	unsigned bank = addr - bank_select_addr;
+	if ( bank < bank_count )
+	{
+		if ( data < emu->total_banks ) {
+			emu->cpu.map_code( (bank + 8) * page_size, page_size,
+					&emu->rom [data * page_size] );
+		}
+		else {
+			dprintf( "Bank %d out of range (%d banks total)\n",
+					data, (int) emu->total_banks );
+		}
+	}
+}
+
+// APU
+
+int Nsf_Emu::read_snd( Nsf_Emu* emu, nes_addr_t addr )
+{
+	if ( addr == Nes_Apu::status_addr )
+		return emu->apu.read_status( emu->cpu.time() );
+	return addr >> 8; // high byte of address stays on bus
+}
+
+void Nsf_Emu::write_snd( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr )
+		 emu->apu.write_register( emu->cpu.time(), addr, data );
+}
+
+int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr )
+{
+	return ((Nsf_Emu*) emu)->cpu.read( addr );
+}
+
+// Low Mem
+
+int Nsf_Emu::read_low_mem( Nsf_Emu* emu, nes_addr_t addr )
+{
+	return emu->cpu.low_mem [addr & (low_mem_size - 1)];
+}
+
+void Nsf_Emu::write_low_mem( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	emu->cpu.low_mem [addr & (low_mem_size - 1)] = data;
+}
+
+// SRAM
+
+int Nsf_Emu::read_sram( Nsf_Emu* emu, nes_addr_t addr )
+{
+	return emu->sram [addr & (sram_size - 1)];
+}
+
+void Nsf_Emu::write_sram( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	emu->sram [addr & (sram_size - 1)] = data;
+}
+
+#if !NSF_EMU_APU_ONLY
+
+// Namco
+int Nsf_Emu::read_namco( Nsf_Emu* emu, nes_addr_t addr )
+{
+	if ( addr == Nes_Namco::data_reg_addr )
+		return emu->namco.read_data();
+	return addr >> 8;
+}
+
+void Nsf_Emu::write_namco( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	if ( addr == Nes_Namco::data_reg_addr )
+		emu->namco.write_data( emu->cpu.time(), data );
+}
+
+void Nsf_Emu::write_namco_addr( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	if ( addr == Nes_Namco::addr_reg_addr )
+		emu->namco.write_addr( data );
+}
+
+// VRC6
+void Nsf_Emu::write_vrc6( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	unsigned reg = addr & (Nes_Vrc6::addr_step - 1);
+	unsigned osc = unsigned (addr - Nes_Vrc6::base_addr) / Nes_Vrc6::addr_step;
+	if ( osc < Nes_Vrc6::osc_count && reg < Nes_Vrc6::reg_count )
+		emu->vrc6.write_osc( emu->cpu.time(), osc, reg, data );
+}
+
+#endif
+
+// Unmapped
+int Nsf_Emu::read_unmapped( Nsf_Emu*, nes_addr_t addr )
+{
+	dprintf( "Read unmapped $%.4X\n", (unsigned) addr );
+	return addr >> 8; // high byte of address stays on bus
+}
+
+void Nsf_Emu::write_unmapped( Nsf_Emu*, nes_addr_t addr, int )
+{
+	if (// some games write to $8000 and $8001 repeatedly
+		addr != 0x8000 && addr != 0x8001 &&
+		
+		// probably namco sound mistakenly turned on in mck
+		addr != 0x4800 && addr != 0xF800 &&
+		
+		// memory mapper?
+		addr != 0xFFF8 )
+	{
+		dprintf( "Write unmapped $%.4X\n", (unsigned) addr );
+	}
+}
+
+static BOOST::uint8_t unmapped_code [Nes_Cpu::page_size];
+
+Nsf_Emu::Nsf_Emu( double gain_ )
+{
+	rom = NULL;
+	play_addr = 0;
+	clocks_per_msec = 0;
+	gain = gain_;
+	cpu.callback_data = this;
+	set_equalizer( equalizer_t( -8.87, 8800, 110 ) );
+	apu.dmc_reader( pcm_read, this );
+	// set unmapped code to illegal instruction
+	memset( unmapped_code, 0x32, sizeof unmapped_code );
+}
+
+Nsf_Emu::~Nsf_Emu()
+{
+	unload();
+}
+
+void Nsf_Emu::unload()
+{
+	delete [] rom;
+	rom = NULL;
+}
+
+const char** Nsf_Emu::voice_names() const
+{
+	static const char* base_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC"
+	};
+	static const char* namco_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
+		"Namco 5&7", "Namco 4&6", "Namco 1-3"
+	};
+	static const char* vrc6_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
+		"VRC6 Square 1", "VRC6 Square 2", "VRC6 Saw"
+	};
+	if ( exp_flags & namco_flag )
+		return namco_names;
+	if ( exp_flags & vrc6_flag )
+		return vrc6_names;
+	return base_names;
+}
+
+blargg_err_t Nsf_Emu::init_sound()
+{
+	if ( exp_flags & ~(namco_flag | vrc6_flag) )
+		return "NSF requires unsupported expansion audio hardware";
+	
+	// map memory
+	cpu.reset( unmapped_code );
+	cpu.map_memory( 0, ram_size, read_unmapped, write_unmapped ); // unmapped
+	cpu.map_memory( 0, low_mem_size, read_low_mem, write_low_mem ); // low mem
+	cpu.map_code( 0, low_mem_size, cpu.low_mem );
+	cpu.map_memory( 0x4000, Nes_Cpu::page_size, read_snd, write_snd ); // apu
+	cpu.map_memory( exram_addr, Nes_Cpu::page_size, read_unmapped, write_exram ); // exram
+	cpu.map_memory( 0x6000, sram_size, read_sram, write_sram ); // sram
+	cpu.map_code  ( 0x6000, sram_size, sram );
+	cpu.map_memory( rom_begin, ram_size - rom_begin, read_code, write_unmapped ); // rom
+	
+	voice_count_ = Nes_Apu::osc_count;
+	
+	double adjusted_gain = gain;
+	
+#if NSF_EMU_APU_ONLY
+	if ( exp_flags )
+		return "NSF requires expansion audio hardware";
+#else
+	// namco
+	if ( exp_flags & namco_flag ) {
+		adjusted_gain *= 0.75;
+		voice_count_ += 3;
+		cpu.map_memory( Nes_Namco::data_reg_addr, Nes_Cpu::page_size,
+				read_namco, write_namco );
+		cpu.map_memory( Nes_Namco::addr_reg_addr, Nes_Cpu::page_size,
+				 read_code, write_namco_addr );
+	}
+	
+	// vrc6
+	if ( exp_flags & vrc6_flag ) {
+		adjusted_gain *= 0.75;
+		voice_count_ += 3;
+		for ( int i = 0; i < Nes_Vrc6::osc_count; i++ )
+			cpu.map_memory( Nes_Vrc6::base_addr + i * Nes_Vrc6::addr_step,
+					Nes_Cpu::page_size, read_code, write_vrc6 );
+	}
+	
+	namco.volume( adjusted_gain );
+	vrc6.volume( adjusted_gain );
+#endif
+	
+	apu.volume( adjusted_gain );
+	
+	return blargg_success;
+}
+
+void Nsf_Emu::update_eq( blip_eq_t const& eq )
+{
+#if !NSF_EMU_APU_ONLY
+	vrc6.treble_eq( eq );
+	namco.treble_eq( eq );
+#endif
+	apu.treble_eq( eq );
+}
+
+blargg_err_t Nsf_Emu::load( const header_t& h, Emu_Reader& in )
+{
+	unload();
+	
+	// check compatibility
+	if ( 0 != memcmp( h.tag, "NESM\x1A", 5 ) )
+		return "Not an NSF file";
+	if ( h.vers != 1 )
+		return "Unsupported NSF format";
+	
+	// sound and memory
+	exp_flags = h.chip_flags;
+	blargg_err_t err = init_sound();
+	if ( err )
+		return err;
+	
+	// set up data
+	nes_addr_t load_addr = get_le16( h.load_addr );
+	init_addr = get_le16( h.init_addr );
+	play_addr = get_le16( h.play_addr );
+	if ( !load_addr ) load_addr = rom_begin;
+	if ( !init_addr ) init_addr = rom_begin;
+	if ( !play_addr ) play_addr = rom_begin;
+	if ( load_addr < rom_begin || init_addr < rom_begin )
+		return "Invalid address in NSF";
+	
+	// set up rom
+	total_banks = (in.remain() + load_addr % page_size + page_size - 1) / page_size;
+	long rom_size = total_banks * page_size;
+	rom = new byte [rom_size];
+	if ( !rom )
+		return "Out of memory";
+	memset( rom, 0, rom_size );
+	err = in.read( &rom [load_addr % page_size], in.remain() );
+	if ( err ) {
+		unload();
+		return err;
+	}
+	
+	// bank switching
+	int first_bank = (load_addr - rom_begin) / page_size;
+	for ( int i = 0; i < bank_count; i++ )
+	{
+		unsigned bank = i - first_bank;
+		initial_banks [i] = (bank < total_banks) ? bank : 0;
+		
+		if ( h.banks [i] ) {
+			// bank-switched
+			memcpy( initial_banks, h.banks, sizeof initial_banks );
+			break;
+		}
+	}
+	
+	// playback rate
+	unsigned playback_rate = get_le16( h.ntsc_speed );
+	unsigned standard_rate = 0x411A;
+	double clock_rate = 1789772.72727;
+	play_period = 262 * 341L * 4 + 2;
+	pal_only = false;
+	
+	// use pal speed if there is no ntsc speed
+	if ( (h.speed_flags & 3) == 1 ) {
+		pal_only = true;
+		play_period = 33247 * master_clock_divisor;
+		clock_rate = 1662607.125;
+		standard_rate = 0x4E20;
+		playback_rate = get_le16( h.pal_speed );
+	}
+	
+	clocks_per_msec = clock_rate * (1.0 / 1000.0);
+	
+	// use custom playback rate if not the standard rate
+	if ( playback_rate && playback_rate != standard_rate )
+		play_period = long (clock_rate * playback_rate * master_clock_divisor /
+				1000000.0);
+	
+	// extra flags
+	int extra_flags = h.speed_flags;
+	#if !NSF_EMU_EXTRA_FLAGS
+		extra_flags = 0;
+	#endif
+	needs_long_frames = (extra_flags & 0x10) != 0;
+	initial_pcm_dac = (extra_flags & 0x20) ? 0x3F : 0;
+
+	track_count_ = h.track_count;
+	
+	return setup_buffer( clock_rate + 0.5 );
+}
+
+void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
+{
+#if !NSF_EMU_APU_ONLY
+	if ( i >= Nes_Apu::osc_count ) {
+		vrc6.osc_output( i - Nes_Apu::osc_count, buf );
+		if ( i < 7 ) {
+			i &= 1;
+			namco.osc_output( i + 4, buf );
+			namco.osc_output( i + 6, buf );
+		}
+		else {
+			for ( int n = 0; n < namco.osc_count / 2; n++ )
+				namco.osc_output( n, buf );
+		}
+		return;
+	}
+#endif
+	apu.osc_output( i, buf );
+}
+
+blargg_err_t Nsf_Emu::start_track( int track )
+{
+	require( rom ); // file must be loaded
+	
+	starting_track();
+	
+	// clear memory
+	memset( cpu.low_mem, 0, sizeof cpu.low_mem );
+	memset( sram, 0, sizeof sram );
+	
+	// initial rom banks
+	for ( int i = 0; i < bank_count; ++i )
+		cpu.write( bank_select_addr + i, initial_banks [i] );
+	
+	// reset sound
+	apu.reset( pal_only, initial_pcm_dac );
+	apu.write_register( 0, 0x4015, 0x0F );
+	apu.write_register( 0, 0x4017, needs_long_frames ? 0x80 : 0 );
+	
+#if !NSF_EMU_APU_ONLY
+	if ( exp_flags ) {
+		namco.reset();
+		vrc6.reset();
+	}
+#endif
+	
+	// reset cpu
+	cpu.r.pc = exram_addr;
+	cpu.r.a = track;
+	cpu.r.x = pal_only;
+	cpu.r.y = 0;
+	cpu.r.sp = 0xFF;
+	cpu.r.status = 0x04; // i flag
+	
+	// first call
+	cpu_jsr( init_addr, -1 );
+	next_play = 0;
+	play_extra = 0;
+	
+	return blargg_success;
+}
+
+void Nsf_Emu::cpu_jsr( nes_addr_t pc, int adj )
+{
+	unsigned ret_addr = cpu.r.pc + adj;
+	cpu.r.pc = pc;
+	cpu.low_mem [cpu.r.sp-- + 0x100] = ret_addr >> 8;
+	cpu.low_mem [cpu.r.sp-- + 0x100] = ret_addr;
+}
+
+blip_time_t Nsf_Emu::run( int msec, bool* )
+{
+	// run cpu
+	blip_time_t duration = clocks_per_msec * msec;
+	cpu.time( 0 );
+	while ( cpu.time() < duration )
+	{
+		// check for idle cpu
+		if ( cpu.r.pc == exram_addr )
+		{
+			if ( next_play > duration ) {
+				cpu.time( duration );
+				break;
+			}
+			
+			if ( next_play > cpu.time() )
+				cpu.time( next_play );
+			
+			nes_time_t period = (play_period + play_extra) / master_clock_divisor;
+			play_extra = play_period - period * master_clock_divisor;
+			next_play += period;
+			cpu_jsr( play_addr, -1 );
+		}
+		
+		Nes_Cpu::result_t result = cpu.run( duration );
+		if ( result == Nes_Cpu::result_badop && cpu.r.pc != exram_addr )
+		{
+			dprintf( "Bad opcode $%.2x at $%.4x\n",
+					(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
+			
+			return 0; // error
+		}
+	}
+	
+	// end time frame
+	duration = cpu.time();
+	next_play -= duration;
+	if ( next_play < 0 ) // could go negative if routine is taking too long to return
+		next_play = 0;
+	apu.end_frame( duration );
+	
+#if !NSF_EMU_APU_ONLY
+	if ( exp_flags & namco_flag )
+		namco.end_frame( duration );
+	if ( exp_flags & vrc6_flag )
+		vrc6.end_frame( duration );
+#endif
+	
+	return duration;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nsf_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,127 @@
+
+// Nintendo Entertainment System (NES) NSF-format game music file emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef NSF_EMU_H
+#define NSF_EMU_H
+
+#include "Classic_Emu.h"
+#include "Nes_Apu.h"
+#include "Nes_Cpu.h"
+
+// If NSF_EMU_APU_ONLY is non-zero, external sound chip support is disabled
+#if !NSF_EMU_APU_ONLY
+	#include "Nes_Vrc6.h"
+	#include "Nes_Namco.h"
+#endif
+
+class Nsf_Emu : public Classic_Emu {
+public:
+	// Set internal gain, where 1.0 results in almost no clamping. Default gain
+	// roughly matches volume of other emulators.
+	Nsf_Emu( double gain = 1.4 );
+	~Nsf_Emu();
+	
+	struct header_t {
+		char tag [5];
+		byte vers;
+		byte track_count;
+		byte first_track;
+		byte load_addr [2];
+		byte init_addr [2];
+		byte play_addr [2];
+		char game [32];
+		char author [32];
+		char copyright [32];
+		byte ntsc_speed [2];
+		byte banks [8];
+		byte pal_speed [2];
+		byte speed_flags;
+		byte chip_flags;
+		byte unused [4];
+		
+		enum { song = 0 }; // no song titles
+	};
+	BOOST_STATIC_ASSERT( sizeof (header_t) == 0x80 );
+	
+	// Load NSF, given its header and reader for remaining data
+	blargg_err_t load( const header_t&, Emu_Reader& );
+	
+	blargg_err_t start_track( int );
+	Nes_Apu* apu_() { return &apu; }
+	const char** voice_names() const;
+	
+
+// End of public interface
+protected:
+	void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+	void update_eq( blip_eq_t const& );
+	blip_time_t run( int, bool* );
+private:
+	// initial state
+	enum { bank_count = 8 };
+	byte initial_banks [bank_count];
+	int initial_pcm_dac;
+	double gain;
+	bool needs_long_frames;
+	bool pal_only;
+	unsigned init_addr;
+	unsigned play_addr;
+	int exp_flags;
+	
+	// timing
+	double clocks_per_msec;
+	nes_time_t next_play;
+	long play_period;
+	int play_extra;
+	nes_time_t clock() const;
+	nes_time_t next_irq( nes_time_t end_time );
+	static void irq_changed( void* );
+	
+	// rom
+	int total_banks;
+	byte* rom;
+	static int read_code( Nsf_Emu*, nes_addr_t );
+	void unload();
+	
+	// cpu
+	Nes_Cpu cpu;
+	void cpu_jsr( unsigned pc, int adj );
+	static int read_low_mem( Nsf_Emu*, nes_addr_t );
+	static void write_low_mem( Nsf_Emu*, nes_addr_t, int );
+	static int read_unmapped( Nsf_Emu*, nes_addr_t );
+	static void write_unmapped( Nsf_Emu*, nes_addr_t, int );
+	static void write_exram( Nsf_Emu*, nes_addr_t, int );
+	
+	blargg_err_t init_sound();
+	
+	// apu
+	Nes_Apu apu;
+	static int read_snd( Nsf_Emu*, nes_addr_t );
+	static void write_snd( Nsf_Emu*, nes_addr_t, int );
+	static int pcm_read( void*, nes_addr_t );
+	
+#if !NSF_EMU_APU_ONLY
+	// namco
+	Nes_Namco namco;
+	static int read_namco( Nsf_Emu*, nes_addr_t );
+	static void write_namco( Nsf_Emu*, nes_addr_t, int );
+	static void write_namco_addr( Nsf_Emu*, nes_addr_t, int );
+	
+	// vrc6
+	Nes_Vrc6 vrc6;
+	static void write_vrc6( Nsf_Emu*, nes_addr_t, int );
+#endif
+	
+	// sram
+	enum { sram_size = 0x2000 };
+	byte sram [sram_size];
+	static int read_sram( Nsf_Emu*, nes_addr_t );
+	static void write_sram( Nsf_Emu*, nes_addr_t, int );
+	
+	friend class Nsf_Remote_Emu; // hack
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Panning_Buffer.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,180 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Panning_Buffer.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+typedef long fixed_t;
+
+#define TO_FIXED( f )   fixed_t ((f) * (1L << 15) + 0.5)
+#define FMUL( x, y )    (((x) * (y)) >> 15)
+
+Panning_Buffer::Panning_Buffer()
+{
+	bufs = NULL;
+	buf_count = 0;
+	bass_freq_ = -1;
+	clock_rate_ = -1;
+}
+
+Panning_Buffer::~Panning_Buffer()
+{
+	delete [] bufs;
+}
+	
+blargg_err_t Panning_Buffer::sample_rate( long rate, int msec )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) );
+	sample_rate_ = rate;
+	length_ = buf_count ? bufs [0].length() : msec;
+	return blargg_success;
+}
+
+void Panning_Buffer::bass_freq( int freq )
+{
+	bass_freq_ = freq;
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].bass_freq( freq );
+}
+
+void Panning_Buffer::clock_rate( long rate )
+{
+	clock_rate_ = rate;
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].clock_rate( clock_rate_ );
+}
+
+void Panning_Buffer::clear()
+{
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].clear();
+}
+
+blargg_err_t Panning_Buffer::set_channel_count( int count )
+{
+	count += 2;
+	if ( count != buf_count )
+	{
+		delete [] bufs;
+		bufs = NULL;
+		
+		bufs = new buf_t [count];
+		if ( !bufs )
+			return "Out of memory";
+		
+		buf_count = count;
+		
+		if ( sample_rate_ )
+			BLARGG_RETURN_ERR( sample_rate( sample_rate_, length_ ) );
+		
+		if ( clock_rate_ >= 0 )
+			clock_rate( clock_rate_ );
+		
+		if ( bass_freq_ >= 0 )
+			bass_freq( bass_freq_ );
+		
+		set_pan( left_chan, 1.0, 0.0 );
+		set_pan( right_chan, 0.0, 1.0 );
+		for ( int i = 0; i < buf_count - 2; i++ )
+			set_pan( i, 1.0, 1.0 );
+	}
+	return blargg_success;
+}
+
+Panning_Buffer::channel_t Panning_Buffer::channel( int i )
+{
+	i += 2;
+	require( i < buf_count );
+	channel_t ch;
+	ch.center = &bufs [i];
+	ch.left   = &bufs [buf_count];
+	ch.right  = &bufs [buf_count];
+	return ch;
+}
+
+
+void Panning_Buffer::set_pan( int i, double left, double right )
+{
+	i += 2;
+	require( i < buf_count );
+	bufs [i].left_gain  = TO_FIXED( left  );
+	bufs [i].right_gain = TO_FIXED( right );
+}
+
+
+void Panning_Buffer::end_frame( blip_time_t time, bool )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		bufs [i].end_frame( time );
+}
+
+long Panning_Buffer::read_samples( blip_sample_t* out, long count )
+{
+	require( count % 2 == 0 ); // count must be even
+	
+	long avail = bufs [0].samples_avail() * 2;
+	if ( count > avail )
+		count = avail;
+	
+	if ( count )
+	{
+		memset( out, 0, count * sizeof *out );
+		
+		int pair_count = count >> 1;
+		
+		int i;
+		for ( i = 0; i < buf_count; i++ )
+			add_panned( bufs [i], out, pair_count );
+		
+		for ( i = 0; i < buf_count; i++ )
+			bufs [i].remove_samples( pair_count );
+	}
+	return count;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Panning_Buffer::add_panned( buf_t& buf, blip_sample_t* out, long count )
+{
+	Blip_Reader in; 
+	
+	fixed_t left_gain = buf.left_gain;
+	fixed_t right_gain = buf.right_gain;
+	int bass = in.begin( buf );
+	
+	while ( count-- )
+	{
+		long s = in.read();
+		long l = out [0] + FMUL( s, left_gain );
+		long r = out [1] + FMUL( s, right_gain );
+		in.next();
+		
+		if ( (BOOST::int16_t) l != l )
+			l = 0x7FFF - (l >> 24);
+		
+		out [0] = l;
+		out [1] = r;
+		out += 2;
+		
+		if ( (BOOST::int16_t) r != r )
+			out [-1] = 0x7FFF - (r >> 24);
+	}
+	
+	in.end( buf );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Panning_Buffer.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,50 @@
+
+// Multi-channel buffer with pan control for each buffer
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+
+#ifndef PANNING_BUFFER_H
+#define PANNING_BUFFER_H
+
+#include "Multi_Buffer.h"
+
+// Panning_Buffer uses several buffers and outputs stereo sample pairs.
+class Panning_Buffer : public Multi_Buffer {
+public:
+	Panning_Buffer();
+	~Panning_Buffer();
+	
+	// Set pan of a channel, using left and right gain values (1.0 = normal).
+	// Use left_chan and right_chan for the common left and right buffers used
+	// by all channels.
+	enum { left_chan = -2 };
+	enum { right_chan = -1 };
+	void set_pan( int channel, double left, double right );
+	
+	// See Multi_Buffer.h
+	blargg_err_t sample_rate( long rate, int msec );
+	void clock_rate( long );
+	void bass_freq( int );
+	void clear();
+	blargg_err_t set_channel_count( int );
+	channel_t channel( int );
+	void end_frame( blip_time_t, bool unused = true );
+	long read_samples( blip_sample_t*, long );
+	
+private:
+	typedef long fixed_t;
+	
+	struct buf_t : Blip_Buffer {
+		fixed_t left_gain;
+		fixed_t right_gain;
+	};
+	buf_t* bufs;
+	int buf_count;
+	long clock_rate_;
+	int bass_freq_;
+	
+	void add_panned( buf_t&, blip_sample_t*, long );
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Sms_Apu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,323 @@
+
+// Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/libs/
+
+#include "Sms_Apu.h"
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Sms_Osc
+
+Sms_Osc::Sms_Osc()
+{
+	output = NULL;
+	outputs [0] = NULL; // always stays NULL
+	outputs [1] = NULL;
+	outputs [2] = NULL;
+	outputs [3] = NULL;
+}
+
+void Sms_Osc::reset()
+{
+	delay = 0;
+	last_amp = 0;
+	volume = 0;
+	output_select = 3;
+	output = outputs [3];
+}
+
+// Sms_Square
+
+Sms_Square::Sms_Square() {
+}
+
+void Sms_Square::reset()
+{
+	period = 0;
+	phase = 0;
+	Sms_Osc::reset();
+}
+
+void Sms_Square::run( sms_time_t time, sms_time_t end_time )
+{
+	if ( !volume || period <= 128 )
+	{
+		// ignore 16kHz and higher
+		if ( last_amp ) {
+			synth->offset( time, -last_amp, output );
+			last_amp = 0;
+		}
+		time += delay;
+		if ( !period ) {
+			time = end_time;
+		}
+		else if ( time < end_time ) {
+			// keep calculating phase
+			int count = (end_time - time + period - 1) / period;
+			phase = (phase + count) & 1;
+			time += count * period;
+		}
+	}
+	else
+	{
+		int amp = phase ? volume : -volume;
+		if ( amp != last_amp ) {
+			synth->offset( time, amp - last_amp, output );
+			last_amp = amp;
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			Blip_Buffer* const output = this->output;
+			amp *= 2;
+			do {
+				amp = -amp; // amp always alternates
+				synth->offset_inline( time, amp, output );
+				time += period;
+				phase ^= 1;
+			}
+			while ( time < end_time );
+			this->last_amp = phase ? volume : -volume;
+		}
+	}
+	delay = time - end_time;
+}
+
+// Sms_Noise
+
+static const int noise_periods [3] = { 0x100, 0x200, 0x400 };
+
+inline Sms_Noise::Sms_Noise() {
+}
+
+inline void Sms_Noise::reset()
+{
+	period = &noise_periods [0];
+	shifter = 0x8000;
+	tap = 12;
+	Sms_Osc::reset();
+}
+
+void Sms_Noise::run( sms_time_t time, sms_time_t end_time )
+{
+	int cur_amp = 0;
+	int period = *this->period * 2;
+	if ( !volume ) {
+		if ( last_amp ) {
+			synth.offset( time, -last_amp, output );
+			last_amp = 0;
+		}
+		delay = 0;
+	}
+	else
+	{
+		int amp = (shifter & 1) ? -volume : volume;
+		if ( !period )
+			period = 16;
+		if ( amp != last_amp ) {
+			synth.offset( time, amp - last_amp, output );
+			last_amp = amp;
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			Blip_Buffer* const output = this->output;
+			unsigned shifter = this->shifter;
+			amp *= 2;
+			
+			do {
+				int changed = (shifter + 1) & 2;
+				shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1);
+				if ( changed ) { // prev and next bits differ
+					amp = -amp;
+					synth.offset_inline( time, amp, output );
+				}
+				time += period;
+			}
+			while ( time < end_time );
+			
+			this->shifter = shifter;
+			this->last_amp = amp >> 1;
+		}
+		delay = time - end_time;
+	}
+}
+
+// Sms_Apu
+
+Sms_Apu::Sms_Apu()
+{
+	for ( int i = 0; i < 3; i++ ) {
+		squares [i].synth = &square_synth;
+		oscs [i] = &squares [i];
+	}
+	oscs [3] = &noise;
+	
+	volume( 1.0 );
+	reset();
+}
+
+Sms_Apu::~Sms_Apu() {
+}
+
+void Sms_Apu::treble_eq( const blip_eq_t& eq )
+{
+	square_synth.treble_eq( eq );
+	noise.synth.treble_eq( eq );
+}
+
+void Sms_Apu::volume( double vol )
+{
+	vol *= 0.85 / osc_count;
+	square_synth.volume( vol );
+	noise.synth.volume( vol );
+}
+
+void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, center, left, right );
+}
+
+void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left,
+		Blip_Buffer* right )
+{
+	require( (unsigned) index < osc_count );
+	
+	Sms_Osc& osc = *oscs [index];
+	if ( center && !left && !right )
+	{
+		// mono
+		left = center;
+		right = center;
+	}
+	else
+	{
+		// must be silenced or stereo
+		require( (!left && !right) || (left && right) );
+	}
+	osc.outputs [1] = right;
+	osc.outputs [2] = left;
+	osc.outputs [3] = center;
+	osc.output = osc.outputs [osc.output_select];
+}
+
+void Sms_Apu::reset()
+{
+	stereo_found = false;
+	last_time = 0;
+	latch = 0;
+	
+	squares [0].reset();
+	squares [1].reset();
+	squares [2].reset();
+	noise.reset();
+}
+
+void Sms_Apu::run_until( sms_time_t end_time )
+{
+	require( end_time >= last_time ); // end_time must not be before previous time
+	
+	if ( end_time > last_time )
+	{
+		// run oscillators
+		for ( int i = 0; i < osc_count; ++i ) {
+			Sms_Osc& osc = *oscs [i];
+			if ( osc.output ) {
+				if ( osc.output != osc.outputs [3] )
+					stereo_found = true; // playing on side output
+				osc.run( last_time, end_time );
+			}
+		}
+		
+		last_time = end_time;
+	}
+}
+
+bool Sms_Apu::end_frame( sms_time_t end_time )
+{
+	run_until( end_time );
+	last_time = 0;
+	
+	bool result = stereo_found;
+	stereo_found = false;
+	return result;
+}
+
+void Sms_Apu::write_ggstereo( sms_time_t time, int data )
+{
+	require( (unsigned) data <= 0xff );
+	
+	run_until( time );
+	
+	// left/right assignments
+	for ( int i = 0; i < osc_count; i++ )
+	{
+		Sms_Osc& osc = *oscs [i];
+		int flags = data >> i;
+		Blip_Buffer* old_output = osc.output;
+		osc.output_select = ((flags >> 3) & 2) | (flags & 1);
+		osc.output = osc.outputs [osc.output_select];
+		if ( osc.output != old_output && osc.last_amp ) {
+			if ( old_output )
+				square_synth.offset( time, -osc.last_amp, old_output );
+			osc.last_amp = 0;
+		}
+	}
+}
+
+static const char volumes [16] = {
+	// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
+	64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
+};
+
+void Sms_Apu::write_data( sms_time_t time, int data )
+{
+	require( (unsigned) data <= 0xff );
+	
+	run_until( time );
+	
+	if ( data & 0x80 )
+		latch = data;
+	
+	int index = (latch >> 5) & 3;
+	if ( latch & 0x10 )
+	{
+		// volume
+		oscs [index]->volume = volumes [data & 15];
+	}
+	else if ( index < 3 )
+	{
+		// square period
+		Sms_Square& sq = squares [index];
+		if ( data & 0x80 )
+			sq.period = (sq.period & ~0xff) | ((data << 4) & 0xff);
+		else
+			sq.period = (sq.period & 0xff) | ((data << 8) & 0x3f00);
+	}
+	else
+	{
+		// noise period/mode
+		int select = data & 3;
+		if ( select < 3 )
+			noise.period = &noise_periods [select];
+		else
+			noise.period = &squares [2].period;
+		
+		noise.tap = (data & 0x04) ? 12 : 16; // 16 disables tap
+		noise.shifter = 0x8000;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Sms_Apu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,78 @@
+
+// Sega Master System SN76489 PSG sound chip emulator
+
+// Sms_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef SMS_APU_H
+#define SMS_APU_H
+
+typedef long sms_time_t; // clock cycle count
+
+#include "Sms_Oscs.h"
+
+class Sms_Apu {
+public:
+	Sms_Apu();
+	~Sms_Apu();
+	
+	// Overall volume of all oscillators, where 1.0 is full volume.
+	void volume( double );
+	
+	// Treble equalization (see notes.txt).
+	void treble_eq( const blip_eq_t& );
+	
+	// Assign all oscillator outputs to specified buffer(s). If buffer
+	// is NULL, silence all oscillators.
+	void output( Blip_Buffer* mono );
+	void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
+	
+	// Assign oscillator output to buffer(s). Valid indicies are 0 to
+	// osc_count - 1, which refer to Square 1, Square 2, Square 3, and
+	// Noise, respectively. If buffer is NULL, silence oscillator.
+	enum { osc_count = 4 };
+	void osc_output( int index, Blip_Buffer* mono );
+	void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
+	
+	// Reset oscillators
+	void reset();
+	
+	// Write GameGear left/right assignment byte
+	void write_ggstereo( sms_time_t, int );
+	
+	// Write to data port
+	void write_data( sms_time_t, int );
+	
+	// Run all oscillators up to specified time, end current frame, then
+	// start a new frame at time 0. Return true if any oscillators added
+	// sound to one of the left/right buffers, false if they only added
+	// to the center buffer.
+	bool end_frame( sms_time_t );
+	
+	
+	// End of public interface
+private:
+	// noncopyable
+	Sms_Apu( const Sms_Apu& );
+	Sms_Apu& operator = ( const Sms_Apu& );
+	
+	Sms_Osc*    oscs [osc_count];
+	Sms_Square  squares [3];
+	Sms_Noise   noise;
+	Sms_Square::Synth square_synth; // shared between squares
+	sms_time_t  last_time;
+	int         latch;
+	bool        stereo_found;
+	
+	void run_until( sms_time_t );
+};
+
+inline void Sms_Apu::output( Blip_Buffer* mono ) {
+	output( mono, NULL, NULL );
+}
+
+inline void Sms_Apu::osc_output( int index, Blip_Buffer* mono ) {
+	osc_output( index, mono, NULL, NULL );
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Sms_Oscs.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,54 @@
+
+// Private oscillators used by Sms_Apu
+
+// Sms_Snd_Emu 0.1.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef SMS_OSCS_H
+#define SMS_OSCS_H
+
+#include "Blip_Buffer.h"
+
+struct Sms_Osc
+{
+	Blip_Buffer* outputs [4]; // NULL, right, left, center
+	Blip_Buffer* output;
+	int output_select;
+	
+	int delay;
+	int last_amp;
+	int volume;
+	
+	Sms_Osc();
+	void reset();
+	virtual void run( sms_time_t start, sms_time_t end ) = 0;
+};
+
+struct Sms_Square : Sms_Osc
+{
+	int period;
+	int phase;
+	
+	typedef Blip_Synth<blip_good_quality,64 * 2> Synth;
+	const Synth* synth;
+	
+	Sms_Square();
+	void reset();
+	void run( sms_time_t, sms_time_t );
+};
+
+struct Sms_Noise : Sms_Osc
+{
+	const int* period;
+	unsigned shifter;
+	unsigned tap;
+	
+	typedef Blip_Synth<blip_med_quality,64 * 2> Synth;
+	Synth synth;
+	
+	Sms_Noise();
+	void reset();
+	void run( sms_time_t, sms_time_t );
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Snes_Spc.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,465 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Snes_Spc.h"
+
+#include <assert.h>
+#include <string.h>
+
+/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Snes_Spc::Snes_Spc() : cpu( ram, this ), dsp( ram )
+{
+	timer [0].shift = 7; // 8 kHz
+	timer [1].shift = 7; // 8 kHz
+	timer [2].shift = 4; // 64 kHz
+	
+	// Put STOP instruction past end of memory to catch PC overflow.
+	memset( ram + ram_size, 0xff, (sizeof ram) - ram_size );
+}
+
+// Load
+
+const char* Snes_Spc::load_spc( const void* data, long size, int clear_echo_ )
+{
+	struct spc_file_t {
+		char    signature [27];
+		char    unused [10];
+		uint8_t pc [2];
+		uint8_t a;
+		uint8_t x;
+		uint8_t y;
+		uint8_t status;
+		uint8_t sp;
+		char    unused2 [212];
+		uint8_t ram [0x10000];
+		uint8_t dsp [128];
+	};
+	BOOST_STATIC_ASSERT( sizeof (spc_file_t) == spc_file_size );
+	
+	const spc_file_t* spc = (spc_file_t*) data;
+	
+	if ( size < spc_file_size )
+		return "Not an SPC file";
+	
+	if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
+		return "Not an SPC file";
+	
+	registers_t regs;
+	regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
+	regs.a = spc->a;
+	regs.x = spc->x;
+	regs.y = spc->y;
+	regs.status = spc->status;
+	regs.sp = spc->sp;
+	
+	const char* error = load_state( regs, spc->ram, spc->dsp );
+	
+	echo_accessed = false;
+	
+	if ( clear_echo_ )
+		clear_echo();
+	
+	return error;
+}
+
+void Snes_Spc::clear_echo()
+{
+	if ( !(dsp.read( 0x6c ) & 0x20) )
+	{
+		unsigned addr = 0x100 * dsp.read( 0x6d );
+		unsigned size = 0x800 * dsp.read( 0x7d );
+		unsigned limit = ram_size - addr;
+		memset( ram + addr, 0xff, (size < limit) ? size : limit );
+	}
+}
+
+// Handle other file formats (emulator save states) in user code, not here.
+
+const char* Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram,
+		const void* dsp_state )
+{
+	// cpu
+	cpu.r = cpu_state;
+	
+	// Allow DSP to generate one sample before code starts
+	// (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it
+	// clears KON 31 cycles from starting execution. It works on the SNES
+	// since the SPC player adds a few extra cycles delay after restoring
+	// KON from the DSP registers at the end of an SPC file).
+	extra_cycles = 32; 
+	
+	// ram
+	memcpy( ram, new_ram, ram_size );
+	memcpy( extra_ram, ram + rom_addr, sizeof extra_ram );
+	
+	// boot rom (have to force enable_rom() to update it)
+	rom_enabled = !(ram [0xf1] & 0x80);
+	enable_rom( !rom_enabled );
+	
+	// dsp
+	dsp.reset();
+	int i;
+	for ( i = 0; i < Spc_Dsp::register_count; i++ )
+		dsp.write( i, ((uint8_t*) dsp_state) [i] );
+	
+	// timers
+	for ( i = 0; i < timer_count; i++ )
+	{
+		Timer& t = timer [i];
+		
+		t.enabled = (ram [0xf1] >> i) & 1;
+		t.count = 0;
+		t.next_tick = 0;
+		t.counter = ram [0xfd + i] & 15;
+		
+		int p = ram [0xfa + i];
+		t.period = p ? p : 0x100;
+	}
+	
+	// Handle registers which already give 0 when read by setting RAM and not changing it.
+	// Put STOP instruction in registers which can be read, to catch attempted CPU execution.
+	ram [0xf0] = 0;
+	ram [0xf1] = 0;
+	ram [0xf3] = 0xff;
+	ram [0xfa] = 0;
+	ram [0xfb] = 0;
+	ram [0xfc] = 0;
+	ram [0xfd] = 0xff;
+	ram [0xfe] = 0xff;
+	ram [0xff] = 0xff;
+	
+	return NULL; // success
+}
+
+// Hardware
+
+// Current time starts negative and ends at 0
+inline spc_time_t Snes_Spc::time() const
+{
+	return -cpu.remain();
+}
+
+// Keep track of next time to run and avoid a function call if it hasn't been reached.
+
+// Timers
+
+void Snes_Spc::Timer::run_until_( spc_time_t time )
+{
+	assert( enabled ); // when disabled, next_tick should always be in the future
+	
+	int elapsed = ((time - next_tick) >> shift) + 1;
+	next_tick += elapsed << shift;
+	elapsed += count;
+	if ( elapsed >= period ) { // avoid costly divide
+		int n = elapsed / period;
+		elapsed -= n * period;
+		counter = (counter + n) & 15;
+	}
+	count = elapsed;
+}
+
+// DSP
+
+const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second
+
+void Snes_Spc::run_dsp_( spc_time_t time )
+{
+	int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample
+	sample_t* buf = sample_buf;
+	if ( buf ) {
+		sample_buf = buf + count * 2; // stereo
+		assert( sample_buf <= buf_end );
+	}
+	next_dsp += count * clocks_per_sample;
+	dsp.run( count, buf );
+}
+
+inline void Snes_Spc::run_dsp( spc_time_t time )
+{
+	if ( time >= next_dsp )
+		run_dsp_( time );
+}
+
+// Debug-only check for read/write within echo buffer, since this might result in
+// inaccurate emulation due to the DSP not being caught up to the present.
+inline void Snes_Spc::check_for_echo_access( spc_addr_t addr )
+{
+	if ( !echo_accessed && !(dsp.read( 0x6c ) & 0x20) )
+	{
+		// ** If echo accesses are found that require running the DSP, cache
+		// the start and end address on DSP writes to speed up checking.
+		
+		unsigned start = 0x100 * dsp.read( 0x6d );
+		unsigned end = start + 0x800 * dsp.read( 0x7d );
+		if ( start <= addr && addr < end ) {
+			echo_accessed = true;
+			dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr );
+		}
+	}
+}
+
+// Read
+
+int Snes_Spc::read( spc_addr_t addr )
+{
+	// zero page ram is used most often
+	if ( addr < 0xf0 )
+		return ram [addr];
+	
+	// dsp
+	if ( addr == 0xf3 ) {
+		run_dsp( time() );
+		if ( ram [0xf2] >= Spc_Dsp::register_count )
+			dprintf( "DSP read from $%02X\n", (int) ram [0xf2] );
+		return dsp.read( ram [0xf2] & 0x7f );
+	}
+	
+	// counters
+	unsigned i = addr - 0xfd; // negative converts to large positive unsigned
+	if ( i < timer_count ) {
+		Timer& t = timer [i];
+		t.run_until( time() );
+		int result = t.counter;
+		t.counter = 0;
+		return result;
+	}
+	
+	if ( addr == 0xf0 || addr == 0xf1 || addr == 0xf8 ||
+			addr == 0xf9 || addr == 0xfa )
+		dprintf( "Read from register $%02X\n", (int) addr );
+	
+	// Registers which always read as 0 are handled by setting ram [reg] to 0
+	// at startup then never changing that value.
+	
+	check(( check_for_echo_access( addr ), true ));
+	
+	// ram
+	return ram [addr];
+}
+
+
+// Write
+
+const unsigned char Snes_Spc::boot_rom [rom_size] = { // verified
+	0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0,
+	0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78,
+	0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4,
+	0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5,
+	0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB,
+	0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA,
+	0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD,
+	0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF
+};
+
+void Snes_Spc::enable_rom( int enable )
+{
+	if ( rom_enabled != enable ) {
+		rom_enabled = enable;
+		memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size );
+	}
+}
+
+void Snes_Spc::write( spc_addr_t addr, int data )
+{
+	// first page is very common
+	if ( addr < 0xf0 ) {
+		ram [addr] = data;
+	}
+	else switch ( addr )
+	{
+		// RAM
+		default:
+			check(( check_for_echo_access( addr ), true ));
+			if ( addr < rom_addr ) {
+				ram [addr] = data;
+			}
+			else {
+				extra_ram [addr - rom_addr] = data;
+				if ( !rom_enabled )
+					ram [addr] = data;
+			}
+			break;
+		
+		// DSP
+		//case 0xf2: // mapped to RAM
+		case 0xf3: {
+			run_dsp( time() );
+			int reg = ram [0xf2];
+			if ( next_dsp > 0 ) {
+				// skip mode
+				
+				// key press
+				if ( reg == 0x4C )
+					keys_pressed |= data & ~dsp.read( 0x5C );
+				
+				// key release
+				if ( reg == 0x5C ) {
+					keys_released |= data;
+					keys_pressed &= ~data;
+				}
+			}
+			if ( reg < Spc_Dsp::register_count ) {
+				dsp.write( reg, data );
+			}
+			else {
+				dprintf( "DSP write to $%02X\n", (int) reg );
+			}
+			break;
+		}
+		
+		case 0xf0: // Test register
+			dprintf( "Wrote $%02X to $F0\n", (int) data );
+			break;
+		
+		// Config
+		case 0xf1:
+		{
+			// timers
+			for ( int i = 0; i < timer_count; i++ )
+			{
+				Timer& t = timer [i];
+				if ( !(data & (1 << i)) ) {
+					t.enabled = 0;
+					t.next_tick = 0;
+				}
+				else if ( !t.enabled ) {
+					// just enabled
+					t.enabled = 1;
+					t.counter = 0;
+					t.count = 0;
+					t.next_tick = time();
+				}
+			}
+			
+			// port clears
+			if ( data & 0x10 ) {
+				ram [0xf4] = 0;
+				ram [0xf5] = 0;
+			}
+			if ( data & 0x20 ) {
+				ram [0xf6] = 0;
+				ram [0xf7] = 0;
+			}
+			
+			enable_rom( data & 0x80 );
+			
+			break;
+		}
+		
+		// Ports
+		case 0xf4:
+		case 0xf5:
+		case 0xf6:
+		case 0xf7:
+			// to do: handle output ports
+			break;
+		
+		//case 0xf8: // verified on SNES that these are read/write (RAM)
+		//case 0xf9:
+		
+		// Timers
+		case 0xfa:
+		case 0xfb:
+		case 0xfc: {
+			Timer& t = timer [addr - 0xfa];
+			if ( (t.period & 0xff) != data ) {
+				t.run_until( time() );
+				t.period = data ? data : 0x100;
+			}
+			break;
+		}
+		
+		// Counters (cleared on write)
+		case 0xfd:
+		case 0xfe:
+		case 0xff:
+			dprintf( "Wrote to counter $%02X\n", (int) addr );
+			timer [addr - 0xfd].counter = 0;
+			break;
+	}
+}
+
+// Play
+
+blargg_err_t Snes_Spc::skip( long count )
+{
+	if ( count > 4 * 32000L )
+	{
+		// don't run DSP for long durations (2-3 times faster)
+		
+		const long sync_count = 32000L * 2;
+		
+		// keep track of any keys pressed/released (and not subsequently released)
+		keys_pressed = 0;
+		keys_released = 0;
+		// sentinel tells play to ignore DSP
+		BLARGG_RETURN_ERR( play( count - sync_count, skip_sentinel ) );
+		
+		// press/release keys now
+		dsp.write( 0x5C, keys_released & ~keys_pressed );
+		dsp.write( 0x4C, keys_pressed );
+		
+		clear_echo();
+		
+		// play the last few seconds normally to help synchronize DSP
+		count = sync_count;
+	}
+	
+	return play( count );
+}
+
+blargg_err_t Snes_Spc::play( long count, sample_t* out )
+{
+	require( count % 2 == 0 ); // output is always in pairs of samples
+	
+	// CPU time() runs from -duration to 0
+	spc_time_t duration = (count / 2) * clocks_per_sample;
+	
+	// DSP output is made on-the-fly when the CPU reads/writes DSP registers
+	sample_buf = out;
+	buf_end = out + (out && out != skip_sentinel ? count : 0);
+	next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample;
+	
+	// Localize timer next_tick times and run them to the present to prevent a running
+	// but ignored timer's next_tick from getting too far behind and overflowing.
+	for ( int i = 0; i < timer_count; i++ ) {
+		Timer& t = timer [i];
+		if ( t.enabled ) {
+			t.next_tick -= duration;
+			t.run_until( -duration );
+		}
+	}
+	
+	// Run CPU for duration, reduced by any extra cycles from previous run
+	int elapsed = cpu.run( duration - extra_cycles );
+	if ( elapsed > 0 )
+	{
+		dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
+				(int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc );
+		return "Emulation error";
+	}
+	extra_cycles = -elapsed;
+	
+	// Catch DSP up to present.
+	run_dsp( 0 );
+	if ( out ) {
+		assert( next_dsp == clocks_per_sample );
+		assert( out == skip_sentinel || sample_buf - out == count );
+	}
+	buf_end = NULL;
+	
+	return blargg_success;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Snes_Spc.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,111 @@
+
+// Super Nintendo (SNES) SPC-700 APU Emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
+
+#ifndef SNES_SPC_H
+#define SNES_SPC_H
+
+#include "blargg_common.h"
+#include "Spc_Cpu.h"
+#include "Spc_Dsp.h"
+
+class Snes_Spc {
+public:
+	Snes_Spc();
+	
+	// Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true.
+	enum { spc_file_size = 0x10180 };
+	blargg_err_t load_spc( const void* spc, long spc_size, int clear_echo = 1 );
+	
+	// Load copy of state into emulator.
+	typedef Spc_Cpu::registers_t registers_t;
+	blargg_err_t load_state( const registers_t& cpu_state, const void* ram_64k,
+		const void* dsp_regs_128 );
+	
+	// Clear echo buffer
+	void clear_echo();
+	
+	// Mute voice n if bit n (1 << n) of mask is set
+	enum { voice_count = Spc_Dsp::voice_count };
+	void mute_voices( int mask );
+	
+	// Generate 'count' samples and optionally write to 'buf'. Count must be even.
+	// Sample output is 16-bit 32kHz, signed stereo pairs with the left channel first.
+	typedef short sample_t;
+	blargg_err_t play( long count, sample_t* buf = NULL );
+	
+	// Skip forward by the specified number of samples (64000 samples = 1 second)
+	blargg_err_t skip( long count );
+	
+	// Set gain, where 1.0 is normal. When greater than 1.0, output is clamped the
+	// 16-bit sample range.
+	void set_gain( double );
+	
+	
+// End of public interface
+private:
+	typedef BOOST::uint8_t uint8_t;
+	
+	// timers
+	struct Timer {
+		spc_time_t next_tick;
+		int period;
+		int count;
+		int shift;
+		int counter;
+		int enabled;
+		
+		void run_until_( spc_time_t );
+		void run_until( spc_time_t time ) {
+			if ( time >= next_tick )
+				run_until_( time );
+		}
+	};
+	enum { timer_count = 3 };
+	Timer timer [timer_count];
+
+	// hardware
+	Spc_Cpu cpu;
+	int extra_cycles;
+	spc_time_t time() const;
+	int  read( spc_addr_t );
+	void write( spc_addr_t, int );
+	friend class Spc_Cpu;
+	
+	// boot rom
+	enum { rom_size = 64 };
+	enum { rom_addr = 0xffc0 };
+	int rom_enabled;
+	uint8_t extra_ram [rom_size];
+	static const uint8_t boot_rom [rom_size];
+	void enable_rom( int );
+	
+	// dsp
+	sample_t* sample_buf;
+	sample_t* buf_end; // to do: remove this once possible bug resolved
+	spc_time_t next_dsp;
+	Spc_Dsp dsp;
+	int keys_pressed;
+	int keys_released;
+	sample_t skip_sentinel [1]; // special value for play() passed by skip()
+	void run_dsp( spc_time_t );
+	void run_dsp_( spc_time_t );
+	bool echo_accessed;
+	void check_for_echo_access( spc_addr_t addr );
+	
+	// 64KB RAM + padding filled with STOP instruction to catch PC overflow.
+	enum { ram_size = 0x10000 };
+	uint8_t ram [ram_size + 0x100];
+};
+
+inline void Snes_Spc::mute_voices( int mask ) {
+	dsp.mute_voices( mask );
+}
+
+inline void Snes_Spc::set_gain( double v ) {
+	dsp.set_gain( v );
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Cpu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,1082 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Spc_Cpu.h"
+
+#include <limits.h>
+
+#include "blargg_endian.h"
+#include "Snes_Spc.h"
+
+/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Several instructions are commented out (or not even implemented). These aren't
+// used by the SPC files tested.
+
+// Optimize performance for the most common instructions, and size for the rest:
+//
+// 15%  0xF0    BEQ rel
+//  8%  0xE4    MOV A,dp
+//  4%  0xF5    MOV A,abs+X
+//  4%  0xD0    BNE rel
+//  4%  0x6F    RET
+//  4%  0x3F    CALL addr
+//  4%  0xF4    MOV A,dp+X
+//  3%  0xC4    MOV dp,A
+//  2%  0xEB    MOV Y,dp
+//  2%  0x3D    INC X
+//  2%  0xF6    MOV A,abs+Y
+// (1% and below not shown)
+
+Spc_Cpu::Spc_Cpu( uint8_t* ram_, Snes_Spc* e ) :
+	ram( ram_ ),
+	emu( *e )
+{
+	remain_ = 0;
+	BOOST_STATIC_ASSERT( sizeof (int) >= 4 );
+}
+
+#define READ( addr )            (emu.read( addr ))
+#define WRITE( addr, value )    (emu.write( addr, value ))
+
+#define READ_DP( addr )         READ( (addr) + dp )
+#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
+
+#define READ_PROG( addr )       (ram [addr])
+#define READ_PROG16( addr )     GET_LE16( &READ_PROG( addr ) )
+
+int Spc_Cpu::read( spc_addr_t addr )
+{
+	return READ( addr );
+}
+
+void Spc_Cpu::write( spc_addr_t addr, int data )
+{
+	WRITE( addr, data );
+}
+
+// Cycle table derived from text copy of SPC-700 manual (using regular expressions)
+static const unsigned char cycle_table [0x100] = {
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8,
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6,
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4,
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8,
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6,
+	2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3,
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5,
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6,
+	2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5,
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,
+	3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4,
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4,
+	3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9,
+	2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3,
+	2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3,
+	2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3
+};
+
+// The C,mem instructions are hardly used, so a non-inline function is used for
+// the common access code.
+unsigned Spc_Cpu::mem_bit( spc_addr_t pc )
+{
+	unsigned addr = READ_PROG16( pc );
+	unsigned t = READ( addr & 0x1fff ) >> (addr >> 13);
+	return (t << 8) & 0x100;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+spc_time_t Spc_Cpu::run( spc_time_t cycle_count )
+{
+	remain_ = cycle_count;
+	
+#if BLARGG_CPU_POWERPC
+	uint8_t* const ram = this->ram; // cache
+#endif
+	
+	// Stack pointer is kept one greater than usual SPC stack pointer to allow
+	// common pre-decrement and post-increment memory instructions that some
+	// processors have. Address wrap-around isn't supported.
+	#define PUSH( v )       (*--sp = (v))
+	#define PUSH16( v )     (sp -= 2, SET_LE16( sp, v ))
+	#define POP()           (*sp++)
+	#define SET_SP( v )     (sp = ram + 0x101 + (v))
+	#define GET_SP()        (sp - 0x101 - ram)
+
+	uint8_t* sp;
+	SET_SP( r.sp );
+	
+	// registers
+	unsigned pc = r.pc;
+	int a = r.a;
+	int x = r.x;
+	int y = r.y;
+	
+	// status flags
+	
+	const int st_n = 0x80;
+	const int st_v = 0x40;
+	const int st_p = 0x20;
+	const int st_b = 0x10;
+	const int st_h = 0x08;
+	const int st_i = 0x04;
+	const int st_z = 0x02;
+	const int st_c = 0x01;
+	
+	// Special encoding for negative and zero being set simultaneously (by POP PSW).
+	// To do: be sure this works properly (I copied it from my NES 6502 emulator).
+	#define IS_NEG (int ((nz + 0x800) | (nz << (CHAR_BIT * sizeof (int) - 8))) < 0)
+	
+	#define CALC_STATUS( out ) do {                 \
+		out = status & ~(st_n | st_z | st_c);       \
+		out |= (c >> 8) & st_c;                     \
+		out |= (dp >> 3) & st_p;                    \
+		if ( IS_NEG ) out |= st_n;                  \
+		if ( !(uint8_t) nz ) out |= st_z;           \
+	} while ( 0 )       
+
+	#define SET_STATUS( in ) do {                   \
+		status = in & ~(st_n | st_z | st_c | st_p); \
+		c = in << 8;                                \
+		nz = in << 4;                               \
+		nz &= 0x820;                                \
+		nz ^= ~0xDF;                                \
+		dp = (in << 3) & 0x100;                     \
+	} while ( 0 )
+	
+	uint8_t status;
+	int c;  // store C as 'c' & 0x100.
+	int nz; // store Z as 'nz' & 0xFF == 0 (see above for encoding of N)
+	unsigned dp; // direct page base
+	{
+		int temp = r.status;
+		SET_STATUS( temp );
+	}
+
+	goto loop;
+	
+	unsigned data; // first operand of instruction and temporary across function calls
+	
+	// Common endings for instructions
+cbranch_taken_loop: // compare and branch
+	data = READ_PROG( pc );
+branch_taken_loop: // taken branch (displacement already in 'data')
+	pc += (BOOST::int8_t) data; // sign-extend
+	remain_ -= 2;
+inc_pc_loop: // end of instruction with an operand
+	pc++;
+loop:
+	
+	// Be sure all registers are in range. PC and SP wrap-around isn't handled so
+	// those checks might fail, but a, x, and y should always be in range.
+	check( (unsigned) pc < 0x10000 );
+	check( (unsigned) GET_SP() < 0x100 );
+	check( (unsigned) a < 0x100 );
+	check( (unsigned) x < 0x100 );
+	check( (unsigned) y < 0x100 );
+	
+	// Read opcode and first operand. Optimize if processor's byte order is known
+	// and non-portable constructs are allowed.
+#if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
+	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	pc++;
+	unsigned opcode = data >> 8;
+	data = (uint8_t) data;
+
+#elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
+	data = *(BOOST::uint16_t*) &READ_PROG( pc );
+	pc++;
+	unsigned opcode = (uint8_t) data;
+	data >>= 8;
+
+#else
+	unsigned opcode = READ_PROG( pc );
+	pc++;
+	data = READ_PROG( pc );
+	
+#endif
+	
+	if ( remain_ <= 0 )
+		goto stop;
+	
+	remain_ -= cycle_table [opcode];
+	
+	// Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise
+	// use a local temporary.
+	switch ( opcode )
+	{
+	
+	#define BRANCH( cond )      \
+		if ( cond )             \
+			goto branch_taken_loop; \
+		goto inc_pc_loop;
+
+// Most-Common
+
+	case 0xF0: // BEQ (most common)
+		BRANCH( !(uint8_t) nz )
+	
+	case 0xD0: // BNE
+		BRANCH( (uint8_t) nz )
+	
+	case 0x3F: // CALL
+		PUSH16( pc + 2 );
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+	case 0x6F: // RET
+		pc = POP();
+		pc += POP() * 0x100;
+		goto loop;
+
+#define CASE( n )   case n:
+
+// Define common address modes based on opcode for immediate mode. Execution
+// ends with data set to the address of the operand.
+#define ADDR_MODES( op )                \
+	CASE( op - 0x02 ) /* (X) */         \
+		data = x + dp;                  \
+		pc--;                           \
+		goto end_##op;                  \
+	CASE( op + 0x0F ) /* (dp)+Y */      \
+		data = READ_PROG16( data + dp ) + y;\
+		goto end_##op;                  \
+	CASE( op - 0x01 ) /* (dp+X) */      \
+		data = READ_PROG16( uint8_t (data + x) + dp );\
+		goto end_##op;                  \
+	CASE( op + 0x0E ) /* abs+Y */       \
+		data += y;                      \
+		goto abs_##op;                  \
+	CASE( op + 0x0D ) /* abs+X */       \
+		data += x;                      \
+	CASE( op - 0x03 ) /* abs */         \
+	abs_##op:                           \
+		pc++;                           \
+		data += 0x100 * READ_PROG( pc );\
+		goto end_##op;                  \
+	CASE( op + 0x0C ) /* dp+X */        \
+		data = uint8_t (data + x);      \
+	CASE( op - 0x04 ) /* dp */          \
+		data += dp;                     \
+	end_##op:
+
+// 1. 8-bit Data Transmission Commands. Group I
+
+	ADDR_MODES( 0xE8 ) // MOV A,addr
+	// case 0xE4: // MOV a,dp (most common)
+	mov_a_addr:
+		a = nz = READ( data );
+		goto inc_pc_loop;
+	case 0xBF: // MOV A,(X)+
+		data = x + dp;
+		x = uint8_t (x + 1);
+		pc--;
+		goto mov_a_addr;
+	
+	case 0xE8: // MOV A,imm
+		a = data;
+		nz = data;
+		goto inc_pc_loop;
+	
+	case 0xF9: // MOV X,dp+Y
+		data = uint8_t (data + y);
+	case 0xF8: // MOV X,dp
+		data += dp;
+		goto mov_x_addr;
+	case 0xE9: // MOV X,abs
+		data = READ_PROG16( pc );
+		pc++;
+	mov_x_addr:
+		data = READ( data );
+	case 0xCD: // MOV X,imm
+		x = data;
+		nz = data;
+		goto inc_pc_loop;
+	
+	case 0xFB: // MOV Y,dp+X
+		data = uint8_t (data + x);
+	case 0xEB: // MOV Y,dp
+		data += dp;
+		goto mov_y_addr;
+	case 0xEC: // MOV Y,abs
+		data = READ_PROG16( pc );
+		pc++;
+	mov_y_addr:
+		data = READ( data );
+	case 0x8D: // MOV Y,imm
+		y = data;
+		nz = data;
+		goto inc_pc_loop;
+
+// 2. 8-BIT DATA TRANSMISSION COMMANDS. GROUP 2.
+
+	ADDR_MODES( 0xC8 ) // MOV addr,A
+		WRITE( data, a );
+		goto inc_pc_loop;
+	
+	{
+		int temp;
+	case 0xCC: // MOV abs,Y
+		temp = y;
+		goto mov_abs_temp;
+	case 0xC9: // MOV abs,X
+		temp = x;
+	mov_abs_temp:
+		WRITE( READ_PROG16( pc ), temp );
+		pc += 2;
+		goto loop;
+	}
+	
+	case 0xD9: // MOV dp+Y,X
+		data = uint8_t (data + y);
+	case 0xD8: // MOV dp,X
+		WRITE( data + dp, x );
+		goto inc_pc_loop;
+	
+	case 0xDB: // MOV dp+X,Y
+		data = uint8_t (data + x);
+	case 0xCB: // MOV dp,Y
+		WRITE( data + dp, y );
+		goto inc_pc_loop;
+
+	case 0xFA: // MOV dp,dp
+		data = READ( data + dp );
+	case 0x8F: // MOV dp,#imm
+		pc++;
+		WRITE_DP( READ_PROG( pc ), data );
+		goto inc_pc_loop;
+	
+// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
+	
+	case 0x7D: // MOV A,X
+		a = x;
+		nz = x;
+		goto loop;
+	
+	case 0xDD: // MOV A,Y
+		a = y;
+		nz = y;
+		goto loop;
+	
+	case 0x5D: // MOV X,A
+		x = a;
+		nz = a;
+		goto loop;
+	
+	case 0xFD: // MOV Y,A
+		y = a;
+		nz = a;
+		goto loop;
+	
+	case 0x9D: // MOV X,SP
+		x = nz = GET_SP();
+		goto loop;
+	
+	case 0xBD: // MOV SP,X
+		SET_SP( x );
+		goto loop;
+	
+	//case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
+	
+	case 0xAF: // MOV (X)+,A
+		WRITE_DP( x, a );
+		x++;
+		goto loop;
+	
+// 5. 8-BIT LOGIC OPERATION COMMANDS.
+	
+#define LOGICAL_OP( op, func )  \
+	ADDR_MODES( op ) /* addr */ \
+		data = READ( data );    \
+	case op: /* imm */          \
+		nz = a func##= data;    \
+		goto inc_pc_loop;       \
+	{   unsigned addr;          \
+	case op + 0x11: /* X,Y */   \
+		data = READ_DP( y );    \
+		addr = x + dp;          \
+		pc--;                   \
+		goto addr_##op;         \
+	case op + 0x01: /* dp,dp */ \
+		data = READ_DP( data ); \
+	case op + 0x10: /*dp,imm*/\
+		pc++;                   \
+		addr = READ_PROG( pc ) + dp;\
+	addr_##op:                  \
+		nz = data func READ( addr );\
+		WRITE( addr, nz );      \
+		goto inc_pc_loop;       \
+	}
+	
+	LOGICAL_OP( 0x28, & ); // AND
+	
+	LOGICAL_OP( 0x08, | ); // OR
+	
+	LOGICAL_OP( 0x48, ^ ); // EOR
+	
+// 4. 8-BIT ARITHMETIC OPERATION COMMANDS.
+
+	ADDR_MODES( 0x68 ) // CMP addr
+		data = READ( data );
+	case 0x68: // CMP imm
+		nz = a - data;
+		c = ~nz;
+		goto inc_pc_loop;
+	
+	case 0x79: // CMP (X),(Y)
+		data = READ_DP( x );
+		nz = data - READ_DP( y );
+		c = ~nz;
+		goto loop;
+	
+	case 0x69: // CMP (dp),(dp)
+		data = READ_DP( data );
+	case 0x78: // CMP dp,imm
+		pc++;
+		nz = READ_DP( READ_PROG( pc ) ) - data;
+		c = ~nz;
+		goto inc_pc_loop;
+	
+	case 0x3E: // CMP X,dp
+		data += dp;
+		goto cmp_x_addr;
+	case 0x1E: // CMP X,abs
+		data = READ_PROG16( pc );
+		pc++;
+	cmp_x_addr:
+		data = READ( data );
+	case 0xC8: // CMP X,imm
+		nz = x - data;
+		c = ~nz;
+		goto inc_pc_loop;
+	
+	case 0x7E: // CMP Y,dp
+		data += dp;
+		goto cmp_y_addr;
+	case 0x5E: // CMP Y,abs
+		data = READ_PROG16( pc );
+		pc++;
+	cmp_y_addr:
+		data = READ( data );
+	case 0xAD: // CMP Y,imm
+		nz = y - data;
+		c = ~nz;
+		goto inc_pc_loop;
+	
+	{
+		int addr;
+	case 0xB9: // SBC (x),(y)
+	case 0x99: // ADC (x),(y)
+		pc--; // compensate for inc later
+		data = READ_DP( x );
+		addr = y + dp;
+		goto adc_addr;
+	case 0xA9: // SBC dp,dp
+	case 0x89: // ADC dp,dp
+		data = READ_DP( data );
+	case 0xB8: // SBC dp,imm
+	case 0x98: // ADC dp,imm
+		pc++;
+		addr = READ_PROG( pc ) + dp;
+	adc_addr:
+		nz = READ( addr );
+		goto adc_data;
+		
+// catch ADC and SBC together, then decode later based on operand
+#undef CASE
+#define CASE( n ) case n: case (n) + 0x20:
+	ADDR_MODES( 0x88 ) // ADC/SBC addr
+		data = READ( data );
+	case 0xA8: // SBC imm
+	case 0x88: // ADC imm
+		addr = -1; // A
+		nz = a;
+	adc_data: {
+		if ( opcode & 0x20 )
+			data ^= 0xff; // SBC
+		int carry = (c >> 8) & 1;
+		int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
+		int hc = (nz & 15) + carry;
+		c = nz += data + carry;
+		hc = (nz & 15) - hc;
+		status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h);
+		if ( addr < 0 ) {
+			a = (uint8_t) nz;
+			goto inc_pc_loop;
+		}
+		WRITE( addr, (uint8_t) nz );
+		goto inc_pc_loop;
+	}
+	
+	}
+	
+// 6. ADDITION & SUBTRACTION COMMANDS.
+
+#define INC_DEC_REG( reg, n )   \
+		nz = reg + n;           \
+		reg = (uint8_t) nz;     \
+		goto loop;
+
+	case 0xBC: INC_DEC_REG( a, 1 )  // INC A
+	case 0x3D: INC_DEC_REG( x, 1 )  // INC X
+	case 0xFC: INC_DEC_REG( y, 1 )  // INC Y
+	
+	case 0x9C: INC_DEC_REG( a, -1 ) // DEC A
+	case 0x1D: INC_DEC_REG( x, -1 ) // DEC X
+	case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y
+
+	case 0x9B: // DEC dp+X
+	case 0xBB: // INC dp+X
+		data = uint8_t (data + x);
+	case 0x8B: // DEC dp
+	case 0xAB: // INC dp
+		data += dp;
+		goto inc_abs;
+	case 0x8C: // DEC abs
+	case 0xAC: // INC abs
+		data = READ_PROG16( pc );
+		pc++;
+	inc_abs:
+		nz = ((opcode >> 4) & 2) - 1;
+		nz += READ( data );
+		WRITE( data, (uint8_t) nz );
+		goto inc_pc_loop;
+	
+// 7. SHIFT, ROTATION COMMANDS
+
+	case 0x5C: // LSR A
+		c = 0;
+	case 0x7C:{// ROR A
+		nz = ((c >> 1) & 0x80) | (a >> 1);
+		c = a << 8;
+		a = nz;
+		goto loop;
+	}
+	
+	case 0x1C: // ASL A
+		c = 0;
+	case 0x3C:{// ROL A
+		int temp = (c >> 8) & 1;
+		c = a << 1;
+		nz = c | temp;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+	case 0x0B: // ASL dp
+		c = 0;
+		data += dp;
+		goto rol_mem;
+	case 0x1B: // ASL dp+X
+		c = 0;
+	case 0x3B: // ROL dp+X
+		data = uint8_t (data + x);
+	case 0x2B: // ROL dp
+		data += dp;
+		goto rol_mem;
+	case 0x0C: // ASL abs
+		c = 0;
+	case 0x2C: // ROL abs
+		data = READ_PROG16( pc );
+		pc++;
+	rol_mem:
+		nz = (c >> 8) & 1;
+		nz |= (c = READ( data ) << 1);
+		WRITE( data, (uint8_t) nz );
+		goto inc_pc_loop;
+	
+	case 0x4B: // LSR dp
+		c = 0;
+		data += dp;
+		goto ror_mem;
+	case 0x5B: // LSR dp+X
+		c = 0;
+	case 0x7B: // ROR dp+X
+		data = uint8_t (data + x);
+	case 0x6B: // ROR dp
+		data += dp;
+		goto ror_mem;
+	case 0x4C: // LSR abs
+		c = 0;
+	case 0x6C: // ROR abs
+		data = READ_PROG16( pc );
+		pc++;
+	ror_mem: {
+		int temp = READ( data );
+		nz = ((c >> 1) & 0x80) | (temp >> 1);
+		c = temp << 8;
+		WRITE( data, nz );
+		goto inc_pc_loop;
+	}
+
+	case 0x9F: // XCN
+		nz = a = (a >> 4) | uint8_t (a << 4);
+		goto loop;
+
+// 8. 16-BIT TRANSMISION COMMANDS
+
+	case 0xBA: // MOVW YA,dp
+		a = READ_DP( data );
+		nz = (a & 0x7f) | (a >> 1);
+		y = READ_DP( uint8_t (data + 1) );
+		nz |= y;
+		goto inc_pc_loop;
+	
+	case 0xDA: // MOVW dp,YA
+		WRITE_DP( data, a );
+		WRITE_DP( uint8_t (data + 1), y );
+		goto inc_pc_loop;
+	
+// 9. 16-BIT OPERATION COMMANDS.
+
+	case 0x3A: // INCW dp
+	case 0x1A:{// DECW dp
+		data += dp;
+		
+		// low byte
+		int temp = READ( data );
+		temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW
+		nz = ((temp >> 1) | temp) & 0x7f;
+		WRITE( data, (uint8_t) temp );
+		
+		// high byte
+		data = uint8_t (data + 1) + dp;
+		temp >>= 8;
+		temp = uint8_t (temp + READ( data ));
+		nz |= temp;
+		WRITE( data, temp );
+		
+		goto inc_pc_loop;
+	}
+		
+	case 0x9A: // SUBW YA,dp
+	case 0x7A: // ADDW YA,dp
+	{
+		// read 16-bit addend
+		int temp = READ_DP( data );
+		int sign = READ_DP( uint8_t (data + 1) );
+		temp += 0x100 * sign;
+		status &= ~(st_v | st_h);
+		
+		// to do: fix half-carry for SUBW (it's probably wrong)
+		
+		// for SUBW, negate and truncate to 16 bits
+		if ( opcode & 0x80 ) {
+			temp = (temp ^ 0xFFFF) + 1;
+			sign = temp >> 8;
+		}
+		
+		// add low byte (A)
+		temp += a;
+		a = (uint8_t) temp;
+		nz = (temp | (temp >> 1)) & 0x7f;
+		
+		// add high byte (Y)
+		temp >>= 8;
+		c = y + temp;
+		nz |= c;
+		
+		// half-carry (temporary avoids CodeWarrior optimizer bug)
+		unsigned hc = (c & 15) - (y & 15);
+		status |= (hc >> 4) & st_h;
+		
+		// overflow if sign of YA changed when previous sign and addend sign were same
+		status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v;
+		
+		y = (uint8_t) c;
+		
+		goto inc_pc_loop;
+	}
+	
+	case 0x5A: { // CMPW YA,dp
+		int temp = a - READ_DP( data );
+		nz = ((temp >> 1) | temp) & 0x7f;
+		temp = y + (temp >> 8);
+		temp -= READ_DP( uint8_t (data + 1) );
+		nz |= temp;
+		c = ~temp;
+		goto inc_pc_loop;
+	}
+	
+// 10. MULTIPLICATION & DIVISON COMMANDS.
+
+	case 0xCF: { // MUL YA
+		unsigned temp = y * a;
+		a = (uint8_t) temp;
+		nz = ((temp >> 1) | temp) & 0x7f;
+		y = temp >> 8;
+		nz |= y;
+		goto loop;
+	}
+	
+	case 0x9E: // DIV YA,X
+	{
+		// behavior based on SPC CPU tests
+		
+		status &= ~(st_h | st_v);
+		
+		if ( y >= x )
+			status |= st_v;
+		
+		if ( (y & 15) >= (x & 15) )
+			status |= st_h;
+		
+		unsigned temp = y * 0x100 + a;
+		if ( y < x * 2 ) {
+			a = temp / x;
+			y = temp - a * x;
+		}
+		else {
+			temp -= x * 0x200;
+			a = temp / (256 - x);
+			y = temp - a * (256 - x) + x;
+			a = 255 - a;
+		}
+		
+		nz = (uint8_t) a;
+		a = (uint8_t) a;
+		
+		goto loop;
+	}
+	
+// 11. DECIMAL COMPENSATION COMMANDS.
+	
+	// seem unused
+	// case 0xDF: // DAA
+	// case 0xBE: // DAS
+	
+// 12. BRANCHING COMMANDS.
+
+	case 0x2F: // BRA rel
+		goto branch_taken_loop;
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG )
+	
+	case 0xB0: // BCS
+		BRANCH( c & 0x100 )
+	
+	case 0x90: // BCC
+		BRANCH( !(c & 0x100) )
+	
+	case 0x70: // BVS
+		BRANCH( status & st_v )
+	
+	case 0x50: // BVC
+		BRANCH( !(status & st_v) )
+	
+	case 0x03: // BBS dp.bit,rel
+	case 0x23:
+	case 0x43:
+	case 0x63:
+	case 0x83:
+	case 0xA3:
+	case 0xC3:
+	case 0xE3:
+		pc++;
+		if ( (READ_DP( data ) >> (opcode >> 5)) & 1 )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0x13: // BBC dp.bit,rel
+	case 0x33:
+	case 0x53:
+	case 0x73:
+	case 0x93:
+	case 0xB3:
+	case 0xD3:
+	case 0xF3:
+		pc++;
+		if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0xDE: // CBNE dp+X,rel
+		data = uint8_t (data + x);
+		// fall through
+	case 0x2E: // CBNE dp,rel
+		pc++;
+		if ( READ_DP( data ) != a )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0xFE: // DBNZ Y,rel
+		y = uint8_t (y - 1);
+		if ( y )
+			goto branch_taken_loop;
+		goto inc_pc_loop;
+	
+	case 0x6E: { // DBNZ dp,rel
+		pc++;
+		unsigned temp = READ_DP( data ) - 1;
+		WRITE_DP( (uint8_t) data, (uint8_t) temp );
+		if ( temp )
+			goto cbranch_taken_loop;
+		goto inc_pc_loop;
+	}
+	
+	case 0x1F: // JMP (abs+X)
+		pc = READ_PROG16( pc ) + x;
+		// fall through
+	case 0x5F: // JMP abs
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+// 13. SUB-ROUTINE CALL RETURN COMMANDS.
+	
+	/*
+	// seems unused
+	case 0x0F: // BRK
+		PUSH16( pc + 1 );
+		pc = READ_PROG16( 0xffde ); // vector address verified
+		int temp;
+		CALC_STATUS( temp );
+		PUSH( temp );
+		status = (status | st_b) & ~st_i;
+		goto loop;
+	*/
+	
+	case 0x4F: // PCALL offset
+		pc++;
+		PUSH16( pc );
+		pc = 0xff00 + data;
+		goto loop;
+	
+	case 0x01: // TCALL n
+	case 0x11:
+	case 0x21:
+	case 0x31:
+	case 0x41:
+	case 0x51:
+	case 0x61:
+	case 0x71:
+	case 0x81:
+	case 0x91:
+	case 0xA1:
+	case 0xB1:
+	case 0xC1:
+	case 0xD1:
+	case 0xE1:
+	case 0xF1:
+		PUSH16( pc );
+		pc = READ_PROG16( 0xffde - (opcode >> 3) );
+		goto loop;
+	
+// 14. STACK OPERATION COMMANDS.
+
+	{
+		int temp;
+	case 0x7F: // RET1
+		temp = POP();
+		pc = POP();
+		pc |= POP() << 8;
+		goto set_status;
+	case 0x8E: // POP PSW
+		temp = POP();
+	set_status:
+		SET_STATUS( temp );
+		goto loop;
+	}
+	
+	case 0x0D: { // PUSH PSW
+		int temp;
+		CALC_STATUS( temp );
+		PUSH( temp );
+		goto loop;
+	}
+
+	case 0x2D: // PUSH A
+		PUSH( a );
+		goto loop;
+	
+	case 0x4D: // PUSH X
+		PUSH( x );
+		goto loop;
+	
+	case 0x6D: // PUSH Y
+		PUSH( y );
+		goto loop;
+	
+	case 0xAE: // POP A
+		a = POP();
+		goto loop;
+	
+	case 0xCE: // POP X
+		x = POP();
+		goto loop;
+	
+	case 0xEE: // POP Y
+		y = POP();
+		goto loop;
+	
+// 15. BIT OPERATION COMMANDS.
+
+	case 0x02: // SET1
+	case 0x22:
+	case 0x42:
+	case 0x62:
+	case 0x82:
+	case 0xA2:
+	case 0xC2:
+	case 0xE2:
+	case 0x12: // CLR1
+	case 0x32:
+	case 0x52:
+	case 0x72:
+	case 0x92:
+	case 0xB2:
+	case 0xD2:
+	case 0xF2: {
+		data += dp;
+		int bit = 1 << (opcode >> 5);
+		int mask = ~bit;
+		if ( opcode & 0x10 )
+			bit = 0;
+		WRITE( data, (READ( data ) & mask) | bit );
+		goto inc_pc_loop;
+	}
+		
+	case 0x0E: // TSET1 abs
+	case 0x4E:{// TCLR1 abs
+		data = READ_PROG16( pc );
+		pc += 2;
+		unsigned temp = READ( data );
+		nz = temp & a;
+		temp &= ~a;
+		if ( !(opcode & 0x40) )
+			temp |= a;
+		WRITE( data, temp );
+		goto loop;
+	}
+	
+	case 0x4A: // AND1 C,mem.bit
+		c &= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	/*
+	 // seem unused
+	case 0x6A: // AND1 C,/mem.bit
+		c &= ~mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x0A: // OR1 C,mem.bit
+		c |= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x2A: // OR1 C,/mem.bit
+		c |= ~mem_bit( pc );
+		pc += 2;
+		goto loop;
+	*/
+	
+	case 0x8A: // EOR1 C,mem.bit
+		c ^= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0xEA: { // NOT1 C,mem.bit
+		data = READ_PROG16( pc );
+		pc += 2;
+		unsigned temp = READ( data & 0x1fff );
+		temp ^= 1 << (data >> 13);
+		WRITE( data & 0x1fff, temp );
+		goto loop;
+	}
+	
+	case 0xCA: { // MOV1 mem.bit,C
+		data = READ_PROG16( pc );
+		pc += 2;
+		unsigned temp = READ( data & 0x1fff );
+		unsigned bit = data >> 13;
+		temp = (temp & ~(1 << bit)) | ((c >> (8 - bit)) & 1);
+		WRITE( data & 0x1fff, temp );
+		goto loop;
+	}
+	
+	case 0xAA: // MOV1 C,mem.bit
+		c = mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+// 16. PROGRAM STATUS FLAG OPERATION COMMANDS.
+
+	case 0x60: // CLRC
+		c = 0;
+		goto loop;
+		
+	case 0x80: // SETC
+		c = ~0;
+		goto loop;
+	
+	case 0xED: // NOTC
+		c ^= 0x100;
+		goto loop;
+		
+	case 0xE0: // CLRV
+		status &= ~(st_v | st_h);
+		goto loop;
+	
+	case 0x20: // CLRP
+		dp = 0;
+		goto loop;
+	
+	case 0x40: // SETP
+		dp = 0x100;
+		goto loop;
+	
+	/* // seem unused
+	case 0xA0: // EI
+		status |= st_i;
+		goto loop;
+	
+	case 0xC0: // DI
+		status &= ~st_i;
+		goto loop;
+	*/
+	
+// 17. OTHER COMMANDS.
+
+	case 0x00: // NOP
+		goto loop;
+	
+	//case 0xEF: // SLEEP
+	//case 0xFF: // STOP
+	
+	} // switch
+	
+	// unhandled instructions fall out of switch so emulator can catch them
+	
+stop:
+	pc--;
+	
+	{
+		int temp;
+		CALC_STATUS( temp );
+		r.status = temp;
+	}
+	
+	r.pc = pc;
+	r.sp = GET_SP();
+	r.a = a;
+	r.x = x;
+	r.y = y;
+	
+	return remain_;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Cpu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,61 @@
+
+// Super Nintendo (SNES) SPC-700 CPU emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+
+#ifndef SPC_CPU_H
+#define SPC_CPU_H
+
+#include "blargg_common.h"
+
+typedef unsigned spc_addr_t;
+typedef long spc_time_t;
+
+class Snes_Spc;
+
+class Spc_Cpu {
+	typedef BOOST::uint8_t uint8_t;
+	uint8_t* const ram;
+	spc_time_t remain_;
+	Snes_Spc& emu;
+public:
+	// Keeps pointer to ram and spc
+	Spc_Cpu( uint8_t ram [0x10000], Snes_Spc* spc );
+	
+	// SPC-700 registers. *Not* kept updated during a call to run().
+	struct registers_t {
+		unsigned short pc;
+		uint8_t a;
+		uint8_t x;
+		uint8_t y;
+		uint8_t status;
+		uint8_t sp;
+	} r;
+	
+	// Run CPU for at least 'count' cycles. Return the number of cycles remaining
+	// when emulation stopped (negative if extra cycles were emulated). Emulation
+	// stops when there are no more remaining cycles or an unhandled instruction
+	// is encountered (STOP, SLEEP, and any others not yet implemented). In the
+	// latter case, the return value is greater than zero.
+	spc_time_t run( spc_time_t count );
+	
+	// Number of clock cycles remaining for current run() call
+	spc_time_t remain() const;
+	
+	// Access memory as the emulated CPU does
+	int  read ( spc_addr_t );
+	void write( spc_addr_t, int );
+	
+private:
+	// noncopyable
+	Spc_Cpu( const Spc_Cpu& );
+	Spc_Cpu& operator = ( const Spc_Cpu& );
+	unsigned mem_bit( spc_addr_t );
+};
+
+inline spc_time_t Spc_Cpu::remain() const {
+	return remain_;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Dsp.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,644 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+// Based on Brad Martin's OpenSPC DSP emulator.
+
+#include "Spc_Dsp.h"
+
+#include <string.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2002 Brad Martin */
+/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ )
+{
+	voices_muted = 0;
+	set_gain( 1.0 );
+	
+	BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count );
+}
+
+void Spc_Dsp::reset()
+{
+	keys = 0;
+	echo_ptr = 0;
+	noise_count = 0;
+	noise = 1;
+	fir_offset = 0;
+	
+	g.flags = 0xE0; // reset, mute, echo off
+	g.key_ons = 0;
+	
+	for ( int i = 0; i < voice_count; i++ ) {
+		voice_state [i].on_cnt = 0;
+		voice_state [i].envstate = state_release;
+	}
+	
+	memset( fir_buf, 0, sizeof fir_buf );
+	memset( voice_vol, 0, sizeof voice_vol );
+}
+
+void Spc_Dsp::write( int i, int data )
+{
+	require( (unsigned) i < register_count );
+	
+	reg [i] = data;
+	int high = i >> 4;
+	switch ( i & 0x0f )
+	{
+		// voice volume
+		case 0:
+		case 1: {
+			int left = (int8_t) reg [i & ~1];
+			int right = (int8_t) reg [i | 1];
+			voice_vol [high] [0] = left; 
+			voice_vol [high] [1] = right; 
+			// kill surround only if signs of volumes differ
+			if ( left * right < 0 )
+			{
+				if ( left < 0 )
+					voice_vol [high] [0] = -left;
+				else
+					voice_vol [high] [1] = -right;
+			}
+			break;
+		}
+		
+		// fir coefficients
+		case 0x0f:
+			fir_coeff [high] = (int8_t) data; // sign-extend
+			break;
+	}
+}
+
+// This table is for envelope timing.  It represents the number of counts
+// that should be subtracted from the counter each sample period (32kHz).
+// The counter starts at 30720 (0x7800). Each count divides exactly into
+// 0x7800 without remainder.
+const int env_rate_init = 0x7800;
+static const short env_rates [0x20] = {
+	0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
+	0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
+	0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
+	0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
+};
+
+const int env_range = 0x800;
+
+int Spc_Dsp::clock_envelope( int v )
+{                               /* Return value is current 
+								 * ENVX */
+	raw_voice_t& raw_voice = this->voice [v];
+	voice_t& voice = voice_state [v];
+	
+	int envx = voice.envx;
+	if ( voice.envstate == state_release )
+	{
+		/*
+		 * Docs: "When in the state of "key off". the "click" sound is 
+		 * prevented by the addition of the fixed value 1/256" WTF???
+		 * Alright, I'm going to choose to interpret that this way:
+		 * When a note is keyed off, start the RELEASE state, which
+		 * subtracts 1/256th each sample period (32kHz).  Note there's 
+		 * no need for a count because it always happens every update. 
+		 */
+		envx -= env_range / 256;
+		if ( envx <= 0 ) {
+			envx = 0;
+			keys &= ~(1 << v);
+			return -1;
+		}
+		voice.envx = envx;
+		raw_voice.envx = envx >> 8;
+		return envx;
+	}
+	
+	int cnt = voice.envcnt;
+	int adsr1 = raw_voice.adsr [0];
+	if ( adsr1 & 0x80 )
+	{
+		switch ( voice.envstate )
+		{
+			case state_attack: {
+				// increase envelope by 1/64 each step
+				int t = adsr1 & 15;
+				if ( t == 15 ) {
+					envx += env_range / 2;
+				}
+				else {
+					cnt -= env_rates [t * 2 + 1];
+					if ( cnt > 0 )
+						break;
+					envx += env_range / 64;
+					cnt = env_rate_init;
+				}
+				if ( envx >= env_range ) {
+					envx = env_range - 1;
+					voice.envstate = state_decay;
+				}
+				voice.envx = envx;
+				break;
+			}
+			
+			case state_decay: {
+				// Docs: "DR... [is multiplied] by the fixed value
+				// 1-1/256." Well, at least that makes some sense.
+				// Multiplying ENVX by 255/256 every time DECAY is
+				// updated. 
+				cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10];
+				if ( cnt <= 0 ) {
+					cnt = env_rate_init;
+					envx -= ((envx - 1) >> 8) + 1;
+					voice.envx = envx;
+				}
+				int sustain_level = raw_voice.adsr [1] >> 5;
+				
+				if ( envx <= (sustain_level + 1) * 0x100 )
+					voice.envstate = state_sustain;
+				break;
+			}
+			
+			case state_sustain:
+				// Docs: "SR [is multiplied] by the fixed value 1-1/256."
+				// Multiplying ENVX by 255/256 every time SUSTAIN is
+				// updated. 
+				cnt -= env_rates [raw_voice.adsr [1] & 0x1f];
+				if ( cnt <= 0 ) {
+					cnt = env_rate_init;
+					envx -= ((envx - 1) >> 8) + 1;
+					voice.envx = envx;
+				}
+				break;
+		}
+	}
+	else
+	{                           /* GAIN mode is set */
+		/*
+		 * Note: if the game switches between ADSR and GAIN modes
+		 * partway through, should the count be reset, or should it
+		 * continue from where it was? Does the DSP actually watch for 
+		 * that bit to change, or does it just go along with whatever
+		 * it sees when it performs the update? I'm going to assume
+		 * the latter and not update the count, unless I see a game
+		 * that obviously wants the other behavior.  The effect would
+		 * be pretty subtle, in any case. 
+		 */
+		int t = raw_voice.gain;
+		if (t < 0x80)
+		{
+			envx = voice.envx = t << 4;
+		}
+		else switch (t >> 5)
+		{
+		case 4:         /* Docs: "Decrease (linear): Subtraction
+							 * of the fixed value 1/64." */
+			cnt -= env_rates [t & 0x1F];
+			if (cnt > 0)
+				break;
+			cnt = env_rate_init;
+			envx -= env_range / 64;
+			if ( envx < 0 ) {
+				envx = 0;
+				if ( voice.envstate == state_attack )
+					voice.envstate = state_decay;
+			}
+			voice.envx = envx;
+			break;
+		case 5:         /* Docs: "Drecrease <sic> (exponential):
+							 * Multiplication by the fixed value
+							 * 1-1/256." */
+			cnt -= env_rates [t & 0x1F];
+			if (cnt > 0)
+				break;
+			cnt = env_rate_init;
+			envx -= ((envx - 1) >> 8) + 1;
+			if ( envx < 0 ) {
+				envx = 0;
+				if ( voice.envstate == state_attack )
+					voice.envstate = state_decay;
+			}
+			voice.envx = envx;
+			break;
+		case 6:         /* Docs: "Increase (linear): Addition of
+							 * the fixed value 1/64." */
+			cnt -= env_rates [t & 0x1F];
+			if (cnt > 0)
+				break;
+			cnt = env_rate_init;
+			envx += env_range / 64;
+			if ( envx >= env_range )
+				envx = env_range - 1;
+			voice.envx = envx;
+			break;
+		case 7:         /* Docs: "Increase (bent line): Addition
+							 * of the constant 1/64 up to .75 of the
+							 * constaint <sic> 1/256 from .75 to 1." */
+			cnt -= env_rates [t & 0x1F];
+			if (cnt > 0)
+				break;
+			cnt = env_rate_init;
+			if ( envx < env_range * 3 / 4 )
+				envx += env_range / 64;
+			else
+				envx += env_range / 256;
+			if ( envx >= env_range )
+				envx = env_range - 1;
+			voice.envx = envx;
+			break;
+		}
+	}
+	voice.envcnt = cnt;
+	raw_voice.envx = envx >> 4;
+	return envx;
+}
+
+// Clamp n into range -32768 <= n <= 32767
+inline int clamp_16( int n )
+{
+	if ( (BOOST::int16_t) n != n )
+		n = BOOST::int16_t (0x7FFF - (n >> 31));
+	return n;
+}
+
+void Spc_Dsp::run( long count, short* out_buf )
+{
+	// to do: make clock_envelope() inline to avoid out-of-line calls?
+	
+	// Should we just fill the buffer with silence? Flags won't be cleared
+	// during this run so it seems it should keep resetting every sample.
+	if ( g.flags & 0x80 )
+		reset();
+	
+	struct src_dir {
+		char start [2];
+		char loop [2];
+	};
+	
+	const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100];
+	
+	int const left_volume = g.left_volume * emu_gain;
+	int right_volume = g.right_volume * emu_gain;
+	if ( left_volume * right_volume < 0 )
+		right_volume = -right_volume; // kill global surround
+	
+	while ( --count >= 0 )
+	{
+		// Here we check for keys on/off.  Docs say that successive writes
+		// to KON/KOF must be separated by at least 2 Ts periods or risk
+		// being neglected.  Therefore DSP only looks at these during an
+		// update, and not at the time of the write.  Only need to do this
+		// once however, since the regs haven't changed over the whole
+		// period we need to catch up with. 
+		
+		g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX.
+		
+		if ( g.noise_enables ) {
+			noise_count -= env_rates [g.flags & 0x1F];
+			if ( noise_count <= 0 ) {
+				noise_count = env_rate_init;
+				
+				if ( noise & 0x4000 )
+					noise_amp += (noise & 1) ? noise : -noise;
+				else
+					noise_amp >>= 1;
+				
+				int feedback = (noise << 13) ^ (noise << 14);
+				noise = (feedback & 0x4000) | (noise >> 1);
+			}
+		}
+		
+		// What is the expected behavior when pitch modulation is enabled on
+		// voice 0? Jurassic Park 2 does this. Assume 0 for now.
+		long prev_outx = 0;
+		
+		int echol = 0;
+		int echor = 0;
+		int left = 0;
+		int right = 0;
+		for ( int vidx = 0; vidx < voice_count; vidx++ )
+		{
+			const int vbit = 1 << vidx;
+			raw_voice_t& raw_voice = voice [vidx];
+			voice_t& voice = voice_state [vidx];
+			
+			if ( voice.on_cnt && !--voice.on_cnt )
+			{
+				// key on
+				keys |= vbit;
+				voice.addr = GET_LE16( sd [raw_voice.waveform].start );
+				voice.block_remain = 1;
+				voice.envx = 0;
+				voice.block_header = 0;
+				voice.fraction = 0x3fff; // decode three samples immediately
+				voice.interp0 = 0; // BRR decoder filter uses previous two samples
+				voice.interp1 = 0;
+				
+				// NOTE: Real SNES does *not* appear to initialize the
+				// envelope counter to anything in particular. The first
+				// cycle always seems to come at a random time sooner than 
+				// expected; as yet, I have been unable to find any
+				// pattern.  I doubt it will matter though, so we'll go
+				// ahead and do the full time for now. 
+				voice.envcnt = env_rate_init;
+				voice.envstate = state_attack;
+			}
+			
+			if ( g.key_ons & vbit & ~g.key_offs ) {
+				// voice doesn't come on if key off is set
+				g.key_ons &= ~vbit;
+				voice.on_cnt = 8;
+			}
+			
+			if ( keys & g.key_offs & vbit ) {
+				// key off
+				voice.envstate = state_release;
+				voice.on_cnt = 0;
+			}
+			
+			int envx;
+			if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) {
+				raw_voice.envx = 0;
+				raw_voice.outx = 0;
+				prev_outx = 0;
+				continue;
+			}
+			
+			// Decode samples when fraction >= 1.0 (0x1000)
+			for ( int n = voice.fraction >> 12; --n >= 0; )
+			{
+				if ( !--voice.block_remain )
+				{
+					if ( voice.block_header & 1 )
+					{
+						g.wave_ended |= vbit;
+					
+						if ( voice.block_header & 2 ) {
+							// verified (played endless looping sample and ENDX was set)
+							voice.addr = GET_LE16( sd [raw_voice.waveform].loop );
+						}
+						else {
+							// first block was end block; don't play anything (verified)
+							goto sample_ended; // to do: find alternative to goto
+						}
+					}
+					
+					voice.block_header = ram [voice.addr++];
+					voice.block_remain = 16; // nybbles
+				}
+				
+				// if next block has end flag set, *this* block ends *early* (verified)
+				if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 &&
+						(voice.block_header & 3) != 3 )
+				{
+			sample_ended:
+					g.wave_ended |= vbit;
+					keys &= ~vbit;
+					raw_voice.envx = 0;
+					voice.envx = 0;
+					// add silence samples to interpolation buffer
+					do {
+						voice.interp3 = voice.interp2;
+						voice.interp2 = voice.interp1;
+						voice.interp1 = voice.interp0;
+						voice.interp0 = 0;
+					}
+					while ( --n >= 0 );
+					break;
+				}
+				
+				int delta = ram [voice.addr];
+				if ( voice.block_remain & 1 ) {
+					delta <<= 4; // use lower nybble
+					voice.addr++;
+				}
+				
+				// Use sign-extended upper nybble
+				delta = int8_t (delta) >> 4;
+				
+				// For invalid ranges (D,E,F): if the nybble is negative,
+				// the result is F000.  If positive, 0000. Nothing else
+				// like previous range, etc seems to have any effect.  If
+				// range is valid, do the shift normally.  Note these are
+				// both shifted right once to do the filters properly, but 
+				// the output will be shifted back again at the end.
+				int shift = voice.block_header >> 4;
+				delta = (delta << shift) >> 1;
+				if ( shift > 0x0C )
+					delta = (delta >> 14) & ~0x7FF;
+				
+				// One, two and three point IIR filters
+				int smp1 = voice.interp0;
+				int smp2 = voice.interp1;
+				switch ( (voice.block_header >> 2) & 3 )
+				{
+					case 0:
+						break;
+					
+					case 1:
+						delta += smp1 >> 1;
+						delta += (-smp1) >> 5;
+						break;
+					
+					case 2:
+						delta += smp1;
+						delta += (-(smp1 + (smp1 >> 1))) >> 5;
+						delta -= smp2 >> 1;
+						delta += smp2 >> 5;
+						break;
+					
+					case 3:
+						delta += smp1;
+						delta += (-(smp1 + (smp1 << 2) + (smp1 << 3))) >> 7;
+						delta -= smp2 >> 1;
+						delta += (smp2 + (smp2 >> 1)) >> 4;
+						break;
+				}
+				
+				voice.interp3 = voice.interp2;
+				voice.interp2 = smp2;
+				voice.interp1 = smp1;
+				voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend
+			}
+			
+			// rate (with possible modulation)
+			int rate = GET_LE16( raw_voice.rate ) & 0x3fff;
+			if ( g.pitch_mods & vbit )
+				rate = (rate * (prev_outx + 32768)) >> 15;
+			
+			// fraction
+			int fraction = voice.fraction & 0xfff;
+			voice.fraction = fraction + rate;
+			fraction >>= 4;
+			
+			// Gaussian interpolation using most recent 4 samples
+			const short* table = gauss [fraction];
+			const short* table2 = gauss [255 - fraction];
+			int s = ((table  [0] * voice.interp3) >> 12) +
+					((table  [1] * voice.interp2) >> 12) +
+					((table2 [1] * voice.interp1) >> 12) +
+					((table2 [0] * voice.interp0) >> 12);
+			int output = noise_amp; // noise is almost never used
+			if ( !(g.noise_enables & vbit) )
+				output = clamp_16( s * 2 );
+			
+			int muted = vbit & voices_muted;
+			
+			// scale output and set outx values
+			output = ((output * envx) >> 11) & ~1;
+			prev_outx = output;
+			raw_voice.outx = output >> 8;
+			
+			// apply muting if voice is externally disabled (not a SNES feature)
+			if ( muted )
+				output = 0;
+			
+			// output
+			int l = (voice_vol [vidx] [0] * output) >> 7;
+			int r = (voice_vol [vidx] [1] * output) >> 7;
+			if ( g.echo_ons & vbit ) {
+				echol += l;
+				echor += r;
+			}
+			left += l;
+			right += r;
+		} // end of channel loop
+		
+		// main volume control
+		left = (left * left_volume) >> (7 + emu_gain_bits);
+		right = (right * right_volume) >> (7 + emu_gain_bits);
+		
+		// Echo FIR filter
+		
+		// read feedback from echo buffer
+		int echo_ptr = this->echo_ptr;
+		uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF];
+		echo_ptr += 4;
+		if ( echo_ptr >= (g.echo_delay & 15) * 0x800 )
+			echo_ptr = 0;
+		int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend
+		int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend
+		this->echo_ptr = echo_ptr;
+		
+		// put samples in history ring buffer
+		const int fir_offset = this->fir_offset;
+		short (*fir_pos) [2] = &fir_buf [fir_offset];
+		this->fir_offset = (fir_offset + 7) & 7; // move backwards one step
+		fir_pos [0] [0] = fb_left;
+		fir_pos [0] [1] = fb_right;
+		fir_pos [8] [0] = fb_left; // duplicate at +8 eliminates wrap checking below
+		fir_pos [8] [1] = fb_right;
+		
+		// FIR
+		fb_left =       fb_left * fir_coeff [7] +
+				fir_pos [1] [0] * fir_coeff [6] +
+				fir_pos [2] [0] * fir_coeff [5] +
+				fir_pos [3] [0] * fir_coeff [4] +
+				fir_pos [4] [0] * fir_coeff [3] +
+				fir_pos [5] [0] * fir_coeff [2] +
+				fir_pos [6] [0] * fir_coeff [1] +
+				fir_pos [7] [0] * fir_coeff [0];
+		
+		fb_right =     fb_right * fir_coeff [7] +
+				fir_pos [1] [1] * fir_coeff [6] +
+				fir_pos [2] [1] * fir_coeff [5] +
+				fir_pos [3] [1] * fir_coeff [4] +
+				fir_pos [4] [1] * fir_coeff [3] +
+				fir_pos [5] [1] * fir_coeff [2] +
+				fir_pos [6] [1] * fir_coeff [1] +
+				fir_pos [7] [1] * fir_coeff [0];
+		
+		// overlap calculations with tests
+		left += (fb_left * g.left_echo_volume) >> 14;
+		
+		// echo buffer feedback
+		if ( !(g.flags & 0x20) ) {
+			echol += (fb_left * g.echo_feedback) >> 14;
+			echor += (fb_right * g.echo_feedback) >> 14;
+			SET_LE16( echo_buf, clamp_16( echol ) );
+			SET_LE16( echo_buf + 2, clamp_16( echor ) );
+		}
+		
+		right += (fb_right * g.right_echo_volume) >> 14;
+		
+		if ( out_buf )
+		{
+			// write final samples
+			
+			left = clamp_16( left );
+			right = clamp_16( right );
+			
+			int mute = g.flags & 0x40;
+			
+			out_buf [0] = left;
+			out_buf [1] = right;
+			out_buf += 2;
+			
+			// muting
+			if ( mute ) {
+				out_buf [-2] = 0;
+				out_buf [-1] = 0;
+			}
+		}
+	}
+}
+
+// Base normal_gauss table is very close to the following:
+#if 0
+double e = 2.718281828;
+for ( int i = 0; i < 512; i++ ) {
+	double x = i / 511.0 * 2.31 - 0.05;
+	double y = pow( e, -x * x ) * 1305.64;
+	normal_gauss [i] = y - 16.54;
+}
+#endif
+
+// Interleved gauss table (to improve cache coherency).
+// gauss [i] [j] = normal_gauss [(1 - j) * 256 + i]
+const short Spc_Dsp::gauss [256] [2] = {
+ 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303,
+ 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299,
+ 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292,
+ 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282,
+ 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269,
+ 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253,
+ 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234,
+ 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213,
+ 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190,
+ 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164,
+ 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136,
+ 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106,
+ 102,1102, 100,1098,  99,1094,  97,1090,  95,1086,  94,1082,  92,1078,  90,1074,
+  89,1070,  87,1066,  86,1061,  84,1057,  83,1053,  81,1049,  80,1045,  78,1040,
+  77,1036,  76,1032,  74,1027,  73,1023,  71,1019,  70,1014,  69,1010,  67,1005,
+  66,1001,  65, 997,  64, 992,  62, 988,  61, 983,  60, 978,  59, 974,  58, 969,
+  56, 965,  55, 960,  54, 955,  53, 951,  52, 946,  51, 941,  50, 937,  49, 932,
+  48, 927,  47, 923,  46, 918,  45, 913,  44, 908,  43, 904,  42, 899,  41, 894,
+  40, 889,  39, 884,  38, 880,  37, 875,  36, 870,  36, 865,  35, 860,  34, 855,
+  33, 851,  32, 846,  32, 841,  31, 836,  30, 831,  29, 826,  29, 821,  28, 816,
+  27, 811,  27, 806,  26, 802,  25, 797,  24, 792,  24, 787,  23, 782,  23, 777,
+  22, 772,  21, 767,  21, 762,  20, 757,  20, 752,  19, 747,  19, 742,  18, 737,
+  17, 732,  17, 728,  16, 723,  16, 718,  15, 713,  15, 708,  15, 703,  14, 698,
+  14, 693,  13, 688,  13, 683,  12, 678,  12, 674,  11, 669,  11, 664,  11, 659,
+  10, 654,  10, 649,  10, 644,   9, 640,   9, 635,   9, 630,   8, 625,   8, 620,
+   8, 615,   7, 611,   7, 606,   7, 601,   6, 596,   6, 592,   6, 587,   6, 582,
+   5, 577,   5, 573,   5, 568,   5, 563,   4, 559,   4, 554,   4, 550,   4, 545,
+   4, 540,   3, 536,   3, 531,   3, 527,   3, 522,   3, 517,   2, 513,   2, 508,
+   2, 504,   2, 499,   2, 495,   2, 491,   2, 486,   1, 482,   1, 477,   1, 473,
+   1, 469,   1, 464,   1, 460,   1, 456,   1, 451,   1, 447,   1, 443,   1, 439,
+   0, 434,   0, 430,   0, 426,   0, 422,   0, 418,   0, 414,   0, 410,   0, 405,
+   0, 401,   0, 397,   0, 393,   0, 389,   0, 385,   0, 381,   0, 378,   0, 374,
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Dsp.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,159 @@
+
+// Super Nintendo (SNES) SPC DSP emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
+// Copyright (C) 2002 Brad Martin
+
+#ifndef SPC_DSP_H
+#define SPC_DSP_H
+
+#include "blargg_common.h"
+
+// Surround effects using opposite volumes for left and right are currently removed
+// by making left and right volume positive if their signs differ.
+
+class Spc_Dsp {
+	typedef BOOST::int8_t int8_t;
+	typedef BOOST::uint8_t uint8_t;
+public:
+	
+	// Keeps pointer to ram
+	Spc_Dsp( uint8_t ram [0x10000] );
+	
+	// Mute voice n if bit n (1 << n) of mask is clear.
+	enum { voice_count = 8 };
+	void mute_voices( int mask );
+	
+	// Clear state and silence everything.
+	void reset();
+	
+	// Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to
+	// the 16-bit sample range.
+	void set_gain( double );
+	
+	// Read/write register 'n', where n ranges from 0 to register_count - 1.
+	enum { register_count = 128 };
+	int  read ( int n );
+	void write( int n, int );
+	
+	// Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL.
+	void run( long count, short* buf = NULL );
+	
+	
+// End of public interface
+private:
+	
+	struct raw_voice_t {
+		int8_t  left_vol;
+		int8_t  right_vol;
+		uint8_t rate [2];
+		uint8_t waveform;
+		uint8_t adsr [2];   // envelope rates for attack, decay, and sustain
+		uint8_t gain;       // envelope gain (if not using ADSR)
+		int8_t  envx;       // current envelope level
+		int8_t  outx;       // current sample
+		int8_t  unused [6];
+	};
+	
+	union {
+		raw_voice_t voice [voice_count];
+		
+		uint8_t reg [register_count];
+		
+		struct {
+			int8_t  unused1 [12];
+			int8_t  left_volume;        // 0C   Main Volume Left (-.7)
+			int8_t  echo_feedback;      // 0D   Echo Feedback (-.7)
+			int8_t  unused2 [14];
+			int8_t  right_volume;       // 1C   Main Volume Right (-.7)
+			int8_t  unused3 [15];
+			int8_t  left_echo_volume;   // 2C   Echo Volume Left (-.7)
+			uint8_t pitch_mods;         // 2D   Pitch Modulation on/off for each voice
+			int8_t  unused4 [14];
+			int8_t  right_echo_volume;  // 3C   Echo Volume Right (-.7)
+			uint8_t noise_enables;      // 3D   Noise output on/off for each voice
+			int8_t  unused5 [14];
+			uint8_t key_ons;            // 4C   Key On for each voice
+			uint8_t echo_ons;           // 4D   Echo on/off for each voice
+			int8_t  unused6 [14];
+			uint8_t key_offs;           // 5C   key off for each voice (instantiates release mode)
+			uint8_t wave_page;          // 5D   source directory (wave table offsets)
+			int8_t  unused7 [14];
+			uint8_t flags;              // 6C   flags and noise freq
+			uint8_t echo_page;          // 6D
+			int8_t  unused8 [14];
+			uint8_t wave_ended;         // 7C
+			uint8_t echo_delay;         // 7D   ms >> 4
+			char    unused9 [2];
+		} g;
+	};
+	
+	uint8_t* const ram;
+	
+	// Cache of echo FIR values for faster access
+	short fir_coeff [voice_count];
+	
+	// fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code
+	short fir_buf [16] [2];
+	int fir_offset; // (0 to 7)
+	
+	short voice_vol [voice_count] [2];
+	
+	enum { emu_gain_bits = 8 };
+	int emu_gain;
+	
+	int keyed_on; // 8-bits for 8 voices
+	int keys;
+	
+	int echo_ptr;
+	int noise_amp;
+	int noise;
+	int noise_count;
+
+	int voices_muted;
+	int disable_surround_; // set to sign bit (0x80) when disabled
+	
+	static const short gauss [] [2];
+	
+	enum state_t {
+		state_attack,
+		state_decay,
+		state_sustain,
+		state_release
+	};
+	
+	struct voice_t {
+		short fraction;// 12-bit fractional position
+		short interp0; // most recent four decoded samples
+		short interp1;
+		short interp2;
+		short interp3;
+		short block_remain; // number of nybbles remaining in current block
+		unsigned short addr;
+		short block_header; // header byte from current block
+		short envcnt;
+		short envx;
+		short on_cnt;
+		state_t envstate;
+	};
+	
+	voice_t voice_state [voice_count];
+	
+	int clock_envelope( int );
+};
+
+inline int Spc_Dsp::read( int i ) {
+	assert( (unsigned) i < register_count );
+	return reg [i];
+}
+
+inline void Spc_Dsp::mute_voices( int mask ) {
+	voices_muted = mask;
+}
+
+inline void Spc_Dsp::set_gain( double v ) {
+	emu_gain = v * (1 << emu_gain_bits);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,113 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Spc_Emu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Spc_Emu::Spc_Emu()
+{
+	resample_ratio = 1.0;
+	use_resampler = false;
+	track_count_ = 0;
+}
+
+Spc_Emu::~Spc_Emu()
+{
+}
+
+const char** Spc_Emu::voice_names() const
+{
+	static const char* names [] = {
+		"DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8"
+	};
+	return names;
+}
+
+blargg_err_t Spc_Emu::init( long sample_rate, double gain )
+{
+	apu.set_gain( gain );
+	use_resampler = false;
+	resample_ratio = (double) native_sample_rate / sample_rate;
+	if ( sample_rate != native_sample_rate )
+	{
+		BLARGG_RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) );
+		resampler.time_ratio( resample_ratio, 0.9965 );
+		use_resampler = true;
+	}
+	
+	return blargg_success;
+}
+
+blargg_err_t Spc_Emu::load( const header_t& h, Emu_Reader& in )
+{
+	if ( in.remain() < sizeof file.data )
+		return "Not an SPC file";
+	
+	if ( strncmp( h.tag, "SNES-SPC700 Sound File Data", 27 ) != 0 )
+		return "Not an SPC file";
+	
+	track_count_ = 1;
+	voice_count_ = Snes_Spc::voice_count;
+	
+	memcpy( &file.header, &h, sizeof file.header );
+	return in.read( file.data, sizeof file.data );
+}
+
+blargg_err_t Spc_Emu::start_track( int )
+{
+	resampler.clear();
+	return apu.load_spc( &file, sizeof file );
+}
+
+blargg_err_t Spc_Emu::skip( long count )
+{
+	count = long (count * resample_ratio) & ~1;
+	
+	count -= resampler.skip_input( count );
+	if ( count > 0 )
+		BLARGG_RETURN_ERR( apu.skip( count ) );
+	
+	// eliminate pop due to resampler
+	const int resampler_latency = 64;
+	sample_t buf [resampler_latency];
+	return play( resampler_latency, buf );
+}
+
+blargg_err_t Spc_Emu::play( long count, sample_t* out )
+{
+	require( track_count_ ); // file must be loaded
+	
+	if ( !use_resampler )
+		return apu.play( count, out );
+	
+	long remain = count;
+	while ( remain > 0 )
+	{
+		remain -= resampler.read( &out [count - remain], remain );
+		if ( remain > 0 )
+		{
+			long n = resampler.max_write();
+			BLARGG_RETURN_ERR( apu.play( n, resampler.buffer() ) );
+			resampler.write( n );
+		}
+	}
+	
+	assert( remain == 0 );
+	
+	return blargg_success;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,79 @@
+
+// Super Nintendo (SNES) SPC music file emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004 Shay Green. GNU LGPL license.
+
+#ifndef SPC_EMU_H
+#define SPC_EMU_H
+
+#include "Fir_Resampler.h"
+#include "Music_Emu.h"
+#include "Snes_Spc.h"
+
+class Spc_Emu : public Music_Emu {
+public:
+	Spc_Emu();
+	~Spc_Emu();
+	
+	// The Super Nintendo hardware samples at 32kHz
+	enum { native_sample_rate = 32000 };
+	
+	// Initialize emulator with given sample rate and gain. A sample rate different than
+	// the native 32kHz results in internal resampling to the desired rate. A gain of 1.0
+	// results in almost no clamping. Default gain roughly matches volume of other emulators.
+	blargg_err_t init( long sample_rate, double gain = 1.4 );
+	
+	struct header_t {
+		char tag [35];
+		byte format;
+		byte version;
+		byte pc [2];
+		byte a, x, y, psw, sp;
+		byte unused [2];
+		char song [32];
+		char game [32];
+		char dumper [16];
+		char comment [32];
+		byte date [11];
+		char len_secs [3];
+		byte fade_msec [5];
+		char author [32];
+		byte mute_mask;
+		byte emulator;
+		byte unused2 [45];
+		
+		enum { copyright = 0 }; // no copyright field
+	};
+	
+	// Load SPC, given its header and reader for remaining data
+	blargg_err_t load( const header_t&, Emu_Reader& );
+	
+	void mute_voices( int );
+	blargg_err_t start_track( int );
+	blargg_err_t play( long count, sample_t* );
+	blargg_err_t skip( long );
+	const char** voice_names() const;
+
+	
+// End of public interface
+private:
+	Snes_Spc apu;
+	Fir_Resampler resampler;
+	double resample_ratio;
+	bool use_resampler;
+	
+	struct spc_file_t {
+		header_t header;
+		char data [0x10080];
+	};
+	BOOST_STATIC_ASSERT( sizeof (spc_file_t) == 0x10180 );
+	
+	spc_file_t file;
+};
+
+inline void Spc_Emu::mute_voices( int m ) {
+	apu.mute_voices( m );
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Tagged_Data.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,31 @@
+
+// Stubs to disable tagged data reflection functions
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2005 Shay Green. GNU LGPL license.
+
+#ifndef TAGGED_DATA_H
+#define TAGGED_DATA_H
+
+typedef long data_tag_t;
+
+class Tagged_Data {
+public:
+	Tagged_Data( Tagged_Data& parent, data_tag_t ) { }
+	int reading() const { return false; }
+	int not_found() const { return true; }
+	int reflect_int8( data_tag_t, int n ) { return n; }
+	int reflect_int16( data_tag_t, int n ) { return n; }
+	long reflect_int32( data_tag_t, long n ) { return n; }
+};
+
+template<class T>
+inline void reflect_int8( Tagged_Data&, data_tag_t, T* ) { }
+
+template<class T>
+inline void reflect_int16( Tagged_Data&, data_tag_t, T* ) { }
+
+template<class T>
+inline void reflect_int32( Tagged_Data&, data_tag_t, T* ) { }
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Vgm_Emu.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,286 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "Vgm_Emu.h"
+
+#include <string.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+const long vgm_sample_rate = 44100;
+
+Vgm_Emu::Vgm_Emu( double gain )
+{
+	data = NULL;
+	pos = NULL;
+	apu.volume( gain );
+	
+	// to do: decide on equalization parameters
+	set_equalizer( equalizer_t( -32, 8000, 66 ) );
+}
+
+Vgm_Emu::~Vgm_Emu()
+{
+	unload();
+}
+
+void Vgm_Emu::unload()
+{
+	delete [] data;
+	data = NULL;
+	pos = NULL;
+	track_ended_ = false;
+}
+
+const char** Vgm_Emu::voice_names() const
+{
+	static const char* names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
+	return names;
+}
+
+void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+	apu.osc_output( i, c, l, r );
+}
+
+void Vgm_Emu::update_eq( blip_eq_t const& eq )
+{
+	apu.treble_eq( eq );
+}
+
+const int time_bits = 12;
+
+inline sms_time_t Vgm_Emu::clocks_from_samples( int samples ) const
+{
+	const long round_up = 1L << (time_bits - 1);
+	return (samples * time_factor + round_up) >> time_bits;
+}
+
+static long get_le32( const BOOST::uint8_t b [4] )
+{
+	return b [3] * 0x1000000L + b [2] * 0x10000L + b [1] * 0x100L + b [0];
+}
+
+blargg_err_t Vgm_Emu::load( const header_t& h, Emu_Reader& in )
+{
+	unload();
+	
+	// compatibility
+	if ( 0 != memcmp( h.tag, "Vgm ", 4 ) )
+		return "Not a VGM file";
+	if ( get_le32( h.vers ) > 0x0101 )
+		return "Unsupported VGM format";
+	
+	// clock rate
+	long clock_rate = get_le32( h.psg_rate );
+	if ( !clock_rate )
+		return "Only PSG sound chip is supported";
+	time_factor = (long) floor( clock_rate * ((1L << time_bits) /
+			(double) vgm_sample_rate) + 0.5 );
+	
+	// data
+	long data_size = in.remain();
+	data = new byte [data_size + 3]; // allow pointer to go past end
+	if ( !data )
+		return "Out of memory";
+	end = data + data_size;
+	data [data_size] = 0;
+	data [data_size + 1] = 0;
+	data [data_size + 2] = 0;
+	blargg_err_t err = in.read( data, data_size );
+	if ( err ) {
+		unload();
+		return err;
+	}
+	long loop_offset = get_le32( h.loop_offset );
+	loop_begin = end;
+	loop_duration = 0;
+	if ( loop_offset )
+	{
+		loop_duration = get_le32( h.loop_duration );
+		if ( loop_duration )
+			loop_begin = &data [loop_offset + 0x1c - sizeof (header_t)];
+	}
+	
+	voice_count_ = Sms_Apu::osc_count;
+	track_count_ = 1;
+	
+	return setup_buffer( clock_rate );
+}
+
+int Vgm_Emu::track_length( const byte** end_out, int* remain_out ) const
+{
+	require( data ); // file must have been loaded
+	
+	long time = 0;
+	
+	if ( end_out || !loop_duration )
+	{
+		const byte* p = data;
+		while ( p < end )
+		{
+			int cmd = *p++;
+			switch ( cmd )
+			{
+				case 0x4f:
+				case 0x50:
+					p += 1;
+					break;
+				
+				case 0x61:
+					if ( p + 1 < end ) {
+						time += p [1] * 0x100L + p [0];
+						p += 2;
+					}
+					break;
+				
+				case 0x62:
+					time += 735; // ntsc frame
+					break;
+				
+				case 0x63:
+					time += 882; // pal frame
+					break;
+				
+				default:
+					if ( (p [-1] & 0xf0) == 0x50 ) {
+						p += 2;
+						break;
+					}
+					dprintf( "Bad command in VGM stream: %02X\n", (int) cmd );
+					break;
+				
+				case 0x66:
+					if ( end_out )
+						*end_out = p;
+					if ( remain_out )
+						*remain_out = end - p;
+					p = end;
+					break;
+			}
+		}
+	}
+	
+	// i.e. ceil( exact_length + 0.5 )
+	return loop_duration ? 0 :
+			(time + (vgm_sample_rate >> 1) + vgm_sample_rate - 1) / vgm_sample_rate;
+}
+
+blargg_err_t Vgm_Emu::start_track( int )
+{
+	require( data ); // file must have been loaded
+	
+	pos = data;
+	loop_remain = 0;
+	delay = 0;
+	track_ended_ = false;
+	apu.reset();
+	starting_track();
+	return blargg_success;
+}
+
+blip_time_t Vgm_Emu::run( int msec, bool* added_stereo )
+{
+	require( pos ); // track must have been started
+	
+	const int duration = vgm_sample_rate / 100 * msec / 10;
+	int time = delay;
+	while ( time < duration && pos < end )
+	{
+		if ( !loop_remain && pos >= loop_begin )
+			loop_remain = loop_duration;
+		
+		int cmd = *pos++;
+		int delay = 0;
+		switch ( cmd )
+		{
+			case 0x66:
+				pos = end; // end
+				if ( loop_duration ) {
+					pos = loop_begin;
+					loop_remain = loop_duration;
+				} 
+				break;
+			
+			case 0x62:
+				delay = 735; // ntsc frame
+				break;
+			
+			case 0x63:
+				delay = 882; // pal frame
+				break;
+			
+			case 0x4f:
+				if ( pos == end ) {
+					check( false ); // missing data
+					break;
+				}
+				apu.write_ggstereo( clocks_from_samples( time ), *pos++ );
+				break;
+			
+			case 0x50:
+				if ( pos == end ) {
+					check( false ); // missing data
+					break;
+				}
+				apu.write_data( clocks_from_samples( time ), *pos++ );
+				break;
+			
+			case 0x61:
+				if ( end - pos < 1 ) {
+					check( false ); // missing data
+					break;
+				}
+				delay = pos [1] * 0x100L + pos [0];
+				pos += 2;
+				break;
+			
+			default:
+				if ( (cmd & 0xf0) == 0x50 )
+				{
+					if ( end - pos < 1 ) {
+						check( false ); // missing data
+						break;
+					}
+					pos += 2;
+					break;
+				}
+				dprintf( "Bad command in VGM stream: %02X\n", (int) cmd );
+				break;
+			
+		}
+		time += delay;
+		if ( loop_remain && (loop_remain -= delay) <= 0 )
+		{
+			pos = loop_begin;
+			loop_remain = 0;
+		}
+	}
+	
+	blip_time_t end_time = clocks_from_samples( duration );
+	if ( pos < end )
+	{
+		delay = time - duration;
+		if ( apu.end_frame( end_time ) && added_stereo )
+			*added_stereo = true;
+	}
+	else {
+		delay = 0;
+		track_ended_ = true;
+	}
+	
+	return end_time;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Vgm_Emu.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,75 @@
+
+// Sega Master System VGM-format game music file emulator (PSG chip only)
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
+
+#ifndef VGM_EMU_H
+#define VGM_EMU_H
+
+#include "Classic_Emu.h"
+#include "Sms_Apu.h"
+
+class Vgm_Emu : public Classic_Emu {
+public:
+	// Set internal gain, where 1.0 results in almost no clamping. Default gain
+	// roughly matches volume of other emulators.
+	Vgm_Emu( double gain = 1.0 );
+	~Vgm_Emu();
+	
+	struct header_t {
+		char tag [4];
+		byte data_size [4];
+		byte vers [4];
+		byte psg_rate [4];
+		byte fm_rate [4];
+		byte g3d_offset [4];
+		byte sample_count [4];
+		byte loop_offset [4];
+		byte loop_duration [4];
+		byte frame_rate [4];
+		char unused [0x18];
+		
+	    enum { track_count = 1 }; // one track per file
+		
+		// no text fields
+		enum { game = 0 };
+		enum { song = 0 };
+		enum { author = 0 };
+		enum { copyright = 0 };
+	};
+	BOOST_STATIC_ASSERT( sizeof (header_t) == 64 );
+	
+	// Load VGM, given its header and reader for remaining data
+	blargg_err_t load( const header_t&, Emu_Reader& );
+	
+	// Determine length of track, in seconds (0 if track is endless).
+	// Optionally returns pointer and size of data past end of sequence data
+	// (i.e. any tagging information).
+	int track_length( const byte** end_out = NULL, int* remain_out = NULL ) const;
+	
+	blargg_err_t start_track( int );
+	const char** voice_names() const;
+	
+
+// End of public interface
+protected:
+	void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+	void update_eq( blip_eq_t const& );
+	blip_time_t run( int, bool* );
+private:
+	byte* data;
+	const byte* pos;
+	const byte* end;
+	const byte* loop_begin;
+	long loop_duration;
+	long loop_remain;
+	long time_factor;
+	int delay;
+	Sms_Apu apu;
+	
+	sms_time_t clocks_from_samples( int ) const;
+	void unload();
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/abstract_file.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,247 @@
+
+#include "abstract_file.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stddef.h>
+
+/* Copyright (C) 2005 by Shay Green. Permission is hereby granted, free of
+charge, to any person obtaining a copy of this software module and associated
+documentation files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+to permit persons to whom the Software is furnished to do so, subject to the
+following conditions: The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software. THE
+SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifndef RAISE_ERROR
+	#define RAISE_ERROR( str ) return str
+#endif
+
+typedef Data_Reader::error_t error_t;
+
+error_t Data_Reader::read( void* p, long s )
+{
+	long result = read_avail( p, s );
+	if ( result != s )
+	{
+		if ( result >= 0 && result < s )
+			RAISE_ERROR( "Unexpected end-of-file" );
+		
+		RAISE_ERROR( "Read error" );
+	}
+	
+	return NULL;
+}
+
+long File_Reader::remain() const
+{
+	return size() - tell();
+}
+
+error_t Data_Reader::skip( long count )
+{
+	char buf [512];
+	while ( count )
+	{
+		int n = sizeof buf;
+		if ( n > count )
+			n = count;
+		count -= n;
+		RAISE_ERROR( read( buf, n ) );
+	}
+	return NULL;
+}
+
+error_t File_Reader::skip( long n )
+{
+	assert( n >= 0 );
+	if ( n )
+		RAISE_ERROR( seek( tell() + n ) );
+	
+	return NULL;
+}
+
+
+// Subset_Reader
+
+Subset_Reader::Subset_Reader( Data_Reader* in_, long size ) :
+	in( in_ ),
+	remain_( in_->remain() )
+{
+	if ( remain_ > size )
+		remain_ = size;
+}
+
+long Subset_Reader::remain() const {
+	return remain_;
+}
+
+long Subset_Reader::read_avail( void* p, long s )
+{
+	if ( s > remain_ )
+		s = remain_;
+	remain_ -= s;
+	return in->read_avail( p, s );
+}
+
+// Mem_File_Reader
+
+Mem_File_Reader::Mem_File_Reader( const void* p, long s ) :
+	begin( (const char*) p ),
+	pos( 0 ),
+	size_( s )
+{
+}
+	
+long Mem_File_Reader::size() const {
+	return size_;
+}
+
+long Mem_File_Reader::read_avail( void* p, long s )
+{
+	long r = remain();
+	if ( s > r )
+		s = r;
+	memcpy( p, begin + pos, s );
+	pos += s;
+	return s;
+}
+
+long Mem_File_Reader::tell() const {
+	return pos;
+}
+
+error_t Mem_File_Reader::seek( long n )
+{
+	if ( n > size_ )
+		RAISE_ERROR( "Tried to go past end of file" );
+	pos = n;
+	return NULL;
+}
+
+// Std_File_Reader
+
+Std_File_Reader::Std_File_Reader() : file( NULL ) {
+}
+
+Std_File_Reader::~Std_File_Reader() {
+	close();
+}
+
+error_t Std_File_Reader::open( const char* path )
+{
+	file = fopen( path, "rb" );
+	if ( !file )
+		RAISE_ERROR( "Couldn't open file" );
+	return NULL;
+}
+
+long Std_File_Reader::size() const
+{
+	long pos = tell();
+	fseek( file, 0, SEEK_END );
+	long result = tell();
+	fseek( file, pos, SEEK_SET );
+	return result;
+}
+
+long Std_File_Reader::read_avail( void* p, long s ) {
+	return fread( p, 1, s, file );
+}
+
+long Std_File_Reader::tell() const {
+	return ftell( file );
+}
+
+error_t Std_File_Reader::seek( long n )
+{
+	if ( fseek( file, n, SEEK_SET ) != 0 )
+		RAISE_ERROR( "Error seeking in file" );
+	return NULL;
+}
+
+void Std_File_Reader::close()
+{
+	if ( file ) {
+		fclose( file );
+		file = NULL;
+	}
+}
+
+// Std_File_Writer
+
+Std_File_Writer::Std_File_Writer() : file( NULL ) {
+}
+
+Std_File_Writer::~Std_File_Writer() {
+	close();
+}
+
+error_t Std_File_Writer::open( const char* path )
+{
+	file = fopen( path, "wb" );
+	if ( !file )
+		RAISE_ERROR( "Couldn't open file for writing" );
+		
+	// to do: increase file buffer size
+	//setvbuf( file, NULL, _IOFBF, 32 * 1024L );
+	
+	return NULL;
+}
+
+error_t Std_File_Writer::write( const void* p, long s )
+{
+	long result = fwrite( p, 1, s, file );
+	if ( result != s )
+		RAISE_ERROR( "Couldn't write to file" );
+	return NULL;
+}
+
+void Std_File_Writer::close()
+{
+	if ( file ) {
+		fclose( file );
+		file = NULL;
+	}
+}
+
+// Mem_Writer
+
+Mem_Writer::Mem_Writer( void* p, long s, int b ) :
+	out( p ),
+	remain_( s ),
+	ignore_excess( b )
+{
+}
+
+error_t Mem_Writer::write( const void* p, long s )
+{
+	if ( s > remain_ )
+	{
+		if ( !ignore_excess )
+			RAISE_ERROR( "Tried to write more data than expected" );
+		s = remain_;
+	}
+	remain_ -= s;
+	memcpy( out, p, s );
+	out = (char*) out + s;
+	return NULL;
+}
+
+long Mem_Writer::remain() const {
+	return remain_;
+}
+
+// Null_Writer
+
+error_t Null_Writer::write( const void*, long ) {
+	return NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/abstract_file.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,151 @@
+
+// Abstract file access interfaces
+
+// Copyright (C) 2005 Shay Green. MIT license.
+
+#ifndef ABSTRACT_FILE_H
+#define ABSTRACT_FILE_H
+
+#include <stdio.h>
+
+// to do: built-in buffering?
+
+class Data_Reader {
+	// noncopyable
+	Data_Reader( const Data_Reader& );
+	Data_Reader& operator = ( const Data_Reader& );
+public:
+	Data_Reader() { }
+	virtual ~Data_Reader() { }
+	
+	// NULL on success, otherwise error string
+	typedef const char* error_t;
+	
+	// Read at most 'n' bytes. Return number of bytes read, negative
+	// value if error.
+	virtual long read_avail( void*, long n ) = 0;
+	
+	// Read exactly 'n' bytes (error if fewer are available). NULL on success,
+	// otherwise error string.
+	virtual error_t read( void*, long );
+	
+	// Number of bytes remaining
+	virtual long remain() const = 0;
+	
+	// Skip forwards by 'n' bytes.
+	virtual error_t skip( long n );
+	
+	// to do: bytes remaining = LONG_MAX when unknown?
+};
+
+class File_Reader : public Data_Reader {
+public:
+	// Size of file
+	virtual long size() const = 0;
+	
+	// Current position in file
+	virtual long tell() const = 0;
+	
+	// Change position in file
+	virtual error_t seek( long ) = 0;
+	
+	virtual long remain() const;
+	
+	error_t skip( long n );
+};
+
+class Subset_Reader : public Data_Reader {
+	Data_Reader* in;
+	long remain_;
+public:
+	Subset_Reader( Data_Reader*, long size );
+	long remain() const;
+	long read_avail( void*, long );
+};
+
+class Mem_File_Reader : public File_Reader {
+	const char* const begin;
+	long pos;
+	const long size_;
+public:
+	Mem_File_Reader( const void*, long size );
+	
+	long size() const;
+	long read_avail( void*, long );
+	
+	long tell() const;
+	error_t seek( long );
+};
+
+class Std_File_Reader : public File_Reader {
+	FILE* file;
+	//FILE* owned_file;
+public:
+	Std_File_Reader();
+	~Std_File_Reader();
+	
+	error_t open( const char* );
+	
+	// Forward read requests to file. Caller must close file later.
+	//void forward( FILE* );
+	
+	long size() const;
+	long read_avail( void*, long );
+	
+	long tell() const;
+	error_t seek( long );
+	
+	void close();
+};
+
+class Data_Writer {
+	// noncopyable
+	Data_Writer( const Data_Writer& );
+	Data_Writer& operator = ( const Data_Writer& );
+public:
+	Data_Writer() { }
+	virtual ~Data_Writer() { }
+	
+	typedef const char* error_t;
+	
+	// Write 'n' bytes. NULL on success, otherwise error string.
+	virtual error_t write( const void*, long n ) = 0;
+};
+
+class Null_Writer : public Data_Writer {
+public:
+	error_t write( const void*, long );
+};
+
+class Std_File_Writer : public Data_Writer {
+	FILE* file;
+public:
+	Std_File_Writer();
+	~Std_File_Writer();
+	
+	error_t open( const char* );
+	
+	// Forward writes to file. Caller must close file later.
+	//void forward( FILE* );
+	
+	error_t write( const void*, long );
+	
+	void close();
+};
+
+// to do: mem file writer
+
+// Write to block of memory
+class Mem_Writer : public Data_Writer {
+	void* out;
+	long remain_;
+	int ignore_excess;
+public:
+	// to do: automatic allocation and expansion of memory?
+	Mem_Writer( void*, long size, int ignore_excess = 1 );
+	error_t write( const void*, long );
+	long remain() const;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/blargg_common.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,161 @@
+
+// Common headers used by Shay Green's libraries
+
+// Copyright (C) 2004-2005 Shay Green.
+
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+// Allow prefix configuration file *which can re-include blargg_common.h*
+// (probably indirectly).
+#ifdef HAVE_CONFIG_H
+	#undef BLARGG_COMMON_H
+	#include "config.h"
+	#define BLARGG_COMMON_H
+#endif
+
+// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code
+#ifndef BLARGG_ENABLE_OPTIMIZER
+	#define BLARGG_ENABLE_OPTIMIZER "blargg_common.h"
+#endif
+
+// Source files have #include BLARGG_SOURCE_BEGIN at the beginning
+#ifndef BLARGG_SOURCE_BEGIN
+	#define BLARGG_SOURCE_BEGIN "blargg_source.h"
+#endif
+
+// Determine compiler's language support
+
+#if defined (__MWERKS__)
+	// Metrowerks CodeWarrior
+	#define BLARGG_COMPILER_HAS_NAMESPACE 1
+	#if !__option(bool)
+		#define BLARGG_COMPILER_HAS_BOOL 0
+	#endif
+
+#elif defined (_MSC_VER)
+	// Microsoft Visual C++
+	#if _MSC_VER < 1100
+		#define BLARGG_COMPILER_HAS_BOOL 0
+	#endif
+
+#elif defined (__GNUC__)
+	// GNU C++
+	#define BLARGG_COMPILER_HAS_BOOL 1
+
+#elif defined (__MINGW32__)
+	// Mingw?
+	#define BLARGG_COMPILER_HAS_BOOL 1
+
+#elif __cplusplus < 199711
+	// Pre-ISO C++ compiler
+	#define BLARGG_COMPILER_HAS_BOOL 0
+
+#endif
+
+// Set up boost
+#include "boost/config.hpp"
+#ifndef BOOST_MINIMAL
+	#define BOOST boost
+	#ifndef BLARGG_COMPILER_HAS_NAMESPACE
+		#define BLARGG_COMPILER_HAS_NAMESPACE 1
+	#endif
+	#ifndef BLARGG_COMPILER_HAS_BOOL
+		#define BLARGG_COMPILER_HAS_BOOL 1
+	#endif
+#endif
+
+// Bool support
+#ifndef BLARGG_COMPILER_HAS_BOOL
+	#define BLARGG_COMPILER_HAS_BOOL 1
+#elif !BLARGG_COMPILER_HAS_BOOL
+	typedef int bool;
+	const bool true  = 1;
+	const bool false = 0;
+#endif
+
+// Set up namespace support
+
+#ifndef BLARGG_COMPILER_HAS_NAMESPACE
+	#define BLARGG_COMPILER_HAS_NAMESPACE 0
+#endif
+
+#ifndef BLARGG_USE_NAMESPACE
+	#define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE
+#endif
+
+#ifndef BOOST
+	#if BLARGG_USE_NAMESPACE
+		#define BOOST boost
+	#else
+		#define BOOST
+	#endif
+#endif
+
+#undef BLARGG_BEGIN_NAMESPACE
+#undef BLARGG_END_NAMESPACE
+#if BLARGG_USE_NAMESPACE
+	#define BLARGG_BEGIN_NAMESPACE( name ) namespace name {
+	#define BLARGG_END_NAMESPACE }
+#else
+	#define BLARGG_BEGIN_NAMESPACE( name )
+	#define BLARGG_END_NAMESPACE
+#endif
+
+#if BLARGG_USE_NAMESPACE
+	#define STD std
+#else
+	#define STD
+#endif
+
+// BOOST::uint8_t, BOOST::int16_t, etc.
+#include "boost/cstdint.hpp"
+
+// BOOST_STATIC_ASSERT( expr )
+#include "boost/static_assert.hpp"
+
+// Common standard headers
+#if BLARGG_COMPILER_HAS_NAMESPACE
+	#include <cstddef>
+	#include <cassert>
+#else
+	#include <stddef.h>
+	#include <assert.h>
+#endif
+
+// blargg_err_t (NULL on success, otherwise error string)
+typedef const char* blargg_err_t;
+const blargg_err_t blargg_success = 0;
+
+// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN
+#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
+	#if defined (__powerc) || defined (macintosh)
+		#define BLARGG_BIG_ENDIAN 1
+	
+	#elif defined (_MSC_VER) && defined (_M_IX86)
+		#define BLARGG_LITTLE_ENDIAN 1
+	
+	#endif
+#endif
+
+// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features)
+#ifndef BLARGG_NONPORTABLE
+	#define BLARGG_NONPORTABLE 0
+#endif
+#ifdef BLARGG_MOST_PORTABLE
+	#error "BLARGG_MOST_PORTABLE has been removed; see BLARGG_NONPORTABLE."
+#endif
+
+// BLARGG_CPU_*
+#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86)
+	#if defined (__powerc)
+		#define BLARGG_CPU_POWERPC 1
+	
+	#elif defined (_MSC_VER) && defined (_M_IX86)
+		#define BLARGG_CPU_X86 1
+	
+	#endif
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/blargg_endian.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,47 @@
+
+// CPU Byte Order Utilities
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2005 Shay Green. BSD license.
+
+#ifndef BLARGG_ENDIAN
+#define BLARGG_ENDIAN
+
+inline unsigned get_le16( const void* p ) {
+	return *((unsigned char*) p + 1) * 0x100u + *(unsigned char*) p;
+}
+
+inline void set_le16( void* p, unsigned n ) {
+	*(unsigned char*) p = n;
+	*((unsigned char*) p + 1) = n >> 8;
+}
+
+#ifndef GET_LE16
+
+	#if 0
+		// Read 16-bit little-endian unsigned integer from memory
+		unsigned GET_LE16( const void* );
+
+		// Write 16-bit little-endian integer to memory
+		void SET_LE16( void*, unsigned );
+	#endif
+
+	// Optimized implementation if byte order is known
+	#if BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
+		#define GET_LE16( addr )        (*(unsigned short*) (addr))
+		#define SET_LE16( addr, data )  (void (*(unsigned short*) (addr) = (data)))
+
+	#elif BLARGG_NONPORTABLE && BLARGG_CPU_POWERPC
+		// PowerPC has special byte-reversed instructions
+		#define GET_LE16( addr )        ((unsigned) __lhbrx( (addr), 0 ))
+		#define SET_LE16( addr, data )  (__sthbrx( (data), (addr), 0 ))
+
+	#else
+		#define GET_LE16( addr )        get_le16( addr )
+		#define SET_LE16( addr, data )  set_le16( addr, data )
+
+	#endif
+
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/blargg_source.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,40 @@
+
+// By default, #included at beginning of library source files
+
+// Copyright (C) 2005 Shay Green.
+
+#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(( "unmet requirement", 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, ... );
+#undef dprintf
+#define dprintf (1) ? ((void) 0) : (void)
+
+// 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 returns error string, return it from current function, otherwise continue.
+#define BLARGG_RETURN_ERR( expr ) do {                          \
+		blargg_err_t blargg_return_err_ = (expr);               \
+		if ( blargg_return_err_ ) return blargg_return_err_;    \
+	} while ( 0 )
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/boost/config.hpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,13 @@
+
+// Boost substitute. For full boost library see http://boost.org
+
+#ifndef BOOST_CONFIG_HPP
+#define BOOST_CONFIG_HPP
+
+#define BOOST_MINIMAL 1
+
+#define BLARGG_BEGIN_NAMESPACE( name )
+#define BLARGG_END_NAMESPACE
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/boost/cstdint.hpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,42 @@
+
+// Boost substitute. For full boost library see http://boost.org
+
+#ifndef BOOST_CSTDINT_HPP
+#define BOOST_CSTDINT_HPP
+
+#if BLARGG_USE_NAMESPACE
+	#include <climits>
+#else
+	#include <limits.h>
+#endif
+
+BLARGG_BEGIN_NAMESPACE( boost )
+
+#if UCHAR_MAX != 0xFF || SCHAR_MAX != 0x7F
+#   error "No suitable 8-bit type available"
+#endif
+
+typedef unsigned char   uint8_t;
+typedef signed char     int8_t;
+
+#if USHRT_MAX != 0xFFFF
+#   error "No suitable 16-bit type available"
+#endif
+
+typedef short           int16_t;
+typedef unsigned short  uint16_t;
+
+#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
+#   error "No suitable 32-bit type available"
+#endif
+
+BLARGG_END_NAMESPACE
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/boost/static_assert.hpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,22 @@
+
+// Boost substitute. For full boost library see http://boost.org
+
+#ifndef BOOST_STATIC_ASSERT_HPP
+#define BOOST_STATIC_ASSERT_HPP
+
+#if defined (_MSC_VER) && _MSC_VER <= 1200
+	// MSVC6 can't handle the ##line concatenation
+	#define BOOST_STATIC_ASSERT( expr ) struct { int n [1 / ((expr) ? 1 : 0)]; }
+
+#else
+	#define BOOST_STATIC_ASSERT3( expr, line ) \
+				typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)]
+
+	#define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line )
+
+	#define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ )
+
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/changes.txt	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,63 @@
+Game_Music_Emu Change Log
+
+
+Game_Music_Emu 0.2.4
+--------------------
+
+- Created a discussion forum for problems and feedback:
+http://groups-beta.google.com/group/blargg-sound-libs
+
+- Added to-do list and design notes
+
+- Added Music_Emu::skip( long sample_count ) to skip ahead in current track
+
+- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for determining the
+length of non-looped GYM and VGM files
+
+- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly using
+abs() instead of fabs()...argh)
+
+- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and now stops
+sample slightly earlier than the end, as the SNES does. Fixed a totally broken
+CPU addressing mode.
+
+- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music sounds
+decent
+
+- Fixed a minor GBS emulation bug
+
+- Fixed GYM loop point bug when track was restarted before loop point had been
+reached
+
+- Made default GBS frequency equalization less muffled
+
+- Added pseudo-surround effect removal for SPC files
+
+- Added Music_Emu::voice_names() which returns names for each voice.
+
+- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be easily
+set for library sources
+
+- Changed assignment of expansion sound chips in Nsf_Emu to be spread more
+evenly when using Effects_Buffer
+
+
+Game_Music_Emu 0.2.0
+--------------------
+
+- Redid framework and rewrote/cleaned up emulators
+
+- Changed licensing to GNU Lesser General Public License (LGPL)
+
+- Added Sega Genesis GYM and Super Nintendo SPC emulators
+
+- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator
+
+- Eliminated use of static mutable data in emulators, allowing multi-instance
+safety
+
+
+Game_Music_Emu 0.1.0
+--------------------
+
+- First release
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/demo.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,63 @@
+
+// Play "test.nsf" using Nsf_Emu and record to "out.wav".
+
+#include "Nsf_Emu.h"
+#include "Wave_Writer.hpp"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void exit_if_error( const char* str )
+{
+	if ( str ) {
+		fprintf( stderr, "Error: %s\n", str );
+		exit( EXIT_FAILURE );
+	}
+}
+
+int main()
+{
+	const long sample_rate = 44100;
+	
+	// Prepare emulator
+	Nsf_Emu* emu = new Nsf_Emu;
+	if ( !emu )
+		exit_if_error( "Out of memory" );
+	exit_if_error( emu->init( sample_rate ) );
+	
+	// Load file
+	Emu_Std_Reader reader;
+	exit_if_error( reader.open( "test.nsf" ) );
+	Nsf_Emu::header_t header;
+	exit_if_error( reader.read( &header, sizeof header ) );
+	exit_if_error( emu->load( header, reader ) );
+	
+	// Print game and song info
+	printf( "Game     : %-32s\n", header.game      ? (char*) header.game : "" );
+	printf( "Song     : %-32s\n", header.song      ? (char*) header.song : "" );
+	printf( "Author   : %-32s\n", header.author    ? (char*) header.author : "" );
+	printf( "Copyright: %-32s\n", header.copyright ? (char*) header.copyright : "" );
+	printf( "Tracks   : %d\n", emu->track_count() );
+	printf( "\n" );
+	
+	// Record first track for several seconds
+	exit_if_error( emu->start_track( 0 ) );
+	Wave_Writer wave( sample_rate, "out.wav" );
+	wave.stereo( true );
+	while ( wave.sample_count() < 2 * sample_rate * 10 )
+	{
+		const long buf_size = 1024; // must be even
+		Music_Emu::sample_t buf [buf_size];
+		
+		// fill buffer
+		exit_if_error( emu->play( buf_size, buf ) );
+		
+		// write to sound file
+		wave.write( buf, buf_size );
+	}
+	
+	delete emu;
+	
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/demo_effects.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,71 @@
+
+// Use Effects_Buffer to add stereo effects while playing "test.gbs" using Gbs_Emu,
+// and record to "out.wav".
+
+#include "Gbs_Emu.h"
+#include "Wave_Writer.hpp"
+#include "Effects_Buffer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void exit_if_error( const char* str )
+{
+	if ( str ) {
+		fprintf( stderr, "Error: %s\n", str );
+		exit( EXIT_FAILURE );
+	}
+}
+
+int main()
+{
+	const long sample_rate = 44100;
+	
+	// Create effects buffer with 1/30 second length
+	Effects_Buffer buf;
+	exit_if_error( buf.sample_rate( sample_rate, 1000 / 30 ) );
+	
+	// Create emulator and output to effects buffer
+	Gbs_Emu* emu = new Gbs_Emu;
+	if ( !emu )
+		exit_if_error( "Out of memory" );
+	exit_if_error( emu->init( &buf ) );
+	
+	// Load file
+	Emu_Std_Reader reader;
+	exit_if_error( reader.open( "test.gbs" ) );
+	Gbs_Emu::header_t header;
+	exit_if_error( reader.read( &header, sizeof header ) );
+	exit_if_error( emu->load( header, reader ) );
+	
+	// Configure effects buffer
+	Effects_Buffer::config_t cfg;
+	cfg.pan_1 = -0.12;          // put first two oscillators slightly off-center
+	cfg.pan_2 = 0.12;
+	cfg.reverb_delay = 88;      // delays are in milliseconds
+	cfg.reverb_level = 0.20;    // significant reverb
+	cfg.echo_delay = 61;        // echo applies to noise and percussion oscillators
+	cfg.echo_level = 0.15;
+	cfg.delay_variance = 18;    // left/right delays must differ for stereo effect
+	cfg.effects_enabled = true;
+	buf.config( cfg );
+	
+	// Record first track for several seconds
+	exit_if_error( emu->start_track( 0 ) );
+	Wave_Writer wave( sample_rate, "out.wav" );
+	wave.stereo( true );
+	while ( wave.sample_count() < 2 * sample_rate * 10 )
+	{
+		const long buf_size = 1024; // must be even
+		Music_Emu::sample_t buf [buf_size];
+		
+		exit_if_error( emu->play( buf_size, buf ) );
+		
+		wave.write( buf, buf_size );
+	}
+	
+	delete emu;
+	
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/demo_panning.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,67 @@
+
+// Use Panning_Buffer to add left/right panning while playing "test.vgm" using Vgm_Emu,
+// and record to "out.wav".
+
+#include "Vgm_Emu.h"
+#include "Wave_Writer.hpp"
+#include "Panning_Buffer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void exit_if_error( const char* str )
+{
+	if ( str ) {
+		fprintf( stderr, "Error: %s\n", str );
+		exit( EXIT_FAILURE );
+	}
+}
+
+int main()
+{
+	const long sample_rate = 44100;
+	
+	// Create panning buffer with 1/30 second length
+	Panning_Buffer buf;
+	exit_if_error( buf.sample_rate( sample_rate, 1000 / 30 ) );
+	
+	// Prepare emulator with output set to panning buffer
+	Vgm_Emu* emu = new Vgm_Emu;
+	if ( !emu )
+		exit_if_error( "Out of memory" );
+	exit_if_error( emu->init( &buf ) );
+	
+	// Load file
+	Emu_Std_Reader reader;
+	exit_if_error( reader.open( "test.vgm" ) );
+	Vgm_Emu::header_t header;
+	exit_if_error( reader.read( &header, sizeof header ) );
+	exit_if_error( emu->load( header, reader ) );
+	
+	// Configure panning buffer
+	buf.set_pan( 0, 1.40, 0.60 );  // pulse 1 - left
+	buf.set_pan( 1, 1.00, 1.00 );  // pulse 2 - center
+	buf.set_pan( 2, 0.40, 1.60 );  // pulse 3 - right
+	buf.set_pan( 3, 1.00,-1.00 );  // noise   - "surround" (phase-inverted left/right)
+	
+	// Record first track for several seconds
+	exit_if_error( emu->start_track( 0 ) );
+	Wave_Writer wave( sample_rate, "out.wav" );
+	wave.stereo( true );
+	while ( wave.sample_count() < 2 * sample_rate * 10 )
+	{
+		const long buf_size = 1234; // can be any size
+		Music_Emu::sample_t buf [buf_size];
+		
+		// fill buffer
+		exit_if_error( emu->play( buf_size, buf ) );
+		
+		// write to sound file
+		wave.write( buf, buf_size );
+	}
+	
+	delete emu;
+	
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/design.txt	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,177 @@
+Game_Music_Emu Design Notes
+
+
+Managing Complexity
+-------------------
+Complexity has been a factor in most library decisions. Many features
+have been passed by due to the complexity they would add. Once past a
+certain level, complexity prevents mentally grasping the library in its
+entirety, at which point more defects will occur and be hard to find.
+
+I chose 16-bit signed samples because it seems to be the most common
+format. Supporting multiple formats would add too much complexity to be
+worth it. Other formats can be obtained via conversion.
+
+I've kept interfaces fairly lean, leaving many possible features
+untapped but easy to add if necessary. For example the classic emulators
+could have volume and frequency equalization adjusted separately for
+each channel, since they each have an associated Blip_Synth.
+
+Source files of 400 lines or less seem to be the best size to limit
+complexity. In a few cases there is no reasonable way to split longer
+files, or there is benefit from having the source together in one file.
+
+
+Library Configuration
+---------------------
+Library optimizations can be configured through macros defined in
+config.h. By default, the library is configured to be most likely to
+compile and work on any platform, rather than be most optimal with
+increased chance of problems. It's easier to track down optimiztation
+problems if the library can first be shown to work correctly without
+them.
+
+
+Flexibility through indirection
+-------------------------------
+I've tried to allow the most flexibility of modules by using indirection
+to allow extension by the user. This keeps each module simple and more
+focused on its unique task.
+
+The classic emulators use Multi_Buffer, which potentially allows a
+separate Blip_Buffer for each channel. This keeps emulators free of
+typical code to allow output in mono, stereo, panning, etc.
+
+All emulators use a reader object to access file data, allowing it to be
+stored in a regular file, compressed archive, memory, or generated
+on-the-fly. Again, the library can be kept free of the particulars of
+file access and changes required to support new formats.
+
+
+Choice of pre-ISO (ARM) C++
+---------------------------
+The library started out as an unreleased NSF player written using ISO
+C++. The code was clean enough that I decided to release it as a player
+library. Before release I evaluated its use of C++ features to determine
+the important ones.
+
+Namespaces and exceptions weren't essential, so I compared a version of
+the library with and without them. I decided that I could do without and
+get better compatibility with older compilers or newer ones with buggy
+namespace and exception implementations.
+
+Templates are the worst area for most compilers, due to their inherent
+complexity, but they are too useful to avoid entirely. I've used them
+sparingly and in ways that compilers are more likely to work with.
+
+Sticking to ARM C++ has helped keep the library simpler to understand.
+
+
+Platform-specific optimization
+------------------------------
+Performance profiling doesn't shown any big bottlenecks that warrant
+heavy platform-specific optimization. The main bottlenecks are CPU
+emulation, Blip_Buffer synthesis and sample reading, Super NES DSP, Sega
+Genesis FM, and Fir_Resampler.
+
+Further optimization of the CPU emulators can probably only be achieved
+by writing them in assembly or using dynamic recompilation. Blip_Buffer
+might benefit somewhat from vector instructions. Sega Genesis FM
+synthesis can probably be made twice as fast by someone who fully
+understands its operation (I am almost clueless about its internals).
+The Super NES DSP might have some room for optimization. Fir_Resampler
+would benefit greatly from vector operations.
+
+Most of the above optimizations add more complexity than they are worth.
+All that seems worthwhile is optimization of Sega Genesis FM emulation
+and Fir_Resampler.
+
+
+Preventing Bugs
+---------------
+I've done many things to reduce the opportunity for defects. A general
+principle is to write code so that defects will be as visible as
+possible. I've used several techniques to achieve this.
+
+I put assertions at key points where defects seem likely or where
+corruption due to a defect is likely to be visible. I've also put
+assetions where violations of the interface are likely. In emulators
+where I am unsure of exact hardware operation in a particular case, I
+output a debug-only message noting that this has occurred; many times I
+haven't implemented a hardware feature because nothing uses it. I've
+made code brittle where there is no clear reason flexibility; code
+written to handle every possibility sacrifices quality and reliability
+to handle vaguely defined situations.
+
+
+Miscellaneous
+-------------
+I don't like naming header files with a ".hpp" suffix for some reason.
+They aren't as visually distinct from ".cpp" source files. The ".cpp"
+suffix is useful to inform the compiler that it's a C++ file rather than
+a C file, but I don't know of significant practical benefits the ".hpp"
+suffix gives over ".h" (I used to use *no* suffix for header files, but
+this does cause problems).
+
+When implementation has to be put in a header file, it's as far near the
+end as possible to prevent distraction from the public interface.
+
+
+CPU Cores
+---------
+I've spent lots of time coming up with techniques to optimize the CPU
+cores. Some of the most important: execute multiple instructions during
+an emulation call, keep state in local variables to allow register
+assignment, optimize state representation for most common instructions,
+defer status flag calculation until actually needed, read program code
+directly without a call to the memory read function, always pre-fetch
+the operand byte before decoding instruction, and emulate instructions
+using common blocks of code.
+
+I've successfully used Nes_Cpu in a simple NES emulator, and I'd like to
+make all the CPU emulators suitable for use in emulators. It seems a
+waste for them to be used only for the small amount of emulation
+necessary for game music files.
+
+I debugged the CPU cores by writing a test shell that ran them in
+parallel with other CPU cores and compared all memory accesses and
+processor states at each step. This provided good value at little cost.
+
+The CPU mapping page size is adjustable to allow the best tradeoff
+between memory/cache usage and handler granularity. The interface allows
+code to be somewhat independent of the page size.
+
+I optimize program memory accesses to to direct reads rather than calls
+to the memory read function. My assumption is that it would be difficult
+to get useful code out of hardware I/O addresses, so no software will
+intentionally execute out of I/O space. Since the page size can be
+changed easily, most program memory mapping schemes can be accommodated.
+This greatly reduces memory access function calls.
+
+
+Sub-Libraries
+-------------
+I've also released the sound cores as individual libraries to reduce
+complexity for emulator authors who just want a single sound core. These
+authors will be using the sound cores directly, while users of this
+music emulator library won't even see them, so documentation and demos
+can be specific to each library.
+
+
+Documentation
+-------------
+I started out with separate documentation in HTML and found that it
+wasn't going to be easy to maintain. I switched to putting descriptions
+of function behavior in header files before the function declarations.
+This has worked well so far.
+
+I think the concrete executable demo code helps the most when someone is
+using the library for the first time, since it shows a complete program
+and provides a framework for using the library. By recording to a sound
+file, I can keep the code portable, and the result can be listened to or
+examined closely, which is important for something real-time like sound.
+
+
+I want to write some tutorials to complement the demo code, desribing
+the basic framework and operation of the modules.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/notes.txt	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,220 @@
+Game_Music_Emu Notes
+
+
+Architecture
+------------
+
+This library has several emulator classes derived from common interface
+classes. Music_Emu specifies the main interface, and Classic_Emu adds
+features available only for "classic" systems (frequency equalization
+and customizable multi-channel sound buffer).
+
+To play a given game music file, do the following:
+
+- Determine its file type
+- Create and set up an appropriate emulator
+- Load file header and file data into emulator
+- Start desired track
+- When samples are needed, call play()
+- When done, delete emulator
+
+Each emulator type defines a nested header_t structure type with members
+for the file header. When loading a file, the emulators expect the file
+header to have already been loaded; this allows the caller to use header
+fields like the game name and music author.
+
+See Music_Emu.h and Classic_Emu.h for reference.
+
+
+Error handling
+--------------
+
+Functions which can fail have a return type of blargg_err_t, which is a
+pointer to an error string (const char*). If the function is successful
+it returns blargg_success (NULL), otherwise it returns a pointer to an
+error string.
+
+To allow compatibility with older C++ compilers, no exceptions are
+thrown by any of the modules. The library is exception-safe, and any
+exceptions which occur are not intercepted.
+
+Due to the different ways compiler runtime libraries handle
+out-of-memory errors, if the library encounters one it will be reported
+in one of two ways: if the compiler is configured to throw an exception
+when operator new can't satisfy a request (which is the case in ISO
+C++), it is allowed to propagate normally, otherwise the error string
+"Out of memory" is returned.
+
+Significant violations of the documented interface are flagged with
+debug-only assertions. Failure of these usually indicates a caller error
+rather than a defect in the library.
+
+
+Configuration
+-------------
+
+The header "blargg_common.h" is used to establish a common environment.
+It attempts to automatically determine the features of the environment,
+but might need help.
+
+If HAVE_CONFIG_H is defined, the file "config.h" is included at the
+beginning of each library header file, allowing configuration options
+for the library to be set. It's fine if other libraries also use this
+scheme, as they won't conflict.
+
+Some libraries depend on the order of bytes in multibyte types. These
+will cause a compilation error if the order can't be determined. If this
+occurs, define the appropriate symbol. For big-endian (most significant
+byte first, i.e. Motorola 68000, PowerPC), #define BLARGG_BIG_ENDIAN to
+1. For little-endian (least significant byte first, i.e. Intel x86),
+#define BLARGG_LITTLE_ENDIAN to 1.
+
+Pre-ISO C++ compilers might not support bool. Support is provided where
+bool is not available, but the compiler's support of bool might not be
+properly determined. If errors occur in "blargg_common.h" in the bool
+section, #define BLARGG_COMPILER_HAS_BOOL to 1 if your compiler supports
+bool, otherwise 0.
+
+If your compiler supports namespaces, blargg_common.h uses standard
+headers with the "c" prefix to avoid bringing names from std into the
+global namespace. If your compiler supports namespaces but this isn't
+being detected by blargg_common.h, #define BLARGG_COMPILER_HAS_NAMESPACE
+to 1 in your config.h file.
+
+If you have any problems with "blargg_common.h", contact me.
+
+
+Game Music File Handling
+------------------------
+
+Game music files include text fields with information about the game and
+track. Each emulator's header_t defines the basic fields (game, song,
+author, copyright, track_count) supported by that music type, or has an
+enum { field = 0 } if unsupported. This allows the same code to be used
+for parsing each header if it checks that the field is non-zero.
+
+Text fields in most game music formats won't have a nul terminator if
+the string completely fills the field. The demos show one way to handle
+this.
+
+This library is focused on playback only and the emulators don't parse
+extended text fields, since this can be complex for some formats and can
+be done by the caller. To load the NSFE format, it must be parsed first
+and an NSF header must be made in memory and passed Nsf_Emu. See the
+Game Music Box source code for an example of this:
+
+    http://www.slack.net/~ant/game-music-box/dev.html 
+
+
+Frequency equalization
+----------------------
+
+The classic emulators allow frequency equalization to be adjusted;
+Classic_Emu::equalizer_t( treble, cutoff, bass ) specifies low-pass and
+high-pass filtering.
+
+Low-pass is an exponential rolloff beginning at 'cutoff' Hz and
+attenuating by 'treble' dB at 22kHz. For example, with cutoff = 8000 Hz
+and treble = -6 dB, the following results:
+
+        cutoff = 8kHz
+  0dB -------*__                
+	            ~~--__ treble = -6dB
+	                  ~~-*__
+	                        ~~--__
+	                              ~~--__
+	                                    ~~--__
+-18dB - - - - - - - - - - - - - - - - - - - - - -
+	  0      8kHz      22kHz               44kHz ...
+
+
+High-pass is a steep rolloff which passes -3dB attenuation at
+'breakpoint' Hz, where useful frequencies range from 0 to 6000 Hz.  For
+example, with breakpoint = 1000 Hz, the following results:
+
+      breakpoint = 1000 Hz
+ 0dB                 ___________
+-3dB      ,_*---~~~~~       
+        _~
+       /
+	  /
+      |
+      |
+-21dB - - - - - - - - - - - - -
+	  0   1000 Hz            4000 Hz
+
+Each emulator defaults to a profile that approximates its particular
+console's sound quality; this default can be determined by getting the
+current equalization just after creating the emulator. See Classic_Emu.h
+for reference.
+
+
+Emulator gain control
+---------------------
+
+Each emulator allows its gain to be adjusted. The default gains are
+selected to give consistent relative volumes between emulators without
+resulting in excessive clamping of samples that would otherwise go
+beyond the 16-bit range. A gain of 1.0 results in a conservative volume
+that rarely requires any clamping to stay within the 16-bit sample
+range. Clamping samples to 16 bits is handled by the library.
+
+
+Output sample rate
+------------------
+
+Each emulator has a way of specifying the output sample rate during
+initialization. All the emulators use internal band-limiting, so there
+is no reason to use a sample rate above 48kHz unless the sound hardware
+demands it; 44-48kHz will yield the best results. You could use the
+following code to choose a rate that is both near this range and an
+integral division of the native rate:
+
+	long adjust_rate( double native )
+	{
+		for ( double divider = 1; divider <= 4; divider++ )
+		{
+			long adjusted = native / divider + 0.5;
+			if ( adjusted <= 56000 )
+				return adjusted;
+		}
+		return 44100; // give up; CD rate probably works well enough
+	}
+
+
+Interface conventions
+----------------------
+
+If a function will keep a pointer to an object passed, to make this
+clear in source code it takes a pointer rather than a reference.
+
+Multi-word names have an underscore '_' separator between individual
+words.
+
+Functions are named with lowercase words. Functions which perform an
+action with side-effects are named with a verb phrase (i.e. load, move,
+run). Functions which set or return the value of a piece of state are
+named using a noun phrase (i.e. loaded, moved, running).
+
+Classes are named with capitalized words. Only the first letter of an
+acronym is capitalized. Class names are nouns, sometimes suggestive of
+what they do (i.e. File_Scanner).
+
+Structure, enumeration, and typedefs to these and built-in types are
+named using lowercase words with a _t suffix.
+
+Macros are named with all-uppercase words.
+
+Internal names which can't be hidden due to technical reasons have an
+underscore '_' suffix.
+
+
+Misc
+----
+
+Special thanks to Chris Moeller (kode54) for help with library testing
+and feedback. His openspc++ library in C++ was an essential starting
+point and framework for developing the SPC emulator. Brad Martin's
+excellent SNES DSP emulator (also part of openspc++) provided an
+essential foundation for the DSP core.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/readme.txt	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,122 @@
+Game_Music_Emu 0.2.4: Multi-Format Game Music Emulation Library
+
+
+Game_Music_Emu is a collection of portable video game music emulators for the
+following file formats: Nintendo NSF, Game Boy GBS, Sega Master System VGM,
+Sega Gensesis GYM, and Super Nintendo SPC.
+
+Licensed under the GNU Lesser General Public License (LGPL); see LGPL.txt.
+Copyright (C) 2003-2005 Shay Green. SNES SPC DSP emulator based on OpenSPC,
+Copyright (C) 2002 Brad Martin. Sega Genesis YM2612 emulator from Gens project,
+Copyright (C) 2002 Stephane Dallongeville.
+
+Website: http://www.slack.net/~ant/libs/
+Forum  : http://groups-beta.google.com/group/blargg-sound-libs
+Contact: hotpop.com@blargg (swap to e-mail)
+
+
+Getting Started
+---------------
+
+This library is written in somewhat conservative C++ that should compile with
+current and older compilers (ANSI/ISO and ARM).
+
+If the Boost library is installed in your environment, delete the included
+"boost" compatibility directory, otherwise add the included "boost" directory
+to your compiler's search paths.
+
+Build a program consisting of the included source files except demo_effects.cpp
+and demo_panning.cpp, and any necessary system libraries. Be sure "test.nsf" is
+in the same directory. The program should generate a WAVE sound file "out.wav"
+of music.
+
+For a full example of using Game_Music_Emu in a music player, see the Game
+Music Box source code: http://www.slack.net/~ant/game-music-box/dev.html
+
+See notes.txt for more information, and respective header (.h) files for
+reference. Visit the discussion forum to get assistance.
+
+
+Files
+-----
+
+notes.txt               Collection of notes about the library
+changes.txt             Changes made since previous releases
+todo.txt                Planned improvements and fixes
+design.txt              Library design notes
+LGPL.TXT                GNU Lesser General Public License
+
+demo.cpp                Record NSF to WAVE sound file using emulator
+demo_effects.cpp        Use Effects_Buffer while recording GBS file
+demo_panning.cpp        Use Panning_Buffer while recording VGM file
+test.nsf                Test file for NSF emulator
+
+Music_Emu.h             Game music emulator interface
+Spc_Emu.h               Super NES SPC emulator
+Gym_Emu.h               Sega Genesis GYM emulator
+
+Classic_Emu.h           "Classic" game music emulator interface
+Nsf_Emu.h               Nintendo NSF emulator
+Gbs_Emu.h               Game Boy GBS emulator
+Vgm_Emu.h               Sega Master System VGM emulator
+
+Multi_Buffer.h          Mono and stereo buffers for classic emulators
+Effects_Buffer.h        Effects buffer for classic emulators
+Panning_Buffer.h        Panning buffer for classic emulators
+
+blargg_common.h         Common library source
+blargg_endian.h
+blargg_source.h
+Blip_Buffer.cpp
+Blip_Buffer.h
+Blip_Synth.h
+Music_Emu.cpp
+Classic_Emu.cpp
+Multi_Buffer.cpp
+Effects_Buffer.cpp
+Panning_Buffer.cpp
+Fir_Resampler.cpp
+Fir_Resampler.h
+abstract_file.cpp
+abstract_file.h
+Nes_Apu.cpp             NSF emulator source
+Nes_Apu.h
+Nes_Cpu.cpp
+Nes_Cpu.h
+Nes_Oscs.cpp
+Nes_Oscs.h
+Nsf_Emu.cpp
+Nes_Namco.cpp
+Nes_Namco.h
+Nes_Vrc6.cpp
+Nes_Vrc6.h
+Tagged_Data.h
+Gbs_Emu.cpp             GBS emulator source
+Gb_Apu.cpp
+Gb_Apu.h
+Gb_Cpu.cpp
+Gb_Cpu.h
+Gb_Oscs.cpp
+Gb_Oscs.h
+Sms_Apu.cpp             VGM emulator source
+Sms_Apu.h
+Sms_Oscs.h
+Vgm_Emu.cpp
+Gym_Emu.cpp             GYM emulator source
+ym2612.cpp
+ym2612.h
+Spc_Emu.cpp             SPC emulator source
+Snes_Spc.cpp
+Snes_Spc.h
+Spc_Cpu.cpp
+Spc_Cpu.h
+Spc_Dsp.cpp
+Spc_Dsp.h
+
+boost/                  Substitute for boost library if it's unavailable
+
+Wave_Writer.hpp         WAVE sound file writer used for demo output
+Wave_Writer.cpp
+
+-- 
+Shay Green <hotpop.com@blargg> (swap to e-mail)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/todo.txt	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,72 @@
+Game_Music_Emu 0.2.4: Problems and planned improvements
+
+
+Interface
+---------
+
+- Refer to sound chip oscillators as channels, since Nes_Channel was
+renamed a long time ago.
+
+- Rename setters with set_ prefix. I decided the terseness wasn't worth
+it.
+
+- Consider putting buffer pointer before count in play() etc. Only
+reason I put count first is to allow making buffer pointer default to
+NULL for skip. But it seems better to have skip() a separate function.
+One other reason to put count first is to follow the left-to-right
+principle in organizing arguments: play( count, buf ) generates 'count'
+samples and writes them into 'buf', and count is a more significant
+parameter than the buffer to write to.
+
+- VGM, GYM: change track_length() to return length and start of loop?
+
+
+Misc
+----
+
+- NSF: Partial implementation of DAC non-linearity causes possible
+drift. Messes up saw wave demo at high frequencies.
+
+- GYM: See if ignoring PCM when it's disabled eliminates pop in some
+tracks.
+
+- All CPU emulators: what if instruction straddles page boundary? Might
+need to completely avoid reading words from code memory.
+
+- Test reloading a new file into emulator after already loading one
+
+- Optimize resampling in Sega Genesis GYM emulator
+
+- Improve GBS emulation
+
+- Improve GYM PCM channel emulation
+
+- SPC: Finish KON and KOFF reverse-engineering and incorporate into
+Spc_Dsp
+
+- GBS: Crystalis track 18 plays really fast. Does same in gbsplay 0.7.
+
+- GBS: Ultima 3 overflows stack. Messes up in gbsplay 0.7.
+
+- Set track_ended flag when emulation error occurs (log emulation error
+only in debug mode).
+
+- Change wave and noise Gb_Oscs to blip_good_quality?
+
+- VGM: Support Sega Genesis
+
+- VGM: Parse extended header
+
+- VGM: Support new version of VGM file format that might be released
+soon
+
+- GYM: Check Altered Beast tracks with samples. They cut off seemingly
+too soon.
+
+- Include example of how to use NSF non-linear handling (with note about
+limitations)
+
+- Keep SP and PC as 32-bit in CPU registers structure so emulator can
+detect overflow/underflow and halt emulation, rather than having them
+masked when written back?
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/ym2612.cpp	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,1328 @@
+
+// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
+
+#include "ym2612.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Copyright (C) 2002 Stéphane Dallongeville (gens@consolemul.com) */
+/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// This is mostly the original source in its C style and all.
+//
+// Somewhat optimized and simplified. Uses a template to generate the many
+// variants of Update_Chan. Rewrote header file. In need of full rewrite by
+// someone more familiar with FM sound and the YM2612. Has some inaccuracies
+// compared to the Sega Genesis sound, particularly being mixed at such a
+// high sample accuracy (the Genesis sounds like it has only 8 bit samples).
+// - Shay
+
+const int max_length = 3072; // A little over 4 frames
+
+const int output_bits = 14;
+
+typedef struct YM2612_slot_t {
+	const int *DT;  // parametre detune
+	int MUL;    // parametre "multiple de frequence"
+	int TL;     // Total Level = volume lorsque l'enveloppe est au plus haut
+	int TLL;    // Total Level ajusted
+	int SLL;    // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression
+	int KSR_S;  // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
+	int KSR;    // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer
+				// sur les differents parametres de l'enveloppe comme l'attaque, le decay ...  comme dans la realite !
+	int SEG;    // Type enveloppe SSG
+	int env_xor;
+	int env_max;
+
+	const int *AR;  // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR])
+	const int *DR;  // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR])
+	const int *SR;  // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR])
+	const int *RR;  // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR])
+	int Fcnt;   // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16])
+	int Finc;   // frequency step = pas d'incrementation du compteur-frequence
+				// plus le pas est grand, plus la frequence est aïgu (ou haute)
+	int Ecurp;  // Envelope current phase = cette variable permet de savoir dans quelle phase
+				// de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ...
+				// en fonction de la valeur de cette variable, on va appeler une fonction permettant
+				// de mettre à jour l'enveloppe courante.
+	int Ecnt;   // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
+	int Einc;   // Envelope step courant
+	int Ecmp;   // Envelope counter limite pour la prochaine phase
+	int EincA;  // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque
+				// cette valeur est egal à AR[KSR]
+	int EincD;  // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression
+				// cette valeur est egal à DR[KSR]
+	int EincS;  // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue
+				// cette valeur est egal à SR[KSR]
+	int EincR;  // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement
+				// cette valeur est egal à RR[KSR]
+	int *OUTp;  // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree
+				// d'un autre ou carrement à la sortie de la voie
+	int INd;    // input data of the slot = donnees en entree du slot
+	int ChgEnM; // Change envelop mask.
+	int AMS;    // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO
+	int AMSon;  // AMS enable flag = drapeau d'activation de l'AMS
+} slot_t;
+
+typedef struct YM2612_channel_t {
+	int S0_OUT[4];          // anciennes sorties slot 0 (pour le feed back)
+	int LEFT;               // LEFT enable flag
+	int RIGHT;              // RIGHT enable flag
+	int ALGO;               // Algorythm = determine les connections entre les operateurs
+	int FB;                 // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree)
+	int FMS;                // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO
+	int AMS;                // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO
+	int FNUM[4];            // hauteur frequence de la voie (+ 3 pour le mode special)
+	int FOCT[4];            // octave de la voie (+ 3 pour le mode special)
+	int KC[4];              // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S)
+	slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie
+	int FFlag;              // Frequency step recalculation flag
+} channel_t;
+
+typedef struct YM2612_state_t {
+	int Clock;          // Horloge YM2612
+	int Rate;           // Sample Rate (11025/22050/44100)
+	int TimerBase;      // TimerBase calculation
+	int Status;         // YM2612 Status (timer overflow)
+	int OPNAadr;        // addresse pour l'ecriture dans l'OPN A (propre à l'emulateur)
+	int OPNBadr;        // addresse pour l'ecriture dans l'OPN B (propre à l'emulateur)
+	int LFOcnt;         // LFO counter = compteur-frequence pour le LFO
+	int TimerA;         // timerA limit = valeur jusqu'à laquelle le timer A doit compter
+	int TimerAL;
+	int TimerAcnt;      // timerA counter = valeur courante du Timer A
+	int TimerB;         // timerB limit = valeur jusqu'à laquelle le timer B doit compter
+	int TimerBL;
+	int TimerBcnt;      // timerB counter = valeur courante du Timer B
+	int Mode;           // Mode actuel des voie 3 et 6 (normal / special)
+	int DAC;            // DAC enabled flag
+	double Frequence;   // Frequence de base, se calcul par rapport à l'horlage et au sample rate
+	channel_t CHANNEL[YM2612_Emu::channel_count];   // Les 6 voies du YM2612
+	int REG[2][0x100];  // Sauvegardes des valeurs de tout les registres, c'est facultatif
+						// cela nous rend le debuggage plus facile
+} state_t;
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+#define ATTACK    0
+#define DECAY     1
+#define SUBSTAIN  2
+#define RELEASE   3
+
+// SIN_LBITS <= 16
+// LFO_HBITS <= 16
+// (SIN_LBITS + SIN_HBITS) <= 26
+// (ENV_LBITS + ENV_HBITS) <= 28
+// (LFO_LBITS + LFO_HBITS) <= 28
+
+#define SIN_HBITS      12                               // Sinus phase counter int part
+#define SIN_LBITS      (26 - SIN_HBITS)                 // Sinus phase counter float part (best setting)
+
+#if (SIN_LBITS > 16)
+#define SIN_LBITS      16                               // Can't be greater than 16 bits
+#endif
+
+#define ENV_HBITS      12                               // Env phase counter int part
+#define ENV_LBITS      (28 - ENV_HBITS)                 // Env phase counter float part (best setting)
+
+#define LFO_HBITS      10                               // LFO phase counter int part
+#define LFO_LBITS      (28 - LFO_HBITS)                 // LFO phase counter float part (best setting)
+
+#define SIN_LENGHT     (1 << SIN_HBITS)
+#define ENV_LENGHT     (1 << ENV_HBITS)
+#define LFO_LENGHT     (1 << LFO_HBITS)
+
+#define TL_LENGHT      (ENV_LENGHT * 3)                 // Env + TL scaling + LFO
+
+#define SIN_MASK       (SIN_LENGHT - 1)
+#define ENV_MASK       (ENV_LENGHT - 1)
+#define LFO_MASK       (LFO_LENGHT - 1)
+
+#define ENV_STEP       (96.0 / ENV_LENGHT)              // ENV_MAX = 96 dB
+
+#define ENV_ATTACK     ((ENV_LENGHT * 0) << ENV_LBITS)
+#define ENV_DECAY      ((ENV_LENGHT * 1) << ENV_LBITS)
+#define ENV_END        ((ENV_LENGHT * 2) << ENV_LBITS)
+
+#define MAX_OUT_BITS   (SIN_HBITS + SIN_LBITS + 2)      // Modulation = -4 <--> +4
+#define MAX_OUT        ((1 << MAX_OUT_BITS) - 1)
+
+#define PG_CUT_OFF     ((int) (78.0 / ENV_STEP))
+#define ENV_CUT_OFF    ((int) (68.0 / ENV_STEP))
+
+#define AR_RATE        399128
+#define DR_RATE        5514396
+
+//#define AR_RATE        426136
+//#define DR_RATE        (AR_RATE * 12)
+
+#define LFO_FMS_LBITS  9    // FIXED (LFO_FMS_BASE gives somethink as 1)
+#define LFO_FMS_BASE   ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))
+
+#define S0             0    // Stupid typo of the YM2612
+#define S1             2
+#define S2             1
+#define S3             3
+
+inline void set_seg( slot_t& s, int seg ) {
+	s.env_xor = 0;
+	s.env_max = INT_MAX;
+	s.SEG = seg;
+	if ( seg & 4 ) {
+		s.env_xor = ENV_MASK;
+		s.env_max = ENV_MASK;
+	}
+}
+
+typedef struct YM2612_tables_t
+{
+	short SIN_TAB [SIN_LENGHT];                 // SINUS TABLE (offset into TL TABLE)
+	int LFOinc;         // LFO step counter = pas d'incrementation du compteur-frequence du LFO
+						// plus le pas est grand, plus la frequence est grande
+	unsigned int AR_TAB [128];                  // Attack rate table
+	unsigned int DR_TAB [96];                   // Decay rate table
+	unsigned int DT_TAB [8] [32];               // Detune table
+	unsigned int SL_TAB [16];                   // Substain level table
+	unsigned int NULL_RATE [32];                // Table for NULL rate
+	int LFO_INC_TAB [8];                        // LFO step table
+	
+	int LFO_ENV_FREQ_UP [max_length * 2];       // Temporary calculated LFO FMS, AMS (adjusted for 11.8 dB) (interleved)
+	unsigned int FINC_TAB [2048];               // Frequency step table
+	
+	short ENV_TAB [2 * ENV_LENGHT + 8];         // ENV CURVE TABLE (attack & decay)
+	
+	unsigned int DECAY_TO_ATTACK [ENV_LENGHT];  // Conversion from decay to attack phase
+	short LFO_ENV_TAB [LFO_LENGHT];             // LFO AMS TABLE (adjusted for 11.8 dB)
+	short LFO_FREQ_TAB [LFO_LENGHT];            // LFO FMS TABLE
+	int TL_TAB [TL_LENGHT * 2];                 // TOTAL LEVEL TABLE (positif and minus)
+} tables_t;
+
+static const unsigned char DT_DEF_TAB [4 * 32] = {
+// FD = 0
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+// FD = 1
+  0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+  2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+
+// FD = 2
+  1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+  5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16,
+
+// FD = 3
+  2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+  8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22
+};
+
+static const unsigned char FKEY_TAB [16] = { 
+	0, 0, 0, 0,
+	0, 0, 0, 1,
+	2, 3, 3, 3,
+	3, 3, 3, 3
+};
+
+static const unsigned int LFO_AMS_TAB [4] = {
+	31, 4, 1, 0
+};
+
+static const unsigned char LFO_FMS_TAB [8] = {
+	LFO_FMS_BASE * 0, LFO_FMS_BASE * 1,
+	LFO_FMS_BASE * 2, LFO_FMS_BASE * 3,
+	LFO_FMS_BASE * 4, LFO_FMS_BASE * 6,
+	LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
+};
+
+inline void YM2612_Special_Update() { }
+
+
+struct YM2612_Impl
+{
+	enum { channel_count = YM2612_Emu::channel_count };
+	
+	state_t YM2612;
+	int mute_mask;
+	tables_t g;
+	
+	void KEY_ON( channel_t&, int );
+	void KEY_OFF( channel_t&, int );
+	int SLOT_SET( int, int );
+	int CHANNEL_SET( int, int );
+	int YM_SET( int, int );
+	
+	blargg_err_t set_rate( long sample_rate, long clock_rate );
+	void reset();
+	void write( int addr, int data );
+	void run_timer( int );
+	void run( YM2612_Emu::sample_t*, int count );
+};
+
+void YM2612_Impl::KEY_ON( channel_t& ch, int nsl)
+{
+	slot_t *SL = &(ch.SLOT [nsl]);  // on recupere le bon pointeur de slot
+	
+	if (SL->Ecurp == RELEASE)       // la touche est-elle rel'chee ?
+	{
+		SL->Fcnt = 0;
+
+		// Fix Ecco 2 splash sound
+		
+		SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM;
+		SL->ChgEnM = ~0;
+
+//      SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK;
+//      SL->Ecnt = 0;
+
+		SL->Einc = SL->EincA;
+		SL->Ecmp = ENV_DECAY;
+		SL->Ecurp = ATTACK;
+	}
+}
+
+
+void YM2612_Impl::KEY_OFF(channel_t& ch, int nsl)
+{
+	slot_t *SL = &(ch.SLOT [nsl]);  // on recupere le bon pointeur de slot
+	
+	if (SL->Ecurp != RELEASE)       // la touche est-elle appuyee ?
+	{
+		if (SL->Ecnt < ENV_DECAY)   // attack phase ?
+		{
+			SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY;
+		}
+
+		SL->Einc = SL->EincR;
+		SL->Ecmp = ENV_END;
+		SL->Ecurp = RELEASE;
+	}
+}
+
+
+int YM2612_Impl::SLOT_SET( int Adr, int data )
+{
+	int nch = Adr & 3;
+	if ( nch == 3 )
+		return 1;
+	
+	channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)];
+	slot_t& sl = ch.SLOT [(Adr >> 2) & 3];
+
+	switch ( Adr & 0xF0 )
+	{
+		case 0x30:
+			if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1;
+			else sl.MUL = 1;
+
+			sl.DT = (int*) g.DT_TAB [(data >> 4) & 7];
+
+			ch.SLOT [0].Finc = -1;
+
+			break;
+
+		case 0x40:
+			sl.TL = data & 0x7F;
+
+			// SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound...
+			YM2612_Special_Update();
+
+#if ((ENV_HBITS - 7) < 0)
+			sl.TLL = sl.TL >> (7 - ENV_HBITS);
+#else
+			sl.TLL = sl.TL << (ENV_HBITS - 7);
+#endif
+
+			break;
+
+		case 0x50:
+			sl.KSR_S = 3 - (data >> 6);
+
+			ch.SLOT [0].Finc = -1;
+
+			if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1];
+			else sl.AR = (int*) &g.NULL_RATE [0];
+
+			sl.EincA = sl.AR [sl.KSR];
+			if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA;
+			break;
+
+		case 0x60:
+			if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS;
+			else sl.AMS = 31;
+
+			if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1];
+			else sl.DR = (int*) &g.NULL_RATE [0];
+
+			sl.EincD = sl.DR [sl.KSR];
+			if (sl.Ecurp == DECAY) sl.Einc = sl.EincD;
+			break;
+
+		case 0x70:
+			if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1];
+			else sl.SR = (int*) &g.NULL_RATE [0];
+
+			sl.EincS = sl.SR [sl.KSR];
+			if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS;
+			break;
+
+		case 0x80:
+			sl.SLL = g.SL_TAB [data >> 4];
+
+			sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2];
+
+			sl.EincR = sl.RR [sl.KSR];
+			if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR;
+			break;
+
+		case 0x90:
+			// SSG-EG envelope shapes :
+			/*
+			   E  At Al H
+			  
+			   1  0  0  0  \\\\
+			   1  0  0  1  \___
+			   1  0  1  0  \/\/
+			   1  0  1  1  \
+			   1  1  0  0  ////
+			   1  1  0  1  /
+			   1  1  1  0  /\/\
+			   1  1  1  1  /___
+			  
+			   E  = SSG-EG enable
+			   At = Start negate
+			   Al = Altern
+			   H  = Hold */
+
+			set_seg( sl, (data & 8) ? (data & 0x0F) : 0 );
+			break;
+	}
+
+	return 0;
+}
+
+
+int YM2612_Impl::CHANNEL_SET( int Adr, int data )
+{
+	int num = Adr & 3;
+	if ( num == 3 )
+		return 1;
+	
+	channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)];
+	
+	switch ( Adr & 0xFC )
+	{
+		case 0xA0:
+			YM2612_Special_Update();
+
+			ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data;
+			ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7];
+
+			ch.SLOT [0].Finc = -1;
+			break;
+
+		case 0xA4:
+			YM2612_Special_Update();
+
+			ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8);
+			ch.FOCT [0] = (data & 0x38) >> 3;
+			ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7];
+
+			ch.SLOT [0].Finc = -1;
+			break;
+
+		case 0xA8:
+			if ( Adr < 0x100 ) {
+				num++;
+
+				YM2612_Special_Update();
+
+				YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data;
+				YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) |
+						FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7];
+
+				YM2612.CHANNEL [2].SLOT [0].Finc = -1;
+			}
+			break;
+
+		case 0xAC:
+			if ( Adr < 0x100 ) {
+				num++;
+
+				YM2612_Special_Update();
+
+				YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8);
+				YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3;
+				YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) |
+						FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7];
+
+				YM2612.CHANNEL [2].SLOT [0].Finc = -1;
+			}
+			break;
+
+		case 0xB0:
+			if ( ch.ALGO != (data & 7) ) {
+				// Fix VectorMan 2 heli sound (level 1)
+				YM2612_Special_Update();
+
+				ch.ALGO = data & 7;
+				
+				ch.SLOT [0].ChgEnM = 0;
+				ch.SLOT [1].ChgEnM = 0;
+				ch.SLOT [2].ChgEnM = 0;
+				ch.SLOT [3].ChgEnM = 0;
+			}
+
+			ch.FB = 9 - ((data >> 3) & 7);                              // Real thing ?
+
+//          if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB;       // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound...
+//          else ch.FB = 31;
+			break;
+
+		case 0xB4: {
+			YM2612_Special_Update();
+			
+			ch.LEFT = 0 - ((data >> 7) & 1);
+			ch.RIGHT = 0 - ((data >> 6) & 1);
+			
+			ch.AMS = LFO_AMS_TAB [(data >> 4) & 3];
+			ch.FMS = LFO_FMS_TAB [data & 7];
+			
+			for ( int i = 0; i < 4; i++ ) {
+				slot_t& sl = ch.SLOT [i];
+				sl.AMS = (sl.AMSon ? ch.AMS : 31);
+			}
+			break;
+		}
+	}
+	
+	return 0;
+}
+
+
+int YM2612_Impl::YM_SET(int Adr, int data)
+{
+	switch ( Adr )
+	{
+		case 0x22:
+			if (data & 8) // LFO enable
+			{
+				// Cool Spot music 1, LFO modified severals time which
+				// distord the sound, have to check that on a real genesis...
+
+				g.LFOinc = g.LFO_INC_TAB [data & 7];
+			}
+			else
+			{
+				g.LFOinc = YM2612.LFOcnt = 0;
+			}
+			break;
+
+		case 0x24:
+			YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2);
+
+			if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12)
+			{
+				YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12;
+			}
+			break;
+
+		case 0x25:
+			YM2612.TimerA = (YM2612.TimerA & 0x3fc) | (data & 3);
+
+			if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12)
+			{
+				YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12;
+			}
+			break;
+
+		case 0x26:
+			YM2612.TimerB = data;
+
+			if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12))
+			{
+				YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12);
+			}
+			break;
+
+		case 0x27:
+			// Parametre divers
+			// b7 = CSM MODE
+			// b6 = 3 slot mode
+			// b5 = reset b
+			// b4 = reset a
+			// b3 = timer enable b
+			// b2 = timer enable a
+			// b1 = load b
+			// b0 = load a
+
+			if ((data ^ YM2612.Mode) & 0x40)
+			{
+				// We changed the channel 2 mode, so recalculate phase step
+				// This fix the punch sound in Street of Rage 2
+
+				YM2612_Special_Update();
+
+				YM2612.CHANNEL [2].SLOT [0].Finc = -1;      // recalculate phase step
+			}
+
+//          if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL;
+//          if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL;
+
+//          YM2612.Status &= (~data >> 4);                  // Reset du Status au cas ou c'est demande
+			YM2612.Status &= (~data >> 4) & (data >> 2);    // Reset Status
+
+			YM2612.Mode = data;
+			break;
+
+		case 0x28: {
+			int nch = data & 3;
+			if ( nch == 3 )
+				return 1;
+			if ( data & 4 )
+				nch += 3;
+			channel_t& ch = YM2612.CHANNEL [nch];
+
+			YM2612_Special_Update();
+
+			if (data & 0x10) KEY_ON(ch, S0);    // On appuie sur la touche pour le slot 1
+			else KEY_OFF(ch, S0);               // On rel'che la touche pour le slot 1
+			if (data & 0x20) KEY_ON(ch, S1);    // On appuie sur la touche pour le slot 3
+			else KEY_OFF(ch, S1);               // On rel'che la touche pour le slot 3
+			if (data & 0x40) KEY_ON(ch, S2);    // On appuie sur la touche pour le slot 2
+			else KEY_OFF(ch, S2);               // On rel'che la touche pour le slot 2
+			if (data & 0x80) KEY_ON(ch, S3);    // On appuie sur la touche pour le slot 4
+			else KEY_OFF(ch, S3);               // On rel'che la touche pour le slot 4
+			break;
+		}
+		
+		case 0x2B:
+			if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update();
+
+			YM2612.DAC = data & 0x80;   // activation/desactivation du DAC
+			break;
+	}
+	
+	return 0;
+}
+
+YM2612_Emu::YM2612_Emu() {
+	impl = NULL;
+}
+
+YM2612_Emu::~YM2612_Emu() {
+	delete impl;
+}
+
+blargg_err_t YM2612_Emu::set_rate( long rate, long clock )
+{
+	if ( !impl ) {
+		impl = new YM2612_Impl;
+		if ( !impl )
+			return "Out of memory";
+		impl->mute_mask = 0;
+	}
+	return impl->set_rate( rate, clock );
+}
+
+blargg_err_t YM2612_Impl::set_rate( long Rate, long Clock )
+{
+	require( Rate );
+	require( Clock );
+	
+	int i, j;
+	double x;
+
+	memset(&YM2612, 0, sizeof(YM2612));
+
+	YM2612.Clock = Clock;
+	YM2612.Rate = Rate;
+
+	// 144 = 12 * (prescale * 2) = 12 * 6 * 2
+	// prescale set to 6 by default
+
+	YM2612.Frequence = ((double) YM2612.Clock / (double) YM2612.Rate) / 144.0;
+	YM2612.TimerBase = (int) (YM2612.Frequence * 4096.0);
+
+	// Tableau TL :
+	// [0     -  4095] = +output  [4095  - ...] = +output overflow (fill with 0)
+	// [12288 - 16383] = -output  [16384 - ...] = -output overflow (fill with 0)
+
+	for(i = 0; i < TL_LENGHT; i++)
+	{
+		if (i >= PG_CUT_OFF)    // YM2612 cut off sound after 78 dB (14 bits output ?)
+		{
+			g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0;
+		}
+		else
+		{
+			x = MAX_OUT;                                // Max output
+			x /= pow( 10.0, (ENV_STEP * i) / 20.0 );    // Decibel -> Voltage
+
+			g.TL_TAB [i] = (int) x;
+			g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i];
+		}
+	}
+	
+	// Tableau SIN :
+	// g.SIN_TAB [x] [y] = sin(x) * y; 
+	// x = phase and y = volume
+
+	g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF;
+
+	for(i = 1; i <= SIN_LENGHT / 4; i++)
+	{
+		x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT));   // Sinus
+		x = 20 * log10(1 / x);                                      // convert to dB
+
+		j = (int) (x / ENV_STEP);                       // Get TL range
+
+		if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF;
+
+		g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j;
+		g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j;
+	}
+
+	// Tableau LFO (LFO wav) :
+
+	for(i = 0; i < LFO_LENGHT; i++)
+	{
+		x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT));   // Sinus
+		x += 1.0;
+		x /= 2.0;                   // positive only
+		x *= 11.8 / ENV_STEP;       // ajusted to MAX enveloppe modulation
+
+		g.LFO_ENV_TAB [i] = (int) x;
+
+		x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT));   // Sinus
+		x *= (double) ((1 << (LFO_HBITS - 1)) - 1);
+
+		g.LFO_FREQ_TAB [i] = (int) x;
+
+	}
+
+	// Tableau Enveloppe :
+	// g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1]              = attack curve
+	// g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve
+
+	for(i = 0; i < ENV_LENGHT; i++)
+	{
+		// Attack curve (x^8 - music level 2 Vectorman 2)
+		x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8);
+		x *= ENV_LENGHT;
+
+		g.ENV_TAB [i] = (int) x;
+
+		// Decay curve (just linear)
+		x = pow(((double) (i) / (double) (ENV_LENGHT)), 1);
+		x *= ENV_LENGHT;
+
+		g.ENV_TAB [ENV_LENGHT + i] = (int) x;
+	}
+
+	g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1;      // for the stopped state
+
+	// Tableau pour la conversion Attack -> Decay and Decay -> Attack
+	
+	for(i = 0, j = ENV_LENGHT - 1; i < ENV_LENGHT; i++)
+	{
+		while (j && (g.ENV_TAB [j] < (unsigned) i)) j--;
+
+		g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
+	}
+
+	// Tableau pour le Substain Level
+	
+	for(i = 0; i < 15; i++)
+	{
+		x = i * 3;                  // 3 and not 6 (Mickey Mania first music for test)
+		x /= ENV_STEP;
+
+		j = (int) x;
+		j <<= ENV_LBITS;
+
+		g.SL_TAB [i] = j + ENV_DECAY;
+	}
+
+	j = ENV_LENGHT - 1;             // special case : volume off
+	j <<= ENV_LBITS;
+	g.SL_TAB [15] = j + ENV_DECAY;
+
+	// Tableau Frequency Step
+
+	for(i = 0; i < 2048; i++)
+	{
+		x = (double) (i) * YM2612.Frequence;
+
+#if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0)
+		x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS));
+#else
+		x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7)));
+#endif
+
+		x /= 2.0;   // because MUL = value * 2
+
+		g.FINC_TAB [i] = (unsigned int) x;
+	}
+
+	// Tableaux Attack & Decay Rate
+
+	for(i = 0; i < 4; i++)
+	{
+		g.AR_TAB [i] = 0;
+		g.DR_TAB [i] = 0;
+	}
+
+	for(i = 0; i < 60; i++)
+	{
+		x = YM2612.Frequence;
+
+		x *= 1.0 + ((i & 3) * 0.25);                    // bits 0-1 : x1.00, x1.25, x1.50, x1.75
+		x *= (double) (1 << ((i >> 2)));                // bits 2-5 : shift bits (x2^0 - x2^15)
+		x *= (double) (ENV_LENGHT << ENV_LBITS);        // on ajuste pour le tableau g.ENV_TAB
+
+		g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE);
+		g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE);
+	}
+
+	for(i = 64; i < 96; i++)
+	{
+		g.AR_TAB [i] = g.AR_TAB [63];
+		g.DR_TAB [i] = g.DR_TAB [63];
+
+		g.NULL_RATE [i - 64] = 0;
+	}
+
+	// Tableau Detune
+
+	for(i = 0; i < 4; i++)
+	{
+		for (j = 0; j < 32; j++)
+		{
+#if ((SIN_LBITS + SIN_HBITS - 21) < 0)
+			x = (double) DT_DEF_TAB [(i << 5) + j] * YM2612.Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS));
+#else
+			x = (double) DT_DEF_TAB [(i << 5) + j] * YM2612.Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21));
+#endif
+
+			g.DT_TAB [i + 0] [j] = (int) x;
+			g.DT_TAB [i + 4] [j] = (int) -x;
+		}
+	}
+
+	// Tableau LFO
+
+	j = YM2612.Rate;
+	
+	g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
+	
+	reset();
+
+	return blargg_success;
+}
+
+void YM2612_Emu::reset() {
+	impl->reset();
+}
+
+void YM2612_Impl::reset()
+{
+	YM2612.LFOcnt = 0;
+	YM2612.TimerA = 0;
+	YM2612.TimerAL = 0;
+	YM2612.TimerAcnt = 0;
+	YM2612.TimerB = 0;
+	YM2612.TimerBL = 0;
+	YM2612.TimerBcnt = 0;
+	YM2612.DAC = 0;
+
+	YM2612.Status = 0;
+
+	YM2612.OPNAadr = 0;
+	YM2612.OPNBadr = 0;
+	
+	int i;
+	for ( i = 0; i < channel_count; i++ )
+	{
+		channel_t& ch = YM2612.CHANNEL [i];
+		
+		ch.LEFT = ~0;
+		ch.RIGHT = ~0;
+		ch.ALGO = 0;;
+		ch.FB = 31;
+		ch.FMS = 0;
+		ch.AMS = 0;
+
+		for ( int j = 0 ;j < 4 ; j++ )
+		{
+			ch.S0_OUT [j] = 0;
+			ch.FNUM [j] = 0;
+			ch.FOCT [j] = 0;
+			ch.KC [j] = 0;
+
+			ch.SLOT [j].Fcnt = 0;
+			ch.SLOT [j].Finc = 0;
+			ch.SLOT [j].Ecnt = ENV_END;     // Put it at the end of Decay phase...
+			ch.SLOT [j].Einc = 0;
+			ch.SLOT [j].Ecmp = 0;
+			ch.SLOT [j].Ecurp = RELEASE;
+
+			ch.SLOT [j].ChgEnM = 0;
+		}
+	}
+
+	for ( i = 0; i < 0x100; i++ ) {
+		YM2612.REG [0] [i] = -1;
+		YM2612.REG [1] [i] = -1;
+	}
+
+	for ( i = 0xB6; i >= 0xB4; i-- ) {
+		write( 0, i );
+		write( 2, i );
+		write( 1, 0xC0 );
+		write( 3, 0xC0 );
+	}
+
+	for ( i = 0xB2; i >= 0x22; i-- ) {
+		write( 0, i );
+		write( 2, i );
+		write( 1, 0 );
+		write( 3, 0 );
+	}
+	
+	write( 0, 0x2A );
+	write( 1, 0x80 );
+}
+
+inline void YM2612_Impl::write( int addr, int data )
+{
+	require( (unsigned) addr < 4 );
+	require( (unsigned) data <= 0xFF );
+	
+	switch( addr )
+	{
+		case 0:
+			YM2612.OPNAadr = data;
+			break;
+
+		case 2:
+			YM2612.OPNBadr = data;
+			break;
+		
+		case 1: {
+			int opn_addr = YM2612.OPNAadr;
+			if ( opn_addr < 0x30 )
+			{
+				YM2612.REG [0] [opn_addr] = data;
+				YM_SET( opn_addr, data );
+			}
+			else if ( YM2612.REG [0] [opn_addr] != data ) {
+				YM2612.REG [0] [opn_addr] = data;
+				
+				if ( opn_addr < 0xA0 )
+					SLOT_SET( opn_addr, data );
+				else
+					CHANNEL_SET( opn_addr, data );
+			}
+			break;
+		}
+		
+		case 3: {
+			int opn_addr = YM2612.OPNBadr;
+			if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data )
+			{
+				YM2612.REG [1] [opn_addr] = data;
+
+				if ( opn_addr < 0xA0 )
+					SLOT_SET( opn_addr + 0x100, data );
+				else
+					CHANNEL_SET( opn_addr + 0x100, data );
+			}
+			break;
+		}
+	}
+}
+
+void YM2612_Emu::write( int addr, int data ) {
+	impl->write( addr, data );
+}
+
+inline void YM2612_Impl::run_timer( int length )
+{
+	int i = YM2612.TimerBase * length;
+
+	if (YM2612.Mode & 1)                            // Timer A ON ?
+	{
+//      if ((YM2612.TimerAcnt -= 14073) <= 0)       // 13879=NTSC (old: 14475=NTSC  14586=PAL)
+		if ((YM2612.TimerAcnt -= i) <= 0)
+		{
+			// timer a overflow
+			
+			YM2612.Status |= (YM2612.Mode & 0x04) >> 2;
+			YM2612.TimerAcnt += YM2612.TimerAL;
+
+			if (YM2612.Mode & 0x80) {
+				KEY_ON( YM2612.CHANNEL [2], 0 );
+				KEY_ON( YM2612.CHANNEL [2], 1 );
+				KEY_ON( YM2612.CHANNEL [2], 2 );
+				KEY_ON( YM2612.CHANNEL [2], 3 );
+			}
+		}
+	}
+
+	if (YM2612.Mode & 2)                            // Timer B ON ?
+	{
+//      if ((YM2612.TimerBcnt -= 14073) <= 0)       // 13879=NTSC (old: 14475=NTSC  14586=PAL)
+		if ((YM2612.TimerBcnt -= i) <= 0)
+		{
+			// timer b overflow
+			YM2612.Status |= (YM2612.Mode & 0x08) >> 2;
+			YM2612.TimerBcnt += YM2612.TimerBL;
+		}
+	}
+}
+
+void YM2612_Emu::run_timer( int length ) {
+	impl->run_timer( length );
+}
+
+void YM2612_Emu::mute_voices( int mask ) {
+	impl->mute_mask = mask;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+static const int no_lfo [2] = { 0, 0 };
+
+static void update_env( channel_t& ch )
+{
+	slot_t* sl = ch.SLOT;
+	for ( int n = 4; n--; sl++ )
+	{
+		if ( (sl->Ecnt += sl->Einc) < sl->Ecmp )
+			continue;
+		
+		switch ( sl->Ecurp ) {
+		case 0:
+			// Env_Attack_Next
+			
+			// Verified with Gynoug even in HQ (explode SFX)
+			sl->Ecnt = ENV_DECAY;
+
+			sl->Einc = sl->EincD;
+			sl->Ecmp = sl->SLL;
+			sl->Ecurp = DECAY;
+			break;
+		
+		case 1:
+			// Env_Decay_Next
+			
+			// Verified with Gynoug even in HQ (explode SFX)
+			sl->Ecnt = sl->SLL;
+
+			sl->Einc = sl->EincS;
+			sl->Ecmp = ENV_END;
+			sl->Ecurp = SUBSTAIN;
+			break;
+		
+		case 2:
+			// Env_Substain_Next(slot_t *SL)
+			if (sl->SEG & 8)    // SSG envelope type
+			{
+				int release = sl->SEG & 1;
+				
+				if ( !release )
+				{
+					// re KEY ON
+
+					// sl->Fcnt = 0;
+					// sl->ChgEnM = ~0;
+
+					sl->Ecnt = 0;
+					sl->Einc = sl->EincA;
+					sl->Ecmp = ENV_DECAY;
+					sl->Ecurp = ATTACK;
+				}
+
+				set_seg( *sl, (sl->SEG << 1) & 4 );
+				
+				if ( !release )
+					break;
+			}
+			// fall through
+		
+		case 3:
+			// Env_Release_Next
+			sl->Ecnt = ENV_END;
+			sl->Einc = 0;
+			sl->Ecmp = ENV_END + 1;
+			break;
+		
+		// default: no op
+		}
+	}
+}
+
+template<int algo>
+struct ym2612_update_chan {
+	static void func( const tables_t& g, channel_t&, YM2612_Emu::sample_t*, int );
+};
+
+typedef void (*ym2612_update_chan_t)( const tables_t& g, channel_t&, YM2612_Emu::sample_t*, int );
+
+template<int algo>
+void ym2612_update_chan<algo>::func( const tables_t& g, channel_t& ch, YM2612_Emu::sample_t* buf, int length )
+{
+	int not_end = ch.SLOT [S3].Ecnt - ENV_END;
+	
+	// algo is a compile-time constant, so all conditions based on it are resolved during compilation
+	
+	// special cases
+	if ( algo == 7 )
+		not_end |= ch.SLOT [S0].Ecnt - ENV_END;
+	if ( algo >= 5 )
+		not_end |= ch.SLOT [S2].Ecnt - ENV_END;
+	if ( algo >= 4 )
+		not_end |= ch.SLOT [S1].Ecnt - ENV_END;
+	
+	const int CH_LEFT = ch.LEFT;
+	const int CH_RIGHT = ch.RIGHT;
+	
+	int CH_S0_OUT_1 = ch.S0_OUT [1];
+	
+	if ( !not_end )
+		return;
+	
+	int in0 = ch.SLOT [S0].Fcnt;
+	int in1 = ch.SLOT [S1].Fcnt;
+	int in2 = ch.SLOT [S2].Fcnt;
+	int in3 = ch.SLOT [S3].Fcnt;
+	
+	const int* lfo_freq_env = g.LFO_ENV_FREQ_UP;
+	int lfo_step = 2;
+	if ( !g.LFOinc ) {
+		lfo_step = 0;
+		lfo_freq_env = no_lfo;
+	}
+	
+	const int* const TL_TAB = g.TL_TAB; // cache
+	
+#define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)])
+	
+	const short* const ENV_TAB = g.ENV_TAB; // cache
+	
+	goto first_iter;
+	
+	while ( --length )
+	{
+		// update envelope
+		update_env( ch );
+	first_iter:
+		
+		// calc envelope
+		int const env_LFO = lfo_freq_env [0];
+		
+	#define CALC_EN( x ) \
+		int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL;  \
+		int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) &    \
+				((temp##x - ch.SLOT [S##x].env_max) >> 31);
+		
+		CALC_EN( 0 )
+		CALC_EN( 1 )
+		CALC_EN( 2 )
+		CALC_EN( 3 )
+		
+		// feedback
+		int CH_S0_OUT_0 = ch.S0_OUT [0];
+		{
+			int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB);
+			CH_S0_OUT_1 = CH_S0_OUT_0;
+			CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 );
+		}
+		
+		int CH_OUTd;
+		if ( algo == 0 ) {
+			int temp = in1 + CH_S0_OUT_1;
+			temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 );
+			temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		}
+		else if ( algo == 1 ) {
+			int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
+			temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		}
+		else if ( algo == 2 ) {
+			int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
+			temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		}
+		else if ( algo == 3 ) {
+			int temp = in1 + CH_S0_OUT_1;
+			temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) +
+					SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		}
+		else if ( algo == 4 ) {
+			int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+			CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) +
+					SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 );
+			//DO_LIMIT
+		}
+		else if ( algo == 5 ) {
+			int temp = CH_S0_OUT_1;
+			CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) +
+					SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) +
+					SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 );
+			//DO_LIMIT
+		}
+		else if ( algo == 6 ) {
+			CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
+					SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) +
+					SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+			//DO_LIMIT
+		}
+		else if ( algo == 7 ) {
+			CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
+					SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) +
+					SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1;
+			//DO_LIMIT
+		}
+		
+		CH_OUTd >>= MAX_OUT_BITS - output_bits + 2;
+		
+		// update phase
+		const unsigned freq_LFO = ((ch.FMS * lfo_freq_env [1]) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1));
+		in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+		in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+		in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+		in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+		
+		int t0 = buf [0] + (CH_OUTd & CH_LEFT);
+		int t1 = buf [1] + (CH_OUTd & CH_RIGHT);
+		
+		ch.S0_OUT [0] = CH_S0_OUT_0;
+		buf [0] = t0;
+		buf [1] = t1;
+		buf += 2;
+		
+		lfo_freq_env += lfo_step;
+	}
+	
+	ch.S0_OUT [1] = CH_S0_OUT_1;
+	
+	ch.SLOT [S0].Fcnt = in0;
+	ch.SLOT [S1].Fcnt = in1;
+	ch.SLOT [S2].Fcnt = in2;
+	ch.SLOT [S3].Fcnt = in3;
+	
+	update_env( ch );
+}
+
+static const ym2612_update_chan_t UPDATE_CHAN [8] = {
+	&ym2612_update_chan<0>::func,
+	&ym2612_update_chan<1>::func,
+	&ym2612_update_chan<2>::func,
+	&ym2612_update_chan<3>::func,
+	&ym2612_update_chan<4>::func,
+	&ym2612_update_chan<5>::func,
+	&ym2612_update_chan<6>::func,
+	&ym2612_update_chan<7>::func
+};
+
+void YM2612_Impl::run( YM2612_Emu::sample_t* buf, int length )
+{
+	require( length % 2 == 0 ); // generates pairs of samples
+	require( length <= max_length * 2 );
+	
+	length >>= 1;
+	
+	// Mise à jour des pas des compteurs-frequences s'ils ont ete modifies
+	
+	for ( int chi = 0; chi < channel_count; chi++ )
+	{
+		channel_t& ch = YM2612.CHANNEL [chi];
+		if ( ch.SLOT [0].Finc != -1 )
+			continue;
+		
+		int i2 = 0;
+		if ( chi == 2 && (YM2612.Mode & 0x40) )
+			i2 = 2;
+		
+		for ( int i = 0; i < 4; i++ )
+		{
+			// static int seq [4] = { 2, 1, 3, 0 };
+			// if ( i2 ) i2 = seq [i];
+			
+			slot_t& sl = ch.SLOT [i];
+			int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]);
+			int ksr = ch.KC [i2] >> sl.KSR_S;   // keycode attenuation
+			sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL;
+			if (sl.KSR != ksr)          // si le KSR a change alors
+			{                       // les differents taux pour l'enveloppe sont mis à jour
+				sl.KSR = ksr;
+
+				sl.EincA = sl.AR [ksr];
+				sl.EincD = sl.DR [ksr];
+				sl.EincS = sl.SR [ksr];
+				sl.EincR = sl.RR [ksr];
+
+				if (sl.Ecurp == ATTACK) {
+					sl.Einc = sl.EincA;
+				}
+				else if (sl.Ecurp == DECAY) {
+					sl.Einc = sl.EincD;
+				}
+				else if (sl.Ecnt < ENV_END) {
+					if (sl.Ecurp == SUBSTAIN)
+						sl.Einc = sl.EincS;
+					else if (sl.Ecurp == RELEASE)
+						sl.Einc = sl.EincR;
+				}
+			}
+			
+			if ( i2 )
+				i2 = (i2 ^ 2) ^ (i2 >> 1);
+		}
+	}
+	
+	if ( g.LFOinc )
+	{
+		// Precalcul LFO wav
+		for(int i = 0; i < length; i++)
+		{
+			int j = ((YM2612.LFOcnt += g.LFOinc) >> LFO_LBITS) & LFO_MASK;
+
+			g.LFO_ENV_FREQ_UP [i * 2] = g.LFO_ENV_TAB [j];
+			g.LFO_ENV_FREQ_UP [i * 2 + 1] = g.LFO_FREQ_TAB [j];
+		}
+	}
+	
+	for ( int i = 0; i < channel_count; i++ )
+		if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) )
+			UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], buf, length );
+}
+
+void YM2612_Emu::run( sample_t* buf, int length ) {
+	impl->run( buf, length );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/ym2612.h	Tue Nov 01 19:57:26 2005 -0800
@@ -0,0 +1,38 @@
+
+// Sega Genesis YM2612 FM Sound Chip Emulator
+
+// Game_Music_Emu 0.2.4. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
+// Copyright (C) 2002 Stéphane Dallongeville
+
+#ifndef YM2612_H
+#define YM2612_H
+
+#include "blargg_common.h"
+
+struct YM2612_Impl;
+
+class YM2612_Emu {
+public:
+	YM2612_Emu();
+	~YM2612_Emu();
+	
+	blargg_err_t set_rate( long sample_rate, long clock_rate );
+	
+	void reset();
+	
+	enum { channel_count = 6 };
+	void mute_voices( int mask );
+	
+	void write( int addr, int data );
+	
+	void run_timer( int );
+	
+	typedef BOOST::int16_t sample_t;
+	void run( sample_t*, int count );
+	
+private:
+	YM2612_Impl* impl;
+};
+
+#endif
+