changeset 1691:e24213152357 trunk

[svn] .cpp -> .cxx This has the nice side effect that we don't need to add .cpp to .SUFFIXES for OpenBSD.
author js
date Thu, 14 Sep 2006 13:33:10 -0700
parents bb1323938306
children 2dd4cf103e1c
files ChangeLog Plugins/Input/console/Audacious_Config.cpp Plugins/Input/console/Audacious_Config.cxx Plugins/Input/console/Audacious_Driver.cpp Plugins/Input/console/Audacious_Driver.cxx Plugins/Input/console/Blip_Buffer.cpp Plugins/Input/console/Blip_Buffer.cxx Plugins/Input/console/Classic_Emu.cpp Plugins/Input/console/Classic_Emu.cxx Plugins/Input/console/Dual_Resampler.cpp Plugins/Input/console/Dual_Resampler.cxx Plugins/Input/console/Fir_Resampler.cpp Plugins/Input/console/Fir_Resampler.cxx Plugins/Input/console/Gb_Apu.cpp Plugins/Input/console/Gb_Apu.cxx Plugins/Input/console/Gb_Cpu.cpp Plugins/Input/console/Gb_Cpu.cxx Plugins/Input/console/Gb_Oscs.cpp Plugins/Input/console/Gb_Oscs.cxx Plugins/Input/console/Gbs_Emu.cpp Plugins/Input/console/Gbs_Emu.cxx Plugins/Input/console/Gym_Emu.cpp Plugins/Input/console/Gym_Emu.cxx Plugins/Input/console/Gzip_File.cpp Plugins/Input/console/Gzip_File.cxx Plugins/Input/console/Jma_File.cpp Plugins/Input/console/Jma_File.cxx Plugins/Input/console/Multi_Buffer.cpp Plugins/Input/console/Multi_Buffer.cxx Plugins/Input/console/Music_Emu.cpp Plugins/Input/console/Music_Emu.cxx Plugins/Input/console/Nes_Apu.cpp Plugins/Input/console/Nes_Apu.cxx Plugins/Input/console/Nes_Cpu.cpp Plugins/Input/console/Nes_Cpu.cxx Plugins/Input/console/Nes_Fme7_Apu.cpp Plugins/Input/console/Nes_Fme7_Apu.cxx Plugins/Input/console/Nes_Namco_Apu.cpp Plugins/Input/console/Nes_Namco_Apu.cxx Plugins/Input/console/Nes_Oscs.cpp Plugins/Input/console/Nes_Oscs.cxx Plugins/Input/console/Nes_Vrc6_Apu.cpp Plugins/Input/console/Nes_Vrc6_Apu.cxx Plugins/Input/console/Nsf_Emu.cpp Plugins/Input/console/Nsf_Emu.cxx Plugins/Input/console/Nsfe_Emu.cpp Plugins/Input/console/Nsfe_Emu.cxx Plugins/Input/console/Sms_Apu.cpp Plugins/Input/console/Sms_Apu.cxx Plugins/Input/console/Snes_Spc.cpp Plugins/Input/console/Snes_Spc.cxx Plugins/Input/console/Spc_Cpu.cpp Plugins/Input/console/Spc_Cpu.cxx Plugins/Input/console/Spc_Dsp.cpp Plugins/Input/console/Spc_Dsp.cxx Plugins/Input/console/Spc_Emu.cpp Plugins/Input/console/Spc_Emu.cxx Plugins/Input/console/Track_Emu.cpp Plugins/Input/console/Track_Emu.cxx Plugins/Input/console/Vfs_File.cpp Plugins/Input/console/Vfs_File.cxx Plugins/Input/console/Vgm_Emu.cpp Plugins/Input/console/Vgm_Emu.cxx Plugins/Input/console/Vgm_Emu_Impl.cpp Plugins/Input/console/Vgm_Emu_Impl.cxx Plugins/Input/console/Ym2413_Emu.cpp Plugins/Input/console/Ym2413_Emu.cxx Plugins/Input/console/Ym2612_Emu.cpp Plugins/Input/console/Ym2612_Emu.cxx Plugins/Input/console/abstract_file.cpp Plugins/Input/console/abstract_file.cxx
diffstat 69 files changed, 13300 insertions(+), 13292 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Sep 14 13:27:38 2006 -0700
+++ b/ChangeLog	Thu Sep 14 13:33:10 2006 -0700
@@ -1,3 +1,11 @@
+2006-09-14 20:27:38 +0000  Giacomo Lozito <james@develia.org>
+  revision [2317]
+  - fix for issue that was fixed in r1946 and then appeared again later :)
+
+  Changes:        Modified:
+  +4 -4           trunk/audacious/playlist.c  
+
+
 2006-09-14 15:46:01 +0000  Jonathan Schleifer <js@h3c.de>
   revision [2315]
   Install audacious_player.xpm as well, so one can use it as a desktop icon.
--- a/Plugins/Input/console/Audacious_Config.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,268 +0,0 @@
-/*
- * 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/
- *
- * Libconsole preferences GUI by Giacomo Lozito
- */
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
-#include "libaudacious/configdb.h"
-#include "Audacious_Config.h"
-
-
-void console_cfg_load( void )
-{
-	ConfigDb *db = bmp_cfg_db_open();
-	bmp_cfg_db_get_int(db, "console", "loop_length", &audcfg.loop_length);
-	bmp_cfg_db_get_bool(db, "console", "resample", &audcfg.resample);
-	bmp_cfg_db_get_int(db, "console", "resample_rate", &audcfg.resample_rate);
-	bmp_cfg_db_get_bool(db, "console", "nsfe_playlist", &audcfg.nsfe_playlist);
-	bmp_cfg_db_get_int(db, "console", "treble", &audcfg.treble);
-	bmp_cfg_db_get_int(db, "console", "bass", &audcfg.bass);
-	bmp_cfg_db_get_bool(db, "console", "ignore_spc_length", &audcfg.ignore_spc_length);
-	bmp_cfg_db_close(db);
-}
-
-
-void console_cfg_save( void )
-{
-	ConfigDb *db = bmp_cfg_db_open();
-	bmp_cfg_db_set_int(db, "console", "loop_length", audcfg.loop_length);
-	bmp_cfg_db_set_bool(db, "console", "resample", audcfg.resample);
-	bmp_cfg_db_set_int(db, "console", "resample_rate", audcfg.resample_rate);
-	bmp_cfg_db_set_bool(db, "console", "nsfe_playlist", audcfg.nsfe_playlist);
-	bmp_cfg_db_set_int(db, "console", "treble", audcfg.treble);
-	bmp_cfg_db_set_int(db, "console", "bass", audcfg.bass);
-	bmp_cfg_db_set_bool(db, "console", "ignore_spc_length", audcfg.ignore_spc_length);
-	bmp_cfg_db_close(db);
-}
-
-
-// CONFIGURATION PANEL (GTK+2 GUI)
-
-static void i_cfg_ev_resample_enable_commit( gpointer cbt )
-{
-  audcfg.resample = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(cbt) );
-}
-
-static void i_cfg_ev_resample_value_commit( gpointer spbt )
-{
-  audcfg.resample_rate = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
-}
-
-static void i_cfg_ev_bass_value_commit( gpointer spbt )
-{
-  audcfg.bass = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
-}
-
-static void i_cfg_ev_treble_value_commit( gpointer spbt )
-{
-  audcfg.treble = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
-}
-
-static void i_cfg_ev_deflen_value_commit( gpointer spbt )
-{
-  audcfg.loop_length = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
-}
-
-static void i_cfg_ev_nsfeoptpl_enable_commit( gpointer cbt )
-{
-  audcfg.nsfe_playlist = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(cbt) );
-}
-
-static void i_cfg_ev_ignorespclen_enable_commit( gpointer cbt )
-{
-  audcfg.ignore_spc_length = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(cbt) );
-}
-
-static void i_cfg_ev_bok( gpointer configwin )
-{
-  console_cfg_save();
-  gtk_widget_destroy( GTK_WIDGET(configwin) );
-}
-
-static void i_cfg_ev_toggle_resample( GtkToggleButton *tbt , gpointer val_hbox )
-{
-  gtk_widget_set_sensitive( GTK_WIDGET(val_hbox) ,
-	gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(tbt) ) );
-}
-
-
-void console_cfg_ui( void )
-{
-	static GtkWidget *configwin = NULL;
-	GtkWidget *configwin_vbox;
-	GtkWidget *configwin_gen_vbox, *configwin_nsf_vbox, *configwin_spc_vbox;
-	GtkWidget *configwin_gen_resample_frame, *configwin_gen_resample_vbox;
-	GtkWidget *configwin_gen_resample_cbt, *configwin_gen_resample_val_hbox;
-	GtkWidget *configwin_gen_resample_val_spbt;
-	GtkWidget *configwin_gen_playback_frame, *configwin_gen_playback_vbox;
-	GtkWidget *configwin_gen_playback_tb_hbox;
-	GtkWidget *configwin_gen_playback_tb_bass_hbox, *configwin_gen_playback_tb_bass_spbt;
-	GtkWidget *configwin_gen_playback_tb_treble_hbox, *configwin_gen_playback_tb_treble_spbt;
-	GtkWidget *configwin_gen_playback_deflen_hbox, *configwin_gen_playback_deflen_spbt;
-	GtkWidget *configwin_nsf_nsfeoptpl_cbt;
-	GtkWidget *configwin_spc_ignorespclen_cbt;
-	GtkWidget *hseparator, *hbuttonbox, *button_ok, *button_cancel;
-	GtkWidget *configwin_notebook;
-	GtkTooltips *tips;
-
-	if ( configwin != NULL )
-		return;
-
-	configwin = gtk_window_new( GTK_WINDOW_TOPLEVEL );
-	gtk_window_set_type_hint( GTK_WINDOW(configwin), GDK_WINDOW_TYPE_HINT_DIALOG );
-	gtk_window_set_title( GTK_WINDOW(configwin), _("Console Music Decoder") );
-	gtk_container_set_border_width( GTK_CONTAINER(configwin), 10 );
-	g_signal_connect( G_OBJECT(configwin) , "destroy" ,
-		G_CALLBACK(gtk_widget_destroyed) , &configwin );
-	button_ok = gtk_button_new_from_stock( GTK_STOCK_OK );
-
-	configwin_vbox = gtk_vbox_new( FALSE , 6 );
-	gtk_container_add( GTK_CONTAINER(configwin) , configwin_vbox );
-
-	tips = gtk_tooltips_new();
-	g_object_set_data_full( G_OBJECT(configwin) , "tt" , tips , g_object_unref );
-
-	configwin_notebook = gtk_notebook_new();
-	gtk_notebook_set_tab_pos( GTK_NOTEBOOK(configwin_notebook) , GTK_POS_TOP );
-	gtk_box_pack_start( GTK_BOX(configwin_vbox) , configwin_notebook , TRUE , TRUE , 2 );
-
-	// GENERAL PAGE
-	configwin_gen_vbox = gtk_vbox_new( FALSE , 3 );
-	gtk_container_set_border_width( GTK_CONTAINER(configwin_gen_vbox), 5 );
-	gtk_notebook_append_page( GTK_NOTEBOOK(configwin_notebook) ,
-		configwin_gen_vbox , gtk_label_new( _("General") ) );
-	// GENERAL PAGE - PLAYBACK FRAME
-	configwin_gen_playback_frame = gtk_frame_new( _("Playback") );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_vbox) ,
-		configwin_gen_playback_frame , TRUE , TRUE , 0 );
-	configwin_gen_playback_vbox = gtk_vbox_new( FALSE , 4 );
-	gtk_container_set_border_width( GTK_CONTAINER(configwin_gen_playback_vbox), 4 );
-	gtk_container_add( GTK_CONTAINER(configwin_gen_playback_frame) , configwin_gen_playback_vbox );
-	configwin_gen_playback_tb_hbox = gtk_hbox_new( FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_vbox) ,
-		configwin_gen_playback_tb_hbox , FALSE , FALSE , 0 );
-	configwin_gen_playback_tb_bass_hbox = gtk_hbox_new( FALSE , 4 );
-	configwin_gen_playback_tb_bass_spbt = gtk_spin_button_new_with_range( -100 , 100 , 1 );
-	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_playback_tb_bass_spbt) , audcfg.bass );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_bass_value_commit) , configwin_gen_playback_tb_bass_spbt );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_bass_hbox) ,
-		gtk_label_new(_("Bass:")) , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_bass_hbox) ,
-		configwin_gen_playback_tb_bass_spbt , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_bass_hbox) ,
-		gtk_label_new(_("secs")) , FALSE , FALSE , 0 );
-	configwin_gen_playback_tb_treble_hbox = gtk_hbox_new( FALSE , 4 );
-	configwin_gen_playback_tb_treble_spbt = gtk_spin_button_new_with_range( -100 , 100 , 1 );
-	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_playback_tb_treble_spbt) , audcfg.treble );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_treble_value_commit) , configwin_gen_playback_tb_treble_spbt );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_treble_hbox) ,
-		gtk_label_new(_("Treble:")) , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_treble_hbox) ,
-		configwin_gen_playback_tb_treble_spbt , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_treble_hbox) ,
-		gtk_label_new(_("secs")) , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_hbox) ,
-		configwin_gen_playback_tb_bass_hbox , TRUE , TRUE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_hbox) ,
-		gtk_vseparator_new() , FALSE , FALSE , 4 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_hbox) ,
-		configwin_gen_playback_tb_treble_hbox , TRUE , TRUE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_vbox) ,
-		gtk_hseparator_new() , FALSE , FALSE , 0 );
-	configwin_gen_playback_deflen_hbox = gtk_hbox_new( FALSE , 4 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_vbox) ,
-		configwin_gen_playback_deflen_hbox , FALSE , FALSE , 0 );
-	configwin_gen_playback_deflen_spbt = gtk_spin_button_new_with_range( 1 , 7200 , 10 );
-	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_playback_deflen_spbt) , audcfg.loop_length );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_deflen_value_commit) , configwin_gen_playback_deflen_spbt );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_deflen_hbox) ,
-		gtk_label_new(_("Default song length:")) , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_deflen_hbox) ,
-		configwin_gen_playback_deflen_spbt , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_deflen_hbox) ,
-		gtk_label_new(_("secs")) , FALSE , FALSE , 0 );
-	// GENERAL PAGE - RESAMPLING FRAME
-	configwin_gen_resample_frame = gtk_frame_new( _("Resampling") );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_vbox) ,
-		configwin_gen_resample_frame , TRUE , TRUE , 0 );
-	configwin_gen_resample_vbox = gtk_vbox_new( FALSE , 4 );
-	gtk_container_set_border_width( GTK_CONTAINER(configwin_gen_resample_vbox), 4 );
-	gtk_container_add( GTK_CONTAINER(configwin_gen_resample_frame) , configwin_gen_resample_vbox );
-	configwin_gen_resample_cbt = gtk_check_button_new_with_label( _("Enable audio resampling") );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_resample_enable_commit) , configwin_gen_resample_cbt );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_vbox) ,
-		configwin_gen_resample_cbt , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_vbox) ,
-		gtk_hseparator_new() , FALSE , FALSE , 0 );
-	configwin_gen_resample_val_hbox = gtk_hbox_new( FALSE , 4 );
-	configwin_gen_resample_val_spbt = gtk_spin_button_new_with_range( 11025 , 96000 , 100 );
-	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_resample_val_spbt) , audcfg.resample_rate );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_resample_value_commit) , configwin_gen_resample_val_spbt );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_vbox) ,
-		configwin_gen_resample_val_hbox , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_val_hbox) ,
-		gtk_label_new(_("Resampling rate:")) , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_val_hbox) ,
-		configwin_gen_resample_val_spbt , FALSE , FALSE , 0 );
-	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_val_hbox) ,
-		gtk_label_new(_("Hz")) , FALSE , FALSE , 0 );
-	gtk_widget_set_sensitive( GTK_WIDGET(configwin_gen_resample_val_hbox) , audcfg.resample );
-	g_signal_connect( G_OBJECT(configwin_gen_resample_cbt) , "toggled" ,
-		G_CALLBACK(i_cfg_ev_toggle_resample) , configwin_gen_resample_val_hbox );
-	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(configwin_gen_resample_cbt) , audcfg.resample );
-
-	// NSF/NSFE PAGE
-	configwin_nsf_vbox = gtk_vbox_new( FALSE , 3 );
-	gtk_container_set_border_width( GTK_CONTAINER(configwin_nsf_vbox), 5 );
-	gtk_notebook_append_page( GTK_NOTEBOOK(configwin_notebook) ,
-		configwin_nsf_vbox , gtk_label_new( _("NSF/NSFE") ) );
-	configwin_nsf_nsfeoptpl_cbt = gtk_check_button_new_with_label( _("Use optional NSFE playlist") );
-	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(configwin_nsf_nsfeoptpl_cbt) , audcfg.nsfe_playlist );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_nsfeoptpl_enable_commit) , configwin_nsf_nsfeoptpl_cbt );
-	gtk_box_pack_start( GTK_BOX(configwin_nsf_vbox) ,
-		configwin_nsf_nsfeoptpl_cbt , FALSE , FALSE , 0 );
-
-	// SPC PAGE
-	configwin_spc_vbox = gtk_vbox_new( FALSE , 3 );
-	gtk_container_set_border_width( GTK_CONTAINER(configwin_spc_vbox), 5 );
-	gtk_notebook_append_page( GTK_NOTEBOOK(configwin_notebook) ,
-		configwin_spc_vbox , gtk_label_new( _("SPC") ) );
-	configwin_spc_ignorespclen_cbt = gtk_check_button_new_with_label( _("Ignore length from SPC tags") );
-	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(configwin_spc_ignorespclen_cbt) , audcfg.ignore_spc_length );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_ignorespclen_enable_commit) , configwin_spc_ignorespclen_cbt );
-	gtk_box_pack_start( GTK_BOX(configwin_spc_vbox) ,
-		configwin_spc_ignorespclen_cbt , FALSE , FALSE , 0 );
-
-	// horizontal separator and buttons
-	hbuttonbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout( GTK_BUTTON_BOX(hbuttonbox) , GTK_BUTTONBOX_END );
-	button_cancel = gtk_button_new_from_stock( GTK_STOCK_CANCEL );
-	g_signal_connect_swapped( G_OBJECT(button_cancel) , "clicked" ,
-		G_CALLBACK(gtk_widget_destroy) , configwin );
-	gtk_container_add( GTK_CONTAINER(hbuttonbox) , button_cancel );
-	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
-		G_CALLBACK(i_cfg_ev_bok) , configwin );
-	gtk_container_add( GTK_CONTAINER(hbuttonbox) , button_ok );
-	gtk_box_pack_start( GTK_BOX(configwin_vbox) , hbuttonbox , FALSE , FALSE , 0 );
-
-	gtk_tooltips_set_tip( GTK_TOOLTIPS(tips) , configwin_gen_playback_deflen_spbt ,
-		_("* Default song length *\n"
-		"The default song length, expressed in seconds, is used for songs "
-		"that do not provide length information (i.e. looping tracks).") , "" );
-
-	gtk_widget_show_all( configwin );
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Audacious_Config.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,268 @@
+/*
+ * 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/
+ *
+ * Libconsole preferences GUI by Giacomo Lozito
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "libaudacious/configdb.h"
+#include "Audacious_Config.h"
+
+
+void console_cfg_load( void )
+{
+	ConfigDb *db = bmp_cfg_db_open();
+	bmp_cfg_db_get_int(db, "console", "loop_length", &audcfg.loop_length);
+	bmp_cfg_db_get_bool(db, "console", "resample", &audcfg.resample);
+	bmp_cfg_db_get_int(db, "console", "resample_rate", &audcfg.resample_rate);
+	bmp_cfg_db_get_bool(db, "console", "nsfe_playlist", &audcfg.nsfe_playlist);
+	bmp_cfg_db_get_int(db, "console", "treble", &audcfg.treble);
+	bmp_cfg_db_get_int(db, "console", "bass", &audcfg.bass);
+	bmp_cfg_db_get_bool(db, "console", "ignore_spc_length", &audcfg.ignore_spc_length);
+	bmp_cfg_db_close(db);
+}
+
+
+void console_cfg_save( void )
+{
+	ConfigDb *db = bmp_cfg_db_open();
+	bmp_cfg_db_set_int(db, "console", "loop_length", audcfg.loop_length);
+	bmp_cfg_db_set_bool(db, "console", "resample", audcfg.resample);
+	bmp_cfg_db_set_int(db, "console", "resample_rate", audcfg.resample_rate);
+	bmp_cfg_db_set_bool(db, "console", "nsfe_playlist", audcfg.nsfe_playlist);
+	bmp_cfg_db_set_int(db, "console", "treble", audcfg.treble);
+	bmp_cfg_db_set_int(db, "console", "bass", audcfg.bass);
+	bmp_cfg_db_set_bool(db, "console", "ignore_spc_length", audcfg.ignore_spc_length);
+	bmp_cfg_db_close(db);
+}
+
+
+// CONFIGURATION PANEL (GTK+2 GUI)
+
+static void i_cfg_ev_resample_enable_commit( gpointer cbt )
+{
+  audcfg.resample = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(cbt) );
+}
+
+static void i_cfg_ev_resample_value_commit( gpointer spbt )
+{
+  audcfg.resample_rate = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
+}
+
+static void i_cfg_ev_bass_value_commit( gpointer spbt )
+{
+  audcfg.bass = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
+}
+
+static void i_cfg_ev_treble_value_commit( gpointer spbt )
+{
+  audcfg.treble = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
+}
+
+static void i_cfg_ev_deflen_value_commit( gpointer spbt )
+{
+  audcfg.loop_length = (gint)gtk_spin_button_get_value( GTK_SPIN_BUTTON(spbt) );
+}
+
+static void i_cfg_ev_nsfeoptpl_enable_commit( gpointer cbt )
+{
+  audcfg.nsfe_playlist = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(cbt) );
+}
+
+static void i_cfg_ev_ignorespclen_enable_commit( gpointer cbt )
+{
+  audcfg.ignore_spc_length = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(cbt) );
+}
+
+static void i_cfg_ev_bok( gpointer configwin )
+{
+  console_cfg_save();
+  gtk_widget_destroy( GTK_WIDGET(configwin) );
+}
+
+static void i_cfg_ev_toggle_resample( GtkToggleButton *tbt , gpointer val_hbox )
+{
+  gtk_widget_set_sensitive( GTK_WIDGET(val_hbox) ,
+	gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(tbt) ) );
+}
+
+
+void console_cfg_ui( void )
+{
+	static GtkWidget *configwin = NULL;
+	GtkWidget *configwin_vbox;
+	GtkWidget *configwin_gen_vbox, *configwin_nsf_vbox, *configwin_spc_vbox;
+	GtkWidget *configwin_gen_resample_frame, *configwin_gen_resample_vbox;
+	GtkWidget *configwin_gen_resample_cbt, *configwin_gen_resample_val_hbox;
+	GtkWidget *configwin_gen_resample_val_spbt;
+	GtkWidget *configwin_gen_playback_frame, *configwin_gen_playback_vbox;
+	GtkWidget *configwin_gen_playback_tb_hbox;
+	GtkWidget *configwin_gen_playback_tb_bass_hbox, *configwin_gen_playback_tb_bass_spbt;
+	GtkWidget *configwin_gen_playback_tb_treble_hbox, *configwin_gen_playback_tb_treble_spbt;
+	GtkWidget *configwin_gen_playback_deflen_hbox, *configwin_gen_playback_deflen_spbt;
+	GtkWidget *configwin_nsf_nsfeoptpl_cbt;
+	GtkWidget *configwin_spc_ignorespclen_cbt;
+	GtkWidget *hseparator, *hbuttonbox, *button_ok, *button_cancel;
+	GtkWidget *configwin_notebook;
+	GtkTooltips *tips;
+
+	if ( configwin != NULL )
+		return;
+
+	configwin = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+	gtk_window_set_type_hint( GTK_WINDOW(configwin), GDK_WINDOW_TYPE_HINT_DIALOG );
+	gtk_window_set_title( GTK_WINDOW(configwin), _("Console Music Decoder") );
+	gtk_container_set_border_width( GTK_CONTAINER(configwin), 10 );
+	g_signal_connect( G_OBJECT(configwin) , "destroy" ,
+		G_CALLBACK(gtk_widget_destroyed) , &configwin );
+	button_ok = gtk_button_new_from_stock( GTK_STOCK_OK );
+
+	configwin_vbox = gtk_vbox_new( FALSE , 6 );
+	gtk_container_add( GTK_CONTAINER(configwin) , configwin_vbox );
+
+	tips = gtk_tooltips_new();
+	g_object_set_data_full( G_OBJECT(configwin) , "tt" , tips , g_object_unref );
+
+	configwin_notebook = gtk_notebook_new();
+	gtk_notebook_set_tab_pos( GTK_NOTEBOOK(configwin_notebook) , GTK_POS_TOP );
+	gtk_box_pack_start( GTK_BOX(configwin_vbox) , configwin_notebook , TRUE , TRUE , 2 );
+
+	// GENERAL PAGE
+	configwin_gen_vbox = gtk_vbox_new( FALSE , 3 );
+	gtk_container_set_border_width( GTK_CONTAINER(configwin_gen_vbox), 5 );
+	gtk_notebook_append_page( GTK_NOTEBOOK(configwin_notebook) ,
+		configwin_gen_vbox , gtk_label_new( _("General") ) );
+	// GENERAL PAGE - PLAYBACK FRAME
+	configwin_gen_playback_frame = gtk_frame_new( _("Playback") );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_vbox) ,
+		configwin_gen_playback_frame , TRUE , TRUE , 0 );
+	configwin_gen_playback_vbox = gtk_vbox_new( FALSE , 4 );
+	gtk_container_set_border_width( GTK_CONTAINER(configwin_gen_playback_vbox), 4 );
+	gtk_container_add( GTK_CONTAINER(configwin_gen_playback_frame) , configwin_gen_playback_vbox );
+	configwin_gen_playback_tb_hbox = gtk_hbox_new( FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_vbox) ,
+		configwin_gen_playback_tb_hbox , FALSE , FALSE , 0 );
+	configwin_gen_playback_tb_bass_hbox = gtk_hbox_new( FALSE , 4 );
+	configwin_gen_playback_tb_bass_spbt = gtk_spin_button_new_with_range( -100 , 100 , 1 );
+	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_playback_tb_bass_spbt) , audcfg.bass );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_bass_value_commit) , configwin_gen_playback_tb_bass_spbt );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_bass_hbox) ,
+		gtk_label_new(_("Bass:")) , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_bass_hbox) ,
+		configwin_gen_playback_tb_bass_spbt , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_bass_hbox) ,
+		gtk_label_new(_("secs")) , FALSE , FALSE , 0 );
+	configwin_gen_playback_tb_treble_hbox = gtk_hbox_new( FALSE , 4 );
+	configwin_gen_playback_tb_treble_spbt = gtk_spin_button_new_with_range( -100 , 100 , 1 );
+	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_playback_tb_treble_spbt) , audcfg.treble );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_treble_value_commit) , configwin_gen_playback_tb_treble_spbt );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_treble_hbox) ,
+		gtk_label_new(_("Treble:")) , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_treble_hbox) ,
+		configwin_gen_playback_tb_treble_spbt , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_treble_hbox) ,
+		gtk_label_new(_("secs")) , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_hbox) ,
+		configwin_gen_playback_tb_bass_hbox , TRUE , TRUE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_hbox) ,
+		gtk_vseparator_new() , FALSE , FALSE , 4 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_tb_hbox) ,
+		configwin_gen_playback_tb_treble_hbox , TRUE , TRUE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_vbox) ,
+		gtk_hseparator_new() , FALSE , FALSE , 0 );
+	configwin_gen_playback_deflen_hbox = gtk_hbox_new( FALSE , 4 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_vbox) ,
+		configwin_gen_playback_deflen_hbox , FALSE , FALSE , 0 );
+	configwin_gen_playback_deflen_spbt = gtk_spin_button_new_with_range( 1 , 7200 , 10 );
+	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_playback_deflen_spbt) , audcfg.loop_length );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_deflen_value_commit) , configwin_gen_playback_deflen_spbt );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_deflen_hbox) ,
+		gtk_label_new(_("Default song length:")) , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_deflen_hbox) ,
+		configwin_gen_playback_deflen_spbt , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_playback_deflen_hbox) ,
+		gtk_label_new(_("secs")) , FALSE , FALSE , 0 );
+	// GENERAL PAGE - RESAMPLING FRAME
+	configwin_gen_resample_frame = gtk_frame_new( _("Resampling") );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_vbox) ,
+		configwin_gen_resample_frame , TRUE , TRUE , 0 );
+	configwin_gen_resample_vbox = gtk_vbox_new( FALSE , 4 );
+	gtk_container_set_border_width( GTK_CONTAINER(configwin_gen_resample_vbox), 4 );
+	gtk_container_add( GTK_CONTAINER(configwin_gen_resample_frame) , configwin_gen_resample_vbox );
+	configwin_gen_resample_cbt = gtk_check_button_new_with_label( _("Enable audio resampling") );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_resample_enable_commit) , configwin_gen_resample_cbt );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_vbox) ,
+		configwin_gen_resample_cbt , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_vbox) ,
+		gtk_hseparator_new() , FALSE , FALSE , 0 );
+	configwin_gen_resample_val_hbox = gtk_hbox_new( FALSE , 4 );
+	configwin_gen_resample_val_spbt = gtk_spin_button_new_with_range( 11025 , 96000 , 100 );
+	gtk_spin_button_set_value( GTK_SPIN_BUTTON(configwin_gen_resample_val_spbt) , audcfg.resample_rate );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_resample_value_commit) , configwin_gen_resample_val_spbt );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_vbox) ,
+		configwin_gen_resample_val_hbox , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_val_hbox) ,
+		gtk_label_new(_("Resampling rate:")) , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_val_hbox) ,
+		configwin_gen_resample_val_spbt , FALSE , FALSE , 0 );
+	gtk_box_pack_start( GTK_BOX(configwin_gen_resample_val_hbox) ,
+		gtk_label_new(_("Hz")) , FALSE , FALSE , 0 );
+	gtk_widget_set_sensitive( GTK_WIDGET(configwin_gen_resample_val_hbox) , audcfg.resample );
+	g_signal_connect( G_OBJECT(configwin_gen_resample_cbt) , "toggled" ,
+		G_CALLBACK(i_cfg_ev_toggle_resample) , configwin_gen_resample_val_hbox );
+	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(configwin_gen_resample_cbt) , audcfg.resample );
+
+	// NSF/NSFE PAGE
+	configwin_nsf_vbox = gtk_vbox_new( FALSE , 3 );
+	gtk_container_set_border_width( GTK_CONTAINER(configwin_nsf_vbox), 5 );
+	gtk_notebook_append_page( GTK_NOTEBOOK(configwin_notebook) ,
+		configwin_nsf_vbox , gtk_label_new( _("NSF/NSFE") ) );
+	configwin_nsf_nsfeoptpl_cbt = gtk_check_button_new_with_label( _("Use optional NSFE playlist") );
+	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(configwin_nsf_nsfeoptpl_cbt) , audcfg.nsfe_playlist );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_nsfeoptpl_enable_commit) , configwin_nsf_nsfeoptpl_cbt );
+	gtk_box_pack_start( GTK_BOX(configwin_nsf_vbox) ,
+		configwin_nsf_nsfeoptpl_cbt , FALSE , FALSE , 0 );
+
+	// SPC PAGE
+	configwin_spc_vbox = gtk_vbox_new( FALSE , 3 );
+	gtk_container_set_border_width( GTK_CONTAINER(configwin_spc_vbox), 5 );
+	gtk_notebook_append_page( GTK_NOTEBOOK(configwin_notebook) ,
+		configwin_spc_vbox , gtk_label_new( _("SPC") ) );
+	configwin_spc_ignorespclen_cbt = gtk_check_button_new_with_label( _("Ignore length from SPC tags") );
+	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(configwin_spc_ignorespclen_cbt) , audcfg.ignore_spc_length );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_ignorespclen_enable_commit) , configwin_spc_ignorespclen_cbt );
+	gtk_box_pack_start( GTK_BOX(configwin_spc_vbox) ,
+		configwin_spc_ignorespclen_cbt , FALSE , FALSE , 0 );
+
+	// horizontal separator and buttons
+	hbuttonbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout( GTK_BUTTON_BOX(hbuttonbox) , GTK_BUTTONBOX_END );
+	button_cancel = gtk_button_new_from_stock( GTK_STOCK_CANCEL );
+	g_signal_connect_swapped( G_OBJECT(button_cancel) , "clicked" ,
+		G_CALLBACK(gtk_widget_destroy) , configwin );
+	gtk_container_add( GTK_CONTAINER(hbuttonbox) , button_cancel );
+	g_signal_connect_swapped( G_OBJECT(button_ok) , "clicked" ,
+		G_CALLBACK(i_cfg_ev_bok) , configwin );
+	gtk_container_add( GTK_CONTAINER(hbuttonbox) , button_ok );
+	gtk_box_pack_start( GTK_BOX(configwin_vbox) , hbuttonbox , FALSE , FALSE , 0 );
+
+	gtk_tooltips_set_tip( GTK_TOOLTIPS(tips) , configwin_gen_playback_deflen_spbt ,
+		_("* Default song length *\n"
+		"The default song length, expressed in seconds, is used for songs "
+		"that do not provide length information (i.e. looping tracks).") , "" );
+
+	gtk_widget_show_all( configwin );
+}
--- a/Plugins/Input/console/Audacious_Driver.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,859 +0,0 @@
-/*
- * 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/
- */
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
-#include "libaudacious/util.h"
-#include "libaudacious/titlestring.h"
-extern "C" {
-#include "audacious/output.h"
-#include "audacious/playlist.h"
-}
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <math.h>
-
-// configdb and prefs ui
-#include "Audacious_Config.h"
-
-// Game_Music_Emu
-#include "Nsf_Emu.h"
-#include "Nsfe_Emu.h"
-#include "Gbs_Emu.h"
-#include "Vgm_Emu.h"
-#include "Gym_Emu.h"
-#include "Spc_Emu.h"
-
-#include "Track_Emu.h"
-#include "Vfs_File.h"
-#include "Gzip_File.h"
-#include "blargg_endian.h"
-
-//typedef Vfs_File_Reader Audacious_Reader; // will use VFS once it handles gzip transparently
-typedef Gzip_File_Reader Audacious_Reader;
-
-AudaciousConsoleConfig audcfg = { 180, FALSE, 32000, TRUE, 0, 0, FALSE };
-static GThread* decode_thread;
-static GStaticMutex playback_mutex = G_STATIC_MUTEX_INIT;
-static int console_ip_is_going;
-static volatile long pending_seek;
-extern InputPlugin console_ip;
-static Music_Emu* emu = 0;
-static Track_Emu track_emu;
-static int track_ended;
-
-static void unload_file()
-{
-	delete emu;
-	emu = NULL;
-}
-
-// Information
-
-typedef unsigned char byte;
-
-#define DUPE_FIELD( field ) g_strndup( field, sizeof (field) );
-
-struct track_info_t
-{
-	int track;  // track to get info for
-	int length; // in msec, -1 = unknown
-	int loop;   // in msec, -1 = unknown, 0 = not looped
-	int intro;  // in msec, -1 = unknown
-	
-	TitleInput* ti;
-};
-
-// NSFE
-
-void get_nsfe_info( Nsfe_Info const& nsfe, track_info_t* out )
-{
-	Nsfe_Info::info_t const& h = nsfe.info();
-	out->ti->performer  = DUPE_FIELD( h.author );
-	out->ti->album_name = DUPE_FIELD( h.game );
-	out->ti->comment    = DUPE_FIELD( h.copyright );
-	out->ti->track_name = g_strdup( nsfe.track_name( out->track ) );
-	int time = nsfe.track_time( out->track );
-	if ( time > 0 )
-		out->length = time;
-	if ( nsfe.info().track_count > 1 )
-		out->ti->track_number = out->track + 1;
-}
-
-inline void get_info_emu( Nsfe_Emu& emu, track_info_t* out )
-{
-	emu.enable_playlist( audcfg.nsfe_playlist ); // to do: kind of hacky
-	get_nsfe_info( emu, out );
-}
-
-inline void get_file_info( Nsfe_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
-{
-	Nsfe_Info nsfe;
-	if ( !nsfe.load( h, in ) )
-	{
-		nsfe.enable_playlist( audcfg.nsfe_playlist );
-		get_nsfe_info( nsfe, out );
-	}
-}
-
-// NSF
-
-static void get_nsf_info_( Nsf_Emu::header_t const& h, track_info_t* out )
-{
-	out->ti->performer  = DUPE_FIELD( h.author );
-	out->ti->album_name = DUPE_FIELD( h.game );
-	out->ti->comment    = DUPE_FIELD( h.copyright );
-	if ( h.track_count > 1 )
-		out->ti->track_number = out->track + 1;
-}
-
-inline void get_info_emu( Nsf_Emu& emu, track_info_t* out )
-{
-	get_nsf_info_( emu.header(), out );
-}
-
-inline void get_file_info( Nsf_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
-{
-	get_nsf_info_( h, out );
-}
-
-// GBS
-
-static void get_gbs_info_( Gbs_Emu::header_t const& h, track_info_t* out )
-{
-	out->ti->performer  = DUPE_FIELD( h.author );
-	out->ti->album_name = DUPE_FIELD( h.game );
-	out->ti->comment    = DUPE_FIELD( h.copyright );
-	if ( h.track_count > 1 )
-		out->ti->track_number = out->track + 1;
-}
-
-inline void get_info_emu( Gbs_Emu& emu, track_info_t* out )
-{
-	get_gbs_info_( emu.header(), out );
-}
-
-inline void get_file_info( Gbs_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
-{
-	get_gbs_info_( h, out );
-}
-
-// GYM
-
-static void get_gym_info_( Gym_Emu::header_t const& h, track_info_t* out )
-{
-	if ( !memcmp( h.tag, "GYMX", 4 ) )
-	{
-		out->ti->performer  = DUPE_FIELD( h.copyright );
-		out->ti->album_name = DUPE_FIELD( h.game );
-		out->ti->track_name = DUPE_FIELD( h.song );
-		out->ti->comment    = DUPE_FIELD( h.comment );
-	}
-}
-
-static void get_gym_timing_( Gym_Emu const& emu, track_info_t* out )
-{
-	out->length = emu.track_length() * 50 / 3; // 1000 / 60
-	out->loop = 0;
-	
-	long loop = get_le32( emu.header().loop_start );
-	if ( loop )
-	{
-		out->intro = loop * 50 / 3;
-		out->loop = out->length - out->intro;
-		out->length = -1;
-	}
-}
-
-inline void get_info_emu( Gym_Emu& emu, track_info_t* out )
-{
-	get_gym_info_( emu.header(), out );
-	get_gym_timing_( emu, out );
-}
-
-inline void get_file_info( Gym_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
-{
-	get_gym_info_( h, out );
-	
-	// have to load and parse entire GYM file to determine length
-	// to do: could make more efficient by manually parsing data (format is simple)
-	// rather than loading into emulator with its FM chips and resampler
-	Gym_Emu* emu = new Gym_Emu;
-	if ( emu && !emu->set_sample_rate( 44100 ) && !emu->load( h, in ) )
-		get_gym_timing_( *emu, out );
-	delete emu;
-}
-
-// SPC
-
-static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
-{
-	// header
-	byte const* end = begin + size;
-	if ( size < 8 || memcmp( begin, "xid6", 4 ) )
-		return;
-	long info_size = get_le32( begin + 4 );
-	byte const* in = begin + 8; 
-	if ( end - in > info_size )
-		end = in + info_size;
-	
-	while ( end - in >= 4 )
-	{
-		// header
-		int id   = in [0];
-		int data = in [3] * 0x100 + in [2];
-		int type = in [1];
-		int len  = type ? data : 0;
-		in += 4;
-		if ( len > end - in )
-			break; // block goes past end of data
-		
-		// handle specific block types
-		switch ( id )
-		{
-			case 0x01: out->ti->track_name = g_strndup( (char*) in, len ); break;
-			case 0x02: out->ti->album_name = g_strndup( (char*) in, len ); break;
-			case 0x03: out->ti->performer  = g_strndup( (char*) in, len ); break;
-			case 0x07: out->ti->comment    = g_strndup( (char*) in, len ); break;
-			//case 0x31: // loop length, but I haven't found any SPC files that use this
-		}
-		
-		// skip to next block
-		in += len;
-		
-		// blocks are supposed to be 4-byte aligned with zero-padding...
-		byte const* unaligned = in;
-		while ( (in - begin) & 3 && in < end )
-		{
-			if ( *in++ != 0 )
-			{
-				// ...but some files have no padding
-				in = unaligned;
-				break;
-			}
-		}
-	}
-}
-
-static void get_spc_info_( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size,
-		track_info_t* out )
-{
-	// decode length (can be in text or binary format)
-	char s [4] = { h.len_secs [0], h.len_secs [1], h.len_secs [2], 0 };
-	int len_secs = (unsigned char) s [1] * 0x100 + s [0];
-	if ( s [1] >= ' ' || (!s [1] && isdigit( s [0] )) )
-		len_secs = atoi( s );
-	if ( len_secs )
-		out->length = len_secs * 1000;
-	
-	if ( xid6_size )
-		get_spc_xid6( xid6, xid6_size, out );
-	
-	// use header to fill any remaining fields
-	if ( !out->ti->performer  ) out->ti->performer  = DUPE_FIELD( h.author );
-	if ( !out->ti->album_name ) out->ti->album_name = DUPE_FIELD( h.game );
-	if ( !out->ti->track_name ) out->ti->track_name = DUPE_FIELD( h.song );
-}
-
-inline void get_info_emu( Spc_Emu& emu, track_info_t* out )
-{
-	get_spc_info_( emu.header(), emu.trailer(), emu.trailer_size(), out );
-}
-
-inline void get_file_info( Spc_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
-{
-	// handle xid6 data at end of file
-	long const xid6_skip = 0x10200 - sizeof (Spc_Emu::header_t);
-	long xid6_size = in.remain() - xid6_skip;
-	blargg_vector<byte> xid6;
-	if ( xid6_size <= 0 || xid6.resize( xid6_size ) || in.skip( xid6_skip ) ||
-			in.read( xid6.begin(), xid6.size() ) )
-		xid6_size = 0;
-	
-	get_spc_info_( h, xid6.begin(), xid6_size, out );
-}
-
-// VGM
-
-static void get_gd3_str( byte const* in, byte const* end, gchar** out )
-{
-	int len = (end - in) / 2 - 1;
-	if ( len > 0 )
-	{
-		*out = g_strndup( "", len );
-		if ( !*out )
-			return;
-		for ( int i = 0; i < len; i++ )
-			(*out) [i] = in [i * 2]; // to do: convert to utf-8
-	}
-}
-
-static byte const* skip_gd3_str( byte const* in, byte const* end )
-{
-	while ( end - in >= 2 )
-	{
-		in += 2;
-		if ( !(in [-2] | in [-1]) )
-			break;
-	}
-	return in;
-}
-
-static byte const* get_gd3_pair( byte const* in, byte const* end, gchar** out )
-{
-	byte const* mid = skip_gd3_str( in, end );
-	if ( out )
-		get_gd3_str( in, mid, out );
-	return skip_gd3_str( mid, end );
-}
-
-static void get_vgm_gd3( byte const* in, long size, track_info_t* out )
-{
-	byte const* end = in + size;
-	in = get_gd3_pair( in, end, &out->ti->track_name );
-	in = get_gd3_pair( in, end, &out->ti->album_name );
-	in = get_gd3_pair( in, end, 0 ); // system
-	in = get_gd3_pair( in, end, &out->ti->performer );
-	in = get_gd3_pair( in, end, 0 ); // copyright
-	// ... other fields (release date, dumper, notes)
-}
-
-static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
-{
-	long length = get_le32( h.track_duration );
-	if ( length > 0 )
-	{
-		out->length = length * 10 / 441; // 1000 / 44100 (VGM files used 44100 as timebase)
-		out->loop = 0;
-		
-		long loop = get_le32( h.loop_duration );
-		if ( loop > 0 && get_le32( h.loop_offset ) )
-		{
-			out->loop = loop * 10 / 441;
-			out->intro = out->length - out->loop;
-			out->length = -1;
-		}
-	}
-}
-
-inline void get_info_emu( Vgm_Emu& emu, track_info_t* out )
-{
-	get_vgm_length( emu.header(), out );
-	
-	int size;
-	byte const* data = emu.gd3_data( &size );
-	if ( data )
-		get_vgm_gd3( data + 12, size, out );
-}
-
-inline void get_file_info( Vgm_Emu::header_t const& vgm_h, Data_Reader& in, track_info_t* out )
-{
-	get_vgm_length( vgm_h, out );
-	
-	// find gd3 header
-	long gd3_offset = get_le32( vgm_h.gd3_offset ) + offsetof(Vgm_Emu::header_t,gd3_offset) -
-			sizeof vgm_h;
-	long gd3_max_size = in.remain() - gd3_offset;
-	byte gd3_h [12];
-	if ( gd3_offset <= 0 || gd3_max_size < (int) sizeof gd3_h )
-		return;
-	
-	// read gd3 header
-	if ( in.skip( gd3_offset ) || in.read( gd3_h, sizeof gd3_h ) )
-		return;
-	
-	// check header signature and version
-	if ( memcmp( gd3_h, "Gd3 ", 4 ) || get_le32( gd3_h + 4 ) >= 0x200 )
-		return;
-	
-	// get and check size
-	long gd3_size = get_le32( gd3_h + 8 );
-	if ( gd3_size > gd3_max_size - 12 )
-		return;
-	
-	// read and parse gd3 data
-	blargg_vector<byte> gd3;
-	if ( gd3.resize( gd3_size ) || in.read( gd3.begin(), gd3.size() ) )
-		return;
-	
-	get_vgm_gd3( gd3.begin(), gd3.size(), out );
-}
-
-// File identification
-
-enum { type_none = 0, type_spc, type_nsf, type_nsfe, type_vgm, type_gbs, type_gym };
-
-int const tag_size = 4;
-typedef char tag_t [tag_size];
-
-static int identify_file( gchar* path, tag_t tag )
-{
-	// GYM file format doesn't require *any* header, just the ".gym" extension
-	if ( g_str_has_suffix( path, ".gym" ) ) // to do: is pathname in unicode?
-		return type_gym;
-	// to do: trust suffix for all file types, avoiding having to look inside files?
-
-	int result = type_none;
-	if ( !memcmp( tag, "SNES", 4 ) ) result = type_spc;
-	if ( !memcmp( tag, "NESM", 4 ) ) result = type_nsf;
-	if ( !memcmp( tag, "NSFE", 4 ) ) result = type_nsfe;
-	if ( !memcmp( tag, "GYMX", 4 ) ) result = type_gym;
-	if ( !memcmp( tag, "GBS" , 3 ) ) result = type_gbs;
-	if ( !memcmp( tag, "Vgm ", 4 ) ) result = type_vgm;
-	return result;
-}
-
-// Get info
-
-static int begin_get_info( const char* path, track_info_t* out )
-{
-	out->track  = 0;
-	out->length = -1;
-	out->loop   = -1;
-	out->intro  = -1;
-	TitleInput* fields = bmp_title_input_new();
-	out->ti = fields;
-	if ( !fields )
-		return true;
-	
-	fields->file_name = g_path_get_basename( path );
-	fields->file_path = g_path_get_dirname( path );
-	return false;
-}
-
-static char* end_get_info( track_info_t const& info, int* length, bool* has_length )
-{
-	*length = info.length;
-	if ( has_length )
-		*has_length = (*length > 0);
-	
-	if ( *length <= 0 )
-		*length = audcfg.loop_length * 1000;
-	
-	// use filename for formats that don't have field for name of game
-	// to do: strip off file extension
-	if ( !info.ti->track_name )
-		info.ti->track_name = g_strdup( info.ti->file_name );
-	
-	char* result = xmms_get_titlestring( xmms_get_gentitle_format(), info.ti );
-	g_free( info.ti );
-	return result;
-}
-
-template<class Header>
-inline void get_info_t( tag_t tag, Data_Reader& in, track_info_t* out, Header* )
-{
-	Header h;
-	memcpy( &h, tag, tag_size );
-	if ( !in.read( (char*) &h + tag_size, sizeof h - tag_size ) )
-		get_file_info( h, in, out );
-}
-
-static void get_song_info( char* path, char** title, int* length )
-{
-	int track = 0; // to do: way to select other tracks
-	
-	// extract the subsong id from the virtual path
-	gchar *path2 = g_strdup(path);
-	gchar *_path = strchr(path2, '?');
-
-	if (_path != NULL && *_path == '?')
-	{
-		*_path = '\0';
-		_path++;
-		track = atoi(_path);
-	}
-
-	*length = -1;
-	*title = NULL;
-	Audacious_Reader in;
-	tag_t tag;
-	if ( in.open( path2 ) || in.read( tag, sizeof tag ) )
-		return;
-	
-	int type = identify_file( path2, tag );
-	if ( !type )
-		return;
-	
-	track_info_t info;
-	if ( begin_get_info( path2, &info ) )
-		return;
-	info.track = track;
-	
-	switch ( type )
-	{
-		case type_nsf: get_info_t( tag, in, &info, (Nsf_Emu::header_t*) 0 ); break;
-		case type_gbs: get_info_t( tag, in, &info, (Gbs_Emu::header_t*) 0 ); break;
-		case type_gym: get_info_t( tag, in, &info, (Gym_Emu::header_t*) 0 ); break;
-		case type_vgm: get_info_t( tag, in, &info, (Vgm_Emu::header_t*) 0 ); break;
-		case type_spc: get_info_t( tag, in, &info, (Spc_Emu::header_t*) 0 ); break;
-		case type_nsfe:get_info_t( tag, in, &info, (Nsfe_Emu::header_t*)0 ); break;
-	}
-	*title = end_get_info( info, length, 0 );
-
-	g_free(path2);
-}
-
-// Get tuple
-
-static TitleInput *get_song_tuple( char *path )
-{
-	int track = 0; // to do: way to select other tracks
-	
-	// extract the subsong id from the virtual path
-	gchar *path2 = g_strdup(path);
-	gchar *_path = strchr(path2, '?');
-
-	if (_path != NULL && *_path == '?')
-	{
-		*_path = '\0';
-		_path++;
-		track = atoi(_path);
-	}	
-
-	Audacious_Reader in;
-	tag_t tag;
-	if ( in.open( path2 ) || in.read( tag, sizeof tag ) )
-		return NULL;
-	
-	int type = identify_file( path2, tag );
-	if ( !type )
-		return NULL;
-	
-	track_info_t info;
-	if ( begin_get_info( path2, &info ) )
-		return NULL;
-	info.track = track;
-	
-	switch ( type )
-	{
-		case type_nsf: get_info_t( tag, in, &info, (Nsf_Emu::header_t*) 0 ); break;
-		case type_gbs: get_info_t( tag, in, &info, (Gbs_Emu::header_t*) 0 ); break;
-		case type_gym: get_info_t( tag, in, &info, (Gym_Emu::header_t*) 0 ); break;
-		case type_vgm: get_info_t( tag, in, &info, (Vgm_Emu::header_t*) 0 ); break;
-		case type_spc: get_info_t( tag, in, &info, (Spc_Emu::header_t*) 0 ); break;
-		case type_nsfe:get_info_t( tag, in, &info, (Nsfe_Emu::header_t*)0 ); break;
-	}
-
-	info.ti->length = info.length;
-	
-	if ( info.ti->length <= 0 )
-		info.ti->length = audcfg.loop_length * 1000;
-
-	return info.ti;
-}
-
-// Playback
-
-static void* play_loop_track( gpointer )
-{
-	g_static_mutex_lock( &playback_mutex );
-	
-	while ( console_ip_is_going )
-	{
-		int const buf_size = 1024;
-		Music_Emu::sample_t buf [buf_size];
-		
-		// handle pending seek
-		long s = pending_seek;
-		pending_seek = -1; // to do: use atomic swap
-		if ( s >= 0 )
-		{
-			console_ip.output->flush( s * 1000 );
-			track_emu.seek( s * 1000 );
-		}
-
-		// fill buffer
-		if ( track_ended )
-		{
-			if ( track_ended++ > emu->sample_rate() * 3 / (buf_size / 2) )
-				console_ip_is_going = false;
-			memset( buf, 0, sizeof buf );
-		}
-		else if ( track_emu.play( buf_size, buf ) )
-		{
-			track_ended = 1;
-		}
-		produce_audio( console_ip.output->written_time(), 
-			FMT_S16_NE, 1, sizeof buf, buf, 
-			&console_ip_is_going );
-	}
-	
-	// stop playing
-	unload_file();
-	console_ip.output->close_audio();
-	console_ip_is_going = 0;
-	g_static_mutex_unlock( &playback_mutex );
-	// to do: should decode_thread be cleared here?
-	g_thread_exit( NULL );
-	return NULL;
-}
-
-template<class Emu>
-void load_file( tag_t tag, Data_Reader& in, long rate, track_info_t* out, Emu* dummy )
-{
-	typename Emu::header_t h;
-	memcpy( &h, tag, tag_size );
-	if ( in.read( (char*) &h + tag_size, sizeof h - tag_size ) )
-		return;
-
-	if ( rate == 0 )
-		rate = 44100;
-	
-	Emu* local_emu = new Emu;
-	if ( !local_emu || local_emu->set_sample_rate( rate ) || local_emu->load( h, in ) )
-	{
-		delete local_emu; // delete NULL is safe
-		return;
-	}
-	
-	emu = local_emu;
-	if (out != NULL)
-		get_info_emu( *local_emu, out );
-}
-
-static void play_file( char* path )
-{
-	int track = 0; // to do: some way to select other tracks
-
-	// open and identify file
-	unload_file();
-	Audacious_Reader in;
-	tag_t tag;
-
-	// extract the subsong id from the virtual path
-	gchar *path2 = g_strdup(path);
-	gchar *_path = strchr(path2, '?');
-
-	if (_path != NULL && *_path == '?')
-	{
-		*_path = '\0';
-		_path++;
-		track = atoi(_path);
-	}
-
-	if ( in.open( path2 ) || in.read( tag, sizeof tag ) )
-		return;
-	int type = identify_file( path2, tag );
-	
-	// setup info
-	long sample_rate = 44100;
-	if ( type == type_spc )
-		sample_rate = Spc_Emu::native_sample_rate;
-	if ( audcfg.resample )
-		sample_rate = audcfg.resample_rate;
-	track_info_t info;
-	if ( begin_get_info( path2, &info ) )
-		return;
-	info.track = track;
-
-	// load in emulator and get info
-	switch ( type )
-	{
-		case type_nsf: load_file( tag, in, sample_rate, &info, (Nsf_Emu*) 0 ); break;
-		case type_nsfe:load_file( tag, in, sample_rate, &info, (Nsfe_Emu*)0 ); break;
-		case type_gbs: load_file( tag, in, sample_rate, &info, (Gbs_Emu*) 0 ); break;
-		case type_gym: load_file( tag, in, sample_rate, &info, (Gym_Emu*) 0 ); break;
-		case type_vgm: load_file( tag, in, sample_rate, &info, (Vgm_Emu*) 0 ); break;
-		case type_spc: load_file( tag, in, sample_rate, &info, (Spc_Emu*) 0 ); break;
-	}
-	in.close();
-	if ( !emu )
-		return;
-	
-	// set info
-	int length = -1;
-	bool has_length = false;
-	
-	if (( type == type_spc ) && ( audcfg.ignore_spc_length == TRUE ))
-		info.length = -1;
-	
-	char* title = end_get_info( info, &length, &has_length );
-	if ( title )
-	{
-		console_ip.set_info( title, length, emu->voice_count() * 1000, sample_rate, 2 );
-		g_free( title );
-	}
-	
-	// set frequency equalization
-	if ( audcfg.treble || audcfg.bass )
-	{
-		Music_Emu::equalizer_t eq = emu->equalizer();
-		
-		// bass - logarithmic, 2 to 8194 Hz
-		double bass = 1.0 - (audcfg.bass / 200.0 + 0.5);
-		eq.bass = (long int) pow(2.0, bass * 13.0) + (long int) 2.0;
-		
-		// treble - -50 to 0 to +5 dB
-		double treble = audcfg.treble / 100.0;
-		eq.treble = treble * (treble < 0 ? 50.0 : 5.0);
-		
-		emu->set_equalizer(eq);
-	}
-	
-	// start
-    if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) )
-		return;
-	pending_seek = -1;
-	track_ended = 0;
-	track_emu.start_track( emu, track, length, !has_length );
-	console_ip_is_going = 1;
-	decode_thread = g_thread_create( play_loop_track, NULL, TRUE, NULL );
-	g_free(path2);
-}
-
-static void seek( gint time )
-{
-	// to do: be sure seek works at all
-	// to do: disallow seek on slow formats (SPC, GYM, VGM using FM)?
-	pending_seek = time;
-}
-
-static void console_stop(void)
-{
-	console_ip_is_going = 0;
-	if ( decode_thread )
-	{
-		g_thread_join( decode_thread );
-		decode_thread = NULL;
-	}
-	console_ip.output->close_audio();
-	unload_file();
-}
-
-static void console_pause(gshort p)
-{
-	console_ip.output->pause(p);
-}
-
-static int get_time(void)
-{
-	return console_ip_is_going ? console_ip.output->output_time() : -1;
-}
-
-static gint is_our_file( gchar* path )
-{
-	Audacious_Reader in;
-	tag_t tag;
-
-	// extract the subsong id from the virtual path
-	gchar *path2 = g_strdup(path);
-	gchar *_path = strchr(path2, '?');
-	gboolean is_subsong = FALSE;
-	gint type;
-
-	if (_path != NULL && *_path == '?')
-	{
-		*_path = '\0';
-		_path++;
-		is_subsong = TRUE;
-	}
-
-	gint ret = !in.open( path2 ) && !in.read( tag, sizeof tag ) && (type = identify_file( path2, tag ));
-
-	if (ret == TRUE && is_subsong == FALSE)
-	{
-		if (type == type_spc || type == type_gym || type == type_vgm)
-		{
-			g_free(path2);
-			return ret;
-		}
-
-		switch ( type )
-		{
-			case type_nsf: load_file( tag, in, 0, NULL, (Nsf_Emu*) 0 ); break;
-			case type_nsfe:load_file( tag, in, 0, NULL, (Nsfe_Emu*)0 ); break;
-			case type_gbs: load_file( tag, in, 0, NULL, (Gbs_Emu*) 0 ); break;
-			case type_gym: load_file( tag, in, 0, NULL, (Gym_Emu*) 0 ); break;
-			case type_vgm: load_file( tag, in, 0, NULL, (Vgm_Emu*) 0 ); break;
-			case type_spc: load_file( tag, in, 0, NULL, (Spc_Emu*) 0 ); break;
-			default: return FALSE;
-		}
-
-		if (emu == NULL)
-			return FALSE;
-
-		for (int i = 0; i < emu->track_count(); i++)
-		{
-			gchar _buf[65535];
-			g_snprintf(_buf, 65535, "%s?%d", path2, i);
-
-			playlist_add_url(_buf);
-		}
-
-		ret = -1;
-
-		unload_file();
-	}
-
-	g_free(path2);
-	return ret;
-}
-
-// Setup
-
-static void console_init(void)
-{
-	console_cfg_load();
-}
-
-extern "C" void console_aboutbox(void)
-{
-	static GtkWidget * aboutbox = NULL;
-
-	if (!aboutbox)
-	{
-		aboutbox = xmms_show_message(_("About the Console Music Decoder"),
-						_("Console music decoder engine based on Game_Music_Emu 0.3.0.\n"
-						"Audacious implementation by: William Pitcock <nenolod@nenolod.net>, \n"
-						// Please do not put my hotpop.com address in the clear (I hate spam)
-						"        Shay Green <hotpop.com@blargg>"),
-						_("Ok"),
-						FALSE, NULL, NULL);
-		gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy",
-					(GCallback)gtk_widget_destroyed, &aboutbox);
-	}
-}
-
-InputPlugin console_ip =
-{
-	NULL,
-	NULL,
-	NULL,
-	console_init,
-	console_aboutbox,
-	console_cfg_ui,
-	is_our_file,
-	NULL,
-	play_file,
-	console_stop,
-	console_pause,
-	seek,
-	NULL,
-	get_time,
-	NULL,
-	NULL,
-	NULL,   
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	get_song_info,
-	NULL,
-	NULL,
-	get_song_tuple,
-};
-
-extern "C" InputPlugin *get_iplugin_info(void)
-{
-	console_ip.description = g_strdup_printf(_("SPC, VGM, NSF/NSFE, GBS, and GYM module decoder"));
-	return &console_ip;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Audacious_Driver.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,859 @@
+/*
+ * 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/
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "libaudacious/util.h"
+#include "libaudacious/titlestring.h"
+extern "C" {
+#include "audacious/output.h"
+#include "audacious/playlist.h"
+}
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+
+// configdb and prefs ui
+#include "Audacious_Config.h"
+
+// Game_Music_Emu
+#include "Nsf_Emu.h"
+#include "Nsfe_Emu.h"
+#include "Gbs_Emu.h"
+#include "Vgm_Emu.h"
+#include "Gym_Emu.h"
+#include "Spc_Emu.h"
+
+#include "Track_Emu.h"
+#include "Vfs_File.h"
+#include "Gzip_File.h"
+#include "blargg_endian.h"
+
+//typedef Vfs_File_Reader Audacious_Reader; // will use VFS once it handles gzip transparently
+typedef Gzip_File_Reader Audacious_Reader;
+
+AudaciousConsoleConfig audcfg = { 180, FALSE, 32000, TRUE, 0, 0, FALSE };
+static GThread* decode_thread;
+static GStaticMutex playback_mutex = G_STATIC_MUTEX_INIT;
+static int console_ip_is_going;
+static volatile long pending_seek;
+extern InputPlugin console_ip;
+static Music_Emu* emu = 0;
+static Track_Emu track_emu;
+static int track_ended;
+
+static void unload_file()
+{
+	delete emu;
+	emu = NULL;
+}
+
+// Information
+
+typedef unsigned char byte;
+
+#define DUPE_FIELD( field ) g_strndup( field, sizeof (field) );
+
+struct track_info_t
+{
+	int track;  // track to get info for
+	int length; // in msec, -1 = unknown
+	int loop;   // in msec, -1 = unknown, 0 = not looped
+	int intro;  // in msec, -1 = unknown
+	
+	TitleInput* ti;
+};
+
+// NSFE
+
+void get_nsfe_info( Nsfe_Info const& nsfe, track_info_t* out )
+{
+	Nsfe_Info::info_t const& h = nsfe.info();
+	out->ti->performer  = DUPE_FIELD( h.author );
+	out->ti->album_name = DUPE_FIELD( h.game );
+	out->ti->comment    = DUPE_FIELD( h.copyright );
+	out->ti->track_name = g_strdup( nsfe.track_name( out->track ) );
+	int time = nsfe.track_time( out->track );
+	if ( time > 0 )
+		out->length = time;
+	if ( nsfe.info().track_count > 1 )
+		out->ti->track_number = out->track + 1;
+}
+
+inline void get_info_emu( Nsfe_Emu& emu, track_info_t* out )
+{
+	emu.enable_playlist( audcfg.nsfe_playlist ); // to do: kind of hacky
+	get_nsfe_info( emu, out );
+}
+
+inline void get_file_info( Nsfe_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	Nsfe_Info nsfe;
+	if ( !nsfe.load( h, in ) )
+	{
+		nsfe.enable_playlist( audcfg.nsfe_playlist );
+		get_nsfe_info( nsfe, out );
+	}
+}
+
+// NSF
+
+static void get_nsf_info_( Nsf_Emu::header_t const& h, track_info_t* out )
+{
+	out->ti->performer  = DUPE_FIELD( h.author );
+	out->ti->album_name = DUPE_FIELD( h.game );
+	out->ti->comment    = DUPE_FIELD( h.copyright );
+	if ( h.track_count > 1 )
+		out->ti->track_number = out->track + 1;
+}
+
+inline void get_info_emu( Nsf_Emu& emu, track_info_t* out )
+{
+	get_nsf_info_( emu.header(), out );
+}
+
+inline void get_file_info( Nsf_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	get_nsf_info_( h, out );
+}
+
+// GBS
+
+static void get_gbs_info_( Gbs_Emu::header_t const& h, track_info_t* out )
+{
+	out->ti->performer  = DUPE_FIELD( h.author );
+	out->ti->album_name = DUPE_FIELD( h.game );
+	out->ti->comment    = DUPE_FIELD( h.copyright );
+	if ( h.track_count > 1 )
+		out->ti->track_number = out->track + 1;
+}
+
+inline void get_info_emu( Gbs_Emu& emu, track_info_t* out )
+{
+	get_gbs_info_( emu.header(), out );
+}
+
+inline void get_file_info( Gbs_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	get_gbs_info_( h, out );
+}
+
+// GYM
+
+static void get_gym_info_( Gym_Emu::header_t const& h, track_info_t* out )
+{
+	if ( !memcmp( h.tag, "GYMX", 4 ) )
+	{
+		out->ti->performer  = DUPE_FIELD( h.copyright );
+		out->ti->album_name = DUPE_FIELD( h.game );
+		out->ti->track_name = DUPE_FIELD( h.song );
+		out->ti->comment    = DUPE_FIELD( h.comment );
+	}
+}
+
+static void get_gym_timing_( Gym_Emu const& emu, track_info_t* out )
+{
+	out->length = emu.track_length() * 50 / 3; // 1000 / 60
+	out->loop = 0;
+	
+	long loop = get_le32( emu.header().loop_start );
+	if ( loop )
+	{
+		out->intro = loop * 50 / 3;
+		out->loop = out->length - out->intro;
+		out->length = -1;
+	}
+}
+
+inline void get_info_emu( Gym_Emu& emu, track_info_t* out )
+{
+	get_gym_info_( emu.header(), out );
+	get_gym_timing_( emu, out );
+}
+
+inline void get_file_info( Gym_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	get_gym_info_( h, out );
+	
+	// have to load and parse entire GYM file to determine length
+	// to do: could make more efficient by manually parsing data (format is simple)
+	// rather than loading into emulator with its FM chips and resampler
+	Gym_Emu* emu = new Gym_Emu;
+	if ( emu && !emu->set_sample_rate( 44100 ) && !emu->load( h, in ) )
+		get_gym_timing_( *emu, out );
+	delete emu;
+}
+
+// SPC
+
+static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
+{
+	// header
+	byte const* end = begin + size;
+	if ( size < 8 || memcmp( begin, "xid6", 4 ) )
+		return;
+	long info_size = get_le32( begin + 4 );
+	byte const* in = begin + 8; 
+	if ( end - in > info_size )
+		end = in + info_size;
+	
+	while ( end - in >= 4 )
+	{
+		// header
+		int id   = in [0];
+		int data = in [3] * 0x100 + in [2];
+		int type = in [1];
+		int len  = type ? data : 0;
+		in += 4;
+		if ( len > end - in )
+			break; // block goes past end of data
+		
+		// handle specific block types
+		switch ( id )
+		{
+			case 0x01: out->ti->track_name = g_strndup( (char*) in, len ); break;
+			case 0x02: out->ti->album_name = g_strndup( (char*) in, len ); break;
+			case 0x03: out->ti->performer  = g_strndup( (char*) in, len ); break;
+			case 0x07: out->ti->comment    = g_strndup( (char*) in, len ); break;
+			//case 0x31: // loop length, but I haven't found any SPC files that use this
+		}
+		
+		// skip to next block
+		in += len;
+		
+		// blocks are supposed to be 4-byte aligned with zero-padding...
+		byte const* unaligned = in;
+		while ( (in - begin) & 3 && in < end )
+		{
+			if ( *in++ != 0 )
+			{
+				// ...but some files have no padding
+				in = unaligned;
+				break;
+			}
+		}
+	}
+}
+
+static void get_spc_info_( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size,
+		track_info_t* out )
+{
+	// decode length (can be in text or binary format)
+	char s [4] = { h.len_secs [0], h.len_secs [1], h.len_secs [2], 0 };
+	int len_secs = (unsigned char) s [1] * 0x100 + s [0];
+	if ( s [1] >= ' ' || (!s [1] && isdigit( s [0] )) )
+		len_secs = atoi( s );
+	if ( len_secs )
+		out->length = len_secs * 1000;
+	
+	if ( xid6_size )
+		get_spc_xid6( xid6, xid6_size, out );
+	
+	// use header to fill any remaining fields
+	if ( !out->ti->performer  ) out->ti->performer  = DUPE_FIELD( h.author );
+	if ( !out->ti->album_name ) out->ti->album_name = DUPE_FIELD( h.game );
+	if ( !out->ti->track_name ) out->ti->track_name = DUPE_FIELD( h.song );
+}
+
+inline void get_info_emu( Spc_Emu& emu, track_info_t* out )
+{
+	get_spc_info_( emu.header(), emu.trailer(), emu.trailer_size(), out );
+}
+
+inline void get_file_info( Spc_Emu::header_t const& h, Data_Reader& in, track_info_t* out )
+{
+	// handle xid6 data at end of file
+	long const xid6_skip = 0x10200 - sizeof (Spc_Emu::header_t);
+	long xid6_size = in.remain() - xid6_skip;
+	blargg_vector<byte> xid6;
+	if ( xid6_size <= 0 || xid6.resize( xid6_size ) || in.skip( xid6_skip ) ||
+			in.read( xid6.begin(), xid6.size() ) )
+		xid6_size = 0;
+	
+	get_spc_info_( h, xid6.begin(), xid6_size, out );
+}
+
+// VGM
+
+static void get_gd3_str( byte const* in, byte const* end, gchar** out )
+{
+	int len = (end - in) / 2 - 1;
+	if ( len > 0 )
+	{
+		*out = g_strndup( "", len );
+		if ( !*out )
+			return;
+		for ( int i = 0; i < len; i++ )
+			(*out) [i] = in [i * 2]; // to do: convert to utf-8
+	}
+}
+
+static byte const* skip_gd3_str( byte const* in, byte const* end )
+{
+	while ( end - in >= 2 )
+	{
+		in += 2;
+		if ( !(in [-2] | in [-1]) )
+			break;
+	}
+	return in;
+}
+
+static byte const* get_gd3_pair( byte const* in, byte const* end, gchar** out )
+{
+	byte const* mid = skip_gd3_str( in, end );
+	if ( out )
+		get_gd3_str( in, mid, out );
+	return skip_gd3_str( mid, end );
+}
+
+static void get_vgm_gd3( byte const* in, long size, track_info_t* out )
+{
+	byte const* end = in + size;
+	in = get_gd3_pair( in, end, &out->ti->track_name );
+	in = get_gd3_pair( in, end, &out->ti->album_name );
+	in = get_gd3_pair( in, end, 0 ); // system
+	in = get_gd3_pair( in, end, &out->ti->performer );
+	in = get_gd3_pair( in, end, 0 ); // copyright
+	// ... other fields (release date, dumper, notes)
+}
+
+static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
+{
+	long length = get_le32( h.track_duration );
+	if ( length > 0 )
+	{
+		out->length = length * 10 / 441; // 1000 / 44100 (VGM files used 44100 as timebase)
+		out->loop = 0;
+		
+		long loop = get_le32( h.loop_duration );
+		if ( loop > 0 && get_le32( h.loop_offset ) )
+		{
+			out->loop = loop * 10 / 441;
+			out->intro = out->length - out->loop;
+			out->length = -1;
+		}
+	}
+}
+
+inline void get_info_emu( Vgm_Emu& emu, track_info_t* out )
+{
+	get_vgm_length( emu.header(), out );
+	
+	int size;
+	byte const* data = emu.gd3_data( &size );
+	if ( data )
+		get_vgm_gd3( data + 12, size, out );
+}
+
+inline void get_file_info( Vgm_Emu::header_t const& vgm_h, Data_Reader& in, track_info_t* out )
+{
+	get_vgm_length( vgm_h, out );
+	
+	// find gd3 header
+	long gd3_offset = get_le32( vgm_h.gd3_offset ) + offsetof(Vgm_Emu::header_t,gd3_offset) -
+			sizeof vgm_h;
+	long gd3_max_size = in.remain() - gd3_offset;
+	byte gd3_h [12];
+	if ( gd3_offset <= 0 || gd3_max_size < (int) sizeof gd3_h )
+		return;
+	
+	// read gd3 header
+	if ( in.skip( gd3_offset ) || in.read( gd3_h, sizeof gd3_h ) )
+		return;
+	
+	// check header signature and version
+	if ( memcmp( gd3_h, "Gd3 ", 4 ) || get_le32( gd3_h + 4 ) >= 0x200 )
+		return;
+	
+	// get and check size
+	long gd3_size = get_le32( gd3_h + 8 );
+	if ( gd3_size > gd3_max_size - 12 )
+		return;
+	
+	// read and parse gd3 data
+	blargg_vector<byte> gd3;
+	if ( gd3.resize( gd3_size ) || in.read( gd3.begin(), gd3.size() ) )
+		return;
+	
+	get_vgm_gd3( gd3.begin(), gd3.size(), out );
+}
+
+// File identification
+
+enum { type_none = 0, type_spc, type_nsf, type_nsfe, type_vgm, type_gbs, type_gym };
+
+int const tag_size = 4;
+typedef char tag_t [tag_size];
+
+static int identify_file( gchar* path, tag_t tag )
+{
+	// GYM file format doesn't require *any* header, just the ".gym" extension
+	if ( g_str_has_suffix( path, ".gym" ) ) // to do: is pathname in unicode?
+		return type_gym;
+	// to do: trust suffix for all file types, avoiding having to look inside files?
+
+	int result = type_none;
+	if ( !memcmp( tag, "SNES", 4 ) ) result = type_spc;
+	if ( !memcmp( tag, "NESM", 4 ) ) result = type_nsf;
+	if ( !memcmp( tag, "NSFE", 4 ) ) result = type_nsfe;
+	if ( !memcmp( tag, "GYMX", 4 ) ) result = type_gym;
+	if ( !memcmp( tag, "GBS" , 3 ) ) result = type_gbs;
+	if ( !memcmp( tag, "Vgm ", 4 ) ) result = type_vgm;
+	return result;
+}
+
+// Get info
+
+static int begin_get_info( const char* path, track_info_t* out )
+{
+	out->track  = 0;
+	out->length = -1;
+	out->loop   = -1;
+	out->intro  = -1;
+	TitleInput* fields = bmp_title_input_new();
+	out->ti = fields;
+	if ( !fields )
+		return true;
+	
+	fields->file_name = g_path_get_basename( path );
+	fields->file_path = g_path_get_dirname( path );
+	return false;
+}
+
+static char* end_get_info( track_info_t const& info, int* length, bool* has_length )
+{
+	*length = info.length;
+	if ( has_length )
+		*has_length = (*length > 0);
+	
+	if ( *length <= 0 )
+		*length = audcfg.loop_length * 1000;
+	
+	// use filename for formats that don't have field for name of game
+	// to do: strip off file extension
+	if ( !info.ti->track_name )
+		info.ti->track_name = g_strdup( info.ti->file_name );
+	
+	char* result = xmms_get_titlestring( xmms_get_gentitle_format(), info.ti );
+	g_free( info.ti );
+	return result;
+}
+
+template<class Header>
+inline void get_info_t( tag_t tag, Data_Reader& in, track_info_t* out, Header* )
+{
+	Header h;
+	memcpy( &h, tag, tag_size );
+	if ( !in.read( (char*) &h + tag_size, sizeof h - tag_size ) )
+		get_file_info( h, in, out );
+}
+
+static void get_song_info( char* path, char** title, int* length )
+{
+	int track = 0; // to do: way to select other tracks
+	
+	// extract the subsong id from the virtual path
+	gchar *path2 = g_strdup(path);
+	gchar *_path = strchr(path2, '?');
+
+	if (_path != NULL && *_path == '?')
+	{
+		*_path = '\0';
+		_path++;
+		track = atoi(_path);
+	}
+
+	*length = -1;
+	*title = NULL;
+	Audacious_Reader in;
+	tag_t tag;
+	if ( in.open( path2 ) || in.read( tag, sizeof tag ) )
+		return;
+	
+	int type = identify_file( path2, tag );
+	if ( !type )
+		return;
+	
+	track_info_t info;
+	if ( begin_get_info( path2, &info ) )
+		return;
+	info.track = track;
+	
+	switch ( type )
+	{
+		case type_nsf: get_info_t( tag, in, &info, (Nsf_Emu::header_t*) 0 ); break;
+		case type_gbs: get_info_t( tag, in, &info, (Gbs_Emu::header_t*) 0 ); break;
+		case type_gym: get_info_t( tag, in, &info, (Gym_Emu::header_t*) 0 ); break;
+		case type_vgm: get_info_t( tag, in, &info, (Vgm_Emu::header_t*) 0 ); break;
+		case type_spc: get_info_t( tag, in, &info, (Spc_Emu::header_t*) 0 ); break;
+		case type_nsfe:get_info_t( tag, in, &info, (Nsfe_Emu::header_t*)0 ); break;
+	}
+	*title = end_get_info( info, length, 0 );
+
+	g_free(path2);
+}
+
+// Get tuple
+
+static TitleInput *get_song_tuple( char *path )
+{
+	int track = 0; // to do: way to select other tracks
+	
+	// extract the subsong id from the virtual path
+	gchar *path2 = g_strdup(path);
+	gchar *_path = strchr(path2, '?');
+
+	if (_path != NULL && *_path == '?')
+	{
+		*_path = '\0';
+		_path++;
+		track = atoi(_path);
+	}	
+
+	Audacious_Reader in;
+	tag_t tag;
+	if ( in.open( path2 ) || in.read( tag, sizeof tag ) )
+		return NULL;
+	
+	int type = identify_file( path2, tag );
+	if ( !type )
+		return NULL;
+	
+	track_info_t info;
+	if ( begin_get_info( path2, &info ) )
+		return NULL;
+	info.track = track;
+	
+	switch ( type )
+	{
+		case type_nsf: get_info_t( tag, in, &info, (Nsf_Emu::header_t*) 0 ); break;
+		case type_gbs: get_info_t( tag, in, &info, (Gbs_Emu::header_t*) 0 ); break;
+		case type_gym: get_info_t( tag, in, &info, (Gym_Emu::header_t*) 0 ); break;
+		case type_vgm: get_info_t( tag, in, &info, (Vgm_Emu::header_t*) 0 ); break;
+		case type_spc: get_info_t( tag, in, &info, (Spc_Emu::header_t*) 0 ); break;
+		case type_nsfe:get_info_t( tag, in, &info, (Nsfe_Emu::header_t*)0 ); break;
+	}
+
+	info.ti->length = info.length;
+	
+	if ( info.ti->length <= 0 )
+		info.ti->length = audcfg.loop_length * 1000;
+
+	return info.ti;
+}
+
+// Playback
+
+static void* play_loop_track( gpointer )
+{
+	g_static_mutex_lock( &playback_mutex );
+	
+	while ( console_ip_is_going )
+	{
+		int const buf_size = 1024;
+		Music_Emu::sample_t buf [buf_size];
+		
+		// handle pending seek
+		long s = pending_seek;
+		pending_seek = -1; // to do: use atomic swap
+		if ( s >= 0 )
+		{
+			console_ip.output->flush( s * 1000 );
+			track_emu.seek( s * 1000 );
+		}
+
+		// fill buffer
+		if ( track_ended )
+		{
+			if ( track_ended++ > emu->sample_rate() * 3 / (buf_size / 2) )
+				console_ip_is_going = false;
+			memset( buf, 0, sizeof buf );
+		}
+		else if ( track_emu.play( buf_size, buf ) )
+		{
+			track_ended = 1;
+		}
+		produce_audio( console_ip.output->written_time(), 
+			FMT_S16_NE, 1, sizeof buf, buf, 
+			&console_ip_is_going );
+	}
+	
+	// stop playing
+	unload_file();
+	console_ip.output->close_audio();
+	console_ip_is_going = 0;
+	g_static_mutex_unlock( &playback_mutex );
+	// to do: should decode_thread be cleared here?
+	g_thread_exit( NULL );
+	return NULL;
+}
+
+template<class Emu>
+void load_file( tag_t tag, Data_Reader& in, long rate, track_info_t* out, Emu* dummy )
+{
+	typename Emu::header_t h;
+	memcpy( &h, tag, tag_size );
+	if ( in.read( (char*) &h + tag_size, sizeof h - tag_size ) )
+		return;
+
+	if ( rate == 0 )
+		rate = 44100;
+	
+	Emu* local_emu = new Emu;
+	if ( !local_emu || local_emu->set_sample_rate( rate ) || local_emu->load( h, in ) )
+	{
+		delete local_emu; // delete NULL is safe
+		return;
+	}
+	
+	emu = local_emu;
+	if (out != NULL)
+		get_info_emu( *local_emu, out );
+}
+
+static void play_file( char* path )
+{
+	int track = 0; // to do: some way to select other tracks
+
+	// open and identify file
+	unload_file();
+	Audacious_Reader in;
+	tag_t tag;
+
+	// extract the subsong id from the virtual path
+	gchar *path2 = g_strdup(path);
+	gchar *_path = strchr(path2, '?');
+
+	if (_path != NULL && *_path == '?')
+	{
+		*_path = '\0';
+		_path++;
+		track = atoi(_path);
+	}
+
+	if ( in.open( path2 ) || in.read( tag, sizeof tag ) )
+		return;
+	int type = identify_file( path2, tag );
+	
+	// setup info
+	long sample_rate = 44100;
+	if ( type == type_spc )
+		sample_rate = Spc_Emu::native_sample_rate;
+	if ( audcfg.resample )
+		sample_rate = audcfg.resample_rate;
+	track_info_t info;
+	if ( begin_get_info( path2, &info ) )
+		return;
+	info.track = track;
+
+	// load in emulator and get info
+	switch ( type )
+	{
+		case type_nsf: load_file( tag, in, sample_rate, &info, (Nsf_Emu*) 0 ); break;
+		case type_nsfe:load_file( tag, in, sample_rate, &info, (Nsfe_Emu*)0 ); break;
+		case type_gbs: load_file( tag, in, sample_rate, &info, (Gbs_Emu*) 0 ); break;
+		case type_gym: load_file( tag, in, sample_rate, &info, (Gym_Emu*) 0 ); break;
+		case type_vgm: load_file( tag, in, sample_rate, &info, (Vgm_Emu*) 0 ); break;
+		case type_spc: load_file( tag, in, sample_rate, &info, (Spc_Emu*) 0 ); break;
+	}
+	in.close();
+	if ( !emu )
+		return;
+	
+	// set info
+	int length = -1;
+	bool has_length = false;
+	
+	if (( type == type_spc ) && ( audcfg.ignore_spc_length == TRUE ))
+		info.length = -1;
+	
+	char* title = end_get_info( info, &length, &has_length );
+	if ( title )
+	{
+		console_ip.set_info( title, length, emu->voice_count() * 1000, sample_rate, 2 );
+		g_free( title );
+	}
+	
+	// set frequency equalization
+	if ( audcfg.treble || audcfg.bass )
+	{
+		Music_Emu::equalizer_t eq = emu->equalizer();
+		
+		// bass - logarithmic, 2 to 8194 Hz
+		double bass = 1.0 - (audcfg.bass / 200.0 + 0.5);
+		eq.bass = (long int) pow(2.0, bass * 13.0) + (long int) 2.0;
+		
+		// treble - -50 to 0 to +5 dB
+		double treble = audcfg.treble / 100.0;
+		eq.treble = treble * (treble < 0 ? 50.0 : 5.0);
+		
+		emu->set_equalizer(eq);
+	}
+	
+	// start
+    if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) )
+		return;
+	pending_seek = -1;
+	track_ended = 0;
+	track_emu.start_track( emu, track, length, !has_length );
+	console_ip_is_going = 1;
+	decode_thread = g_thread_create( play_loop_track, NULL, TRUE, NULL );
+	g_free(path2);
+}
+
+static void seek( gint time )
+{
+	// to do: be sure seek works at all
+	// to do: disallow seek on slow formats (SPC, GYM, VGM using FM)?
+	pending_seek = time;
+}
+
+static void console_stop(void)
+{
+	console_ip_is_going = 0;
+	if ( decode_thread )
+	{
+		g_thread_join( decode_thread );
+		decode_thread = NULL;
+	}
+	console_ip.output->close_audio();
+	unload_file();
+}
+
+static void console_pause(gshort p)
+{
+	console_ip.output->pause(p);
+}
+
+static int get_time(void)
+{
+	return console_ip_is_going ? console_ip.output->output_time() : -1;
+}
+
+static gint is_our_file( gchar* path )
+{
+	Audacious_Reader in;
+	tag_t tag;
+
+	// extract the subsong id from the virtual path
+	gchar *path2 = g_strdup(path);
+	gchar *_path = strchr(path2, '?');
+	gboolean is_subsong = FALSE;
+	gint type;
+
+	if (_path != NULL && *_path == '?')
+	{
+		*_path = '\0';
+		_path++;
+		is_subsong = TRUE;
+	}
+
+	gint ret = !in.open( path2 ) && !in.read( tag, sizeof tag ) && (type = identify_file( path2, tag ));
+
+	if (ret == TRUE && is_subsong == FALSE)
+	{
+		if (type == type_spc || type == type_gym || type == type_vgm)
+		{
+			g_free(path2);
+			return ret;
+		}
+
+		switch ( type )
+		{
+			case type_nsf: load_file( tag, in, 0, NULL, (Nsf_Emu*) 0 ); break;
+			case type_nsfe:load_file( tag, in, 0, NULL, (Nsfe_Emu*)0 ); break;
+			case type_gbs: load_file( tag, in, 0, NULL, (Gbs_Emu*) 0 ); break;
+			case type_gym: load_file( tag, in, 0, NULL, (Gym_Emu*) 0 ); break;
+			case type_vgm: load_file( tag, in, 0, NULL, (Vgm_Emu*) 0 ); break;
+			case type_spc: load_file( tag, in, 0, NULL, (Spc_Emu*) 0 ); break;
+			default: return FALSE;
+		}
+
+		if (emu == NULL)
+			return FALSE;
+
+		for (int i = 0; i < emu->track_count(); i++)
+		{
+			gchar _buf[65535];
+			g_snprintf(_buf, 65535, "%s?%d", path2, i);
+
+			playlist_add_url(_buf);
+		}
+
+		ret = -1;
+
+		unload_file();
+	}
+
+	g_free(path2);
+	return ret;
+}
+
+// Setup
+
+static void console_init(void)
+{
+	console_cfg_load();
+}
+
+extern "C" void console_aboutbox(void)
+{
+	static GtkWidget * aboutbox = NULL;
+
+	if (!aboutbox)
+	{
+		aboutbox = xmms_show_message(_("About the Console Music Decoder"),
+						_("Console music decoder engine based on Game_Music_Emu 0.3.0.\n"
+						"Audacious implementation by: William Pitcock <nenolod@nenolod.net>, \n"
+						// Please do not put my hotpop.com address in the clear (I hate spam)
+						"        Shay Green <hotpop.com@blargg>"),
+						_("Ok"),
+						FALSE, NULL, NULL);
+		gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy",
+					(GCallback)gtk_widget_destroyed, &aboutbox);
+	}
+}
+
+InputPlugin console_ip =
+{
+	NULL,
+	NULL,
+	NULL,
+	console_init,
+	console_aboutbox,
+	console_cfg_ui,
+	is_our_file,
+	NULL,
+	play_file,
+	console_stop,
+	console_pause,
+	seek,
+	NULL,
+	get_time,
+	NULL,
+	NULL,
+	NULL,   
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	get_song_info,
+	NULL,
+	NULL,
+	get_song_tuple,
+};
+
+extern "C" InputPlugin *get_iplugin_info(void)
+{
+	console_ip.description = g_strdup_printf(_("SPC, VGM, NSF/NSFE, GBS, and GYM module decoder"));
+	return &console_ip;
+}
+
--- a/Plugins/Input/console/Blip_Buffer.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,406 +0,0 @@
-
-// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
-
-#include "Blip_Buffer.h"
-
-#include <assert.h>
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-#include <math.h>
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-int const buffer_extra = blip_widest_impulse_ + 2;
-
-Blip_Buffer::Blip_Buffer()
-{
-	factor_ = LONG_MAX;
-	offset_ = 0;
-	buffer_ = 0;
-	buffer_size_ = 0;
-	sample_rate_ = 0;
-	reader_accum = 0;
-	bass_shift = 0;
-	clock_rate_ = 0;
-	bass_freq_ = 16;
-	length_ = 0;
-	
-	// assumptions code makes about implementation-defined features
-	#ifndef NDEBUG
-		// right shift of negative value preserves sign
-		buf_t_ i = -0x7FFFFFFE;
-		assert( (i >> 1) == -0x3FFFFFFF );
-		
-		// casting to short truncates to 16 bits and sign-extends
-		i = 0x18000;
-		assert( (short) i == -0x8000 );
-	#endif
-}
-
-Blip_Buffer::~Blip_Buffer()
-{
-	free( buffer_ );
-}
-
-void Blip_Buffer::clear( int entire_buffer )
-{
-	offset_ = 0;
-	reader_accum = 0;
-	if ( buffer_ )
-	{
-		long count = (entire_buffer ? buffer_size_ : samples_avail());
-		memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) );
-	}
-}
-
-Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec )
-{
-	// start with maximum length that resampled time can represent
-	long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64;
-	if ( msec != blip_max_length )
-	{
-		long s = (new_rate * (msec + 1) + 999) / 1000;
-		if ( s < new_size )
-			new_size = s;
-		else
-			assert( 0 ); // fails if requested buffer length exceeds limit
-	}
-	
-	if ( buffer_size_ != new_size )
-	{
-		void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ );
-		if ( !p )
-			return "Out of memory";
-		buffer_ = (buf_t_*) p;
-	}
-	
-	buffer_size_ = new_size;
-	
-	// update things based on the sample rate
-	sample_rate_ = new_rate;
-	length_ = new_size * 1000 / new_rate - 1;
-	if ( msec )
-		assert( length_ == msec ); // ensure length is same as that passed in
-	if ( clock_rate_ )
-		clock_rate( clock_rate_ );
-	bass_freq( bass_freq_ );
-	
-	clear();
-	
-	return 0; // success
-}
-
-blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const
-{
-	double ratio = (double) sample_rate_ / clock_rate;
-	long factor = (long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
-	assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large
-	return (blip_resampled_time_t) factor;
-}
-
-void Blip_Buffer::bass_freq( int freq )
-{
-	bass_freq_ = freq;
-	int shift = 31;
-	if ( freq > 0 )
-	{
-		shift = 13;
-		long f = (freq << 16) / sample_rate_;
-		while ( (f >>= 1) && --shift ) { }
-	}
-	bass_shift = shift;
-}
-
-void Blip_Buffer::end_frame( blip_time_t t )
-{
-	offset_ += t * factor_;
-	assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length
-}
-
-void Blip_Buffer::remove_silence( long count )
-{
-	assert( count <= samples_avail() ); // tried to remove more samples than available
-	offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
-}
-
-long Blip_Buffer::count_samples( blip_time_t t ) const
-{
-	unsigned long last_sample  = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
-	unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
-	return (long) (last_sample - first_sample);
-}
-
-blip_time_t Blip_Buffer::count_clocks( long count ) const
-{
-	if ( count > buffer_size_ )
-		count = buffer_size_;
-	blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
-	return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_);
-}
-
-void Blip_Buffer::remove_samples( long count )
-{
-	if ( count )
-	{
-		remove_silence( count );
-		
-		// copy remaining samples to beginning and clear old samples
-		long remain = samples_avail() + buffer_extra;
-		memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
-		memset( buffer_ + remain, 0, count * sizeof *buffer_ );
-	}
-}
-
-// Blip_Synth_
-
-Blip_Synth_::Blip_Synth_( short* p, int w ) :
-	impulses( p ),
-	width( w )
-{
-	volume_unit_ = 0.0;
-	kernel_unit = 0;
-	buf = 0;
-	last_amp = 0;
-	delta_factor = 0;
-}
-
-static double const pi = 3.1415926535897932384626433832795029;
-
-static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff )
-{
-	if ( cutoff >= 0.999 )
-		cutoff = 0.999;
-	
-	if ( treble < -300.0 )
-		treble = -300.0;
-	if ( treble > 5.0 )
-		treble = 5.0;
-	
-	double const maxh = 4096.0;
-	double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) );
-	double const pow_a_n = pow( rolloff, maxh - maxh * cutoff );
-	double const to_angle = pi / 2 / maxh / oversample;
-	for ( int i = 0; i < count; i++ )
-	{
-		double angle = ((i - count) * 2 + 1) * to_angle;
-		double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle );
-		double cos_nc_angle = cos( maxh * cutoff * angle );
-		double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle );
-		double cos_angle = cos( angle );
-		
-		c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
-		double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
-		double b = 2.0 - cos_angle - cos_angle;
-		double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
-		
-		out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d
-	}
-}
-
-void blip_eq_t::generate( float* out, int count ) const
-{
-	// lower cutoff freq for narrow kernels with their wider transition band
-	// (8 points->1.49, 16 points->1.15)
-	double oversample = blip_res * 2.25 / count + 0.85;
-	double half_rate = sample_rate * 0.5;
-	if ( cutoff_freq )
-		oversample = half_rate / cutoff_freq;
-	double cutoff = rolloff_freq * oversample / half_rate;
-	
-	gen_sinc( out, count, blip_res * oversample, treble, cutoff );
-	
-	// apply (half of) hamming window
-	double to_fraction = pi / (count - 1);
-	for ( int i = count; i--; )
-		out [i] *= 0.54 - 0.46 * cos( i * to_fraction );
-}
-
-void Blip_Synth_::adjust_impulse()
-{
-	// sum pairs for each phase and add error correction to end of first half
-	int const size = impulses_size();
-	for ( int p = blip_res; p-- >= blip_res / 2; )
-	{
-		int p2 = blip_res - 2 - p;
-		long error = kernel_unit;
-		for ( int i = 1; i < size; i += blip_res )
-		{
-			error -= impulses [i + p ];
-			error -= impulses [i + p2];
-		}
-		if ( p == p2 )
-			error /= 2; // phase = 0.5 impulse uses same half for both sides
-		impulses [size - blip_res + p] += error;
-		//printf( "error: %ld\n", error );
-	}
-	
-	//for ( int i = blip_res; i--; printf( "\n" ) )
-	//  for ( int j = 0; j < width / 2; j++ )
-	//      printf( "%5ld,", impulses [j * blip_res + i + 1] );
-}
-
-void Blip_Synth_::treble_eq( blip_eq_t const& eq )
-{
-	float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
-	
-	int const half_size = blip_res / 2 * (width - 1);
-	eq.generate( &fimpulse [blip_res], half_size );
-	
-	int i;
-	
-	// need mirror slightly past center for calculation
-	for ( i = blip_res; i--; )
-		fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i];
-	
-	// starts at 0
-	for ( i = 0; i < blip_res; i++ )
-		fimpulse [i] = 0.0f;
-	
-	// find rescale factor
-	double total = 0.0;
-	for ( i = 0; i < half_size; i++ )
-		total += fimpulse [blip_res + i];
-	
-	//double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
-	//double const base_unit = 37888.0; // allows treble to +5 dB
-	double const base_unit = 32768.0; // necessary for blip_unscaled to work
-	double rescale = base_unit / 2 / total;
-	kernel_unit = (long) base_unit;
-	
-	// integrate, first difference, rescale, convert to int
-	double sum = 0.0;
-	double next = 0.0;
-	int const impulses_size = this->impulses_size();
-	for ( i = 0; i < impulses_size; i++ )
-	{
-		impulses [i] = (short) floor( (next - sum) * rescale + 0.5 );
-		sum += fimpulse [i];
-		next += fimpulse [i + blip_res];
-	}
-	adjust_impulse();
-	
-	// volume might require rescaling
-	double vol = volume_unit_;
-	if ( vol )
-	{
-		volume_unit_ = 0.0;
-		volume_unit( vol );
-	}
-}
-
-void Blip_Synth_::volume_unit( double new_unit )
-{
-	if ( new_unit != volume_unit_ )
-	{
-		// use default eq if it hasn't been set yet
-		if ( !kernel_unit )
-			treble_eq( -8.0 );
-		
-		volume_unit_ = new_unit;
-		double factor = new_unit * (1L << blip_sample_bits) / kernel_unit;
-		
-		if ( factor > 0.0 )
-		{
-			int shift = 0;
-			
-			// if unit is really small, might need to attenuate kernel
-			while ( factor < 2.0 )
-			{
-				shift++;
-				factor *= 2.0;
-			}
-			
-			if ( shift )
-			{
-				kernel_unit >>= shift;
-				assert( kernel_unit > 0 ); // fails if volume unit is too low
-				
-				// keep values positive to avoid round-towards-zero of sign-preserving
-				// right shift for negative values
-				long offset = 0x8000 + (1 << (shift - 1));
-				long offset2 = 0x8000 >> shift;
-				for ( int i = impulses_size(); i--; )
-					impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2);
-				adjust_impulse();
-			}
-		}
-		delta_factor = (int) floor( factor + 0.5 );
-		//printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
-	}
-}
-
-long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo )
-{
-	long count = samples_avail();
-	if ( count > max_samples )
-		count = max_samples;
-	
-	if ( count )
-	{
-		int const sample_shift = blip_sample_bits - 16;
-		int const bass_shift = this->bass_shift;
-		long accum = reader_accum;
-		buf_t_* in = buffer_;
-		
-		if ( !stereo )
-		{
-			for ( long n = count; n--; )
-			{
-				long s = accum >> sample_shift;
-				accum -= accum >> bass_shift;
-				accum += *in++;
-				*out++ = (blip_sample_t) s;
-				
-				// clamp sample
-				if ( (blip_sample_t) s != s )
-					out [-1] = (blip_sample_t) (0x7FFF - (s >> 24));
-			}
-		}
-		else
-		{
-			for ( long n = count; n--; )
-			{
-				long s = accum >> sample_shift;
-				accum -= accum >> bass_shift;
-				accum += *in++;
-				*out = (blip_sample_t) s;
-				out += 2;
-				
-				// clamp sample
-				if ( (blip_sample_t) s != s )
-					out [-2] = (blip_sample_t) (0x7FFF - (s >> 24));
-			}
-		}
-		
-		reader_accum = accum;
-		remove_samples( count );
-	}
-	return count;
-}
-
-void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
-{
-	buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
-	
-	int const sample_shift = blip_sample_bits - 16;
-	int prev = 0;
-	while ( count-- )
-	{
-		long s = (long) *in++ << sample_shift;
-		*out += s - prev;
-		prev = s;
-		++out;
-	}
-	*out -= prev;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Blip_Buffer.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,406 @@
+
+// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
+
+#include "Blip_Buffer.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+int const buffer_extra = blip_widest_impulse_ + 2;
+
+Blip_Buffer::Blip_Buffer()
+{
+	factor_ = LONG_MAX;
+	offset_ = 0;
+	buffer_ = 0;
+	buffer_size_ = 0;
+	sample_rate_ = 0;
+	reader_accum = 0;
+	bass_shift = 0;
+	clock_rate_ = 0;
+	bass_freq_ = 16;
+	length_ = 0;
+	
+	// assumptions code makes about implementation-defined features
+	#ifndef NDEBUG
+		// right shift of negative value preserves sign
+		buf_t_ i = -0x7FFFFFFE;
+		assert( (i >> 1) == -0x3FFFFFFF );
+		
+		// casting to short truncates to 16 bits and sign-extends
+		i = 0x18000;
+		assert( (short) i == -0x8000 );
+	#endif
+}
+
+Blip_Buffer::~Blip_Buffer()
+{
+	free( buffer_ );
+}
+
+void Blip_Buffer::clear( int entire_buffer )
+{
+	offset_ = 0;
+	reader_accum = 0;
+	if ( buffer_ )
+	{
+		long count = (entire_buffer ? buffer_size_ : samples_avail());
+		memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) );
+	}
+}
+
+Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec )
+{
+	// start with maximum length that resampled time can represent
+	long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64;
+	if ( msec != blip_max_length )
+	{
+		long s = (new_rate * (msec + 1) + 999) / 1000;
+		if ( s < new_size )
+			new_size = s;
+		else
+			assert( 0 ); // fails if requested buffer length exceeds limit
+	}
+	
+	if ( buffer_size_ != new_size )
+	{
+		void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ );
+		if ( !p )
+			return "Out of memory";
+		buffer_ = (buf_t_*) p;
+	}
+	
+	buffer_size_ = new_size;
+	
+	// update things based on the sample rate
+	sample_rate_ = new_rate;
+	length_ = new_size * 1000 / new_rate - 1;
+	if ( msec )
+		assert( length_ == msec ); // ensure length is same as that passed in
+	if ( clock_rate_ )
+		clock_rate( clock_rate_ );
+	bass_freq( bass_freq_ );
+	
+	clear();
+	
+	return 0; // success
+}
+
+blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const
+{
+	double ratio = (double) sample_rate_ / clock_rate;
+	long factor = (long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
+	assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large
+	return (blip_resampled_time_t) factor;
+}
+
+void Blip_Buffer::bass_freq( int freq )
+{
+	bass_freq_ = freq;
+	int shift = 31;
+	if ( freq > 0 )
+	{
+		shift = 13;
+		long f = (freq << 16) / sample_rate_;
+		while ( (f >>= 1) && --shift ) { }
+	}
+	bass_shift = shift;
+}
+
+void Blip_Buffer::end_frame( blip_time_t t )
+{
+	offset_ += t * factor_;
+	assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length
+}
+
+void Blip_Buffer::remove_silence( long count )
+{
+	assert( count <= samples_avail() ); // tried to remove more samples than available
+	offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
+
+long Blip_Buffer::count_samples( blip_time_t t ) const
+{
+	unsigned long last_sample  = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
+	unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
+	return (long) (last_sample - first_sample);
+}
+
+blip_time_t Blip_Buffer::count_clocks( long count ) const
+{
+	if ( count > buffer_size_ )
+		count = buffer_size_;
+	blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+	return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_);
+}
+
+void Blip_Buffer::remove_samples( long count )
+{
+	if ( count )
+	{
+		remove_silence( count );
+		
+		// copy remaining samples to beginning and clear old samples
+		long remain = samples_avail() + buffer_extra;
+		memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
+		memset( buffer_ + remain, 0, count * sizeof *buffer_ );
+	}
+}
+
+// Blip_Synth_
+
+Blip_Synth_::Blip_Synth_( short* p, int w ) :
+	impulses( p ),
+	width( w )
+{
+	volume_unit_ = 0.0;
+	kernel_unit = 0;
+	buf = 0;
+	last_amp = 0;
+	delta_factor = 0;
+}
+
+static double const pi = 3.1415926535897932384626433832795029;
+
+static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff )
+{
+	if ( cutoff >= 0.999 )
+		cutoff = 0.999;
+	
+	if ( treble < -300.0 )
+		treble = -300.0;
+	if ( treble > 5.0 )
+		treble = 5.0;
+	
+	double const maxh = 4096.0;
+	double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) );
+	double const pow_a_n = pow( rolloff, maxh - maxh * cutoff );
+	double const to_angle = pi / 2 / maxh / oversample;
+	for ( int i = 0; i < count; i++ )
+	{
+		double angle = ((i - count) * 2 + 1) * to_angle;
+		double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle );
+		double cos_nc_angle = cos( maxh * cutoff * angle );
+		double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle );
+		double cos_angle = cos( angle );
+		
+		c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
+		double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
+		double b = 2.0 - cos_angle - cos_angle;
+		double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
+		
+		out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d
+	}
+}
+
+void blip_eq_t::generate( float* out, int count ) const
+{
+	// lower cutoff freq for narrow kernels with their wider transition band
+	// (8 points->1.49, 16 points->1.15)
+	double oversample = blip_res * 2.25 / count + 0.85;
+	double half_rate = sample_rate * 0.5;
+	if ( cutoff_freq )
+		oversample = half_rate / cutoff_freq;
+	double cutoff = rolloff_freq * oversample / half_rate;
+	
+	gen_sinc( out, count, blip_res * oversample, treble, cutoff );
+	
+	// apply (half of) hamming window
+	double to_fraction = pi / (count - 1);
+	for ( int i = count; i--; )
+		out [i] *= 0.54 - 0.46 * cos( i * to_fraction );
+}
+
+void Blip_Synth_::adjust_impulse()
+{
+	// sum pairs for each phase and add error correction to end of first half
+	int const size = impulses_size();
+	for ( int p = blip_res; p-- >= blip_res / 2; )
+	{
+		int p2 = blip_res - 2 - p;
+		long error = kernel_unit;
+		for ( int i = 1; i < size; i += blip_res )
+		{
+			error -= impulses [i + p ];
+			error -= impulses [i + p2];
+		}
+		if ( p == p2 )
+			error /= 2; // phase = 0.5 impulse uses same half for both sides
+		impulses [size - blip_res + p] += error;
+		//printf( "error: %ld\n", error );
+	}
+	
+	//for ( int i = blip_res; i--; printf( "\n" ) )
+	//  for ( int j = 0; j < width / 2; j++ )
+	//      printf( "%5ld,", impulses [j * blip_res + i + 1] );
+}
+
+void Blip_Synth_::treble_eq( blip_eq_t const& eq )
+{
+	float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
+	
+	int const half_size = blip_res / 2 * (width - 1);
+	eq.generate( &fimpulse [blip_res], half_size );
+	
+	int i;
+	
+	// need mirror slightly past center for calculation
+	for ( i = blip_res; i--; )
+		fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i];
+	
+	// starts at 0
+	for ( i = 0; i < blip_res; i++ )
+		fimpulse [i] = 0.0f;
+	
+	// find rescale factor
+	double total = 0.0;
+	for ( i = 0; i < half_size; i++ )
+		total += fimpulse [blip_res + i];
+	
+	//double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
+	//double const base_unit = 37888.0; // allows treble to +5 dB
+	double const base_unit = 32768.0; // necessary for blip_unscaled to work
+	double rescale = base_unit / 2 / total;
+	kernel_unit = (long) base_unit;
+	
+	// integrate, first difference, rescale, convert to int
+	double sum = 0.0;
+	double next = 0.0;
+	int const impulses_size = this->impulses_size();
+	for ( i = 0; i < impulses_size; i++ )
+	{
+		impulses [i] = (short) floor( (next - sum) * rescale + 0.5 );
+		sum += fimpulse [i];
+		next += fimpulse [i + blip_res];
+	}
+	adjust_impulse();
+	
+	// volume might require rescaling
+	double vol = volume_unit_;
+	if ( vol )
+	{
+		volume_unit_ = 0.0;
+		volume_unit( vol );
+	}
+}
+
+void Blip_Synth_::volume_unit( double new_unit )
+{
+	if ( new_unit != volume_unit_ )
+	{
+		// use default eq if it hasn't been set yet
+		if ( !kernel_unit )
+			treble_eq( -8.0 );
+		
+		volume_unit_ = new_unit;
+		double factor = new_unit * (1L << blip_sample_bits) / kernel_unit;
+		
+		if ( factor > 0.0 )
+		{
+			int shift = 0;
+			
+			// if unit is really small, might need to attenuate kernel
+			while ( factor < 2.0 )
+			{
+				shift++;
+				factor *= 2.0;
+			}
+			
+			if ( shift )
+			{
+				kernel_unit >>= shift;
+				assert( kernel_unit > 0 ); // fails if volume unit is too low
+				
+				// keep values positive to avoid round-towards-zero of sign-preserving
+				// right shift for negative values
+				long offset = 0x8000 + (1 << (shift - 1));
+				long offset2 = 0x8000 >> shift;
+				for ( int i = impulses_size(); i--; )
+					impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2);
+				adjust_impulse();
+			}
+		}
+		delta_factor = (int) floor( factor + 0.5 );
+		//printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
+	}
+}
+
+long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo )
+{
+	long count = samples_avail();
+	if ( count > max_samples )
+		count = max_samples;
+	
+	if ( count )
+	{
+		int const sample_shift = blip_sample_bits - 16;
+		int const bass_shift = this->bass_shift;
+		long accum = reader_accum;
+		buf_t_* in = buffer_;
+		
+		if ( !stereo )
+		{
+			for ( long n = count; n--; )
+			{
+				long s = accum >> sample_shift;
+				accum -= accum >> bass_shift;
+				accum += *in++;
+				*out++ = (blip_sample_t) s;
+				
+				// clamp sample
+				if ( (blip_sample_t) s != s )
+					out [-1] = (blip_sample_t) (0x7FFF - (s >> 24));
+			}
+		}
+		else
+		{
+			for ( long n = count; n--; )
+			{
+				long s = accum >> sample_shift;
+				accum -= accum >> bass_shift;
+				accum += *in++;
+				*out = (blip_sample_t) s;
+				out += 2;
+				
+				// clamp sample
+				if ( (blip_sample_t) s != s )
+					out [-2] = (blip_sample_t) (0x7FFF - (s >> 24));
+			}
+		}
+		
+		reader_accum = accum;
+		remove_samples( count );
+	}
+	return count;
+}
+
+void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
+{
+	buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
+	
+	int const sample_shift = blip_sample_bits - 16;
+	int prev = 0;
+	while ( count-- )
+	{
+		long s = (long) *in++ << sample_shift;
+		*out += s - prev;
+		prev = s;
+		++out;
+	}
+	*out -= prev;
+}
+
--- a/Plugins/Input/console/Classic_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Classic_Emu.h"
-
-#include "Multi_Buffer.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Classic_Emu::Classic_Emu()
-{
-	buf = NULL;
-	stereo_buffer = NULL;
-}
-
-Classic_Emu::~Classic_Emu()
-{
-	delete stereo_buffer;
-}
-
-void Classic_Emu::set_equalizer( equalizer_t const& eq )
-{
-	Music_Emu::set_equalizer( eq );
-	update_eq( eq.treble );
-	if ( buf )
-		buf->bass_freq( equalizer().bass );
-}
-	
-blargg_err_t Classic_Emu::set_sample_rate( long sample_rate )
-{
-	if ( !buf )
-	{
-		if ( !stereo_buffer )
-			BLARGG_CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer );
-		buf = stereo_buffer;
-	}
-	
-	BLARGG_RETURN_ERR( buf->set_sample_rate( sample_rate, 1000 / 20 ) );
-	return Music_Emu::set_sample_rate( sample_rate );
-}
-
-void Classic_Emu::mute_voices( int mask )
-{
-	require( buf ); // set_sample_rate() must have been called
-	
-	Music_Emu::mute_voices( mask );
-	for ( int i = voice_count(); i--; )
-	{
-		if ( mask & (1 << i) )
-		{
-			set_voice( i, NULL, NULL, 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 new_clock_rate )
-{
-	require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet
-	
-	clock_rate = new_clock_rate;
-	buf->clock_rate( clock_rate );
-	BLARGG_RETURN_ERR( buf->set_channel_count( voice_count() ) );
-	set_equalizer( equalizer() );
-	remute_voices();
-	return blargg_success;
-}
-
-void Classic_Emu::start_track( int track )
-{
-	Music_Emu::start_track( track );
-	buf->clear();
-}
-
-blip_time_t Classic_Emu::run_clocks( blip_time_t t, bool* )
-{
-	assert( false );
-	return t;
-}
-
-blip_time_t Classic_Emu::run( int msec, bool* added_stereo )
-{
-	return run_clocks( (long) msec * clock_rate / 1000, added_stereo );
-}
-
-void Classic_Emu::play( long count, sample_t* out )
-{
-	require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet
-	
-	long remain = count;
-	while ( remain )
-	{
-		remain -= buf->read_samples( &out [count - remain], remain );
-		if ( remain )
-		{
-			bool added_stereo = false;
-			blip_time_t clocks_emulated = run( buf->length(), &added_stereo );
-			buf->end_frame( clocks_emulated, added_stereo );
-		}
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Classic_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,117 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Classic_Emu.h"
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Classic_Emu::Classic_Emu()
+{
+	buf = NULL;
+	stereo_buffer = NULL;
+}
+
+Classic_Emu::~Classic_Emu()
+{
+	delete stereo_buffer;
+}
+
+void Classic_Emu::set_equalizer( equalizer_t const& eq )
+{
+	Music_Emu::set_equalizer( eq );
+	update_eq( eq.treble );
+	if ( buf )
+		buf->bass_freq( equalizer().bass );
+}
+	
+blargg_err_t Classic_Emu::set_sample_rate( long sample_rate )
+{
+	if ( !buf )
+	{
+		if ( !stereo_buffer )
+			BLARGG_CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer );
+		buf = stereo_buffer;
+	}
+	
+	BLARGG_RETURN_ERR( buf->set_sample_rate( sample_rate, 1000 / 20 ) );
+	return Music_Emu::set_sample_rate( sample_rate );
+}
+
+void Classic_Emu::mute_voices( int mask )
+{
+	require( buf ); // set_sample_rate() must have been called
+	
+	Music_Emu::mute_voices( mask );
+	for ( int i = voice_count(); i--; )
+	{
+		if ( mask & (1 << i) )
+		{
+			set_voice( i, NULL, NULL, 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 new_clock_rate )
+{
+	require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet
+	
+	clock_rate = new_clock_rate;
+	buf->clock_rate( clock_rate );
+	BLARGG_RETURN_ERR( buf->set_channel_count( voice_count() ) );
+	set_equalizer( equalizer() );
+	remute_voices();
+	return blargg_success;
+}
+
+void Classic_Emu::start_track( int track )
+{
+	Music_Emu::start_track( track );
+	buf->clear();
+}
+
+blip_time_t Classic_Emu::run_clocks( blip_time_t t, bool* )
+{
+	assert( false );
+	return t;
+}
+
+blip_time_t Classic_Emu::run( int msec, bool* added_stereo )
+{
+	return run_clocks( (long) msec * clock_rate / 1000, added_stereo );
+}
+
+void Classic_Emu::play( long count, sample_t* out )
+{
+	require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet
+	
+	long remain = count;
+	while ( remain )
+	{
+		remain -= buf->read_samples( &out [count - remain], remain );
+		if ( remain )
+		{
+			bool added_stereo = false;
+			blip_time_t clocks_emulated = run( buf->length(), &added_stereo );
+			buf->end_frame( clocks_emulated, added_stereo );
+		}
+	}
+}
+
--- a/Plugins/Input/console/Dual_Resampler.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Dual_Resampler.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-int const resampler_extra = 256;
-
-Dual_Resampler::Dual_Resampler()
-{
-}
-
-Dual_Resampler::~Dual_Resampler()
-{
-}
-
-blargg_err_t Dual_Resampler::resize( int pairs )
-{
-	BLARGG_RETURN_ERR( sample_buf.resize( pairs * 2 ) );
-	buf_pos = sample_buf.size();
-	oversamples_per_frame = int (pairs * resampler.ratio()) * 2 + 2;
-	return resampler.buffer_size( oversamples_per_frame + resampler_extra );
-}
-
-void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, sample_t* out )
-{
-	long pair_count = sample_buf.size() >> 1;
-	blip_time_t blip_time = blip_buf.count_clocks( pair_count );
-	int sample_count = oversamples_per_frame - resampler.written();
-	
-	int new_count = play_frame( blip_time, sample_count, resampler.buffer() );
-	assert( unsigned (new_count - sample_count) < resampler_extra );
-	
-	blip_buf.end_frame( blip_time );
-	assert( blip_buf.samples_avail() == pair_count );
-	
-	resampler.write( new_count );
-	
-	long count = resampler.read( sample_buf.begin(), sample_buf.size() );
-	assert( count == (long) sample_buf.size() );
-	
-	mix_samples( blip_buf, out );
-	blip_buf.remove_samples( pair_count );
-}
-
-void Dual_Resampler::play( long count, sample_t* out, Blip_Buffer& blip_buf )
-{
-	// empty extra buffer
-	long remain = sample_buf.size() - buf_pos;
-	if ( remain )
-	{
-		if ( remain > count )
-			remain = count;
-		count -= remain;
-		memcpy( out, &sample_buf [buf_pos], remain * sizeof *out );
-		out += remain;
-		buf_pos += remain;
-	}
-	
-	// entire frames
-	while ( count >= (long) sample_buf.size() )
-	{
-		play_frame_( blip_buf, out );
-		out += sample_buf.size();
-		count -= sample_buf.size();
-	}
-	
-	// extra
-	if ( count )
-	{
-		play_frame_( blip_buf, sample_buf.begin() );
-		buf_pos = count;
-		memcpy( out, sample_buf.begin(), count * sizeof *out );
-		out += count;
-	}
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, sample_t* out )
-{
-	Blip_Reader sn;
-	int bass = sn.begin( blip_buf );
-	const sample_t* in = sample_buf.begin();
-	
-	for ( int n = sample_buf.size() >> 1; n--; )
-	{
-		int s = sn.read();
-		long l = (long) in [0] * 2 + s;
-		sn.next( bass );
-		long r = in [1];
-		if ( (BOOST::int16_t) l != l )
-			l = 0x7FFF - (l >> 24);
-		r = r * 2 + s;
-		in += 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/Dual_Resampler.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,120 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Dual_Resampler.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+int const resampler_extra = 256;
+
+Dual_Resampler::Dual_Resampler()
+{
+}
+
+Dual_Resampler::~Dual_Resampler()
+{
+}
+
+blargg_err_t Dual_Resampler::resize( int pairs )
+{
+	BLARGG_RETURN_ERR( sample_buf.resize( pairs * 2 ) );
+	buf_pos = sample_buf.size();
+	oversamples_per_frame = int (pairs * resampler.ratio()) * 2 + 2;
+	return resampler.buffer_size( oversamples_per_frame + resampler_extra );
+}
+
+void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, sample_t* out )
+{
+	long pair_count = sample_buf.size() >> 1;
+	blip_time_t blip_time = blip_buf.count_clocks( pair_count );
+	int sample_count = oversamples_per_frame - resampler.written();
+	
+	int new_count = play_frame( blip_time, sample_count, resampler.buffer() );
+	assert( unsigned (new_count - sample_count) < resampler_extra );
+	
+	blip_buf.end_frame( blip_time );
+	assert( blip_buf.samples_avail() == pair_count );
+	
+	resampler.write( new_count );
+	
+	long count = resampler.read( sample_buf.begin(), sample_buf.size() );
+	assert( count == (long) sample_buf.size() );
+	
+	mix_samples( blip_buf, out );
+	blip_buf.remove_samples( pair_count );
+}
+
+void Dual_Resampler::play( long count, sample_t* out, Blip_Buffer& blip_buf )
+{
+	// empty extra buffer
+	long remain = sample_buf.size() - buf_pos;
+	if ( remain )
+	{
+		if ( remain > count )
+			remain = count;
+		count -= remain;
+		memcpy( out, &sample_buf [buf_pos], remain * sizeof *out );
+		out += remain;
+		buf_pos += remain;
+	}
+	
+	// entire frames
+	while ( count >= (long) sample_buf.size() )
+	{
+		play_frame_( blip_buf, out );
+		out += sample_buf.size();
+		count -= sample_buf.size();
+	}
+	
+	// extra
+	if ( count )
+	{
+		play_frame_( blip_buf, sample_buf.begin() );
+		buf_pos = count;
+		memcpy( out, sample_buf.begin(), count * sizeof *out );
+		out += count;
+	}
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, sample_t* out )
+{
+	Blip_Reader sn;
+	int bass = sn.begin( blip_buf );
+	const sample_t* in = sample_buf.begin();
+	
+	for ( int n = sample_buf.size() >> 1; n--; )
+	{
+		int s = sn.read();
+		long l = (long) in [0] * 2 + s;
+		sn.next( bass );
+		long r = in [1];
+		if ( (BOOST::int16_t) l != l )
+			l = 0x7FFF - (l >> 24);
+		r = r * 2 + s;
+		in += 2;
+		out [0] = l;
+		out [1] = r;
+		out += 2;
+		if ( (BOOST::int16_t) r != r )
+			out [-1] = 0x7FFF - (r >> 24);
+	}
+	
+	sn.end( blip_buf );
+}
+
--- a/Plugins/Input/console/Fir_Resampler.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Fir_Resampler.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-
-/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-// to do: fix problems with rolloff < 0.99 or so, and rolloff == 1.0, and related problems
-
-// Sinc impulse genertor
-
-const bool show_impulse = 0;
-
-static const double pi = 3.1415926535897932384626433832795029L;
-
-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 Sinc>
-void gen_sinc( int width, double offset, double spacing, int count, double scale, short* 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 )
-		{
-			double window = cos( pi * w ) * 0.5 + 0.5;
-			y = sinc( a ) * window;
-		}
-		
-		*p++ = (short) (y * scale);
-		a += step;
-	}
-}
-
-static double plain_sinc( double a )
-{
-	return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a;
-}
-
-// Fir_Resampler
-
-Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) :
-	width_( width ),
-	write_offset( width * stereo - stereo ),
-	impulses( impulses_ )
-{
-	write_pos = NULL;
-	res = 1;
-	imp = 0;
-	skip_bits = 0;
-	step = stereo;
-	ratio_ = 1.0;
-}
-
-Fir_Resampler_::~Fir_Resampler_()
-{
-}
-
-void Fir_Resampler_::clear()
-{
-	imp = 0;
-	if ( buf.size() )
-	{
-		write_pos = &buf [write_offset];
-		memset( buf.begin(), 0, write_offset * sizeof buf [0] );
-	}
-}
-
-blargg_err_t Fir_Resampler_::buffer_size( int new_size )
-{
-	BLARGG_RETURN_ERR( buf.resize( new_size + write_offset ) );
-	clear();
-	return blargg_success;
-}
-	
-double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain )
-{
-	ratio_ = new_factor;
-	
-	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 = stereo * (int) floor( fstep );
-	
-	ratio_ = fstep;
-	fstep = fmod( fstep, 1.0 );
-	
-	double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
-	double pos = 0.0;
-	input_per_cycle = 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 * gain * filter), impulses + i * width_, dsf );
-		
-		if ( show_impulse )
-		{
-			for ( int j = 0; j < width_; j++ )
-				printf( "%d ", (int) impulses [i * width_ + j] );
-			printf( "\n" );
-		}
-		
-		pos += fstep;
-		input_per_cycle += step;
-		if ( pos >= 0.9999999 )
-		{
-			pos -= 1.0;
-			skip_bits |= 1 << i;
-			input_per_cycle++;
-		}
-	}
-	
-	if ( show_impulse )
-	{
-		printf( "skip = %8lX\n", (long) skip_bits );
-		printf( "step = %d\n", step );
-	}
-	
-	clear();
-	
-	return ratio_;
-}
-
-int Fir_Resampler_::input_needed( long output_count ) const
-{
-	long input_count = 0;
-	
-	unsigned long skip = skip_bits >> imp;
-	int remain = res - imp;
-	while ( (output_count -= 2) > 0 )
-	{
-		input_count += step + (skip & 1) * stereo;
-		skip >>= 1;
-		if ( !--remain )
-		{
-			skip = skip_bits;
-			remain = res;
-		}
-		output_count -= 2;
-	}
-	
-	long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]);
-	if ( input_extra < 0 )
-		input_extra = 0;
-	return input_extra;
-}
-
-int Fir_Resampler_::avail_( long input_count ) const
-{
-	int cycle_count = input_count / input_per_cycle;
-	int output_count = cycle_count * res * stereo;
-	input_count -= cycle_count * input_per_cycle;
-	
-	unsigned long skip = skip_bits >> imp;
-	int remain = res - imp;
-	while ( input_count >= 0 )
-	{
-		input_count -= step + (skip & 1) * stereo;
-		skip >>= 1;
-		if ( !--remain )
-		{
-			skip = skip_bits;
-			remain = res;
-		}
-		output_count += 2;
-	}
-	return output_count;
-}
-
-int Fir_Resampler_::skip_input( long count )
-{
-	int remain = write_pos - buf.begin();
-	int avail = remain - width_ * stereo;
-	if ( avail < 0 ) avail = 0; // inserted
-	if ( count > avail )
-		count = avail;
-	
-	remain -= count;
-	write_pos = &buf [remain];
-	memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
-	
-	return count;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Fir_Resampler.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,254 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Fir_Resampler.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// to do: fix problems with rolloff < 0.99 or so, and rolloff == 1.0, and related problems
+
+// Sinc impulse genertor
+
+const bool show_impulse = 0;
+
+static const double pi = 3.1415926535897932384626433832795029L;
+
+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 Sinc>
+void gen_sinc( int width, double offset, double spacing, int count, double scale, short* 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 )
+		{
+			double window = cos( pi * w ) * 0.5 + 0.5;
+			y = sinc( a ) * window;
+		}
+		
+		*p++ = (short) (y * scale);
+		a += step;
+	}
+}
+
+static double plain_sinc( double a )
+{
+	return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a;
+}
+
+// Fir_Resampler
+
+Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) :
+	width_( width ),
+	write_offset( width * stereo - stereo ),
+	impulses( impulses_ )
+{
+	write_pos = NULL;
+	res = 1;
+	imp = 0;
+	skip_bits = 0;
+	step = stereo;
+	ratio_ = 1.0;
+}
+
+Fir_Resampler_::~Fir_Resampler_()
+{
+}
+
+void Fir_Resampler_::clear()
+{
+	imp = 0;
+	if ( buf.size() )
+	{
+		write_pos = &buf [write_offset];
+		memset( buf.begin(), 0, write_offset * sizeof buf [0] );
+	}
+}
+
+blargg_err_t Fir_Resampler_::buffer_size( int new_size )
+{
+	BLARGG_RETURN_ERR( buf.resize( new_size + write_offset ) );
+	clear();
+	return blargg_success;
+}
+	
+double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain )
+{
+	ratio_ = new_factor;
+	
+	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 = stereo * (int) floor( fstep );
+	
+	ratio_ = fstep;
+	fstep = fmod( fstep, 1.0 );
+	
+	double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
+	double pos = 0.0;
+	input_per_cycle = 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 * gain * filter), impulses + i * width_, dsf );
+		
+		if ( show_impulse )
+		{
+			for ( int j = 0; j < width_; j++ )
+				printf( "%d ", (int) impulses [i * width_ + j] );
+			printf( "\n" );
+		}
+		
+		pos += fstep;
+		input_per_cycle += step;
+		if ( pos >= 0.9999999 )
+		{
+			pos -= 1.0;
+			skip_bits |= 1 << i;
+			input_per_cycle++;
+		}
+	}
+	
+	if ( show_impulse )
+	{
+		printf( "skip = %8lX\n", (long) skip_bits );
+		printf( "step = %d\n", step );
+	}
+	
+	clear();
+	
+	return ratio_;
+}
+
+int Fir_Resampler_::input_needed( long output_count ) const
+{
+	long input_count = 0;
+	
+	unsigned long skip = skip_bits >> imp;
+	int remain = res - imp;
+	while ( (output_count -= 2) > 0 )
+	{
+		input_count += step + (skip & 1) * stereo;
+		skip >>= 1;
+		if ( !--remain )
+		{
+			skip = skip_bits;
+			remain = res;
+		}
+		output_count -= 2;
+	}
+	
+	long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]);
+	if ( input_extra < 0 )
+		input_extra = 0;
+	return input_extra;
+}
+
+int Fir_Resampler_::avail_( long input_count ) const
+{
+	int cycle_count = input_count / input_per_cycle;
+	int output_count = cycle_count * res * stereo;
+	input_count -= cycle_count * input_per_cycle;
+	
+	unsigned long skip = skip_bits >> imp;
+	int remain = res - imp;
+	while ( input_count >= 0 )
+	{
+		input_count -= step + (skip & 1) * stereo;
+		skip >>= 1;
+		if ( !--remain )
+		{
+			skip = skip_bits;
+			remain = res;
+		}
+		output_count += 2;
+	}
+	return output_count;
+}
+
+int Fir_Resampler_::skip_input( long count )
+{
+	int remain = write_pos - buf.begin();
+	int avail = remain - width_ * stereo;
+	if ( avail < 0 ) avail = 0; // inserted
+	if ( count > avail )
+		count = avail;
+	
+	remain -= count;
+	write_pos = &buf [remain];
+	memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
+	
+	return count;
+}
+
--- a/Plugins/Input/console/Gb_Apu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +0,0 @@
-
-// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
-
-#include "Gb_Apu.h"
-
-#include <string.h>
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-int const vol_reg    = 0xFF24;
-int const status_reg = 0xFF26;
-
-Gb_Apu::Gb_Apu()
-{
-	square1.synth = &square_synth;
-	square2.synth = &square_synth;
-	wave.synth  = &other_synth;
-	noise.synth = &other_synth;
-	
-	oscs [0] = &square1;
-	oscs [1] = &square2;
-	oscs [2] = &wave;
-	oscs [3] = &noise;
-	
-	for ( int i = 0; i < osc_count; i++ )
-	{
-		Gb_Osc& osc = *oscs [i];
-		osc.regs = &regs [i * 5];
-		osc.output = NULL;
-		osc.outputs [0] = NULL;
-		osc.outputs [1] = NULL;
-		osc.outputs [2] = NULL;
-		osc.outputs [3] = NULL;
-	}
-	
-	volume( 1.0 );
-	reset();
-}
-
-Gb_Apu::~Gb_Apu()
-{
-}
-
-void Gb_Apu::treble_eq( const blip_eq_t& eq )
-{
-	square_synth.treble_eq( eq );
-	other_synth.treble_eq( eq );
-}
-
-void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
-{
-	require( (unsigned) index < osc_count );
-	require( (center && left && right) || (!center && !left && !right) );
-	Gb_Osc& osc = *oscs [index];
-	osc.outputs [1] = right;
-	osc.outputs [2] = left;
-	osc.outputs [3] = center;
-	osc.output = osc.outputs [osc.output_select];
-}
-
-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::update_volume()
-{
-	// to do: doesn't handle differing left/right global volume
-	int data = regs [vol_reg - start_addr];
-	double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit;
-	square_synth.volume( vol );
-	other_synth.volume( vol );
-}
-
-static unsigned char const powerup_regs [0x30] = {
-	0x80,0x3F,0x00,0xFF,0xBF, // square 1
-	0xFF,0x3F,0x00,0xFF,0xBF, // square 2
-	0x7F,0xFF,0x9F,0xFF,0xBF, // wave
-	0xFF,0xFF,0x00,0x00,0xBF, // noise
-	0x00, // left/right enables
-	0x77, // master volume
-	0x80, // power
-	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
-	0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
-	0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA
-};
-
-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();
-	noise.bits = 1;
-	wave.wave_pos = 0;
-	
-	// avoid click at beginning
-	regs [vol_reg - start_addr] = 0x77;
-	update_volume();
-	
-	regs [status_reg - start_addr] = 0x01; // force power
-	write_register( 0, status_reg, 0x00 );
-}
-
-// to do: remove
-static unsigned long abs_time;
-
-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 )
-			{
-				int playing = false;
-				if ( osc.enabled && osc.volume &&
-						(!(osc.regs [4] & osc.len_enabled_mask) || osc.length) )
-					playing = -1;
-				if ( osc.output != osc.outputs [3] )
-					stereo_found = true;
-				switch ( i )
-				{
-				case 0: square1.run( last_time, time, playing ); break;
-				case 1: square2.run( last_time, time, playing ); break;
-				case 2: wave   .run( last_time, time, playing ); break;
-				case 3: noise  .run( last_time, time, playing ); break;
-				}
-			}
-		}
-		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 )
-{
-	if ( end_time > last_time )
-		run_until( end_time );
-	
-	abs_time += end_time;
-	
-	assert( next_frame_time >= end_time );
-	next_frame_time -= end_time;
-	
-	assert( last_time >= end_time );
-	last_time -= end_time;
-	
-	bool result = stereo_found;
-	stereo_found = false;
-	return result;
-}
-
-void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data )
-{
-	require( (unsigned) data < 0x100 );
-	
-	int reg = addr - start_addr;
-	if ( (unsigned) reg >= register_count )
-		return;
-	
-	run_until( time );
-	
-	int old_reg = regs [reg];
-	regs [reg] = data;
-	
-	if ( addr < vol_reg )
-	{
-		write_osc( reg / 5, reg, data );
-	}
-	else if ( addr == vol_reg && data != old_reg ) // global volume
-	{
-		// return all oscs to 0
-		for ( int i = 0; i < osc_count; i++ )
-		{
-			Gb_Osc& osc = *oscs [i];
-			int amp = osc.last_amp;
-			osc.last_amp = 0;
-			if ( amp && osc.enabled && osc.output )
-				other_synth.offset( time, -amp, osc.output );
-		}
-		
-		if ( wave.outputs [3] )
-			other_synth.offset( time, 30, wave.outputs [3] );
-		
-		update_volume();
-		
-		if ( wave.outputs [3] )
-			other_synth.offset( time, -30, wave.outputs [3] );
-		
-		// oscs will update with new amplitude when next run
-	}
-	else if ( addr == 0xFF25 || addr == status_reg )
-	{
-		int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
-		int flags = regs [0xFF25 - start_addr] & mask;
-		
-		// left/right assignments
-		for ( int i = 0; i < osc_count; i++ )
-		{
-			Gb_Osc& osc = *oscs [i];
-			osc.enabled &= mask;
-			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 )
-			{
-				int amp = osc.last_amp;
-				osc.last_amp = 0;
-				if ( amp && old_output )
-					other_synth.offset( time, -amp, old_output );
-			}
-		}
-		
-		if ( addr == status_reg && data != old_reg )
-		{
-			if ( !(data & 0x80) )
-			{
-				for ( int i = 0; i < (int) sizeof powerup_regs; i++ )
-				{
-					if ( i != status_reg - start_addr )
-						write_register( time, i + start_addr, powerup_regs [i] );
-				}
-			}
-			else
-			{
-				//dprintf( "APU powered on\n" );
-			}
-		}
-	}
-	else if ( addr >= 0xFF30 )
-	{
-		
-		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 )
-{
-	run_until( time );
-	
-	int index = addr - start_addr;
-	require( (unsigned) index < register_count );
-	int data = regs [index];
-	
-	if ( addr == status_reg )
-	{
-		data = (data & 0x80) | 0x70;
-		for ( int i = 0; i < osc_count; i++ )
-		{
-			const Gb_Osc& osc = *oscs [i];
-			if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) )
-				data |= 1 << i;
-		}
-	}
-	
-	return data;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Apu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,311 @@
+
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+int const vol_reg    = 0xFF24;
+int const status_reg = 0xFF26;
+
+Gb_Apu::Gb_Apu()
+{
+	square1.synth = &square_synth;
+	square2.synth = &square_synth;
+	wave.synth  = &other_synth;
+	noise.synth = &other_synth;
+	
+	oscs [0] = &square1;
+	oscs [1] = &square2;
+	oscs [2] = &wave;
+	oscs [3] = &noise;
+	
+	for ( int i = 0; i < osc_count; i++ )
+	{
+		Gb_Osc& osc = *oscs [i];
+		osc.regs = &regs [i * 5];
+		osc.output = NULL;
+		osc.outputs [0] = NULL;
+		osc.outputs [1] = NULL;
+		osc.outputs [2] = NULL;
+		osc.outputs [3] = NULL;
+	}
+	
+	volume( 1.0 );
+	reset();
+}
+
+Gb_Apu::~Gb_Apu()
+{
+}
+
+void Gb_Apu::treble_eq( const blip_eq_t& eq )
+{
+	square_synth.treble_eq( eq );
+	other_synth.treble_eq( eq );
+}
+
+void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+	require( (unsigned) index < osc_count );
+	require( (center && left && right) || (!center && !left && !right) );
+	Gb_Osc& osc = *oscs [index];
+	osc.outputs [1] = right;
+	osc.outputs [2] = left;
+	osc.outputs [3] = center;
+	osc.output = osc.outputs [osc.output_select];
+}
+
+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::update_volume()
+{
+	// to do: doesn't handle differing left/right global volume
+	int data = regs [vol_reg - start_addr];
+	double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit;
+	square_synth.volume( vol );
+	other_synth.volume( vol );
+}
+
+static unsigned char const powerup_regs [0x30] = {
+	0x80,0x3F,0x00,0xFF,0xBF, // square 1
+	0xFF,0x3F,0x00,0xFF,0xBF, // square 2
+	0x7F,0xFF,0x9F,0xFF,0xBF, // wave
+	0xFF,0xFF,0x00,0x00,0xBF, // noise
+	0x00, // left/right enables
+	0x77, // master volume
+	0x80, // power
+	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+	0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
+	0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA
+};
+
+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();
+	noise.bits = 1;
+	wave.wave_pos = 0;
+	
+	// avoid click at beginning
+	regs [vol_reg - start_addr] = 0x77;
+	update_volume();
+	
+	regs [status_reg - start_addr] = 0x01; // force power
+	write_register( 0, status_reg, 0x00 );
+}
+
+// to do: remove
+static unsigned long abs_time;
+
+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 )
+			{
+				int playing = false;
+				if ( osc.enabled && osc.volume &&
+						(!(osc.regs [4] & osc.len_enabled_mask) || osc.length) )
+					playing = -1;
+				if ( osc.output != osc.outputs [3] )
+					stereo_found = true;
+				switch ( i )
+				{
+				case 0: square1.run( last_time, time, playing ); break;
+				case 1: square2.run( last_time, time, playing ); break;
+				case 2: wave   .run( last_time, time, playing ); break;
+				case 3: noise  .run( last_time, time, playing ); break;
+				}
+			}
+		}
+		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 )
+{
+	if ( end_time > last_time )
+		run_until( end_time );
+	
+	abs_time += end_time;
+	
+	assert( next_frame_time >= end_time );
+	next_frame_time -= end_time;
+	
+	assert( last_time >= end_time );
+	last_time -= end_time;
+	
+	bool result = stereo_found;
+	stereo_found = false;
+	return result;
+}
+
+void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data )
+{
+	require( (unsigned) data < 0x100 );
+	
+	int reg = addr - start_addr;
+	if ( (unsigned) reg >= register_count )
+		return;
+	
+	run_until( time );
+	
+	int old_reg = regs [reg];
+	regs [reg] = data;
+	
+	if ( addr < vol_reg )
+	{
+		write_osc( reg / 5, reg, data );
+	}
+	else if ( addr == vol_reg && data != old_reg ) // global volume
+	{
+		// return all oscs to 0
+		for ( int i = 0; i < osc_count; i++ )
+		{
+			Gb_Osc& osc = *oscs [i];
+			int amp = osc.last_amp;
+			osc.last_amp = 0;
+			if ( amp && osc.enabled && osc.output )
+				other_synth.offset( time, -amp, osc.output );
+		}
+		
+		if ( wave.outputs [3] )
+			other_synth.offset( time, 30, wave.outputs [3] );
+		
+		update_volume();
+		
+		if ( wave.outputs [3] )
+			other_synth.offset( time, -30, wave.outputs [3] );
+		
+		// oscs will update with new amplitude when next run
+	}
+	else if ( addr == 0xFF25 || addr == status_reg )
+	{
+		int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
+		int flags = regs [0xFF25 - start_addr] & mask;
+		
+		// left/right assignments
+		for ( int i = 0; i < osc_count; i++ )
+		{
+			Gb_Osc& osc = *oscs [i];
+			osc.enabled &= mask;
+			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 )
+			{
+				int amp = osc.last_amp;
+				osc.last_amp = 0;
+				if ( amp && old_output )
+					other_synth.offset( time, -amp, old_output );
+			}
+		}
+		
+		if ( addr == status_reg && data != old_reg )
+		{
+			if ( !(data & 0x80) )
+			{
+				for ( int i = 0; i < (int) sizeof powerup_regs; i++ )
+				{
+					if ( i != status_reg - start_addr )
+						write_register( time, i + start_addr, powerup_regs [i] );
+				}
+			}
+			else
+			{
+				//dprintf( "APU powered on\n" );
+			}
+		}
+	}
+	else if ( addr >= 0xFF30 )
+	{
+		
+		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 )
+{
+	run_until( time );
+	
+	int index = addr - start_addr;
+	require( (unsigned) index < register_count );
+	int data = regs [index];
+	
+	if ( addr == status_reg )
+	{
+		data = (data & 0x80) | 0x70;
+		for ( int i = 0; i < osc_count; i++ )
+		{
+			const Gb_Osc& osc = *oscs [i];
+			if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) )
+				data |= 1 << i;
+		}
+	}
+	
+	return data;
+}
+
--- a/Plugins/Input/console/Gb_Cpu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1113 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Gb_Cpu.h"
-
-#include <string.h>
-#include <limits.h>
-
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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)
-
-#if BLARGG_NONPORTABLE
-	#define PAGE_OFFSET( addr ) (addr)
-#else
-	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
-#endif
-
-Gb_Cpu::Gb_Cpu( Gbs_Emu* gbs_emu )
-{
-	callback_data = gbs_emu;
-	rst_base = 0;
-	reset();
-}
-
-inline void Gb_Cpu::set_code_page( int i, uint8_t const* p )
-{
-	code_map [i] = p - PAGE_OFFSET( i * page_size );
-}
-
-void Gb_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write )
-{
-	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++ )
-	{
-		set_code_page( i, (uint8_t*) unmapped_code_page );
-		data_reader [i] = read;
-		data_writer [i] = write;
-	}
-}
-
-void Gb_Cpu::map_code( gb_addr_t start, unsigned long size, const void* data )
-{
-	// address range must begin and end on page boundaries
-	require( start % page_size == 0 );
-	require( size % page_size == 0 );
-	
-	unsigned first_page = start / page_size;
-	for ( unsigned i = size / page_size; i--; )
-		set_code_page( 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 )
-{
-	// address range must begin and end on page boundaries
-	require( start % page_size == 0 );
-	require( size % page_size == 0 );
-	
-	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] [PAGE_OFFSET( addr )])
-#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
-
-int Gb_Cpu::read( gb_addr_t addr )
-{
-	return READ( addr );
-}
-
-void Gb_Cpu::write( gb_addr_t addr, int data )
-{
-	WRITE( addr, data );
-}
-
-BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr )
-{
-	return (uint8_t*) &READ_PROG( addr );
-}
-
-#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;
-	
-	// to do: use cycle table
-	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;
-	
-loop:
-	
-	int new_remain = remain_ - cycles_per_instruction;
-	
-	check( (unsigned) pc < 0x10000 );
-	check( (unsigned) sp < 0x10000 );
-	check( (flags & ~0xf0) == 0 );
-	
-	uint8_t const* page = code_map [pc >> page_bits];
-	unsigned op = page [PAGE_OFFSET( pc )];
-	unsigned data = page [PAGE_OFFSET( pc ) + 1];
-	check( op == 0xC9 || &READ_PROG( pc + 1 ) == &page [PAGE_OFFSET( pc ) + 1] );
-	
-	pc++;
-	
-	remain_ = new_remain;
-	if ( new_remain <= 0 )
-		goto stop;
-	
-	switch ( op )
-	{
-
-#define BRANCH( cond )          \
-{                               \
-	pc++;                       \
-	int offset = (BOOST::int8_t) data;  \
-	if ( !(cond) ) goto loop;   \
-	pc += offset;               \
-	goto loop;                  \
-}
-
-// Most Common
-
-	case 0x20: // JR NZ
-		BRANCH( !(flags & z_flag) )
-	
-	case 0x21: // LD HL,IMM (common)
-		rp.hl = READ_PROG16( pc );
-		pc += 2;
-		goto loop;
-	
-	case 0x28: // JR Z
-		BRANCH( flags & z_flag )
-	
-	{
-		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 = (sp - 1) & 0xFFFF;
-		WRITE( sp, data >> 8 );
-		sp = (sp - 1) & 0xFFFF;
-		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) & 0xFFFF );
-		sp = (sp + 2) & 0xFFFF;
-		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;
-		pc++;
-		goto loop;
-	
-	case 0x0E: // LD C,IMM
-		rg.c = data;
-		pc++;
-		goto loop;
-	
-	case 0x16: // LD D,IMM
-		rg.d = data;
-		pc++;
-		goto loop;
-	
-	case 0x1E: // LD E,IMM
-		rg.e = data;
-		pc++;
-		goto loop;
-	
-	case 0x26: // LD H,IMM
-		rg.h = data;
-		pc++;
-		goto loop;
-	
-	case 0x2E: // LD L,IMM
-		rg.l = data;
-		pc++;
-		goto loop;
-	
-	case 0x36: // LD (HL),IMM
-		WRITE( rp.hl, data );
-		pc++;
-		goto loop;
-	
-	case 0x3E: // LD A,IMM
-		rg.a = data;
-		pc++;
-		goto 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 = (sp + 1) & 0xFFFF;
-		goto loop;
-
-	case 0x0B: // DEC BC
-	case 0x1B: // DEC DE
-	case 0x2B: // DEC HL
-		r16 [op >> 4]--;
-		goto loop;
-	
-	case 0x3B: // DEC SP
-		sp = (sp - 1) & 0xFFFF;
-		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); // 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); // sign-extend to 16 bits
-		pc++;
-		flags = 0;
-		temp += sp;
-		prev = sp;
-		sp = temp & 0xffff;
-		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) & 0xFFFF );
-		sp = (sp + 2) & 0xFFFF;
-		goto loop;
-	}
-	
-	case 0xF1: // POP FA
-		rg.a = READ( sp );
-		flags = READ( (sp + 1) & 0xFFFF ) & 0xf0;
-		sp = (sp + 2) & 0xFFFF;
-		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
-		BRANCH( true )
-	
-	case 0x30: // JR NC
-		BRANCH( !(flags & c_flag) )
-	
-	case 0x38: // JR C
-		BRANCH( flags & c_flag )
-	
-	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;
-	}
-	
-	// If this fails then the case above is missing an opcode
-	assert( false );
-	
-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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,1113 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Gb_Cpu.h"
+
+#include <string.h>
+#include <limits.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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)
+
+#if BLARGG_NONPORTABLE
+	#define PAGE_OFFSET( addr ) (addr)
+#else
+	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
+Gb_Cpu::Gb_Cpu( Gbs_Emu* gbs_emu )
+{
+	callback_data = gbs_emu;
+	rst_base = 0;
+	reset();
+}
+
+inline void Gb_Cpu::set_code_page( int i, uint8_t const* p )
+{
+	code_map [i] = p - PAGE_OFFSET( i * page_size );
+}
+
+void Gb_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write )
+{
+	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++ )
+	{
+		set_code_page( i, (uint8_t*) unmapped_code_page );
+		data_reader [i] = read;
+		data_writer [i] = write;
+	}
+}
+
+void Gb_Cpu::map_code( gb_addr_t start, unsigned long size, const void* data )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		set_code_page( 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 )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	
+	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] [PAGE_OFFSET( addr )])
+#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
+
+int Gb_Cpu::read( gb_addr_t addr )
+{
+	return READ( addr );
+}
+
+void Gb_Cpu::write( gb_addr_t addr, int data )
+{
+	WRITE( addr, data );
+}
+
+BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr )
+{
+	return (uint8_t*) &READ_PROG( addr );
+}
+
+#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;
+	
+	// to do: use cycle table
+	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;
+	
+loop:
+	
+	int new_remain = remain_ - cycles_per_instruction;
+	
+	check( (unsigned) pc < 0x10000 );
+	check( (unsigned) sp < 0x10000 );
+	check( (flags & ~0xf0) == 0 );
+	
+	uint8_t const* page = code_map [pc >> page_bits];
+	unsigned op = page [PAGE_OFFSET( pc )];
+	unsigned data = page [PAGE_OFFSET( pc ) + 1];
+	check( op == 0xC9 || &READ_PROG( pc + 1 ) == &page [PAGE_OFFSET( pc ) + 1] );
+	
+	pc++;
+	
+	remain_ = new_remain;
+	if ( new_remain <= 0 )
+		goto stop;
+	
+	switch ( op )
+	{
+
+#define BRANCH( cond )          \
+{                               \
+	pc++;                       \
+	int offset = (BOOST::int8_t) data;  \
+	if ( !(cond) ) goto loop;   \
+	pc += offset;               \
+	goto loop;                  \
+}
+
+// Most Common
+
+	case 0x20: // JR NZ
+		BRANCH( !(flags & z_flag) )
+	
+	case 0x21: // LD HL,IMM (common)
+		rp.hl = READ_PROG16( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x28: // JR Z
+		BRANCH( flags & z_flag )
+	
+	{
+		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 = (sp - 1) & 0xFFFF;
+		WRITE( sp, data >> 8 );
+		sp = (sp - 1) & 0xFFFF;
+		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) & 0xFFFF );
+		sp = (sp + 2) & 0xFFFF;
+		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;
+		pc++;
+		goto loop;
+	
+	case 0x0E: // LD C,IMM
+		rg.c = data;
+		pc++;
+		goto loop;
+	
+	case 0x16: // LD D,IMM
+		rg.d = data;
+		pc++;
+		goto loop;
+	
+	case 0x1E: // LD E,IMM
+		rg.e = data;
+		pc++;
+		goto loop;
+	
+	case 0x26: // LD H,IMM
+		rg.h = data;
+		pc++;
+		goto loop;
+	
+	case 0x2E: // LD L,IMM
+		rg.l = data;
+		pc++;
+		goto loop;
+	
+	case 0x36: // LD (HL),IMM
+		WRITE( rp.hl, data );
+		pc++;
+		goto loop;
+	
+	case 0x3E: // LD A,IMM
+		rg.a = data;
+		pc++;
+		goto 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 = (sp + 1) & 0xFFFF;
+		goto loop;
+
+	case 0x0B: // DEC BC
+	case 0x1B: // DEC DE
+	case 0x2B: // DEC HL
+		r16 [op >> 4]--;
+		goto loop;
+	
+	case 0x3B: // DEC SP
+		sp = (sp - 1) & 0xFFFF;
+		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); // 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); // sign-extend to 16 bits
+		pc++;
+		flags = 0;
+		temp += sp;
+		prev = sp;
+		sp = temp & 0xffff;
+		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) & 0xFFFF );
+		sp = (sp + 2) & 0xFFFF;
+		goto loop;
+	}
+	
+	case 0xF1: // POP FA
+		rg.a = READ( sp );
+		flags = READ( (sp + 1) & 0xFFFF ) & 0xf0;
+		sp = (sp + 2) & 0xFFFF;
+		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
+		BRANCH( true )
+	
+	case 0x30: // JR NC
+		BRANCH( !(flags & c_flag) )
+	
+	case 0x38: // JR C
+		BRANCH( flags & c_flag )
+	
+	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;
+	}
+	
+	// If this fails then the case above is missing an opcode
+	assert( false );
+	
+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
+
--- a/Plugins/Input/console/Gb_Oscs.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,333 +0,0 @@
-
-// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
-
-#include "Gb_Apu.h"
-
-#include <string.h>
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-// Gb_Osc
-
-void Gb_Osc::reset()
-{
-	delay = 0;
-	last_amp = 0;
-	length = 0;
-	output_select = 3;
-	output = outputs [output_select];
-}
-
-void Gb_Osc::clock_length()
-{
-	if ( (regs [4] & len_enabled_mask) && length )
-		length--;
-}
-
-// Gb_Env
-
-void Gb_Env::clock_envelope()
-{
-	if ( env_delay && !--env_delay )
-	{
-		env_delay = regs [2] & 7;
-		int v = volume - 1 + (regs [2] >> 2 & 2);
-		if ( (unsigned) v < 15 )
-			volume = v;
-	}
-}
-
-bool Gb_Env::write_register( int reg, int data )
-{
-	switch ( reg )
-	{
-	case 1:
-		length = 64 - (regs [1] & 0x3f);
-		break;
-	
-	case 2:
-		if ( !(data >> 4) )
-			enabled = false;
-		break;
-	
-	case 4:
-		if ( data & trigger )
-		{
-			env_delay = regs [2] & 7;
-			volume = regs [2] >> 4;
-			enabled = true;
-			if ( length == 0 )
-				length = 64;
-			return true;
-		}
-	}
-	return false;
-}
-
-// Gb_Square
-
-void Gb_Square::reset()
-{
-	phase = 0;
-	sweep_freq = 0;
-	sweep_delay = 0;
-	Gb_Env::reset();
-}
-
-void Gb_Square::clock_sweep()
-{
-	int sweep_period = (regs [0] & period_mask) >> 4;
-	if ( sweep_period && sweep_delay && !--sweep_delay )
-	{
-		sweep_delay = sweep_period;
-		regs [3] = sweep_freq & 0xFF;
-		regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07);
-		
-		int offset = sweep_freq >> (regs [0] & shift_mask);
-		if ( regs [0] & 0x08 )
-			offset = -offset;
-		sweep_freq += offset;
-		
-		if ( sweep_freq < 0 )
-		{
-			sweep_freq = 0;
-		}
-		else if ( sweep_freq >= 2048 )
-		{
-			sweep_delay = 0; // don't modify channel frequency any further
-			sweep_freq = 2048; // silence sound immediately
-		}
-	}
-}
-
-void Gb_Square::run( gb_time_t time, gb_time_t end_time, int playing )
-{
-	if ( sweep_freq == 2048 )
-		playing = false;
-	
-	static unsigned char const table [4] = { 1, 2, 4, 6 };
-	int const duty = table [regs [1] >> 6];
-	int amp = volume & playing;
-	if ( phase >= duty )
-		amp = -amp;
-	
-	int frequency = this->frequency();
-	if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041
-	{
-		// really high frequency results in DC at half volume
-		amp = volume >> 1;
-		playing = false;
-	}
-	
-	int delta = amp - last_amp;
-	if ( delta )
-	{
-		last_amp = amp;
-		synth->offset( time, delta, output );
-	}
-	
-	time += delay;
-	if ( !playing )
-		time = end_time;
-	
-	if ( time < end_time )
-	{
-		int const period = (2048 - frequency) * 4;
-		Blip_Buffer* const output = this->output;
-		int phase = this->phase;
-		int delta = amp * 2;
-		do
-		{
-			phase = (phase + 1) & 7;
-			if ( phase == 0 || phase == duty )
-			{
-				delta = -delta;
-				synth->offset_inline( time, delta, output );
-			}
-			time += period;
-		}
-		while ( time < end_time );
-		
-		this->phase = phase;
-		last_amp = delta >> 1;
-	}
-	delay = time - end_time;
-}
-
-// Gb_Noise
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Gb_Noise::run( gb_time_t time, gb_time_t end_time, int playing )
-{
-	int amp = volume & playing;
-	int tap = 13 - (regs [3] & 8);
-	if ( bits >> tap & 2 )
-		amp = -amp;
-	
-	int delta = amp - last_amp;
-	if ( delta )
-	{
-		last_amp = amp;
-		synth->offset( time, delta, output );
-	}
-		
-	time += delay;
-	if ( !playing )
-		time = end_time;
-	
-	if ( time < end_time )
-	{
-		static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 };
-		int period = table [regs [3] & 7] << (regs [3] >> 4);
-		
-		// keep parallel resampled time to eliminate time conversion in the loop
-		Blip_Buffer* const output = this->output;
-		const blip_resampled_time_t resampled_period =
-				output->resampled_duration( period );
-		blip_resampled_time_t resampled_time = output->resampled_time( time );
-		unsigned bits = this->bits;
-		int delta = amp * 2;
-		
-		do
-		{
-			unsigned changed = (bits >> tap) + 1;
-			time += period;
-			bits <<= 1;
-			if ( changed & 2 )
-			{
-				delta = -delta;
-				bits |= 1;
-				synth->offset_resampled( resampled_time, delta, output );
-			}
-			resampled_time += resampled_period;
-		}
-		while ( time < end_time );
-		
-		this->bits = bits;
-		last_amp = delta >> 1;
-	}
-	delay = time - end_time;
-}
-
-// Gb_Wave
-
-inline void Gb_Wave::write_register( int reg, int data )
-{
-	switch ( reg )
-	{
-	case 0:
-		if ( !(data & 0x80) )
-			enabled = false;
-		break;
-	
-	case 1:
-		length = 256 - regs [1];
-		break;
-	
-	case 2:
-		volume = data >> 5 & 3;
-		break;
-	
-	case 4:
-		if ( data & trigger & regs [0] )
-		{
-			wave_pos = 0;
-			enabled = true;
-			if ( length == 0 )
-				length = 256;
-		}
-	}
-}
-
-void Gb_Wave::run( gb_time_t time, gb_time_t end_time, int playing )
-{
-	int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7
-	int amp = (wave [wave_pos] >> volume_shift & playing) * 2;
-	
-	int frequency = this->frequency();
-	if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045
-	{
-		amp = 30 >> volume_shift & playing;
-		playing = false;
-	}
-	
-	int delta = amp - last_amp;
-	if ( delta )
-	{
-		last_amp = amp;
-		synth->offset( time, delta, output );
-	}
-	
-	time += delay;
-	if ( !playing )
-		time = end_time;
-	
-	if ( time < end_time )
-	{
-		Blip_Buffer* const output = this->output;
-		int const period = (2048 - frequency) * 2;
-	 	int wave_pos = (this->wave_pos + 1) & (wave_size - 1);
-	 	
-		do
-		{
-			int amp = (wave [wave_pos] >> volume_shift) * 2;
-			wave_pos = (wave_pos + 1) & (wave_size - 1);
-			int delta = amp - last_amp;
-			if ( delta )
-			{
-				last_amp = amp;
-				synth->offset_inline( time, delta, output );
-			}
-			time += period;
-		}
-		while ( time < end_time );
-		
-		this->wave_pos = (wave_pos - 1) & (wave_size - 1);
-	}
-	delay = time - end_time;
-}
-
-// Gb_Apu::write_osc
-
-void Gb_Apu::write_osc( int index, int reg, int data )
-{
-	reg -= index * 5;
-	Gb_Square* sq = &square2;
-	switch ( index )
-	{
-	case 0:
-		sq = &square1;
-	case 1:
-		if ( sq->write_register( reg, data ) && index == 0 )
-		{
-			square1.sweep_freq = square1.frequency();
-			if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) )
-			{
-				square1.sweep_delay = 1; // cause sweep to recalculate now
-				square1.clock_sweep();
-			}
-		}
-		break;
-	
-	case 2:
-		wave.write_register( reg, data );
-		break;
-	
-	case 3:
-		if ( noise.write_register( reg, data ) )
-			noise.bits = 0x7FFF;
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gb_Oscs.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,333 @@
+
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// Gb_Osc
+
+void Gb_Osc::reset()
+{
+	delay = 0;
+	last_amp = 0;
+	length = 0;
+	output_select = 3;
+	output = outputs [output_select];
+}
+
+void Gb_Osc::clock_length()
+{
+	if ( (regs [4] & len_enabled_mask) && length )
+		length--;
+}
+
+// Gb_Env
+
+void Gb_Env::clock_envelope()
+{
+	if ( env_delay && !--env_delay )
+	{
+		env_delay = regs [2] & 7;
+		int v = volume - 1 + (regs [2] >> 2 & 2);
+		if ( (unsigned) v < 15 )
+			volume = v;
+	}
+}
+
+bool Gb_Env::write_register( int reg, int data )
+{
+	switch ( reg )
+	{
+	case 1:
+		length = 64 - (regs [1] & 0x3f);
+		break;
+	
+	case 2:
+		if ( !(data >> 4) )
+			enabled = false;
+		break;
+	
+	case 4:
+		if ( data & trigger )
+		{
+			env_delay = regs [2] & 7;
+			volume = regs [2] >> 4;
+			enabled = true;
+			if ( length == 0 )
+				length = 64;
+			return true;
+		}
+	}
+	return false;
+}
+
+// Gb_Square
+
+void Gb_Square::reset()
+{
+	phase = 0;
+	sweep_freq = 0;
+	sweep_delay = 0;
+	Gb_Env::reset();
+}
+
+void Gb_Square::clock_sweep()
+{
+	int sweep_period = (regs [0] & period_mask) >> 4;
+	if ( sweep_period && sweep_delay && !--sweep_delay )
+	{
+		sweep_delay = sweep_period;
+		regs [3] = sweep_freq & 0xFF;
+		regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07);
+		
+		int offset = sweep_freq >> (regs [0] & shift_mask);
+		if ( regs [0] & 0x08 )
+			offset = -offset;
+		sweep_freq += offset;
+		
+		if ( sweep_freq < 0 )
+		{
+			sweep_freq = 0;
+		}
+		else if ( sweep_freq >= 2048 )
+		{
+			sweep_delay = 0; // don't modify channel frequency any further
+			sweep_freq = 2048; // silence sound immediately
+		}
+	}
+}
+
+void Gb_Square::run( gb_time_t time, gb_time_t end_time, int playing )
+{
+	if ( sweep_freq == 2048 )
+		playing = false;
+	
+	static unsigned char const table [4] = { 1, 2, 4, 6 };
+	int const duty = table [regs [1] >> 6];
+	int amp = volume & playing;
+	if ( phase >= duty )
+		amp = -amp;
+	
+	int frequency = this->frequency();
+	if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041
+	{
+		// really high frequency results in DC at half volume
+		amp = volume >> 1;
+		playing = false;
+	}
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth->offset( time, delta, output );
+	}
+	
+	time += delay;
+	if ( !playing )
+		time = end_time;
+	
+	if ( time < end_time )
+	{
+		int const period = (2048 - frequency) * 4;
+		Blip_Buffer* const output = this->output;
+		int phase = this->phase;
+		int delta = amp * 2;
+		do
+		{
+			phase = (phase + 1) & 7;
+			if ( phase == 0 || phase == duty )
+			{
+				delta = -delta;
+				synth->offset_inline( time, delta, output );
+			}
+			time += period;
+		}
+		while ( time < end_time );
+		
+		this->phase = phase;
+		last_amp = delta >> 1;
+	}
+	delay = time - end_time;
+}
+
+// Gb_Noise
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Gb_Noise::run( gb_time_t time, gb_time_t end_time, int playing )
+{
+	int amp = volume & playing;
+	int tap = 13 - (regs [3] & 8);
+	if ( bits >> tap & 2 )
+		amp = -amp;
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth->offset( time, delta, output );
+	}
+		
+	time += delay;
+	if ( !playing )
+		time = end_time;
+	
+	if ( time < end_time )
+	{
+		static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 };
+		int period = table [regs [3] & 7] << (regs [3] >> 4);
+		
+		// keep parallel resampled time to eliminate time conversion in the loop
+		Blip_Buffer* const output = this->output;
+		const blip_resampled_time_t resampled_period =
+				output->resampled_duration( period );
+		blip_resampled_time_t resampled_time = output->resampled_time( time );
+		unsigned bits = this->bits;
+		int delta = amp * 2;
+		
+		do
+		{
+			unsigned changed = (bits >> tap) + 1;
+			time += period;
+			bits <<= 1;
+			if ( changed & 2 )
+			{
+				delta = -delta;
+				bits |= 1;
+				synth->offset_resampled( resampled_time, delta, output );
+			}
+			resampled_time += resampled_period;
+		}
+		while ( time < end_time );
+		
+		this->bits = bits;
+		last_amp = delta >> 1;
+	}
+	delay = time - end_time;
+}
+
+// Gb_Wave
+
+inline void Gb_Wave::write_register( int reg, int data )
+{
+	switch ( reg )
+	{
+	case 0:
+		if ( !(data & 0x80) )
+			enabled = false;
+		break;
+	
+	case 1:
+		length = 256 - regs [1];
+		break;
+	
+	case 2:
+		volume = data >> 5 & 3;
+		break;
+	
+	case 4:
+		if ( data & trigger & regs [0] )
+		{
+			wave_pos = 0;
+			enabled = true;
+			if ( length == 0 )
+				length = 256;
+		}
+	}
+}
+
+void Gb_Wave::run( gb_time_t time, gb_time_t end_time, int playing )
+{
+	int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7
+	int amp = (wave [wave_pos] >> volume_shift & playing) * 2;
+	
+	int frequency = this->frequency();
+	if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045
+	{
+		amp = 30 >> volume_shift & playing;
+		playing = false;
+	}
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth->offset( time, delta, output );
+	}
+	
+	time += delay;
+	if ( !playing )
+		time = end_time;
+	
+	if ( time < end_time )
+	{
+		Blip_Buffer* const output = this->output;
+		int const period = (2048 - frequency) * 2;
+	 	int wave_pos = (this->wave_pos + 1) & (wave_size - 1);
+	 	
+		do
+		{
+			int amp = (wave [wave_pos] >> volume_shift) * 2;
+			wave_pos = (wave_pos + 1) & (wave_size - 1);
+			int delta = amp - last_amp;
+			if ( delta )
+			{
+				last_amp = amp;
+				synth->offset_inline( time, delta, output );
+			}
+			time += period;
+		}
+		while ( time < end_time );
+		
+		this->wave_pos = (wave_pos - 1) & (wave_size - 1);
+	}
+	delay = time - end_time;
+}
+
+// Gb_Apu::write_osc
+
+void Gb_Apu::write_osc( int index, int reg, int data )
+{
+	reg -= index * 5;
+	Gb_Square* sq = &square2;
+	switch ( index )
+	{
+	case 0:
+		sq = &square1;
+	case 1:
+		if ( sq->write_register( reg, data ) && index == 0 )
+		{
+			square1.sweep_freq = square1.frequency();
+			if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) )
+			{
+				square1.sweep_delay = 1; // cause sweep to recalculate now
+				square1.clock_sweep();
+			}
+		}
+		break;
+	
+	case 2:
+		wave.write_register( reg, data );
+		break;
+	
+	case 3:
+		if ( noise.write_register( reg, data ) )
+			noise.bits = 0x7FFF;
+	}
+}
+
--- a/Plugins/Input/console/Gbs_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,366 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Gbs_Emu.h"
-
-#include <string.h>
-
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-#ifndef RUN_GB_CPU
-	#define RUN_GB_CPU( cpu, n ) cpu.run( n )
-#endif
-
-const long bank_size = 0x4000;
-const gb_addr_t ram_addr = 0xa000;
-const gb_addr_t halt_addr = 0x9EFE;
-static BOOST::uint8_t unmapped_code [Gb_Cpu::page_size];
-
-Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq   = { -47.0, 2000 };
-Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = {   0.0, 300 };
-
-// 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
-}
-
-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 )
-	{
-		// to do: what is the correct behavior? Current Wario Land 3 and
-		// Tetris DX GBS rips require that this have no effect or set to bank 1.
-		//return;
-		//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 )
-	{
-		static byte const rates [4] = { 10, 4, 6, 8 };
-		play_period = (gb_time_t) (256 - modulo) << (rates [rate & 3] - 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 ) : cpu( this )
-{
-	apu.volume( gain );
-	
-	static equalizer_t const eq = { -1.0, 120 };
-	set_equalizer( eq );
-	
- 	// unmapped code is all HALT instructions
-	memset( unmapped_code, 0x76, sizeof unmapped_code );
-	
-	// cpu
-	cpu.reset( unmapped_code, read_unmapped, write_unmapped );
-	cpu.map_memory( 0x0000, 0x4000, read_rom, write_rom );
-	cpu.map_memory( 0x4000, 0x4000, read_bank, write_rom );
-	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()
-{
-}
-
-void Gbs_Emu::unload()
-{
-	cpu.r.pc = halt_addr;
-	rom.clear();
-}
-
-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( Data_Reader& in )
-{
-	header_t h;
-	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
-	return load( h, in );
-}
-
-blargg_err_t Gbs_Emu::load( const header_t& h, Data_Reader& in )
-{
-	header_ = h;
-	unload();
-	
-	// check compatibility
-	if ( 0 != memcmp( header_.tag, "GBS", 3 ) )
-		return "Not a GBS file";
-	if ( header_.vers != 1 )
-		return "Unsupported GBS format";
-	
-	// gather relevant fields
-	load_addr = get_le16( header_.load_addr );
-	init_addr = get_le16( header_.init_addr );
-	play_addr = get_le16( header_.play_addr );
-	stack_ptr = get_le16( header_.stack_ptr );
-	double_speed = (header_.timer_mode & 0x80) != 0;
-	timer_modulo_init = header_.timer_modulo;
-	timer_mode = header_.timer_mode;
-	if ( !(timer_mode & 0x04) )
-		timer_mode = 0; // using vbl
-	
-	#ifndef NDEBUG
-	{
-		if ( header_.timer_mode & 0x78 )
-			dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) header_.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;
-	BLARGG_RETURN_ERR( rom.resize( bank_count * bank_size ) );
-	memset( rom.begin(), 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.begin() );
-	
-	set_voice_count( Gb_Apu::osc_count );
-	set_track_count( header_.track_count );
-	
-	return setup_buffer( 4194304 );
-}
-
-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;
-}
-
-void Gbs_Emu::start_track( int track_index )
-{
-	require( rom.size() ); // file must be loaded
-	
-	Classic_Emu::start_track( track_index );
-	
-	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 < (int) 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 );
-}
-
-blip_time_t Gbs_Emu::run_clocks( blip_time_t duration, bool* added_stereo )
-{
-	require( rom.size() ); // file must be loaded
-	
-	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 = RUN_GB_CPU( cpu, count );
-		cpu_time -= cpu.remain();
-		
-		if ( (result == Gb_Cpu::result_halt && cpu.r.pc != halt_addr) ||
-				result == Gb_Cpu::result_badop )
-		{
-			if ( cpu.r.pc > 0xFFFF )
-			{
-				dprintf( "PC wrapped around\n" );
-				cpu.r.pc &= 0xFFFF;
-			}
-			else
-			{
-				log_error();
-				dprintf( "Bad opcode $%.2x at $%.4x\n",
-						(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
-				cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF;
-				cpu_time += 6;
-			}
-		}
-	}
-	
-	// 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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,366 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Gbs_Emu.h"
+
+#include <string.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+#ifndef RUN_GB_CPU
+	#define RUN_GB_CPU( cpu, n ) cpu.run( n )
+#endif
+
+const long bank_size = 0x4000;
+const gb_addr_t ram_addr = 0xa000;
+const gb_addr_t halt_addr = 0x9EFE;
+static BOOST::uint8_t unmapped_code [Gb_Cpu::page_size];
+
+Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq   = { -47.0, 2000 };
+Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = {   0.0, 300 };
+
+// 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
+}
+
+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 )
+	{
+		// to do: what is the correct behavior? Current Wario Land 3 and
+		// Tetris DX GBS rips require that this have no effect or set to bank 1.
+		//return;
+		//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 )
+	{
+		static byte const rates [4] = { 10, 4, 6, 8 };
+		play_period = (gb_time_t) (256 - modulo) << (rates [rate & 3] - 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 ) : cpu( this )
+{
+	apu.volume( gain );
+	
+	static equalizer_t const eq = { -1.0, 120 };
+	set_equalizer( eq );
+	
+ 	// unmapped code is all HALT instructions
+	memset( unmapped_code, 0x76, sizeof unmapped_code );
+	
+	// cpu
+	cpu.reset( unmapped_code, read_unmapped, write_unmapped );
+	cpu.map_memory( 0x0000, 0x4000, read_rom, write_rom );
+	cpu.map_memory( 0x4000, 0x4000, read_bank, write_rom );
+	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()
+{
+}
+
+void Gbs_Emu::unload()
+{
+	cpu.r.pc = halt_addr;
+	rom.clear();
+}
+
+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( Data_Reader& in )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Gbs_Emu::load( const header_t& h, Data_Reader& in )
+{
+	header_ = h;
+	unload();
+	
+	// check compatibility
+	if ( 0 != memcmp( header_.tag, "GBS", 3 ) )
+		return "Not a GBS file";
+	if ( header_.vers != 1 )
+		return "Unsupported GBS format";
+	
+	// gather relevant fields
+	load_addr = get_le16( header_.load_addr );
+	init_addr = get_le16( header_.init_addr );
+	play_addr = get_le16( header_.play_addr );
+	stack_ptr = get_le16( header_.stack_ptr );
+	double_speed = (header_.timer_mode & 0x80) != 0;
+	timer_modulo_init = header_.timer_modulo;
+	timer_mode = header_.timer_mode;
+	if ( !(timer_mode & 0x04) )
+		timer_mode = 0; // using vbl
+	
+	#ifndef NDEBUG
+	{
+		if ( header_.timer_mode & 0x78 )
+			dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) header_.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;
+	BLARGG_RETURN_ERR( rom.resize( bank_count * bank_size ) );
+	memset( rom.begin(), 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.begin() );
+	
+	set_voice_count( Gb_Apu::osc_count );
+	set_track_count( header_.track_count );
+	
+	return setup_buffer( 4194304 );
+}
+
+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;
+}
+
+void Gbs_Emu::start_track( int track_index )
+{
+	require( rom.size() ); // file must be loaded
+	
+	Classic_Emu::start_track( track_index );
+	
+	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 < (int) 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 );
+}
+
+blip_time_t Gbs_Emu::run_clocks( blip_time_t duration, bool* added_stereo )
+{
+	require( rom.size() ); // file must be loaded
+	
+	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 = RUN_GB_CPU( cpu, count );
+		cpu_time -= cpu.remain();
+		
+		if ( (result == Gb_Cpu::result_halt && cpu.r.pc != halt_addr) ||
+				result == Gb_Cpu::result_badop )
+		{
+			if ( cpu.r.pc > 0xFFFF )
+			{
+				dprintf( "PC wrapped around\n" );
+				cpu.r.pc &= 0xFFFF;
+			}
+			else
+			{
+				log_error();
+				dprintf( "Bad opcode $%.2x at $%.4x\n",
+						(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
+				cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF;
+				cpu_time += 6;
+			}
+		}
+	}
+	
+	// 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;
+}
+
--- a/Plugins/Input/console/Gym_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,350 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Gym_Emu.h"
-
-#include <string.h>
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-double const gain = 3.0;
-double const oversample_factor = 5 / 3.0;
-
-const long base_clock = 53700300;
-const long clock_rate = base_clock / 15;
-
-Gym_Emu::Gym_Emu()
-{
-	data = NULL;
-	pos = NULL;
-}
-
-Gym_Emu::~Gym_Emu()
-{
-	unload();
-}
-
-void Gym_Emu::unload()
-{
-	data = NULL;
-	pos = NULL;
-	set_track_ended( false );
-	mem.clear();
-}
-
-blargg_err_t Gym_Emu::set_sample_rate( long sample_rate )
-{
-	blip_eq_t eq( -32, 8000, sample_rate );
-	apu.treble_eq( eq );
-	apu.volume( 0.135 * gain );
-	dac_synth.treble_eq( eq );
-	dac_synth.volume( 0.125 / 256 * gain );
-	
-	BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 60 ) );
-	blip_buf.clock_rate( clock_rate );
-	
-	double factor = Dual_Resampler::setup( oversample_factor, 0.990, gain );
-	double fm_sample_rate = sample_rate * factor;
-	BLARGG_RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) );
-	BLARGG_RETURN_ERR( Dual_Resampler::resize( sample_rate / 60 ) );
-	
-	return Music_Emu::set_sample_rate( sample_rate );
-}
-
-void Gym_Emu::mute_voices( int mask )
-{
-	Music_Emu::mute_voices( mask );
-	fm.mute_voices( mask );
-	dac_muted = 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", "PSG"
-	};
-	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( blip_buf.length() );
-	
-	data = (const byte*) file + data_offset;
-	data_end = (const byte*) file + file_size;
-	
-	loop_begin = NULL;
-	if ( data_offset )
-		header_ = *(header_t*) file;
-	else
-		memset( &header_, 0, sizeof header_ );
-	
-	set_voice_count( 8 );
-	set_track_count( 1 );
-	remute_voices();
-	
-	return blargg_success;
-}
-
-blargg_err_t Gym_Emu::load( const void* file, long file_size )
-{
-	unload();
-	
-	if ( file_size < (int) 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( Data_Reader& in )
-{
-	header_t h;
-	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
-	return load( h, in );
-}
-
-blargg_err_t Gym_Emu::load( const header_t& h, Data_Reader& in )
-{
-	unload();
-	
-	int data_offset = 0;
-	BLARGG_RETURN_ERR( check_header( h, &data_offset ) );
-	
-	BLARGG_RETURN_ERR( mem.resize( sizeof h + in.remain() ) );
-	memcpy( mem.begin(), &h, sizeof h );
-	BLARGG_RETURN_ERR( in.read( &mem [sizeof h], mem.size() - sizeof h ) );
-	
-	return load_( mem.begin(), data_offset, mem.size() );
-}
-
-long Gym_Emu::track_length() const
-{
-	long time = 0;
-	const byte* p = data;
-	while ( p < data_end )
-	{
-		switch ( *p++ )
-		{
-			case 0:
-				time++;
-				break;
-			
-			case 1:
-			case 2:
-				p += 2;
-				break;
-			
-			case 3:
-				p += 1;
-				break;
-		}
-	}
-	return time;
-}
-
-void Gym_Emu::start_track( int track )
-{
-	require( data );
-	
-	Music_Emu::start_track( track );
-	
-	pos = &data [0];
-	loop_remain = get_le32( header_.loop_start );
-	
-	prev_dac_count = 0;
-	dac_enabled = false;
-	dac_amp = -1;
-	
-	fm.reset();
-	apu.reset();
-	blip_buf.clear();
-	Dual_Resampler::clear();
-}
-
-void Gym_Emu::run_dac( int dac_count )
-{
-	// 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++;
-	}
-	
-	// detect beginning and end of sample
-	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_resampled_time_t period =
-			blip_buf.resampled_duration( clock_rate / 60 ) / rate_count;
-	
-	blip_resampled_time_t time = blip_buf.resampled_time( 0 ) +
-			period * start + (period >> 1);
-	
-	int dac_amp = this->dac_amp;
-	if ( dac_amp < 0 )
-		dac_amp = dac_buf [0];
-	
-	for ( int i = 0; i < dac_count; i++ )
-	{
-		int delta = dac_buf [i] - dac_amp;
-		dac_amp += delta;
-		dac_synth.offset_resampled( time, delta, &blip_buf );
-		time += period;
-	}
-	this->dac_amp = dac_amp;
-}
-
-void Gym_Emu::parse_frame()
-{
-	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 ( data == 0x2B )
-					dac_enabled = (data2 & 0x80) != 0;
-				
-				fm.write0( data, data2 );
-			}
-			else if ( dac_count < (int) sizeof dac_buf )
-			{
-				dac_buf [dac_count] = data2;
-				dac_count += dac_enabled;
-			}
-		}
-		else if ( cmd == 2 )
-		{
-			fm.write1( data, *pos++ );
-		}
-		else if ( cmd == 3 )
-		{
-			apu.write_data( 0, data );
-		}
-		else
-		{
-			// to do: many GYM streams are full of errors, and error count should
-			// reflect cases where music is really having problems
-			//log_error(); 
-			--pos; // put data back
-		}
-	}
-	
-	// loop
-	if ( pos >= data_end )
-	{
-		if ( pos > data_end )
-			log_error();
-		
-		if ( loop_begin )
-			pos = loop_begin;
-		else
-			set_track_ended();
-	}
-	this->pos = pos;
-	
-	// dac
-	if ( dac_count && !dac_muted )
-		run_dac( dac_count );
-	prev_dac_count = dac_count;
-}
-
-int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
-{
-	if ( !track_ended() )
-		parse_frame();
-	
-	apu.end_frame( blip_time );
-	
-	memset( buf, 0, sample_count * sizeof *buf );
-	fm.run( sample_count >> 1, buf );
-	
-	return sample_count;
-}
-
-void Gym_Emu::play( long count, sample_t* out )
-{
-	require( pos );
-	
-	Dual_Resampler::play( count, out, blip_buf );
-}
-
-void 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;
-		play( n, buf );
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gym_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,350 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Gym_Emu.h"
+
+#include <string.h>
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+double const gain = 3.0;
+double const oversample_factor = 5 / 3.0;
+
+const long base_clock = 53700300;
+const long clock_rate = base_clock / 15;
+
+Gym_Emu::Gym_Emu()
+{
+	data = NULL;
+	pos = NULL;
+}
+
+Gym_Emu::~Gym_Emu()
+{
+	unload();
+}
+
+void Gym_Emu::unload()
+{
+	data = NULL;
+	pos = NULL;
+	set_track_ended( false );
+	mem.clear();
+}
+
+blargg_err_t Gym_Emu::set_sample_rate( long sample_rate )
+{
+	blip_eq_t eq( -32, 8000, sample_rate );
+	apu.treble_eq( eq );
+	apu.volume( 0.135 * gain );
+	dac_synth.treble_eq( eq );
+	dac_synth.volume( 0.125 / 256 * gain );
+	
+	BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 60 ) );
+	blip_buf.clock_rate( clock_rate );
+	
+	double factor = Dual_Resampler::setup( oversample_factor, 0.990, gain );
+	double fm_sample_rate = sample_rate * factor;
+	BLARGG_RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) );
+	BLARGG_RETURN_ERR( Dual_Resampler::resize( sample_rate / 60 ) );
+	
+	return Music_Emu::set_sample_rate( sample_rate );
+}
+
+void Gym_Emu::mute_voices( int mask )
+{
+	Music_Emu::mute_voices( mask );
+	fm.mute_voices( mask );
+	dac_muted = 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", "PSG"
+	};
+	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( blip_buf.length() );
+	
+	data = (const byte*) file + data_offset;
+	data_end = (const byte*) file + file_size;
+	
+	loop_begin = NULL;
+	if ( data_offset )
+		header_ = *(header_t*) file;
+	else
+		memset( &header_, 0, sizeof header_ );
+	
+	set_voice_count( 8 );
+	set_track_count( 1 );
+	remute_voices();
+	
+	return blargg_success;
+}
+
+blargg_err_t Gym_Emu::load( const void* file, long file_size )
+{
+	unload();
+	
+	if ( file_size < (int) 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( Data_Reader& in )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Gym_Emu::load( const header_t& h, Data_Reader& in )
+{
+	unload();
+	
+	int data_offset = 0;
+	BLARGG_RETURN_ERR( check_header( h, &data_offset ) );
+	
+	BLARGG_RETURN_ERR( mem.resize( sizeof h + in.remain() ) );
+	memcpy( mem.begin(), &h, sizeof h );
+	BLARGG_RETURN_ERR( in.read( &mem [sizeof h], mem.size() - sizeof h ) );
+	
+	return load_( mem.begin(), data_offset, mem.size() );
+}
+
+long Gym_Emu::track_length() const
+{
+	long time = 0;
+	const byte* p = data;
+	while ( p < data_end )
+	{
+		switch ( *p++ )
+		{
+			case 0:
+				time++;
+				break;
+			
+			case 1:
+			case 2:
+				p += 2;
+				break;
+			
+			case 3:
+				p += 1;
+				break;
+		}
+	}
+	return time;
+}
+
+void Gym_Emu::start_track( int track )
+{
+	require( data );
+	
+	Music_Emu::start_track( track );
+	
+	pos = &data [0];
+	loop_remain = get_le32( header_.loop_start );
+	
+	prev_dac_count = 0;
+	dac_enabled = false;
+	dac_amp = -1;
+	
+	fm.reset();
+	apu.reset();
+	blip_buf.clear();
+	Dual_Resampler::clear();
+}
+
+void Gym_Emu::run_dac( int dac_count )
+{
+	// 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++;
+	}
+	
+	// detect beginning and end of sample
+	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_resampled_time_t period =
+			blip_buf.resampled_duration( clock_rate / 60 ) / rate_count;
+	
+	blip_resampled_time_t time = blip_buf.resampled_time( 0 ) +
+			period * start + (period >> 1);
+	
+	int dac_amp = this->dac_amp;
+	if ( dac_amp < 0 )
+		dac_amp = dac_buf [0];
+	
+	for ( int i = 0; i < dac_count; i++ )
+	{
+		int delta = dac_buf [i] - dac_amp;
+		dac_amp += delta;
+		dac_synth.offset_resampled( time, delta, &blip_buf );
+		time += period;
+	}
+	this->dac_amp = dac_amp;
+}
+
+void Gym_Emu::parse_frame()
+{
+	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 ( data == 0x2B )
+					dac_enabled = (data2 & 0x80) != 0;
+				
+				fm.write0( data, data2 );
+			}
+			else if ( dac_count < (int) sizeof dac_buf )
+			{
+				dac_buf [dac_count] = data2;
+				dac_count += dac_enabled;
+			}
+		}
+		else if ( cmd == 2 )
+		{
+			fm.write1( data, *pos++ );
+		}
+		else if ( cmd == 3 )
+		{
+			apu.write_data( 0, data );
+		}
+		else
+		{
+			// to do: many GYM streams are full of errors, and error count should
+			// reflect cases where music is really having problems
+			//log_error(); 
+			--pos; // put data back
+		}
+	}
+	
+	// loop
+	if ( pos >= data_end )
+	{
+		if ( pos > data_end )
+			log_error();
+		
+		if ( loop_begin )
+			pos = loop_begin;
+		else
+			set_track_ended();
+	}
+	this->pos = pos;
+	
+	// dac
+	if ( dac_count && !dac_muted )
+		run_dac( dac_count );
+	prev_dac_count = dac_count;
+}
+
+int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
+{
+	if ( !track_ended() )
+		parse_frame();
+	
+	apu.end_frame( blip_time );
+	
+	memset( buf, 0, sample_count * sizeof *buf );
+	fm.run( sample_count >> 1, buf );
+	
+	return sample_count;
+}
+
+void Gym_Emu::play( long count, sample_t* out )
+{
+	require( pos );
+	
+	Dual_Resampler::play( count, out, blip_buf );
+}
+
+void 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;
+		play( n, buf );
+	}
+}
+
--- a/Plugins/Input/console/Gzip_File.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-
-#include "Gzip_File.h"
-
-#include "zlib.h"
-
-/* Copyright (C) 2005 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. */
-
-static const char* get_gzip_eof( FILE* file, long* eof )
-{
-	unsigned char buf [4];
-	if ( !fread( buf, 2, 1, file ) )
-		return "Couldn't read from file";
-	
-	if ( buf [0] == 0x1F && buf [1] == 0x8B )
-	{
-		if ( fseek( file, -4, SEEK_END ) )
-			return "Couldn't seek in file";
-		
-		if ( !fread( buf, 4, 1, file ) )
-			return "Couldn't read from file";
-		
-		*eof = buf [3] * 0x1000000L + buf [2] * 0x10000L + buf [1] * 0x100L + buf [0];
-	}
-	else
-	{
-		if ( fseek( file, 0, SEEK_END ) )
-			return "Couldn't seek in file";
-		
-		*eof = ftell( file );
-	}
-	
-	return NULL;
-}
-
-const char* get_gzip_eof( const char* path, long* eof )
-{
-	FILE* file = fopen( path, "rb" );
-	if ( !file )
-		return "Couldn't open file";
-	const char* error = get_gzip_eof( file, eof );
-	fclose( file );
-	return error;
-}
-
-// Gzip_File_Reader
-
-Gzip_File_Reader::Gzip_File_Reader() : file_( NULL )
-{
-}
-
-Gzip_File_Reader::~Gzip_File_Reader()
-{
-	close();
-}
-
-Gzip_File_Reader::error_t Gzip_File_Reader::open( const char* path )
-{
-	error_t error = get_gzip_eof( path, &size_ );
-	if ( error )
-		return error;
-	
-	file_ = gzopen( path, "rb" );
-	if ( !file_ )
-		return "Couldn't open file";
-	
-	return NULL;
-}
-
-long Gzip_File_Reader::size() const
-{
-	return size_;
-}
-
-long Gzip_File_Reader::read_avail( void* p, long s )
-{
-	return (long) gzread( file_, p, s );
-}
-
-long Gzip_File_Reader::tell() const
-{
-	return gztell( file_ );
-}
-
-Gzip_File_Reader::error_t Gzip_File_Reader::seek( long n )
-{
-	if ( gzseek( file_, n, SEEK_SET ) < 0 )
-		return "Error seeking in file";
-	return NULL;
-}
-
-void Gzip_File_Reader::close()
-{
-	if ( file_ )
-	{
-		gzclose( file_ );
-		file_ = NULL;
-	}
-}
-
-// Gzip_File_Writer
-
-Gzip_File_Writer::Gzip_File_Writer() : file_( NULL )
-{
-}
-
-Gzip_File_Writer::~Gzip_File_Writer()
-{
-	close();
-}
-
-Gzip_File_Writer::error_t Gzip_File_Writer::open( const char* path )
-{
-	file_ = gzopen( path, "wb9" );
-	if ( !file_ )
-		return "Couldn't open file for writing";
-	
-	return NULL;
-}
-
-Gzip_File_Writer::error_t Gzip_File_Writer::write( const void* p, long s )
-{
-	long result = (long) gzwrite( file_ , (void*) p, s );
-	if ( result != s )
-		return "Couldn't write to file";
-	return NULL;
-}
-
-void Gzip_File_Writer::close()
-{
-	if ( file_ )
-	{
-		gzclose( file_ );
-		file_ = NULL;
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Gzip_File.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,148 @@
+
+#include "Gzip_File.h"
+
+#include "zlib.h"
+
+/* Copyright (C) 2005 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. */
+
+static const char* get_gzip_eof( FILE* file, long* eof )
+{
+	unsigned char buf [4];
+	if ( !fread( buf, 2, 1, file ) )
+		return "Couldn't read from file";
+	
+	if ( buf [0] == 0x1F && buf [1] == 0x8B )
+	{
+		if ( fseek( file, -4, SEEK_END ) )
+			return "Couldn't seek in file";
+		
+		if ( !fread( buf, 4, 1, file ) )
+			return "Couldn't read from file";
+		
+		*eof = buf [3] * 0x1000000L + buf [2] * 0x10000L + buf [1] * 0x100L + buf [0];
+	}
+	else
+	{
+		if ( fseek( file, 0, SEEK_END ) )
+			return "Couldn't seek in file";
+		
+		*eof = ftell( file );
+	}
+	
+	return NULL;
+}
+
+const char* get_gzip_eof( const char* path, long* eof )
+{
+	FILE* file = fopen( path, "rb" );
+	if ( !file )
+		return "Couldn't open file";
+	const char* error = get_gzip_eof( file, eof );
+	fclose( file );
+	return error;
+}
+
+// Gzip_File_Reader
+
+Gzip_File_Reader::Gzip_File_Reader() : file_( NULL )
+{
+}
+
+Gzip_File_Reader::~Gzip_File_Reader()
+{
+	close();
+}
+
+Gzip_File_Reader::error_t Gzip_File_Reader::open( const char* path )
+{
+	error_t error = get_gzip_eof( path, &size_ );
+	if ( error )
+		return error;
+	
+	file_ = gzopen( path, "rb" );
+	if ( !file_ )
+		return "Couldn't open file";
+	
+	return NULL;
+}
+
+long Gzip_File_Reader::size() const
+{
+	return size_;
+}
+
+long Gzip_File_Reader::read_avail( void* p, long s )
+{
+	return (long) gzread( file_, p, s );
+}
+
+long Gzip_File_Reader::tell() const
+{
+	return gztell( file_ );
+}
+
+Gzip_File_Reader::error_t Gzip_File_Reader::seek( long n )
+{
+	if ( gzseek( file_, n, SEEK_SET ) < 0 )
+		return "Error seeking in file";
+	return NULL;
+}
+
+void Gzip_File_Reader::close()
+{
+	if ( file_ )
+	{
+		gzclose( file_ );
+		file_ = NULL;
+	}
+}
+
+// Gzip_File_Writer
+
+Gzip_File_Writer::Gzip_File_Writer() : file_( NULL )
+{
+}
+
+Gzip_File_Writer::~Gzip_File_Writer()
+{
+	close();
+}
+
+Gzip_File_Writer::error_t Gzip_File_Writer::open( const char* path )
+{
+	file_ = gzopen( path, "wb9" );
+	if ( !file_ )
+		return "Couldn't open file for writing";
+	
+	return NULL;
+}
+
+Gzip_File_Writer::error_t Gzip_File_Writer::write( const void* p, long s )
+{
+	long result = (long) gzwrite( file_ , (void*) p, s );
+	if ( result != s )
+		return "Couldn't write to file";
+	return NULL;
+}
+
+void Gzip_File_Writer::close()
+{
+	if ( file_ )
+	{
+		gzclose( file_ );
+		file_ = NULL;
+	}
+}
--- a/Plugins/Input/console/Multi_Buffer.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,215 +0,0 @@
-
-// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
-
-#include "Multi_Buffer.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
-{
-	length_ = 0;
-	sample_rate_ = 0;
-	channels_changed_count_ = 1;
-}
-
-blargg_err_t Multi_Buffer::set_channel_count( int )
-{
-	return blargg_success;
-}
-
-Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
-{
-}
-
-Mono_Buffer::~Mono_Buffer()
-{
-}
-
-blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
-{
-	BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) );
-	return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
-}
-
-// Silent_Buffer
-
-Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
-{
-	chan.left   = NULL;
-	chan.center = NULL;
-	chan.right  = NULL;
-}
-
-// Mono_Buffer
-
-Mono_Buffer::channel_t Mono_Buffer::channel( int )
-{
-	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::Stereo_Buffer() : Multi_Buffer( 2 )
-{
-	chan.center = &bufs [0];
-	chan.left = &bufs [1];
-	chan.right = &bufs [2];
-}
-
-Stereo_Buffer::~Stereo_Buffer()
-{
-}
-
-blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
-{
-	for ( int i = 0; i < buf_count; i++ )
-		BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
-	return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
-}
-
-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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,215 @@
+
+// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
+{
+	length_ = 0;
+	sample_rate_ = 0;
+	channels_changed_count_ = 1;
+}
+
+blargg_err_t Multi_Buffer::set_channel_count( int )
+{
+	return blargg_success;
+}
+
+Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
+{
+}
+
+Mono_Buffer::~Mono_Buffer()
+{
+}
+
+blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
+{
+	BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) );
+	return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
+}
+
+// Silent_Buffer
+
+Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
+{
+	chan.left   = NULL;
+	chan.center = NULL;
+	chan.right  = NULL;
+}
+
+// Mono_Buffer
+
+Mono_Buffer::channel_t Mono_Buffer::channel( int )
+{
+	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::Stereo_Buffer() : Multi_Buffer( 2 )
+{
+	chan.center = &bufs [0];
+	chan.left = &bufs [1];
+	chan.right = &bufs [2];
+}
+
+Stereo_Buffer::~Stereo_Buffer()
+{
+}
+
+blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
+{
+	for ( int i = 0; i < buf_count; i++ )
+		BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
+	return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
+}
+
+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] );
+}
+
--- a/Plugins/Input/console/Music_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Music_Emu.h"
-
-#include <string.h>
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 };
-
-Music_Emu::Music_Emu()
-{
-	equalizer_.treble = -1.0;
-	equalizer_.bass = 60;
-	sample_rate_ = 0;
-	voice_count_ = 0;
-	mute_mask_ = 0;
-	track_count_ = 0;
-	error_count_ = 0;
-	track_ended_ = false;
-}
-
-Music_Emu::~Music_Emu()
-{
-}
-
-blargg_err_t Music_Emu::load_file( const char* path )
-{
-	Std_File_Reader in;
-	BLARGG_RETURN_ERR( in.open( path ) );
-	return load( in );
-}
-
-void 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 )
-		{
-			play( buf_size, buf );
-			count -= buf_size;
-		}
-		
-		mute_voices( saved_mute );
-	}
-	
-	while ( count )
-	{
-		int n = buf_size;
-		if ( n > count )
-			n = count;
-		count -= n;
-		play( n, buf );
-	}
-}
-
-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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,84 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Music_Emu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 };
+
+Music_Emu::Music_Emu()
+{
+	equalizer_.treble = -1.0;
+	equalizer_.bass = 60;
+	sample_rate_ = 0;
+	voice_count_ = 0;
+	mute_mask_ = 0;
+	track_count_ = 0;
+	error_count_ = 0;
+	track_ended_ = false;
+}
+
+Music_Emu::~Music_Emu()
+{
+}
+
+blargg_err_t Music_Emu::load_file( const char* path )
+{
+	Std_File_Reader in;
+	BLARGG_RETURN_ERR( in.open( path ) );
+	return load( in );
+}
+
+void 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 )
+		{
+			play( buf_size, buf );
+			count -= buf_size;
+		}
+		
+		mute_voices( saved_mute );
+	}
+	
+	while ( count )
+	{
+		int n = buf_size;
+		if ( n > count )
+			n = count;
+		count -= n;
+		play( n, buf );
+	}
+}
+
+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;
+}
+
--- a/Plugins/Input/console/Nes_Apu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,383 +0,0 @@
-
-// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
-
-#include "Nes_Apu.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-int const amp_range = 15;
-
-Nes_Apu::Nes_Apu() :
-	square1( &square_synth ),
-	square2( &square_synth )
-{
-	dmc.apu = this;
-	dmc.rom_reader = NULL;
-	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::enable_nonlinear( double v )
-{
-	dmc.nonlinear = true;
-	square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
-	
-	const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
-	triangle.synth.volume( 3.0 * tnd );
-	noise.synth.volume( 2.0 * tnd );
-	dmc.synth.volume( tnd );
-	
-	square1 .last_amp = 0;
-	square2 .last_amp = 0;
-	triangle.last_amp = 0;
-	noise   .last_amp = 0;
-	dmc     .last_amp = 0;
-}
-
-void Nes_Apu::volume( double v )
-{
-	dmc.nonlinear = false;
-	square_synth.volume(   0.1128  / amp_range * v );
-	triangle.synth.volume( 0.12765 / amp_range * v );
-	noise.synth.volume(    0.0741  / amp_range * v );
-	dmc.synth.volume(      0.42545 / 127 * 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;
-	last_dmc_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 ) // to do: remove?
-	//  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_dmc_time );
-	if ( end_time > next_dmc_read_time() )
-	{
-		nes_time_t start = last_dmc_time;
-		last_dmc_time = end_time;
-		dmc.run( start, end_time );
-	}
-}
-
-void Nes_Apu::run_until_( nes_time_t end_time )
-{
-	require( end_time >= last_time );
-	
-	if ( end_time == last_time )
-		return;
-	
-	if ( last_dmc_time < end_time )
-	{
-		nes_time_t start = last_dmc_time;
-		last_dmc_time = end_time;
-		dmc.run( start, end_time );
-	}
-	
-	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 );
-		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:
-				if ( !(frame_mode & 0xc0) ) {
-		 			next_irq = time + frame_period * 4 + 1;
-		 			irq_flag = true;
-		 		}
-		 		// 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();
-	}
-}
-
-// to do: remove
-static long abs_time;
-
-template<class T>
-inline void zero_apu_osc( T* osc, nes_time_t time )
-{
-	Blip_Buffer* output = osc->output;
-	int last_amp = osc->last_amp;
-	osc->last_amp = 0;
-	if ( output && last_amp )
-		osc->synth.offset( time, -last_amp, output );
-}
-
-void Nes_Apu::end_frame( nes_time_t end_time )
-{
-	if ( end_time > last_time )
-		run_until_( end_time );
-	
-	abs_time += end_time;
-	
-	if ( dmc.nonlinear )
-	{
-		zero_apu_osc( &square1,  last_time );
-		zero_apu_osc( &square2,  last_time );
-		zero_apu_osc( &triangle, last_time );
-		zero_apu_osc( &noise,    last_time );
-		zero_apu_osc( &dmc,      last_time );
-	}
-	
-	// make times relative to new frame
-	last_time -= end_time;
-	require( last_time >= 0 );
-	
-	last_dmc_time -= end_time;
-	require( last_dmc_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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,383 @@
+
+// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
+
+#include "Nes_Apu.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+int const amp_range = 15;
+
+Nes_Apu::Nes_Apu() :
+	square1( &square_synth ),
+	square2( &square_synth )
+{
+	dmc.apu = this;
+	dmc.rom_reader = NULL;
+	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::enable_nonlinear( double v )
+{
+	dmc.nonlinear = true;
+	square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
+	
+	const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
+	triangle.synth.volume( 3.0 * tnd );
+	noise.synth.volume( 2.0 * tnd );
+	dmc.synth.volume( tnd );
+	
+	square1 .last_amp = 0;
+	square2 .last_amp = 0;
+	triangle.last_amp = 0;
+	noise   .last_amp = 0;
+	dmc     .last_amp = 0;
+}
+
+void Nes_Apu::volume( double v )
+{
+	dmc.nonlinear = false;
+	square_synth.volume(   0.1128  / amp_range * v );
+	triangle.synth.volume( 0.12765 / amp_range * v );
+	noise.synth.volume(    0.0741  / amp_range * v );
+	dmc.synth.volume(      0.42545 / 127 * 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;
+	last_dmc_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 ) // to do: remove?
+	//  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_dmc_time );
+	if ( end_time > next_dmc_read_time() )
+	{
+		nes_time_t start = last_dmc_time;
+		last_dmc_time = end_time;
+		dmc.run( start, end_time );
+	}
+}
+
+void Nes_Apu::run_until_( nes_time_t end_time )
+{
+	require( end_time >= last_time );
+	
+	if ( end_time == last_time )
+		return;
+	
+	if ( last_dmc_time < end_time )
+	{
+		nes_time_t start = last_dmc_time;
+		last_dmc_time = end_time;
+		dmc.run( start, end_time );
+	}
+	
+	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 );
+		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:
+				if ( !(frame_mode & 0xc0) ) {
+		 			next_irq = time + frame_period * 4 + 1;
+		 			irq_flag = true;
+		 		}
+		 		// 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();
+	}
+}
+
+// to do: remove
+static long abs_time;
+
+template<class T>
+inline void zero_apu_osc( T* osc, nes_time_t time )
+{
+	Blip_Buffer* output = osc->output;
+	int last_amp = osc->last_amp;
+	osc->last_amp = 0;
+	if ( output && last_amp )
+		osc->synth.offset( time, -last_amp, output );
+}
+
+void Nes_Apu::end_frame( nes_time_t end_time )
+{
+	if ( end_time > last_time )
+		run_until_( end_time );
+	
+	abs_time += end_time;
+	
+	if ( dmc.nonlinear )
+	{
+		zero_apu_osc( &square1,  last_time );
+		zero_apu_osc( &square2,  last_time );
+		zero_apu_osc( &triangle, last_time );
+		zero_apu_osc( &noise,    last_time );
+		zero_apu_osc( &dmc,      last_time );
+	}
+	
+	// make times relative to new frame
+	last_time -= end_time;
+	require( last_time >= 0 );
+	
+	last_dmc_time -= end_time;
+	require( last_dmc_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;
+}
+
--- a/Plugins/Input/console/Nes_Cpu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,950 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/nes-emu/
-
-#include "Nes_Cpu.h"
-
-#include <string.h>
-#include <limits.h>
-
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-#if BLARGG_NONPORTABLE
-	#define PAGE_OFFSET( addr ) (addr)
-#else
-	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
-#endif
-
-Nes_Cpu::Nes_Cpu()
-{
-	callback_data = NULL;
-	reset();
-}
-
-inline void Nes_Cpu::set_code_page( int i, uint8_t const* p )
-{
-	code_map [i] = p - PAGE_OFFSET( i * page_size );
-}
-
-void Nes_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write )
-{
-	r.status = 0;
-	r.sp = 0;
-	r.pc = 0;
-	r.a = 0;
-	r.x = 0;
-	r.y = 0;
-	
-	clock_count = 0;
-	base_time = 0;
-	clock_limit = 0;
-	irq_time_ = LONG_MAX / 2 + 1;
-	end_time_ = LONG_MAX / 2 + 1;
-	
-	for ( int i = 0; i < page_count + 1; i++ )
-	{
-		set_code_page( i, (uint8_t*) unmapped_code_page );
-		data_reader [i] = read;
-		data_writer [i] = write;
-	}
-}
-
-void Nes_Cpu::map_code( nes_addr_t start, unsigned long size, const void* data )
-{
-	// address range must begin and end on page boundaries
-	require( start % page_size == 0 );
-	require( size % page_size == 0 );
-	require( start + size <= 0x10000 );
-	
-	unsigned first_page = start / page_size;
-	for ( unsigned i = size / page_size; i--; )
-		set_code_page( first_page + i, (uint8_t*) data + i * page_size );
-}
-
-void Nes_Cpu::set_reader( nes_addr_t start, unsigned long size, reader_t func )
-{
-	// address range must begin and end on page boundaries
-	require( start % page_size == 0 );
-	require( size % page_size == 0 );
-	require( start + size <= 0x10000 + page_size );
-	
-	unsigned first_page = start / page_size;
-	for ( unsigned i = size / page_size; i--; )
-		data_reader [first_page + i] = func;
-}
-
-void Nes_Cpu::set_writer( nes_addr_t start, unsigned long size, writer_t func )
-{
-	// address range must begin and end on page boundaries
-	require( start % page_size == 0 );
-	require( size % page_size == 0 );
-	require( start + size <= 0x10000 + page_size );
-	
-	unsigned first_page = start / page_size;
-	for ( unsigned i = size / page_size; i--; )
-		data_writer [first_page + i] = func;
-}
-
-// 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_LOW( addr )        (low_mem [int (addr)])
-#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
-
-#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )])
-#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
-
-#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
-#define GET_SP()        ((sp - 1) & 0xff)
-
-#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
-
-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 clock_table [256] = {
-//  0 1 2 3 4 5 6 7 8 9 A B C D E F
-	7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
-	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
-	6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
-	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
-	6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
-	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
-	6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
-	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
-	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
-	3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
-	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
-	3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
-	2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
-	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
-	2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
-	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
-};
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end )
-{
-	set_end_time( end );
-	
-	volatile result_t result = result_cycles;
-	
-#if BLARGG_CPU_POWERPC
-	// cache commonly-used values in registers
-	long clock_count = this->clock_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
-	
-	// registers
-	unsigned pc = r.pc;
-	int sp;
-	SET_SP( r.sp );
-	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_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 IS_NEG (nz & 0x880)
-	
-	#define CALC_STATUS( out ) do {             \
-		out = status & (st_v | st_d | st_i);    \
-		out |= (c >> 8) & st_c;                 \
-		if ( IS_NEG ) out |= st_n;              \
-		if ( !(nz & 0xFF) ) out |= st_z;        \
-	} while ( false )
-
-	#define SET_STATUS( in ) do {               \
-		status = in & (st_v | st_d | st_i);     \
-		c = in << 8;                            \
-		nz = (in << 4) & 0x800;                 \
-		nz |= ~in & st_z;                       \
-	} while ( false )
-	
-	int status;
-	int c;  // carry set if (c & 0x100) != 0
-	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
-	{
-		int temp = r.status;
-		SET_STATUS( temp );
-	}
-	
-	goto loop;
-dec_clock_loop:
-	clock_count--;
-loop:
-	
-	assert( unsigned (GET_SP()) < 0x100 );
-	assert( unsigned (a) < 0x100 );
-	assert( unsigned (x) < 0x100 );
-	assert( unsigned (y) < 0x100 );
-	
-	uint8_t const* page = code_map [pc >> page_bits];
-	unsigned opcode = page [PAGE_OFFSET( pc )];
-	unsigned data = page [PAGE_OFFSET( pc ) + 1];
-	pc++;
-	
-	// page crossing
-	//check( opcode == 0x60 || &READ_PROG( pc ) == &page [PAGE_OFFSET( pc )] );
-	
-	if ( clock_count >= clock_limit )
-		goto stop;
-	
-	clock_count += clock_table [opcode];
-	
-	#if BLARGG_CPU_POWERPC
-		this->clock_count = clock_count;
-	#endif
-	
-	switch ( opcode )
-	{
-
-// Macros
-
-#define ADD_PAGE        (pc++, data += 0x100 * READ_PROG( pc ));
-#define GET_ADDR()      READ_PROG16( pc )
-
-#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8;
-
-#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
-
-#define IND_Y {                                                 \
-		int temp = READ_LOW( data ) + y;                        \
-		data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );   \
-		HANDLE_PAGE_CROSSING( temp );                           \
-	}
-	
-#define IND_X {                                                 \
-		int temp = data + x;                                    \
-		data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \
-	}
-	
-#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 = READ_LOW( 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:                                \
-
-#define BRANCH( cond )      \
-{                           \
-	pc++;                   \
-	int offset = (BOOST::int8_t) data;  \
-	int extra_clock = (pc & 0xff) + offset; \
-	if ( !(cond) ) goto dec_clock_loop; \
-	pc += offset;       \
-	clock_count += (extra_clock >> 8) & 1;  \
-	goto loop;          \
-}
-
-// Often-Used
-
-	case 0xB5: // LDA zp,x
-		data = uint8_t (data + x);
-	case 0xA5: // LDA zp
-		a = nz = READ_LOW( data );
-		pc++;
-		goto loop;
-	
-	case 0xD0: // BNE
-		BRANCH( (uint8_t) nz );
-	
-	case 0x20: { // JSR
-		int temp = pc + 1;
-		pc = READ_PROG16( pc );
-		WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
-		sp = (sp - 2) | 0x100;
-		WRITE_LOW( sp, temp );
-		goto loop;
-	}
-	
-	case 0x4C: // JMP abs
-		pc = READ_PROG16( pc );
-		goto loop;
-	
-	case 0xE8: INC_DEC_XY( x, 1 )  // INX
-	
-	case 0x10: // BPL
-		BRANCH( !IS_NEG )
-	
-	ARITH_ADDR_MODES( 0xC5 ) // CMP
-		nz = a - data;
-		pc++;
-		c = ~nz;
-		nz &= 0xff;
-		goto loop;
-	
-	case 0x30: // BMI
-		BRANCH( IS_NEG )
-	
-	case 0xF0: // BEQ
-		BRANCH( !(uint8_t) nz );
-	
-	case 0x95: // STA zp,x
-		data = uint8_t (data + x);
-	case 0x85: // STA zp
-		pc++;
-		WRITE_LOW( data, a );
-		goto loop;
-	
-	case 0xC8: INC_DEC_XY( y, 1 )  // INY
-
-	case 0xA8: // TAY
-		y = a;
-	case 0x98: // TYA
-		a = nz = y;
-		goto loop;
-	
-	case 0xB9: // LDA abs,Y
-		data += y;
-		goto lda_ind_common;
-	
-	case 0xBD: // LDA abs,X
-		data += x;
-	lda_ind_common:
-		HANDLE_PAGE_CROSSING( data );
-	case 0xAD: // LDA abs
-		ADD_PAGE
-	lda_ptr:
-		a = nz = READ( data );
-		pc++;
-		goto loop;
-	
-	case 0x60: // RTS
-		pc = 1 + READ_LOW( sp );
-		pc += READ_LOW( 0x100 | (sp - 0xff) ) * 0x100;
-		sp = (sp - 0xfe) | 0x100;
-		goto loop;
-
-	case 0x99: // STA abs,Y
-		data += y;
-		goto sta_ind_common;
-	
-	case 0x9D: // STA abs,X
-		data += x;
-	sta_ind_common:
-		HANDLE_PAGE_CROSSING( data );
-	case 0x8D: // STA abs
-		ADD_PAGE
-	sta_ptr:
-		pc++;
-		WRITE( data, a );
-		goto loop;
-	
-	case 0xA9: // LDA #imm
-		pc++;
-		a = data;
-		nz = data;
-		goto loop;
-
-// Branch
-
-	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) )
-	
-// Load/store
-	
-	case 0x94: // STY zp,x
-		data = uint8_t (data + x);
-	case 0x84: // STY zp
-		pc++;
-		WRITE_LOW( data, y );
-		goto loop;
-	
-	case 0x96: // STX zp,y
-		data = uint8_t (data + y);
-	case 0x86: // STX zp
-		pc++;
-		WRITE_LOW( data, x );
-		goto loop;
-	
-	case 0xB6: // LDX zp,y
-		data = uint8_t (data + y);
-	case 0xA6: // LDX zp
-		data = READ_LOW( data );
-	case 0xA2: // LDX #imm
-		pc++;
-		x = data;
-		nz = data;
-		goto loop;
-	
-	case 0xB4: // LDY zp,x
-		data = uint8_t (data + x);
-	case 0xA4: // LDY zp
-		data = READ_LOW( data );
-	case 0xA0: // LDY #imm
-		pc++;
-		y = data;
-		nz = data;
-		goto loop;
-	
-	case 0xB1: // LDA (ind),Y
-		IND_Y
-		goto lda_ptr;
-	
-	case 0xA1: // LDA (ind,X)
-		IND_X
-		goto lda_ptr;
-	
-	case 0x91: // STA (ind),Y
-		IND_Y
-		goto sta_ptr;
-	
-	case 0x81: // STA (ind,X)
-		IND_X
-		goto sta_ptr;
-	
-	case 0xBC: // LDY abs,X
-		data += x;
-		HANDLE_PAGE_CROSSING( data );
-	case 0xAC:{// LDY abs
-		pc++;
-		unsigned addr = data + 0x100 * READ_PROG( pc );
-		pc++;
-		y = nz = READ( addr );
-		goto loop;
-	}
-	
-	case 0xBE: // LDX abs,y
-		data += y;
-		HANDLE_PAGE_CROSSING( data );
-	case 0xAE:{// LDX abs
-		pc++;
-		unsigned addr = data + 0x100 * READ_PROG( pc );
-		pc++;
-		x = nz = READ( addr );
-		goto loop;
-	}
-	
-	{
-		int temp;
-	case 0x8C: // STY abs
-		temp = y;
-		goto store_abs;
-	
-	case 0x8E: // STX abs
-		temp = x;
-	store_abs:
-		unsigned 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 = READ_LOW( data );
-	case 0xE0: // CPX #imm
-	cpx_data:
-		nz = x - data;
-		pc++;
-		c = ~nz;
-		nz &= 0xff;
-		goto loop;
-	
-	case 0xCC:{// CPY abs
-		unsigned addr = GET_ADDR();
-		pc++;
-		data = READ( addr );
-		goto cpy_data;
-	}
-	
-	case 0xC4: // CPY zp
-		data = READ_LOW( data );
-	case 0xC0: // CPY #imm
-	cpy_data:
-		nz = y - data;
-		pc++;
-		c = ~nz;
-		nz &= 0xff;
-		goto loop;
-	
-// Logical
-
-	ARITH_ADDR_MODES( 0x25 ) // AND
-		nz = (a &= data);
-		pc++;
-		goto loop;
-	
-	ARITH_ADDR_MODES( 0x45 ) // EOR
-		nz = (a ^= data);
-		pc++;
-		goto loop;
-	
-	ARITH_ADDR_MODES( 0x05 ) // ORA
-		nz = (a |= data);
-		pc++;
-		goto loop;
-	
-	case 0x2C:{// BIT abs
-		unsigned addr = GET_ADDR();
-		pc++;
-		nz = READ( addr );
-		goto bit_common;
-	}
-	
-	case 0x24: // BIT zp
-		nz = READ_LOW( data );
-	bit_common:
-		pc++;
-		status &= ~st_v;
-		status |= nz & st_v;
-		// if result is zero, might also be negative, so use secondary N bit
-		if ( !(a & nz) )
-			nz = (nz << 4) & 0x800;
-		goto loop;
-		
-// Add/subtract
-
-	ARITH_ADDR_MODES( 0xE5 ) // SBC
-	case 0xEB: // unofficial equivalent
-		data ^= 0xFF;
-		goto adc_imm;
-	
-	ARITH_ADDR_MODES( 0x65 ) // ADC
-	adc_imm: {
-		int carry = (c >> 8) & 1;
-		int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
-		status &= ~st_v;
-		status |= (ov >> 2) & 0x40;
-		c = nz = a + data + carry;
-		pc++;
-		a = (uint8_t) nz;
-		goto 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:
-		pc++;
-		WRITE( data, (uint8_t) nz );
-		goto 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 = READ_LOW( 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 = READ_LOW( data ) << 1);
-		goto write_nz_zp;
-	
-// Increment/decrement
-
-	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 += READ_LOW( data );
-	write_nz_zp:
-		pc++;
-		WRITE_LOW( data, nz );
-		goto 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 = READ_LOW( sp );
-		sp = (sp - 0xff) | 0x100;
-		goto loop;
-		
-	case 0x40: // RTI
-		{
-			int temp = READ_LOW( sp );
-			pc   = READ_LOW( 0x100 | (sp - 0xff) );
-			pc  |= READ_LOW( 0x100 | (sp - 0xfe) ) * 0x100;
-			sp = (sp - 0xfd) | 0x100;
-			data = status;
-			SET_STATUS( temp );
-		}
-		if ( !((data ^ status) & st_i) )
-			goto loop; // I flag didn't change
-	i_flag_changed:
-		//dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") );
-		this->r.status = status; // update externally-visible I flag
-		// update clock_limit based on modified I flag
-		clock_limit = end_time_;
-		if ( end_time_ <= irq_time_ )
-			goto loop;
-		if ( status & st_i )
-			goto loop;
-		clock_limit = irq_time_;
-		goto loop;
-	
-	case 0x28:{// PLP
-		int temp = READ_LOW( sp );
-		sp = (sp - 0xff) | 0x100;
-		data = status;
-		SET_STATUS( temp );
-		if ( !((data ^ status) & st_i) )
-			goto loop; // I flag didn't change
-		if ( !(status & st_i) )
-			goto handle_cli;
-		goto handle_sei;
-	}
-	
-	case 0x08: { // PHP
-		int temp;
-		CALC_STATUS( temp );
-		PUSH( temp | st_b | st_r );
-		goto loop;
-	}
-	
-	case 0x6C: // JMP (ind)
-		data = GET_ADDR();
-		pc = READ( data );
-		pc |= READ( (data & 0xff00) | ((data + 1) & 0xff) ) << 8;
-		goto loop;
-	
-	case 0x00: { // BRK
-		pc++;
-		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
-		WRITE_LOW( 0x100 | (sp - 2), pc );
-		int temp;
-		CALC_STATUS( temp );
-		sp = (sp - 3) | 0x100;
-		WRITE_LOW( sp, temp | st_b | st_r );
-		pc = READ_PROG16( 0xFFFE );
-		status |= st_i;
-		goto i_flag_changed;
-	}
-	
-// 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 ( !(status & st_i) )
-			goto loop;
-		status &= ~st_i;
-	handle_cli:
-		//dprintf( "%6d CLI\n", time() );
-		this->r.status = status; // update externally-visible I flag
-		if ( clock_count < end_time_ )
-		{
-			assert( clock_limit == end_time_ );
-			if ( end_time_ <= irq_time_ )
-				goto loop; // irq is later
-			if ( clock_count >= irq_time_ )
-				irq_time_ = clock_count + 1; // delay IRQ until after next instruction
-			clock_limit = irq_time_;
-			goto loop;
-		}
-		// execution is stopping now, so delayed CLI must be handled by caller
-		result = result_cli;
-		goto end;
-		
-	case 0x78: // SEI
-		if ( status & st_i )
-			goto loop;
-		status |= st_i;
-	handle_sei:
-		//dprintf( "%6d SEI\n", time() );
-		this->r.status = status; // update externally-visible I flag
-		clock_limit = end_time_;
-		if ( clock_count < irq_time_ )
-			goto loop;
-		result = result_sei; // IRQ will occur now, even though I flag is set
-		goto end;
-
-// 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:
-		pc++;
-	case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP
-		goto loop;
-
-// Unimplemented
-	
-	case page_wrap_opcode: // HLT
-		if ( pc > 0x10000 )
-		{
-			// handle wrap-around (assumes caller has put page of HLT at 0x10000)
-			pc = (pc - 1) & 0xffff;
-			clock_count -= 2;
-			goto loop;
-		}
-		// fall through
-	case 0x02: case 0x12: case 0x22: case 0x32: // HLT
-	case 0x42: case 0x52: case 0x62: case 0x72:
-	case 0x92: case 0xB2: case 0xD2:
-	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 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;
-	}
-	
-	// If this fails then the case above is missing an opcode
-	assert( false );
-	
-stop:
-	pc--;
-end:
-	
-	{
-		int temp;
-		CALC_STATUS( temp );
-		r.status = temp;
-	}
-	
-	base_time += clock_count;
-	clock_limit -= clock_count;
-	this->clock_count = 0;
-	r.pc = pc;
-	r.sp = GET_SP();
-	r.a = a;
-	r.x = x;
-	r.y = y;
-	irq_time_ = LONG_MAX / 2 + 1;
-	
-	return result;
-}
-
-#endif
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Cpu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,950 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/nes-emu/
+
+#include "Nes_Cpu.h"
+
+#include <string.h>
+#include <limits.h>
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+#if BLARGG_NONPORTABLE
+	#define PAGE_OFFSET( addr ) (addr)
+#else
+	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
+Nes_Cpu::Nes_Cpu()
+{
+	callback_data = NULL;
+	reset();
+}
+
+inline void Nes_Cpu::set_code_page( int i, uint8_t const* p )
+{
+	code_map [i] = p - PAGE_OFFSET( i * page_size );
+}
+
+void Nes_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write )
+{
+	r.status = 0;
+	r.sp = 0;
+	r.pc = 0;
+	r.a = 0;
+	r.x = 0;
+	r.y = 0;
+	
+	clock_count = 0;
+	base_time = 0;
+	clock_limit = 0;
+	irq_time_ = LONG_MAX / 2 + 1;
+	end_time_ = LONG_MAX / 2 + 1;
+	
+	for ( int i = 0; i < page_count + 1; i++ )
+	{
+		set_code_page( i, (uint8_t*) unmapped_code_page );
+		data_reader [i] = read;
+		data_writer [i] = write;
+	}
+}
+
+void Nes_Cpu::map_code( nes_addr_t start, unsigned long size, const void* data )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	require( start + size <= 0x10000 );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		set_code_page( first_page + i, (uint8_t*) data + i * page_size );
+}
+
+void Nes_Cpu::set_reader( nes_addr_t start, unsigned long size, reader_t func )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	require( start + size <= 0x10000 + page_size );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		data_reader [first_page + i] = func;
+}
+
+void Nes_Cpu::set_writer( nes_addr_t start, unsigned long size, writer_t func )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size % page_size == 0 );
+	require( start + size <= 0x10000 + page_size );
+	
+	unsigned first_page = start / page_size;
+	for ( unsigned i = size / page_size; i--; )
+		data_writer [first_page + i] = func;
+}
+
+// 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_LOW( addr )        (low_mem [int (addr)])
+#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
+
+#define READ_PROG( addr )   (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )])
+#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
+
+#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
+#define GET_SP()        ((sp - 1) & 0xff)
+
+#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
+
+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 clock_table [256] = {
+//  0 1 2 3 4 5 6 7 8 9 A B C D E F
+	7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
+	6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
+	6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
+	6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
+	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
+	3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
+	2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
+	3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
+	2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
+	2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
+	3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
+};
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end )
+{
+	set_end_time( end );
+	
+	volatile result_t result = result_cycles;
+	
+#if BLARGG_CPU_POWERPC
+	// cache commonly-used values in registers
+	long clock_count = this->clock_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
+	
+	// registers
+	unsigned pc = r.pc;
+	int sp;
+	SET_SP( r.sp );
+	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_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 IS_NEG (nz & 0x880)
+	
+	#define CALC_STATUS( out ) do {             \
+		out = status & (st_v | st_d | st_i);    \
+		out |= (c >> 8) & st_c;                 \
+		if ( IS_NEG ) out |= st_n;              \
+		if ( !(nz & 0xFF) ) out |= st_z;        \
+	} while ( false )
+
+	#define SET_STATUS( in ) do {               \
+		status = in & (st_v | st_d | st_i);     \
+		c = in << 8;                            \
+		nz = (in << 4) & 0x800;                 \
+		nz |= ~in & st_z;                       \
+	} while ( false )
+	
+	int status;
+	int c;  // carry set if (c & 0x100) != 0
+	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
+	{
+		int temp = r.status;
+		SET_STATUS( temp );
+	}
+	
+	goto loop;
+dec_clock_loop:
+	clock_count--;
+loop:
+	
+	assert( unsigned (GET_SP()) < 0x100 );
+	assert( unsigned (a) < 0x100 );
+	assert( unsigned (x) < 0x100 );
+	assert( unsigned (y) < 0x100 );
+	
+	uint8_t const* page = code_map [pc >> page_bits];
+	unsigned opcode = page [PAGE_OFFSET( pc )];
+	unsigned data = page [PAGE_OFFSET( pc ) + 1];
+	pc++;
+	
+	// page crossing
+	//check( opcode == 0x60 || &READ_PROG( pc ) == &page [PAGE_OFFSET( pc )] );
+	
+	if ( clock_count >= clock_limit )
+		goto stop;
+	
+	clock_count += clock_table [opcode];
+	
+	#if BLARGG_CPU_POWERPC
+		this->clock_count = clock_count;
+	#endif
+	
+	switch ( opcode )
+	{
+
+// Macros
+
+#define ADD_PAGE        (pc++, data += 0x100 * READ_PROG( pc ));
+#define GET_ADDR()      READ_PROG16( pc )
+
+#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8;
+
+#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
+
+#define IND_Y {                                                 \
+		int temp = READ_LOW( data ) + y;                        \
+		data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );   \
+		HANDLE_PAGE_CROSSING( temp );                           \
+	}
+	
+#define IND_X {                                                 \
+		int temp = data + x;                                    \
+		data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \
+	}
+	
+#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 = READ_LOW( 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:                                \
+
+#define BRANCH( cond )      \
+{                           \
+	pc++;                   \
+	int offset = (BOOST::int8_t) data;  \
+	int extra_clock = (pc & 0xff) + offset; \
+	if ( !(cond) ) goto dec_clock_loop; \
+	pc += offset;       \
+	clock_count += (extra_clock >> 8) & 1;  \
+	goto loop;          \
+}
+
+// Often-Used
+
+	case 0xB5: // LDA zp,x
+		data = uint8_t (data + x);
+	case 0xA5: // LDA zp
+		a = nz = READ_LOW( data );
+		pc++;
+		goto loop;
+	
+	case 0xD0: // BNE
+		BRANCH( (uint8_t) nz );
+	
+	case 0x20: { // JSR
+		int temp = pc + 1;
+		pc = READ_PROG16( pc );
+		WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
+		sp = (sp - 2) | 0x100;
+		WRITE_LOW( sp, temp );
+		goto loop;
+	}
+	
+	case 0x4C: // JMP abs
+		pc = READ_PROG16( pc );
+		goto loop;
+	
+	case 0xE8: INC_DEC_XY( x, 1 )  // INX
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG )
+	
+	ARITH_ADDR_MODES( 0xC5 ) // CMP
+		nz = a - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
+	
+	case 0xF0: // BEQ
+		BRANCH( !(uint8_t) nz );
+	
+	case 0x95: // STA zp,x
+		data = uint8_t (data + x);
+	case 0x85: // STA zp
+		pc++;
+		WRITE_LOW( data, a );
+		goto loop;
+	
+	case 0xC8: INC_DEC_XY( y, 1 )  // INY
+
+	case 0xA8: // TAY
+		y = a;
+	case 0x98: // TYA
+		a = nz = y;
+		goto loop;
+	
+	case 0xB9: // LDA abs,Y
+		data += y;
+		goto lda_ind_common;
+	
+	case 0xBD: // LDA abs,X
+		data += x;
+	lda_ind_common:
+		HANDLE_PAGE_CROSSING( data );
+	case 0xAD: // LDA abs
+		ADD_PAGE
+	lda_ptr:
+		a = nz = READ( data );
+		pc++;
+		goto loop;
+	
+	case 0x60: // RTS
+		pc = 1 + READ_LOW( sp );
+		pc += READ_LOW( 0x100 | (sp - 0xff) ) * 0x100;
+		sp = (sp - 0xfe) | 0x100;
+		goto loop;
+
+	case 0x99: // STA abs,Y
+		data += y;
+		goto sta_ind_common;
+	
+	case 0x9D: // STA abs,X
+		data += x;
+	sta_ind_common:
+		HANDLE_PAGE_CROSSING( data );
+	case 0x8D: // STA abs
+		ADD_PAGE
+	sta_ptr:
+		pc++;
+		WRITE( data, a );
+		goto loop;
+	
+	case 0xA9: // LDA #imm
+		pc++;
+		a = data;
+		nz = data;
+		goto loop;
+
+// Branch
+
+	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) )
+	
+// Load/store
+	
+	case 0x94: // STY zp,x
+		data = uint8_t (data + x);
+	case 0x84: // STY zp
+		pc++;
+		WRITE_LOW( data, y );
+		goto loop;
+	
+	case 0x96: // STX zp,y
+		data = uint8_t (data + y);
+	case 0x86: // STX zp
+		pc++;
+		WRITE_LOW( data, x );
+		goto loop;
+	
+	case 0xB6: // LDX zp,y
+		data = uint8_t (data + y);
+	case 0xA6: // LDX zp
+		data = READ_LOW( data );
+	case 0xA2: // LDX #imm
+		pc++;
+		x = data;
+		nz = data;
+		goto loop;
+	
+	case 0xB4: // LDY zp,x
+		data = uint8_t (data + x);
+	case 0xA4: // LDY zp
+		data = READ_LOW( data );
+	case 0xA0: // LDY #imm
+		pc++;
+		y = data;
+		nz = data;
+		goto loop;
+	
+	case 0xB1: // LDA (ind),Y
+		IND_Y
+		goto lda_ptr;
+	
+	case 0xA1: // LDA (ind,X)
+		IND_X
+		goto lda_ptr;
+	
+	case 0x91: // STA (ind),Y
+		IND_Y
+		goto sta_ptr;
+	
+	case 0x81: // STA (ind,X)
+		IND_X
+		goto sta_ptr;
+	
+	case 0xBC: // LDY abs,X
+		data += x;
+		HANDLE_PAGE_CROSSING( data );
+	case 0xAC:{// LDY abs
+		pc++;
+		unsigned addr = data + 0x100 * READ_PROG( pc );
+		pc++;
+		y = nz = READ( addr );
+		goto loop;
+	}
+	
+	case 0xBE: // LDX abs,y
+		data += y;
+		HANDLE_PAGE_CROSSING( data );
+	case 0xAE:{// LDX abs
+		pc++;
+		unsigned addr = data + 0x100 * READ_PROG( pc );
+		pc++;
+		x = nz = READ( addr );
+		goto loop;
+	}
+	
+	{
+		int temp;
+	case 0x8C: // STY abs
+		temp = y;
+		goto store_abs;
+	
+	case 0x8E: // STX abs
+		temp = x;
+	store_abs:
+		unsigned 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 = READ_LOW( data );
+	case 0xE0: // CPX #imm
+	cpx_data:
+		nz = x - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
+	
+	case 0xCC:{// CPY abs
+		unsigned addr = GET_ADDR();
+		pc++;
+		data = READ( addr );
+		goto cpy_data;
+	}
+	
+	case 0xC4: // CPY zp
+		data = READ_LOW( data );
+	case 0xC0: // CPY #imm
+	cpy_data:
+		nz = y - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xff;
+		goto loop;
+	
+// Logical
+
+	ARITH_ADDR_MODES( 0x25 ) // AND
+		nz = (a &= data);
+		pc++;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x45 ) // EOR
+		nz = (a ^= data);
+		pc++;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x05 ) // ORA
+		nz = (a |= data);
+		pc++;
+		goto loop;
+	
+	case 0x2C:{// BIT abs
+		unsigned addr = GET_ADDR();
+		pc++;
+		nz = READ( addr );
+		goto bit_common;
+	}
+	
+	case 0x24: // BIT zp
+		nz = READ_LOW( data );
+	bit_common:
+		pc++;
+		status &= ~st_v;
+		status |= nz & st_v;
+		// if result is zero, might also be negative, so use secondary N bit
+		if ( !(a & nz) )
+			nz = (nz << 4) & 0x800;
+		goto loop;
+		
+// Add/subtract
+
+	ARITH_ADDR_MODES( 0xE5 ) // SBC
+	case 0xEB: // unofficial equivalent
+		data ^= 0xFF;
+		goto adc_imm;
+	
+	ARITH_ADDR_MODES( 0x65 ) // ADC
+	adc_imm: {
+		int carry = (c >> 8) & 1;
+		int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
+		status &= ~st_v;
+		status |= (ov >> 2) & 0x40;
+		c = nz = a + data + carry;
+		pc++;
+		a = (uint8_t) nz;
+		goto 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:
+		pc++;
+		WRITE( data, (uint8_t) nz );
+		goto 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 = READ_LOW( 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 = READ_LOW( data ) << 1);
+		goto write_nz_zp;
+	
+// Increment/decrement
+
+	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 += READ_LOW( data );
+	write_nz_zp:
+		pc++;
+		WRITE_LOW( data, nz );
+		goto 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 = READ_LOW( sp );
+		sp = (sp - 0xff) | 0x100;
+		goto loop;
+		
+	case 0x40: // RTI
+		{
+			int temp = READ_LOW( sp );
+			pc   = READ_LOW( 0x100 | (sp - 0xff) );
+			pc  |= READ_LOW( 0x100 | (sp - 0xfe) ) * 0x100;
+			sp = (sp - 0xfd) | 0x100;
+			data = status;
+			SET_STATUS( temp );
+		}
+		if ( !((data ^ status) & st_i) )
+			goto loop; // I flag didn't change
+	i_flag_changed:
+		//dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") );
+		this->r.status = status; // update externally-visible I flag
+		// update clock_limit based on modified I flag
+		clock_limit = end_time_;
+		if ( end_time_ <= irq_time_ )
+			goto loop;
+		if ( status & st_i )
+			goto loop;
+		clock_limit = irq_time_;
+		goto loop;
+	
+	case 0x28:{// PLP
+		int temp = READ_LOW( sp );
+		sp = (sp - 0xff) | 0x100;
+		data = status;
+		SET_STATUS( temp );
+		if ( !((data ^ status) & st_i) )
+			goto loop; // I flag didn't change
+		if ( !(status & st_i) )
+			goto handle_cli;
+		goto handle_sei;
+	}
+	
+	case 0x08: { // PHP
+		int temp;
+		CALC_STATUS( temp );
+		PUSH( temp | st_b | st_r );
+		goto loop;
+	}
+	
+	case 0x6C: // JMP (ind)
+		data = GET_ADDR();
+		pc = READ( data );
+		pc |= READ( (data & 0xff00) | ((data + 1) & 0xff) ) << 8;
+		goto loop;
+	
+	case 0x00: { // BRK
+		pc++;
+		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
+		WRITE_LOW( 0x100 | (sp - 2), pc );
+		int temp;
+		CALC_STATUS( temp );
+		sp = (sp - 3) | 0x100;
+		WRITE_LOW( sp, temp | st_b | st_r );
+		pc = READ_PROG16( 0xFFFE );
+		status |= st_i;
+		goto i_flag_changed;
+	}
+	
+// 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 ( !(status & st_i) )
+			goto loop;
+		status &= ~st_i;
+	handle_cli:
+		//dprintf( "%6d CLI\n", time() );
+		this->r.status = status; // update externally-visible I flag
+		if ( clock_count < end_time_ )
+		{
+			assert( clock_limit == end_time_ );
+			if ( end_time_ <= irq_time_ )
+				goto loop; // irq is later
+			if ( clock_count >= irq_time_ )
+				irq_time_ = clock_count + 1; // delay IRQ until after next instruction
+			clock_limit = irq_time_;
+			goto loop;
+		}
+		// execution is stopping now, so delayed CLI must be handled by caller
+		result = result_cli;
+		goto end;
+		
+	case 0x78: // SEI
+		if ( status & st_i )
+			goto loop;
+		status |= st_i;
+	handle_sei:
+		//dprintf( "%6d SEI\n", time() );
+		this->r.status = status; // update externally-visible I flag
+		clock_limit = end_time_;
+		if ( clock_count < irq_time_ )
+			goto loop;
+		result = result_sei; // IRQ will occur now, even though I flag is set
+		goto end;
+
+// 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:
+		pc++;
+	case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP
+		goto loop;
+
+// Unimplemented
+	
+	case page_wrap_opcode: // HLT
+		if ( pc > 0x10000 )
+		{
+			// handle wrap-around (assumes caller has put page of HLT at 0x10000)
+			pc = (pc - 1) & 0xffff;
+			clock_count -= 2;
+			goto loop;
+		}
+		// fall through
+	case 0x02: case 0x12: case 0x22: case 0x32: // HLT
+	case 0x42: case 0x52: case 0x62: case 0x72:
+	case 0x92: case 0xB2: case 0xD2:
+	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 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;
+	}
+	
+	// If this fails then the case above is missing an opcode
+	assert( false );
+	
+stop:
+	pc--;
+end:
+	
+	{
+		int temp;
+		CALC_STATUS( temp );
+		r.status = temp;
+	}
+	
+	base_time += clock_count;
+	clock_limit -= clock_count;
+	this->clock_count = 0;
+	r.pc = pc;
+	r.sp = GET_SP();
+	r.a = a;
+	r.x = x;
+	r.y = y;
+	irq_time_ = LONG_MAX / 2 + 1;
+	
+	return result;
+}
+
+#endif
+
--- a/Plugins/Input/console/Nes_Fme7_Apu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Nes_Fme7_Apu.h"
-
-#include <string.h>
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-void Nes_Fme7_Apu::reset()
-{
-	last_time = 0;
-	
-	for ( int i = 0; i < osc_count; i++ )
-		oscs [i].last_amp = 0;
-	
-	fme7_snapshot_t* state = this;
-	memset( state, 0, sizeof *state );
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-unsigned char Nes_Fme7_Apu::amp_table [16] =
-{
-	#define ENTRY( n ) (unsigned char) (n * amp_range + 0.5)
-	ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156),
-	ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624),
-	ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498),
-	ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
-	#undef ENTRY
-};
-
-void Nes_Fme7_Apu::run_until( blip_time_t end_time )
-{
-	require( end_time >= last_time );
-	
-	for ( int index = 0; index < osc_count; index++ )
-	{
-		int mode = regs [7] >> index;
-		int vol_mode = regs [010 + index];
-		int volume = amp_table [vol_mode & 0x0f];
-		
-		if ( !oscs [index].output )
-			continue;
-		
-		// check for unsupported mode
-		#ifndef NDEBUG
-			if ( (mode & 011) <= 001 && vol_mode & 0x1f )
-				dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n",
-						mode, vol_mode & 0x1f );
-		#endif
-		
-		if ( (mode & 001) | (vol_mode & 0x10) )
-			volume = 0; // noise and envelope aren't supported
-		
-		// period
-		int const period_factor = 16;
-		unsigned period = (regs [index * 2 + 1] & 0x0f) * 0x100 * period_factor +
-				regs [index * 2] * period_factor;
-		if ( period < 50 ) // around 22 kHz
-		{
-			volume = 0;
-			if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
-				period = period_factor;
-		}
-		
-		// current amplitude
-		int amp = volume;
-		if ( !phases [index] )
-			amp = 0;
-		int delta = amp - oscs [index].last_amp;
-		if ( delta )
-		{
-			oscs [index].last_amp = amp;
-			synth.offset( last_time, delta, oscs [index].output );
-		}
-		
-		blip_time_t time = last_time + delays [index];
-		if ( time < end_time )
-		{
-			Blip_Buffer* const osc_output = oscs [index].output;
-			int delta = amp * 2 - volume;
-			
-			if ( volume )
-			{
-				do
-				{
-					delta = -delta;
-					synth.offset_inline( time, delta, osc_output );
-					time += period;
-				}
-				while ( time < end_time );
-				
-				oscs [index].last_amp = (delta + volume) >> 1;
-				phases [index] = (delta > 0);
-			}
-			else
-			{
-				// maintain phase when silent
-				int count = (end_time - time + period - 1) / period;
-				phases [index] ^= count & 1;
-				time += (long) count * period;
-			}
-		}
-		
-		delays [index] = time - end_time;
-	}
-	
-	last_time = end_time;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nes_Fme7_Apu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,122 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Nes_Fme7_Apu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+void Nes_Fme7_Apu::reset()
+{
+	last_time = 0;
+	
+	for ( int i = 0; i < osc_count; i++ )
+		oscs [i].last_amp = 0;
+	
+	fme7_snapshot_t* state = this;
+	memset( state, 0, sizeof *state );
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+unsigned char Nes_Fme7_Apu::amp_table [16] =
+{
+	#define ENTRY( n ) (unsigned char) (n * amp_range + 0.5)
+	ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156),
+	ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624),
+	ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498),
+	ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
+	#undef ENTRY
+};
+
+void Nes_Fme7_Apu::run_until( blip_time_t end_time )
+{
+	require( end_time >= last_time );
+	
+	for ( int index = 0; index < osc_count; index++ )
+	{
+		int mode = regs [7] >> index;
+		int vol_mode = regs [010 + index];
+		int volume = amp_table [vol_mode & 0x0f];
+		
+		if ( !oscs [index].output )
+			continue;
+		
+		// check for unsupported mode
+		#ifndef NDEBUG
+			if ( (mode & 011) <= 001 && vol_mode & 0x1f )
+				dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n",
+						mode, vol_mode & 0x1f );
+		#endif
+		
+		if ( (mode & 001) | (vol_mode & 0x10) )
+			volume = 0; // noise and envelope aren't supported
+		
+		// period
+		int const period_factor = 16;
+		unsigned period = (regs [index * 2 + 1] & 0x0f) * 0x100 * period_factor +
+				regs [index * 2] * period_factor;
+		if ( period < 50 ) // around 22 kHz
+		{
+			volume = 0;
+			if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
+				period = period_factor;
+		}
+		
+		// current amplitude
+		int amp = volume;
+		if ( !phases [index] )
+			amp = 0;
+		int delta = amp - oscs [index].last_amp;
+		if ( delta )
+		{
+			oscs [index].last_amp = amp;
+			synth.offset( last_time, delta, oscs [index].output );
+		}
+		
+		blip_time_t time = last_time + delays [index];
+		if ( time < end_time )
+		{
+			Blip_Buffer* const osc_output = oscs [index].output;
+			int delta = amp * 2 - volume;
+			
+			if ( volume )
+			{
+				do
+				{
+					delta = -delta;
+					synth.offset_inline( time, delta, osc_output );
+					time += period;
+				}
+				while ( time < end_time );
+				
+				oscs [index].last_amp = (delta + volume) >> 1;
+				phases [index] = (delta > 0);
+			}
+			else
+			{
+				// maintain phase when silent
+				int count = (end_time - time + period - 1) / period;
+				phases [index] ^= count & 1;
+				time += (long) count * period;
+			}
+		}
+		
+		delays [index] = time - end_time;
+	}
+	
+	last_time = end_time;
+}
+
--- a/Plugins/Input/console/Nes_Namco_Apu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-
-// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
-
-#include "Nes_Namco_Apu.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Nes_Namco_Apu::Nes_Namco_Apu()
-{
-	output( NULL );
-	volume( 1.0 );
-	reset();
-}
-
-Nes_Namco_Apu::~Nes_Namco_Apu()
-{
-}
-
-void Nes_Namco_Apu::reset()
-{
-	last_time = 0;
-	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_Apu::output( Blip_Buffer* buf )
-{
-	for ( int i = 0; i < osc_count; i++ )
-		osc_output( i, buf );
-}
-
-/*
-void Nes_Namco_Apu::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 );
-	}
-}
-*/
-
-void Nes_Namco_Apu::end_frame( nes_time_t time )
-{
-	if ( time > last_time )
-		run_until( time );
-	
-	assert( last_time >= time );
-	last_time -= time;
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Nes_Namco_Apu::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_resampled_time_t time =
-				output->resampled_time( last_time ) + osc.delay;
-		blip_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 < 64 * active_oscs )
-				continue; // prevent low frequencies from excessively delaying freq changes
-			blip_resampled_time_t period =
-					output->resampled_duration( 983040 ) / freq * active_oscs;
-			
-			int wave_size = 32 - (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] >> (addr << 2 & 4);
-				wave_pos++;
-				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_Apu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,151 @@
+
+// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
+
+#include "Nes_Namco_Apu.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Nes_Namco_Apu::Nes_Namco_Apu()
+{
+	output( NULL );
+	volume( 1.0 );
+	reset();
+}
+
+Nes_Namco_Apu::~Nes_Namco_Apu()
+{
+}
+
+void Nes_Namco_Apu::reset()
+{
+	last_time = 0;
+	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_Apu::output( Blip_Buffer* buf )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, buf );
+}
+
+/*
+void Nes_Namco_Apu::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 );
+	}
+}
+*/
+
+void Nes_Namco_Apu::end_frame( nes_time_t time )
+{
+	if ( time > last_time )
+		run_until( time );
+	
+	assert( last_time >= time );
+	last_time -= time;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Nes_Namco_Apu::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_resampled_time_t time =
+				output->resampled_time( last_time ) + osc.delay;
+		blip_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 < 64 * active_oscs )
+				continue; // prevent low frequencies from excessively delaying freq changes
+			blip_resampled_time_t period =
+					output->resampled_duration( 983040 ) / freq * active_oscs;
+			
+			int wave_size = 32 - (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] >> (addr << 2 & 4);
+				wave_pos++;
+				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;
+}
+
--- a/Plugins/Input/console/Nes_Oscs.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,498 +0,0 @@
-
-// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
-
-#include "Nes_Apu.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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;
-}
-
-inline int Nes_Triangle::calc_amp() const
-{
-	int amp = phase_range - phase;
-	if ( amp < 0 )
-		amp = phase - (phase_range + 1);
-	return amp;
-}
-
-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.
-	
-	int delta = update_amp( calc_amp() );
-	if ( delta )
-		synth.offset( time, delta, output );
-	
-	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;
-		last_amp = calc_amp();
- 	}
-	delay = time - end_time;
-}
-
-// Nes_Dmc
-
-void Nes_Dmc::reset()
-{
-	address = 0;
-	dac = 0;
-	buf = 0;
-	bits_remain = 1;
-	bits = 0;
-	buf_full = false;
-	silence = true;
-	next_irq = Nes_Apu::no_irq;
-	irq_flag = false;
-	irq_enabled = false;
-	
-	Nes_Osc::reset();
-	period = 0x1ac;
-}
-
-void Nes_Dmc::recalc_irq()
-{
-	nes_time_t irq = Nes_Apu::no_irq;
-	if ( irq_enabled && length_counter )
-		irq = apu->last_dmc_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 = next_read_time();
-	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, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14,
-	15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
-	27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
-	39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
-	50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
-	59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
-	68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
-	76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
-};
-
-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 )
-	{
-		int old_dac = dac;
-		dac = data & 0x7F;
-		
-		// adjust last_amp so that "pop" amplitude will be properly non-linear
-		// with respect to change in dac
-		int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]);
-		if ( !nonlinear )
-			last_amp = faked_nonlinear;
-	}
-}
-
-void Nes_Dmc::start()
-{
-	reload_sample();
-	fill_buffer();
-	recalc_irq();
-}
-
-void Nes_Dmc::fill_buffer()
-{
-	if ( !buf_full && length_counter )
-	{
-		require( rom_reader ); // rom_reader must be set
-		buf = rom_reader( rom_reader_data, 0x8000u + address );
-		address = (address + 1) & 0x7FFF;
-		buf_full = true;
-		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 )
-{
-	int delta = update_amp( dac );
-	if ( !output )
-		silence = true;
-	else if ( delta )
-		synth.offset( time, delta, output );
-	
-	time += delay;
-	if ( time < end_time )
-	{
-		int bits_remain = this->bits_remain;
-		if ( silence && !buf_full )
-		{
-			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 )
-				{
-					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_full ) {
-						silence = true;
-					}
-					else {
-						silence = false;
-						bits = buf;
-						buf_full = false;
-						if ( !output )
-							silence = 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
-			// 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_resampled_time_t rperiod = output->resampled_duration( period );
-			blip_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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,498 @@
+
+// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
+
+#include "Nes_Apu.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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;
+}
+
+inline int Nes_Triangle::calc_amp() const
+{
+	int amp = phase_range - phase;
+	if ( amp < 0 )
+		amp = phase - (phase_range + 1);
+	return amp;
+}
+
+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.
+	
+	int delta = update_amp( calc_amp() );
+	if ( delta )
+		synth.offset( time, delta, output );
+	
+	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;
+		last_amp = calc_amp();
+ 	}
+	delay = time - end_time;
+}
+
+// Nes_Dmc
+
+void Nes_Dmc::reset()
+{
+	address = 0;
+	dac = 0;
+	buf = 0;
+	bits_remain = 1;
+	bits = 0;
+	buf_full = false;
+	silence = true;
+	next_irq = Nes_Apu::no_irq;
+	irq_flag = false;
+	irq_enabled = false;
+	
+	Nes_Osc::reset();
+	period = 0x1ac;
+}
+
+void Nes_Dmc::recalc_irq()
+{
+	nes_time_t irq = Nes_Apu::no_irq;
+	if ( irq_enabled && length_counter )
+		irq = apu->last_dmc_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 = next_read_time();
+	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, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14,
+	15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
+	27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
+	39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
+	50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
+	59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
+	68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
+	76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
+};
+
+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 )
+	{
+		int old_dac = dac;
+		dac = data & 0x7F;
+		
+		// adjust last_amp so that "pop" amplitude will be properly non-linear
+		// with respect to change in dac
+		int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]);
+		if ( !nonlinear )
+			last_amp = faked_nonlinear;
+	}
+}
+
+void Nes_Dmc::start()
+{
+	reload_sample();
+	fill_buffer();
+	recalc_irq();
+}
+
+void Nes_Dmc::fill_buffer()
+{
+	if ( !buf_full && length_counter )
+	{
+		require( rom_reader ); // rom_reader must be set
+		buf = rom_reader( rom_reader_data, 0x8000u + address );
+		address = (address + 1) & 0x7FFF;
+		buf_full = true;
+		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 )
+{
+	int delta = update_amp( dac );
+	if ( !output )
+		silence = true;
+	else if ( delta )
+		synth.offset( time, delta, output );
+	
+	time += delay;
+	if ( time < end_time )
+	{
+		int bits_remain = this->bits_remain;
+		if ( silence && !buf_full )
+		{
+			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 )
+				{
+					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_full ) {
+						silence = true;
+					}
+					else {
+						silence = false;
+						bits = buf;
+						buf_full = false;
+						if ( !output )
+							silence = 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
+			// 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_resampled_time_t rperiod = output->resampled_duration( period );
+			blip_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;
+}
+
--- a/Plugins/Input/console/Nes_Vrc6_Apu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,219 +0,0 @@
-
-// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
-
-#include "Nes_Vrc6_Apu.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Nes_Vrc6_Apu::Nes_Vrc6_Apu()
-{
-	output( NULL );
-	volume( 1.0 );
-	reset();
-}
-
-Nes_Vrc6_Apu::~Nes_Vrc6_Apu()
-{
-}
-
-void Nes_Vrc6_Apu::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;
-	}
-}
-
-void Nes_Vrc6_Apu::output( Blip_Buffer* buf )
-{
-	for ( int i = 0; i < osc_count; i++ )
-		osc_output( i, buf );
-}
-
-void Nes_Vrc6_Apu::run_until( nes_time_t time )
-{
-	require( time >= last_time );
-	run_square( oscs [0], time );
-	run_square( oscs [1], time );
-	run_saw( time );
-	last_time = time;
-}
-
-void Nes_Vrc6_Apu::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;
-}
-
-void Nes_Vrc6_Apu::end_frame( nes_time_t time )
-{
-	if ( time > last_time )
-		run_until( time );
-	
-	assert( last_time >= time );
-	last_time -= time;
-}
-
-void Nes_Vrc6_Apu::save_snapshot( vrc6_snapshot_t* out ) const
-{
-	out->saw_amp = oscs [2].amp;
-	for ( int i = 0; i < osc_count; i++ )
-	{
-		Vrc6_Osc const& osc = oscs [i];
-		for ( int r = 0; r < reg_count; r++ )
-			out->regs [i] [r] = osc.regs [r];
-		
-		out->delays [i] = osc.delay;
-		out->phases [i] = osc.phase;
-	}
-}
-
-void Nes_Vrc6_Apu::load_snapshot( vrc6_snapshot_t const& in )
-{
-	reset();
-	oscs [2].amp = in.saw_amp;
-	for ( int i = 0; i < osc_count; i++ )
-	{
-		Vrc6_Osc& osc = oscs [i];
-		for ( int r = 0; r < reg_count; r++ )
-			osc.regs [r] = in.regs [i] [r];
-		
-		osc.delay = in.delays [i];
-		osc.phase = in.phases [i];
-	}
-	if ( !oscs [2].phase )
-		oscs [2].phase = 1;
-}
-
-#include BLARGG_ENABLE_OPTIMIZER
-
-void Nes_Vrc6_Apu::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_Apu::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_Apu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,219 @@
+
+// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
+
+#include "Nes_Vrc6_Apu.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Nes_Vrc6_Apu::Nes_Vrc6_Apu()
+{
+	output( NULL );
+	volume( 1.0 );
+	reset();
+}
+
+Nes_Vrc6_Apu::~Nes_Vrc6_Apu()
+{
+}
+
+void Nes_Vrc6_Apu::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;
+	}
+}
+
+void Nes_Vrc6_Apu::output( Blip_Buffer* buf )
+{
+	for ( int i = 0; i < osc_count; i++ )
+		osc_output( i, buf );
+}
+
+void Nes_Vrc6_Apu::run_until( nes_time_t time )
+{
+	require( time >= last_time );
+	run_square( oscs [0], time );
+	run_square( oscs [1], time );
+	run_saw( time );
+	last_time = time;
+}
+
+void Nes_Vrc6_Apu::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;
+}
+
+void Nes_Vrc6_Apu::end_frame( nes_time_t time )
+{
+	if ( time > last_time )
+		run_until( time );
+	
+	assert( last_time >= time );
+	last_time -= time;
+}
+
+void Nes_Vrc6_Apu::save_snapshot( vrc6_snapshot_t* out ) const
+{
+	out->saw_amp = oscs [2].amp;
+	for ( int i = 0; i < osc_count; i++ )
+	{
+		Vrc6_Osc const& osc = oscs [i];
+		for ( int r = 0; r < reg_count; r++ )
+			out->regs [i] [r] = osc.regs [r];
+		
+		out->delays [i] = osc.delay;
+		out->phases [i] = osc.phase;
+	}
+}
+
+void Nes_Vrc6_Apu::load_snapshot( vrc6_snapshot_t const& in )
+{
+	reset();
+	oscs [2].amp = in.saw_amp;
+	for ( int i = 0; i < osc_count; i++ )
+	{
+		Vrc6_Osc& osc = oscs [i];
+		for ( int r = 0; r < reg_count; r++ )
+			osc.regs [r] = in.regs [i] [r];
+		
+		osc.delay = in.delays [i];
+		osc.phase = in.phases [i];
+	}
+	if ( !oscs [2].phase )
+		oscs [2].phase = 1;
+}
+
+#include BLARGG_ENABLE_OPTIMIZER
+
+void Nes_Vrc6_Apu::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_Apu::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;
+}
+
--- a/Plugins/Input/console/Nsf_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,623 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Nsf_Emu.h"
-
-#include <string.h>
-#include <stdio.h>
-
-#if !NSF_EMU_APU_ONLY
-	#include "Nes_Vrc6_Apu.h"
-	#include "Nes_Namco_Apu.h"
-	#include "Nes_Fme7_Apu.h"
-#endif
-
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-#ifndef RUN_NES_CPU
-	#define RUN_NES_CPU( cpu, count ) cpu.run( count )
-#endif
-
-#ifndef NSF_BEGIN_FRAME
-	#define NSF_BEGIN_FRAME()
-#endif
-
-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 fds_flag = 0x04;
-const int namco_flag = 0x10;
-const int fme7_flag = 0x20;
-
-static BOOST::uint8_t unmapped_code [Nes_Cpu::page_size];
-
-Nes_Emu::equalizer_t const Nes_Emu::nes_eq     = {  -1.0, 80 };
-Nes_Emu::equalizer_t const Nes_Emu::famicom_eq = { -15.0, 80 };
-
-// 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];
-}
-
-void Nsf_Emu::write_low_mem( Nsf_Emu* emu, nes_addr_t addr, int data )
-{
-	emu->cpu.low_mem [addr] = 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_Apu::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_Apu::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_Apu::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_Apu::addr_step - 1);
-	unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step;
-	if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count )
-		emu->vrc6->write_osc( emu->cpu.time(), osc, reg, data );
-}
-
-// FME-7
-void Nsf_Emu::write_fme7( Nsf_Emu* emu, nes_addr_t addr, int data )
-{
-	switch ( addr & Nes_Fme7_Apu::addr_mask )
-	{
-		case Nes_Fme7_Apu::latch_addr:
-			emu->fme7->write_latch( data );
-			break;
-		
-		case Nes_Fme7_Apu::data_addr:
-			emu->fme7->write_data( emu->cpu.time(), data );
-			break;
-	}
-}
-
-#endif
-
-// Unmapped
-int Nsf_Emu::read_unmapped( Nsf_Emu*, nes_addr_t addr )
-{
-	dprintf( "Read unmapped $%.4X\n", (unsigned) addr );
-	return (addr >> 8) & 0xff; // high byte of address stays on bus
-}
-
-void Nsf_Emu::write_unmapped( Nsf_Emu*, nes_addr_t addr, int data )
-{
-	#ifdef NDEBUG
-		return;
-	#endif
-	
-	// some games write to $8000 and $8001 repeatedly
-	if ( addr == 0x8000 || addr == 0x8001 )
-		return;
-	
-	// probably namco sound mistakenly turned on in mck
-	if ( addr == 0x4800 || addr == 0xF800 )
-		return;
-	
-	// memory mapper?
-	if ( addr == 0xFFF8 )
-		return;
-	
-	dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data );
-}
-
-Nes_Emu::Nes_Emu( double gain_ )
-{
-	cpu.set_emu( this );
-	play_addr = 0;
-	gain = gain_;
-	apu.dmc_reader( pcm_read, this );
-	vrc6 = NULL;
-	namco = NULL;
-	fme7 = NULL;
-	Music_Emu::set_equalizer( nes_eq );
-	
-	// set unmapped code to illegal instruction
-	memset( unmapped_code, 0x32, sizeof unmapped_code );
-}
-
-Nes_Emu::~Nes_Emu()
-{
-	unload();
-}
-
-void Nsf_Emu::unload()
-{
-	#if !NSF_EMU_APU_ONLY
-		delete vrc6;
-		vrc6 = NULL;
-		
-		delete namco;
-		namco = NULL;
-		
-		delete fme7;
-		fme7 = NULL;
-		
-	#endif
-	
-	rom.clear();
-}
-
-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"
-	};
-	static const char* dual_names [] = {
-		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
-		"VRC6.1,N106.5&7", "VRC6.2,N106.4&6", "VRC6.3,N106.1-3"
-	};
-	
-	static const char* fme7_names [] = {
-		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
-		"Square 3", "Square 4", "Square 5"
-	};
-	
-	if ( namco )
-		return vrc6 ? dual_names : namco_names;
-	
-	if ( vrc6 )
-		return vrc6_names;
-	
-	if ( fme7 )
-		return fme7_names;
-	
-	return base_names;
-}
-
-blargg_err_t Nsf_Emu::init_sound()
-{
-	if ( exp_flags & ~(namco_flag | vrc6_flag | fme7_flag | fds_flag) )
-		return "NSF requires unsupported expansion audio hardware";
-	
-	// map memory
-	cpu.reset( unmapped_code, read_unmapped, write_unmapped );
-	cpu.map_memory( 0, low_mem_size, read_low_mem, write_low_mem );
-	cpu.map_code( 0, low_mem_size, cpu.low_mem );
-	cpu.map_memory( 0x4000, Nes_Cpu::page_size, read_snd, write_snd );
-	cpu.map_memory( exram_addr, Nes_Cpu::page_size, read_unmapped, write_exram );
-	cpu.map_memory( 0x6000, sram_size, read_sram, write_sram );
-	cpu.map_code  ( 0x6000, sram_size, sram );
-	cpu.map_memory( rom_begin, ram_size - rom_begin, read_code, write_unmapped );
-	
-	set_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
-	
-	if ( exp_flags )
-		set_voice_count( Nes_Apu::osc_count + 3 );
-	
-	// namco
-	if ( exp_flags & namco_flag )
-	{
-		namco = BLARGG_NEW Nes_Namco_Apu;
-		BLARGG_CHECK_ALLOC( namco );
-		
-		adjusted_gain *= 0.75;
-		cpu.map_memory( Nes_Namco_Apu::data_reg_addr, Nes_Cpu::page_size,
-				read_namco, write_namco );
-		cpu.map_memory( Nes_Namco_Apu::addr_reg_addr, Nes_Cpu::page_size,
-				 read_code, write_namco_addr );
-	}
-	
-	// vrc6
-	if ( exp_flags & vrc6_flag )
-	{
-		vrc6 = BLARGG_NEW Nes_Vrc6_Apu;
-		BLARGG_CHECK_ALLOC( vrc6 );
-		
-		adjusted_gain *= 0.75;
-		for ( int i = 0; i < Nes_Vrc6_Apu::osc_count; i++ )
-			cpu.map_memory( Nes_Vrc6_Apu::base_addr + i * Nes_Vrc6_Apu::addr_step,
-					Nes_Cpu::page_size, read_code, write_vrc6 );
-	}
-	
-	// fme7
-	if ( exp_flags & fme7_flag )
-	{
-		fme7 = BLARGG_NEW Nes_Fme7_Apu;
-		BLARGG_CHECK_ALLOC( fme7 );
-		
-		adjusted_gain *= 0.75;
-		cpu.map_memory( fme7->latch_addr, ram_size - fme7->latch_addr,
-				read_code, write_fme7 );
-	}
-	// to do: is gain adjustment even needed? other sound chip volumes should work
-	// naturally with the apu without change.
-	
-	if ( namco )
-		namco->volume( adjusted_gain );
-	
-	if ( vrc6 )
-		vrc6->volume( adjusted_gain );
-	
-	if ( fme7 )
-		fme7->volume( adjusted_gain );
-	
-#endif
-	
-	apu.volume( adjusted_gain );
-	
-	return blargg_success;
-}
-
-void Nsf_Emu::update_eq( blip_eq_t const& eq )
-{
-	apu.treble_eq( eq );
-	
-	#if !NSF_EMU_APU_ONLY
-		if ( vrc6 )
-			vrc6->treble_eq( eq );
-		
-		if ( namco )
-			namco->treble_eq( eq );
-		
-		if ( fme7 )
-			fme7->treble_eq( eq );
-	#endif
-}
-
-blargg_err_t Nsf_Emu::load( Data_Reader& in )
-{
-	header_t h;
-	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
-	return load( h, in );
-}
-
-blargg_err_t Nsf_Emu::load( const header_t& h, Data_Reader& in )
-{
-	header_ = h;
-	unload();
-	
-	// check compatibility
-	if ( 0 != memcmp( header_.tag, "NESM\x1A", 5 ) )
-		return "Not an NSF file";
-	if ( header_.vers != 1 )
-		return "Unsupported NSF format";
-	
-	// sound and memory
-	exp_flags = header_.chip_flags;
-	blargg_err_t err = init_sound();
-	if ( err )
-		return err;
-	
-	// set up data
-	nes_addr_t load_addr = get_le16( header_.load_addr );
-	init_addr = get_le16( header_.init_addr );
-	play_addr = get_le16( header_.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;
-	BLARGG_RETURN_ERR( rom.resize( total_banks * page_size ) );
-	memset( rom.begin(), 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 < (unsigned) total_banks) ? bank : 0;
-		
-		if ( header_.banks [i] )
-		{
-			// bank-switched
-			memcpy( initial_banks, header_.banks, sizeof initial_banks );
-			break;
-		}
-	}
-	
-	// playback rate
-	unsigned playback_rate = get_le16( header_.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 ( (header_.speed_flags & 3) == 1 )
-	{
-		pal_only = true;
-		play_period = 33247 * master_clock_divisor;
-		clock_rate = 1662607.125;
-		standard_rate = 0x4E20;
-		playback_rate = get_le16( header_.pal_speed );
-	}
-	
-	// 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 = header_.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;
-
-	set_track_count( header_.track_count );
-	
-	return setup_buffer( (long) (clock_rate + 0.5) );
-}
-
-void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
-{
-	if ( i < Nes_Apu::osc_count )
-	{
-		apu.osc_output( i, buf );
-		return;
-	}
-	
-	#if !NSF_EMU_APU_ONLY
-		if ( vrc6 )
-			vrc6->osc_output( i - Nes_Apu::osc_count, buf );
-		
-		if ( fme7 )
-			fme7->osc_output( i - Nes_Apu::osc_count, buf );
-		
-		if ( namco )
-		{
-			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 );
-			}
-		}
-	#endif
-}
-
-void Nsf_Emu::start_track( int track )
-{
-	require( rom.size() ); // file must be loaded
-	
-	Classic_Emu::start_track( 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 ( namco )
-			namco->reset();
-		
-		if ( vrc6 )
-			vrc6->reset();
-		
-		if ( fme7 )
-			fme7->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;
-}
-
-void Nsf_Emu::cpu_jsr( nes_addr_t pc, int adj )
-{
-	unsigned addr = cpu.r.pc + adj;
-	cpu.r.pc = pc;
-	cpu.push_byte( addr >> 8 );
-	cpu.push_byte( addr );
-}
-
-void Nsf_Emu::call_play()
-{
-	cpu_jsr( play_addr, -1 );
-}
-
-blip_time_t Nsf_Emu::run_clocks( blip_time_t duration, bool* )
-{
-	// run cpu
-	cpu.set_time( 0 );
-	bool first_illegal = true; // avoid swamping output with illegal instruction errors
-	while ( cpu.time() < duration )
-	{
-		// check for idle cpu
-		if ( cpu.r.pc == exram_addr )
-		{
-			if ( next_play > duration )
-			{
-				cpu.set_time( duration );
-				break;
-			}
-			
-			if ( next_play > cpu.time() )
-				cpu.set_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;
-			call_play();
-		}
-		
-		Nes_Cpu::result_t result = RUN_NES_CPU( cpu, duration );
-		if ( result == Nes_Cpu::result_badop && cpu.r.pc != exram_addr )
-		{
-			if ( cpu.r.pc > 0xffff )
-			{
-				cpu.r.pc &= 0xffff;
-				dprintf( "PC wrapped around\n" );
-			}
-			else
-			{
-				cpu.r.pc = (cpu.r.pc + 1) & 0xffff;
-				cpu.set_time( cpu.time() + 4 );
-				log_error();
-				if ( first_illegal )
-				{
-					first_illegal = false;
-					dprintf( "Bad opcode $%.2x at $%.4x\n",
-							(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
-				}
-			}
-		}
-	}
-	
-	// 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 ( namco )
-			namco->end_frame( duration );
-		
-		if ( vrc6 )
-			vrc6->end_frame( duration );
-		
-		if ( fme7 )
-			fme7->end_frame( duration );
-		
-	#endif
-	
-	return duration;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nsf_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,623 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Nsf_Emu.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#if !NSF_EMU_APU_ONLY
+	#include "Nes_Vrc6_Apu.h"
+	#include "Nes_Namco_Apu.h"
+	#include "Nes_Fme7_Apu.h"
+#endif
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+#ifndef RUN_NES_CPU
+	#define RUN_NES_CPU( cpu, count ) cpu.run( count )
+#endif
+
+#ifndef NSF_BEGIN_FRAME
+	#define NSF_BEGIN_FRAME()
+#endif
+
+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 fds_flag = 0x04;
+const int namco_flag = 0x10;
+const int fme7_flag = 0x20;
+
+static BOOST::uint8_t unmapped_code [Nes_Cpu::page_size];
+
+Nes_Emu::equalizer_t const Nes_Emu::nes_eq     = {  -1.0, 80 };
+Nes_Emu::equalizer_t const Nes_Emu::famicom_eq = { -15.0, 80 };
+
+// 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];
+}
+
+void Nsf_Emu::write_low_mem( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	emu->cpu.low_mem [addr] = 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_Apu::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_Apu::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_Apu::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_Apu::addr_step - 1);
+	unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step;
+	if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count )
+		emu->vrc6->write_osc( emu->cpu.time(), osc, reg, data );
+}
+
+// FME-7
+void Nsf_Emu::write_fme7( Nsf_Emu* emu, nes_addr_t addr, int data )
+{
+	switch ( addr & Nes_Fme7_Apu::addr_mask )
+	{
+		case Nes_Fme7_Apu::latch_addr:
+			emu->fme7->write_latch( data );
+			break;
+		
+		case Nes_Fme7_Apu::data_addr:
+			emu->fme7->write_data( emu->cpu.time(), data );
+			break;
+	}
+}
+
+#endif
+
+// Unmapped
+int Nsf_Emu::read_unmapped( Nsf_Emu*, nes_addr_t addr )
+{
+	dprintf( "Read unmapped $%.4X\n", (unsigned) addr );
+	return (addr >> 8) & 0xff; // high byte of address stays on bus
+}
+
+void Nsf_Emu::write_unmapped( Nsf_Emu*, nes_addr_t addr, int data )
+{
+	#ifdef NDEBUG
+		return;
+	#endif
+	
+	// some games write to $8000 and $8001 repeatedly
+	if ( addr == 0x8000 || addr == 0x8001 )
+		return;
+	
+	// probably namco sound mistakenly turned on in mck
+	if ( addr == 0x4800 || addr == 0xF800 )
+		return;
+	
+	// memory mapper?
+	if ( addr == 0xFFF8 )
+		return;
+	
+	dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data );
+}
+
+Nes_Emu::Nes_Emu( double gain_ )
+{
+	cpu.set_emu( this );
+	play_addr = 0;
+	gain = gain_;
+	apu.dmc_reader( pcm_read, this );
+	vrc6 = NULL;
+	namco = NULL;
+	fme7 = NULL;
+	Music_Emu::set_equalizer( nes_eq );
+	
+	// set unmapped code to illegal instruction
+	memset( unmapped_code, 0x32, sizeof unmapped_code );
+}
+
+Nes_Emu::~Nes_Emu()
+{
+	unload();
+}
+
+void Nsf_Emu::unload()
+{
+	#if !NSF_EMU_APU_ONLY
+		delete vrc6;
+		vrc6 = NULL;
+		
+		delete namco;
+		namco = NULL;
+		
+		delete fme7;
+		fme7 = NULL;
+		
+	#endif
+	
+	rom.clear();
+}
+
+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"
+	};
+	static const char* dual_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
+		"VRC6.1,N106.5&7", "VRC6.2,N106.4&6", "VRC6.3,N106.1-3"
+	};
+	
+	static const char* fme7_names [] = {
+		"Square 1", "Square 2", "Triangle", "Noise", "DMC",
+		"Square 3", "Square 4", "Square 5"
+	};
+	
+	if ( namco )
+		return vrc6 ? dual_names : namco_names;
+	
+	if ( vrc6 )
+		return vrc6_names;
+	
+	if ( fme7 )
+		return fme7_names;
+	
+	return base_names;
+}
+
+blargg_err_t Nsf_Emu::init_sound()
+{
+	if ( exp_flags & ~(namco_flag | vrc6_flag | fme7_flag | fds_flag) )
+		return "NSF requires unsupported expansion audio hardware";
+	
+	// map memory
+	cpu.reset( unmapped_code, read_unmapped, write_unmapped );
+	cpu.map_memory( 0, low_mem_size, read_low_mem, write_low_mem );
+	cpu.map_code( 0, low_mem_size, cpu.low_mem );
+	cpu.map_memory( 0x4000, Nes_Cpu::page_size, read_snd, write_snd );
+	cpu.map_memory( exram_addr, Nes_Cpu::page_size, read_unmapped, write_exram );
+	cpu.map_memory( 0x6000, sram_size, read_sram, write_sram );
+	cpu.map_code  ( 0x6000, sram_size, sram );
+	cpu.map_memory( rom_begin, ram_size - rom_begin, read_code, write_unmapped );
+	
+	set_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
+	
+	if ( exp_flags )
+		set_voice_count( Nes_Apu::osc_count + 3 );
+	
+	// namco
+	if ( exp_flags & namco_flag )
+	{
+		namco = BLARGG_NEW Nes_Namco_Apu;
+		BLARGG_CHECK_ALLOC( namco );
+		
+		adjusted_gain *= 0.75;
+		cpu.map_memory( Nes_Namco_Apu::data_reg_addr, Nes_Cpu::page_size,
+				read_namco, write_namco );
+		cpu.map_memory( Nes_Namco_Apu::addr_reg_addr, Nes_Cpu::page_size,
+				 read_code, write_namco_addr );
+	}
+	
+	// vrc6
+	if ( exp_flags & vrc6_flag )
+	{
+		vrc6 = BLARGG_NEW Nes_Vrc6_Apu;
+		BLARGG_CHECK_ALLOC( vrc6 );
+		
+		adjusted_gain *= 0.75;
+		for ( int i = 0; i < Nes_Vrc6_Apu::osc_count; i++ )
+			cpu.map_memory( Nes_Vrc6_Apu::base_addr + i * Nes_Vrc6_Apu::addr_step,
+					Nes_Cpu::page_size, read_code, write_vrc6 );
+	}
+	
+	// fme7
+	if ( exp_flags & fme7_flag )
+	{
+		fme7 = BLARGG_NEW Nes_Fme7_Apu;
+		BLARGG_CHECK_ALLOC( fme7 );
+		
+		adjusted_gain *= 0.75;
+		cpu.map_memory( fme7->latch_addr, ram_size - fme7->latch_addr,
+				read_code, write_fme7 );
+	}
+	// to do: is gain adjustment even needed? other sound chip volumes should work
+	// naturally with the apu without change.
+	
+	if ( namco )
+		namco->volume( adjusted_gain );
+	
+	if ( vrc6 )
+		vrc6->volume( adjusted_gain );
+	
+	if ( fme7 )
+		fme7->volume( adjusted_gain );
+	
+#endif
+	
+	apu.volume( adjusted_gain );
+	
+	return blargg_success;
+}
+
+void Nsf_Emu::update_eq( blip_eq_t const& eq )
+{
+	apu.treble_eq( eq );
+	
+	#if !NSF_EMU_APU_ONLY
+		if ( vrc6 )
+			vrc6->treble_eq( eq );
+		
+		if ( namco )
+			namco->treble_eq( eq );
+		
+		if ( fme7 )
+			fme7->treble_eq( eq );
+	#endif
+}
+
+blargg_err_t Nsf_Emu::load( Data_Reader& in )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Nsf_Emu::load( const header_t& h, Data_Reader& in )
+{
+	header_ = h;
+	unload();
+	
+	// check compatibility
+	if ( 0 != memcmp( header_.tag, "NESM\x1A", 5 ) )
+		return "Not an NSF file";
+	if ( header_.vers != 1 )
+		return "Unsupported NSF format";
+	
+	// sound and memory
+	exp_flags = header_.chip_flags;
+	blargg_err_t err = init_sound();
+	if ( err )
+		return err;
+	
+	// set up data
+	nes_addr_t load_addr = get_le16( header_.load_addr );
+	init_addr = get_le16( header_.init_addr );
+	play_addr = get_le16( header_.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;
+	BLARGG_RETURN_ERR( rom.resize( total_banks * page_size ) );
+	memset( rom.begin(), 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 < (unsigned) total_banks) ? bank : 0;
+		
+		if ( header_.banks [i] )
+		{
+			// bank-switched
+			memcpy( initial_banks, header_.banks, sizeof initial_banks );
+			break;
+		}
+	}
+	
+	// playback rate
+	unsigned playback_rate = get_le16( header_.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 ( (header_.speed_flags & 3) == 1 )
+	{
+		pal_only = true;
+		play_period = 33247 * master_clock_divisor;
+		clock_rate = 1662607.125;
+		standard_rate = 0x4E20;
+		playback_rate = get_le16( header_.pal_speed );
+	}
+	
+	// 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 = header_.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;
+
+	set_track_count( header_.track_count );
+	
+	return setup_buffer( (long) (clock_rate + 0.5) );
+}
+
+void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
+{
+	if ( i < Nes_Apu::osc_count )
+	{
+		apu.osc_output( i, buf );
+		return;
+	}
+	
+	#if !NSF_EMU_APU_ONLY
+		if ( vrc6 )
+			vrc6->osc_output( i - Nes_Apu::osc_count, buf );
+		
+		if ( fme7 )
+			fme7->osc_output( i - Nes_Apu::osc_count, buf );
+		
+		if ( namco )
+		{
+			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 );
+			}
+		}
+	#endif
+}
+
+void Nsf_Emu::start_track( int track )
+{
+	require( rom.size() ); // file must be loaded
+	
+	Classic_Emu::start_track( 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 ( namco )
+			namco->reset();
+		
+		if ( vrc6 )
+			vrc6->reset();
+		
+		if ( fme7 )
+			fme7->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;
+}
+
+void Nsf_Emu::cpu_jsr( nes_addr_t pc, int adj )
+{
+	unsigned addr = cpu.r.pc + adj;
+	cpu.r.pc = pc;
+	cpu.push_byte( addr >> 8 );
+	cpu.push_byte( addr );
+}
+
+void Nsf_Emu::call_play()
+{
+	cpu_jsr( play_addr, -1 );
+}
+
+blip_time_t Nsf_Emu::run_clocks( blip_time_t duration, bool* )
+{
+	// run cpu
+	cpu.set_time( 0 );
+	bool first_illegal = true; // avoid swamping output with illegal instruction errors
+	while ( cpu.time() < duration )
+	{
+		// check for idle cpu
+		if ( cpu.r.pc == exram_addr )
+		{
+			if ( next_play > duration )
+			{
+				cpu.set_time( duration );
+				break;
+			}
+			
+			if ( next_play > cpu.time() )
+				cpu.set_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;
+			call_play();
+		}
+		
+		Nes_Cpu::result_t result = RUN_NES_CPU( cpu, duration );
+		if ( result == Nes_Cpu::result_badop && cpu.r.pc != exram_addr )
+		{
+			if ( cpu.r.pc > 0xffff )
+			{
+				cpu.r.pc &= 0xffff;
+				dprintf( "PC wrapped around\n" );
+			}
+			else
+			{
+				cpu.r.pc = (cpu.r.pc + 1) & 0xffff;
+				cpu.set_time( cpu.time() + 4 );
+				log_error();
+				if ( first_illegal )
+				{
+					first_illegal = false;
+					dprintf( "Bad opcode $%.2x at $%.4x\n",
+							(int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
+				}
+			}
+		}
+	}
+	
+	// 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 ( namco )
+			namco->end_frame( duration );
+		
+		if ( vrc6 )
+			vrc6->end_frame( duration );
+		
+		if ( fme7 )
+			fme7->end_frame( duration );
+		
+	#endif
+	
+	return duration;
+}
+
--- a/Plugins/Input/console/Nsfe_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Nsfe_Emu.h"
-
-#include "blargg_endian.h"
-#include <string.h>
-
-/* Copyright (C) 2005-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-#define NSFE_TAG( a, b, c, d ) (d*0x1000000L + c*0x10000L + b*0x100L + a)
-
-Nsfe_Info::Nsfe_Info()
-{
-	playlist_enabled = false;
-}
-
-Nsfe_Info::~Nsfe_Info() { }
-
-void Nsfe_Info::enable_playlist( bool b )
-{
-	playlist_enabled = b;
-	info_.track_count = (b && playlist_size()) ? playlist_size() : track_count_;
-}
-
-int Nsfe_Info::remap_track( int i ) const
-{
-	if ( !playlist_enabled || !playlist_size() )
-		return i;
-	
-	return playlist_entry( i );
-}
-
-void Nsfe_Emu::start_track( int i )
-{
-	Nsf_Emu::start_track( remap_track( i ) );
-}
-
-const char* Nsfe_Info::track_name( int i ) const
-{
-	i = remap_track( i );
-	if ( i < track_names.size() )
-		return track_names [i];
-	
-	return "";
-}
-
-long Nsfe_Info::track_time( int i ) const
-{
-	i = remap_track( i );
-	if ( i < track_times.size() )
-		return track_times [i];
-	
-	return 0;
-}
-
-// Read little-endian 32-bit int
-static blargg_err_t read_le32( Emu_Reader& in, long* out )
-{
-	unsigned char buf [4];
-	BLARGG_RETURN_ERR( in.read( buf, sizeof buf ) );
-	*out = get_le32( buf );
-	return blargg_success;
-}
-
-// Read multiple strings and separate into individual strings
-static blargg_err_t read_strs( Emu_Reader& in, long size, std::vector<char>& chars,
-		std::vector<const char*>& strs )
-{
-	chars.resize( size + 1 );
-	chars [size] = 0; // in case last string doesn't have terminator
-	BLARGG_RETURN_ERR( in.read( &chars [0], size ) );
-	
-	for ( int i = 0; i < size; i++ )
-	{
-		strs.push_back( &chars [i] );
-		while ( i < size && chars [i] )
-			i++;
-	}
-	
-	return blargg_success;
-}
-
-// Copy in to out, where out has out_max characters allocated. Truncate to
-// out_max - 1 characters.
-static void copy_str( const char* in, char* out, int out_max )
-{
-	out [out_max - 1] = 0;
-	strncpy( out, in, out_max - 1 );
-}
-
-struct nsfe_info_t {
-	unsigned char load_addr [2];
-	unsigned char init_addr [2];
-	unsigned char play_addr [2];
-	unsigned char speed_flags;
-	unsigned char chip_flags;
-	unsigned char track_count;
-	unsigned char first_track;
-};
-BOOST_STATIC_ASSERT( sizeof (nsfe_info_t) == 10 );
-
-blargg_err_t Nsfe_Info::load( const header_t& nsfe_tag, Emu_Reader& in, Nsf_Emu* nsf_emu )
-{
-	// check header
-	if ( memcmp( nsfe_tag.tag, "NSFE", 4 ) )
-		return "Not an NSFE file";
-	
-	// free previous info
-	track_name_data.clear();
-	track_names.clear();
-	playlist.clear();
-	track_times.clear();
-	
-	// default nsf header
-	static const Nsf_Emu::header_t base_header =
-	{
-		{'N','E','S','M','\x1A'},// tag
-		1,                  // version
-		1, 1,               // track count, first track
-		{0,0},{0,0},{0,0},  // addresses
-		"","","",           // strings
-		{0x1A, 0x41},       // NTSC rate
-		{0,0,0,0,0,0,0,0},  // banks
-		{0x20, 0x4E},       // PAL rate
-		0, 0,               // flags
-		{0,0,0,0}           // unused
-	};
-	Nsf_Emu::header_t& header = info_;
-	header = base_header;
-	
-	// parse tags
-	int phase = 0;
-	while ( phase != 3 )
-	{
-		// read size and tag
-		long size = 0;
-		long tag = 0;
-		BLARGG_RETURN_ERR( read_le32( in, &size ) );
-		BLARGG_RETURN_ERR( read_le32( in, &tag ) );
-		
-		switch ( tag )
-		{
-			case NSFE_TAG('I','N','F','O'): {
-				check( phase == 0 );
-				if ( size < 8 )
-					return "Bad NSFE file";
-				
-				nsfe_info_t info;
-				info.track_count = 1;
-				info.first_track = 0;
-				
-				int s = size;
-				if ( s > (int) sizeof info )
-					s = sizeof info;
-				BLARGG_RETURN_ERR( in.read( &info, s ) );
-				BLARGG_RETURN_ERR( in.skip( size - s ) );
-				phase = 1;
-				info_.speed_flags = info.speed_flags;
-				info_.chip_flags = info.chip_flags;
-				info_.track_count = info.track_count;
-				this->track_count_ = info.track_count;
-				info_.first_track = info.first_track;
-				std::memcpy( info_.load_addr, info.load_addr, 2 * 3 );
-				break;
-			}
-			
-			case NSFE_TAG('B','A','N','K'):
-				if ( size > (int) sizeof info_.banks )
-					return "Bad NSFE file";
-				BLARGG_RETURN_ERR( in.read( info_.banks, size ) );
-				break;
-			
-			case NSFE_TAG('a','u','t','h'): {
-				std::vector<char> chars;
-				std::vector<const char*> strs;
-				BLARGG_RETURN_ERR( read_strs( in, size, chars, strs ) );
-				int n = strs.size();
-				
-				if ( n > 3 )
-					copy_str( strs [3], info_.ripper, sizeof info_.ripper );
-				
-				if ( n > 2 )
-					copy_str( strs [2], info_.copyright, sizeof info_.copyright );
-				
-				if ( n > 1 )
-					copy_str( strs [1], info_.author, sizeof info_.author );
-				
-				if ( n > 0 )
-					copy_str( strs [0], info_.game, sizeof info_.game );
-				
-				break;
-			}
-			
-			case NSFE_TAG('t','i','m','e'): {
-				track_times.resize( size / 4 );
-				for ( int i = 0; i < track_times.size(); i++ )
-					BLARGG_RETURN_ERR( read_le32( in, &track_times [i] ) );
-				break;
-			}
-			
-			case NSFE_TAG('t','l','b','l'):
-				BLARGG_RETURN_ERR( read_strs( in, size, track_name_data, track_names ) );
-				break;
-			
-			case NSFE_TAG('p','l','s','t'):
-				playlist.resize( size );
-				BLARGG_RETURN_ERR( in.read( &playlist [0], size ) );
-				break;
-			
-			case NSFE_TAG('D','A','T','A'): {
-				check( phase == 1 );
-				phase = 2;
-				if ( !nsf_emu )
-				{
-					in.skip( size );
-				}
-				else
-				{
-					Subset_Reader sub( &in, size ); // limit emu to nsf data
-					BLARGG_RETURN_ERR( nsf_emu->load( info_, sub ) );
-					check( sub.remain() == 0 );
-				}
-				break;
-			}
-			
-			case NSFE_TAG('N','E','N','D'):
-				check( phase == 2 );
-				phase = 3;
-				break;
-			
-			default:
-				// tags that can be skipped start with a lowercase character
-				check( std::islower( (tag >> 24) & 0xff ) );
-				BLARGG_RETURN_ERR( in.skip( size ) );
-				break;
-		}
-	}
-	
-	enable_playlist( playlist_enabled );
-	
-	return blargg_success;
-}
-
-blargg_err_t Nsfe_Info::load( Emu_Reader& in, Nsf_Emu* nsf_emu )
-{
-	header_t h;
-	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
-	return load( h, in, nsf_emu );
-}
-
-blargg_err_t Nsfe_Info::load_file( const char* path, Nsf_Emu* emu )
-{
-	Std_File_Reader in;
-	BLARGG_RETURN_ERR( in.open( path ) );
-	return load( in, emu );
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Nsfe_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,269 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Nsfe_Emu.h"
+
+#include "blargg_endian.h"
+#include <string.h>
+
+/* Copyright (C) 2005-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+#define NSFE_TAG( a, b, c, d ) (d*0x1000000L + c*0x10000L + b*0x100L + a)
+
+Nsfe_Info::Nsfe_Info()
+{
+	playlist_enabled = false;
+}
+
+Nsfe_Info::~Nsfe_Info() { }
+
+void Nsfe_Info::enable_playlist( bool b )
+{
+	playlist_enabled = b;
+	info_.track_count = (b && playlist_size()) ? playlist_size() : track_count_;
+}
+
+int Nsfe_Info::remap_track( int i ) const
+{
+	if ( !playlist_enabled || !playlist_size() )
+		return i;
+	
+	return playlist_entry( i );
+}
+
+void Nsfe_Emu::start_track( int i )
+{
+	Nsf_Emu::start_track( remap_track( i ) );
+}
+
+const char* Nsfe_Info::track_name( int i ) const
+{
+	i = remap_track( i );
+	if ( i < track_names.size() )
+		return track_names [i];
+	
+	return "";
+}
+
+long Nsfe_Info::track_time( int i ) const
+{
+	i = remap_track( i );
+	if ( i < track_times.size() )
+		return track_times [i];
+	
+	return 0;
+}
+
+// Read little-endian 32-bit int
+static blargg_err_t read_le32( Emu_Reader& in, long* out )
+{
+	unsigned char buf [4];
+	BLARGG_RETURN_ERR( in.read( buf, sizeof buf ) );
+	*out = get_le32( buf );
+	return blargg_success;
+}
+
+// Read multiple strings and separate into individual strings
+static blargg_err_t read_strs( Emu_Reader& in, long size, std::vector<char>& chars,
+		std::vector<const char*>& strs )
+{
+	chars.resize( size + 1 );
+	chars [size] = 0; // in case last string doesn't have terminator
+	BLARGG_RETURN_ERR( in.read( &chars [0], size ) );
+	
+	for ( int i = 0; i < size; i++ )
+	{
+		strs.push_back( &chars [i] );
+		while ( i < size && chars [i] )
+			i++;
+	}
+	
+	return blargg_success;
+}
+
+// Copy in to out, where out has out_max characters allocated. Truncate to
+// out_max - 1 characters.
+static void copy_str( const char* in, char* out, int out_max )
+{
+	out [out_max - 1] = 0;
+	strncpy( out, in, out_max - 1 );
+}
+
+struct nsfe_info_t {
+	unsigned char load_addr [2];
+	unsigned char init_addr [2];
+	unsigned char play_addr [2];
+	unsigned char speed_flags;
+	unsigned char chip_flags;
+	unsigned char track_count;
+	unsigned char first_track;
+};
+BOOST_STATIC_ASSERT( sizeof (nsfe_info_t) == 10 );
+
+blargg_err_t Nsfe_Info::load( const header_t& nsfe_tag, Emu_Reader& in, Nsf_Emu* nsf_emu )
+{
+	// check header
+	if ( memcmp( nsfe_tag.tag, "NSFE", 4 ) )
+		return "Not an NSFE file";
+	
+	// free previous info
+	track_name_data.clear();
+	track_names.clear();
+	playlist.clear();
+	track_times.clear();
+	
+	// default nsf header
+	static const Nsf_Emu::header_t base_header =
+	{
+		{'N','E','S','M','\x1A'},// tag
+		1,                  // version
+		1, 1,               // track count, first track
+		{0,0},{0,0},{0,0},  // addresses
+		"","","",           // strings
+		{0x1A, 0x41},       // NTSC rate
+		{0,0,0,0,0,0,0,0},  // banks
+		{0x20, 0x4E},       // PAL rate
+		0, 0,               // flags
+		{0,0,0,0}           // unused
+	};
+	Nsf_Emu::header_t& header = info_;
+	header = base_header;
+	
+	// parse tags
+	int phase = 0;
+	while ( phase != 3 )
+	{
+		// read size and tag
+		long size = 0;
+		long tag = 0;
+		BLARGG_RETURN_ERR( read_le32( in, &size ) );
+		BLARGG_RETURN_ERR( read_le32( in, &tag ) );
+		
+		switch ( tag )
+		{
+			case NSFE_TAG('I','N','F','O'): {
+				check( phase == 0 );
+				if ( size < 8 )
+					return "Bad NSFE file";
+				
+				nsfe_info_t info;
+				info.track_count = 1;
+				info.first_track = 0;
+				
+				int s = size;
+				if ( s > (int) sizeof info )
+					s = sizeof info;
+				BLARGG_RETURN_ERR( in.read( &info, s ) );
+				BLARGG_RETURN_ERR( in.skip( size - s ) );
+				phase = 1;
+				info_.speed_flags = info.speed_flags;
+				info_.chip_flags = info.chip_flags;
+				info_.track_count = info.track_count;
+				this->track_count_ = info.track_count;
+				info_.first_track = info.first_track;
+				std::memcpy( info_.load_addr, info.load_addr, 2 * 3 );
+				break;
+			}
+			
+			case NSFE_TAG('B','A','N','K'):
+				if ( size > (int) sizeof info_.banks )
+					return "Bad NSFE file";
+				BLARGG_RETURN_ERR( in.read( info_.banks, size ) );
+				break;
+			
+			case NSFE_TAG('a','u','t','h'): {
+				std::vector<char> chars;
+				std::vector<const char*> strs;
+				BLARGG_RETURN_ERR( read_strs( in, size, chars, strs ) );
+				int n = strs.size();
+				
+				if ( n > 3 )
+					copy_str( strs [3], info_.ripper, sizeof info_.ripper );
+				
+				if ( n > 2 )
+					copy_str( strs [2], info_.copyright, sizeof info_.copyright );
+				
+				if ( n > 1 )
+					copy_str( strs [1], info_.author, sizeof info_.author );
+				
+				if ( n > 0 )
+					copy_str( strs [0], info_.game, sizeof info_.game );
+				
+				break;
+			}
+			
+			case NSFE_TAG('t','i','m','e'): {
+				track_times.resize( size / 4 );
+				for ( int i = 0; i < track_times.size(); i++ )
+					BLARGG_RETURN_ERR( read_le32( in, &track_times [i] ) );
+				break;
+			}
+			
+			case NSFE_TAG('t','l','b','l'):
+				BLARGG_RETURN_ERR( read_strs( in, size, track_name_data, track_names ) );
+				break;
+			
+			case NSFE_TAG('p','l','s','t'):
+				playlist.resize( size );
+				BLARGG_RETURN_ERR( in.read( &playlist [0], size ) );
+				break;
+			
+			case NSFE_TAG('D','A','T','A'): {
+				check( phase == 1 );
+				phase = 2;
+				if ( !nsf_emu )
+				{
+					in.skip( size );
+				}
+				else
+				{
+					Subset_Reader sub( &in, size ); // limit emu to nsf data
+					BLARGG_RETURN_ERR( nsf_emu->load( info_, sub ) );
+					check( sub.remain() == 0 );
+				}
+				break;
+			}
+			
+			case NSFE_TAG('N','E','N','D'):
+				check( phase == 2 );
+				phase = 3;
+				break;
+			
+			default:
+				// tags that can be skipped start with a lowercase character
+				check( std::islower( (tag >> 24) & 0xff ) );
+				BLARGG_RETURN_ERR( in.skip( size ) );
+				break;
+		}
+	}
+	
+	enable_playlist( playlist_enabled );
+	
+	return blargg_success;
+}
+
+blargg_err_t Nsfe_Info::load( Emu_Reader& in, Nsf_Emu* nsf_emu )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in, nsf_emu );
+}
+
+blargg_err_t Nsfe_Info::load_file( const char* path, Nsf_Emu* emu )
+{
+	Std_File_Reader in;
+	BLARGG_RETURN_ERR( in.open( path ) );
+	return load( in, emu );
+}
+
--- a/Plugins/Input/console/Sms_Apu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,319 +0,0 @@
-
-// Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/
-
-#include "Sms_Apu.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
-
-inline 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;
-		int delta = amp - last_amp;
-		if ( delta )
-		{
-			last_amp = amp;
-			synth->offset( time, delta, output );
-		}
-		
-		time += delay;
-		if ( time < end_time )
-		{
-			Blip_Buffer* const output = this->output;
-			int delta = amp * 2;
-			do
-			{
-				delta = -delta;
-				synth->offset_inline( time, delta, 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 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 amp = volume;
-	if ( shifter & 1 )
-		amp = -amp;
-	
-	int delta = amp - last_amp;
-	if ( delta )
-	{
-		last_amp = amp;
-		synth.offset( time, delta, output );
-	}
-	
-	time += delay;
-	if ( !volume )
-		time = end_time;
-	
-	if ( time < end_time )
-	{
-		Blip_Buffer* const output = this->output;
-		unsigned shifter = this->shifter;
-		int delta = amp * 2;
-		int period = *this->period * 2;
-		if ( !period )
-			period = 16;
-		
-		do
-		{
-			int changed = (shifter + 1) & 2; // set if prev and next bits differ
-			shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1);
-			if ( changed )
-			{
-				delta = -delta;
-				synth.offset_inline( time, delta, output );
-			}
-			time += period;
-		}
-		while ( time < end_time );
-		
-		this->shifter = shifter;
-		this->last_amp = delta >> 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::volume( double vol )
-{
-	vol *= 0.85 / (osc_count * 64 * 2);
-	square_synth.volume( vol );
-	noise.synth.volume( vol );
-}
-
-void Sms_Apu::treble_eq( const blip_eq_t& eq )
-{
-	square_synth.treble_eq( eq );
-	noise.synth.treble_eq( eq );
-}
-
-void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
-{
-	require( (unsigned) index < osc_count );
-	require( (center && left && right) || (!center && !left && !right) );
-	Sms_Osc& osc = *oscs [index];
-	osc.outputs [1] = right;
-	osc.outputs [2] = left;
-	osc.outputs [3] = center;
-	osc.output = osc.outputs [osc.output_select];
-}
-
-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::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
-				
-				if ( i < 3 )
-					squares [i].run( last_time, end_time );
-				else
-					noise.run( last_time, end_time );
-			}
-		}
-		
-		last_time = end_time;
-	}
-}
-
-bool Sms_Apu::end_frame( sms_time_t end_time )
-{
-	if ( end_time > last_time )
-		run_until( end_time );
-	
-	assert( last_time >= end_time );
-	last_time -= end_time;
-	
-	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 );
-	
-	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 unsigned 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 )
-	{
-		oscs [index]->volume = volumes [data & 15];
-	}
-	else if ( index < 3 )
-	{
-		Sms_Square& sq = squares [index];
-		if ( data & 0x80 )
-			sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
-		else
-			sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
-	}
-	else
-	{
-		int select = data & 3;
-		if ( select < 3 )
-			noise.period = &noise_periods [select];
-		else
-			noise.period = &squares [2].period;
-		
-		int const tap_disabled = 16;
-		noise.tap = (data & 0x04) ? 12 : tap_disabled;
-		noise.shifter = 0x8000;
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Sms_Apu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,319 @@
+
+// Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/
+
+#include "Sms_Apu.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
+
+inline 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;
+		int delta = amp - last_amp;
+		if ( delta )
+		{
+			last_amp = amp;
+			synth->offset( time, delta, output );
+		}
+		
+		time += delay;
+		if ( time < end_time )
+		{
+			Blip_Buffer* const output = this->output;
+			int delta = amp * 2;
+			do
+			{
+				delta = -delta;
+				synth->offset_inline( time, delta, 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 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 amp = volume;
+	if ( shifter & 1 )
+		amp = -amp;
+	
+	int delta = amp - last_amp;
+	if ( delta )
+	{
+		last_amp = amp;
+		synth.offset( time, delta, output );
+	}
+	
+	time += delay;
+	if ( !volume )
+		time = end_time;
+	
+	if ( time < end_time )
+	{
+		Blip_Buffer* const output = this->output;
+		unsigned shifter = this->shifter;
+		int delta = amp * 2;
+		int period = *this->period * 2;
+		if ( !period )
+			period = 16;
+		
+		do
+		{
+			int changed = (shifter + 1) & 2; // set if prev and next bits differ
+			shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1);
+			if ( changed )
+			{
+				delta = -delta;
+				synth.offset_inline( time, delta, output );
+			}
+			time += period;
+		}
+		while ( time < end_time );
+		
+		this->shifter = shifter;
+		this->last_amp = delta >> 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::volume( double vol )
+{
+	vol *= 0.85 / (osc_count * 64 * 2);
+	square_synth.volume( vol );
+	noise.synth.volume( vol );
+}
+
+void Sms_Apu::treble_eq( const blip_eq_t& eq )
+{
+	square_synth.treble_eq( eq );
+	noise.synth.treble_eq( eq );
+}
+
+void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+	require( (unsigned) index < osc_count );
+	require( (center && left && right) || (!center && !left && !right) );
+	Sms_Osc& osc = *oscs [index];
+	osc.outputs [1] = right;
+	osc.outputs [2] = left;
+	osc.outputs [3] = center;
+	osc.output = osc.outputs [osc.output_select];
+}
+
+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::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
+				
+				if ( i < 3 )
+					squares [i].run( last_time, end_time );
+				else
+					noise.run( last_time, end_time );
+			}
+		}
+		
+		last_time = end_time;
+	}
+}
+
+bool Sms_Apu::end_frame( sms_time_t end_time )
+{
+	if ( end_time > last_time )
+		run_until( end_time );
+	
+	assert( last_time >= end_time );
+	last_time -= end_time;
+	
+	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 );
+	
+	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 unsigned 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 )
+	{
+		oscs [index]->volume = volumes [data & 15];
+	}
+	else if ( index < 3 )
+	{
+		Sms_Square& sq = squares [index];
+		if ( data & 0x80 )
+			sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
+		else
+			sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
+	}
+	else
+	{
+		int select = data & 3;
+		if ( select < 3 )
+			noise.period = &noise_periods [select];
+		else
+			noise.period = &squares [2].period;
+		
+		int const tap_disabled = 16;
+		noise.tap = (data & 0x04) ? 12 : tap_disabled;
+		noise.shifter = 0x8000;
+	}
+}
+
--- a/Plugins/Input/console/Snes_Spc.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,475 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Snes_Spc.h"
-
-#include <assert.h>
-#include <string.h>
-
-/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-// always in the future (CPU time can go over 0, but not by this much)
-int const timer_disabled_time = 127;
-
-Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, 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
-
-blargg_err_t Snes_Spc::load_spc( const void* data, long size, bool 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.
-
-blargg_err_t 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.next_tick = 0;
-		t.enabled = (ram [0xf1] >> i) & 1;
-		if ( !t.enabled )
-			t.next_tick = timer_disabled_time;
-		t.count = 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 )
-{
-	if ( !enabled )
-		dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) 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( bool 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 = timer_disabled_time;
-				}
-				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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,475 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Snes_Spc.h"
+
+#include <assert.h>
+#include <string.h>
+
+/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+// always in the future (CPU time can go over 0, but not by this much)
+int const timer_disabled_time = 127;
+
+Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, 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
+
+blargg_err_t Snes_Spc::load_spc( const void* data, long size, bool 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.
+
+blargg_err_t 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.next_tick = 0;
+		t.enabled = (ram [0xf1] >> i) & 1;
+		if ( !t.enabled )
+			t.next_tick = timer_disabled_time;
+		t.count = 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 )
+{
+	if ( !enabled )
+		dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) 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( bool 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 = timer_disabled_time;
+				}
+				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;
+}
+
--- a/Plugins/Input/console/Spc_Cpu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1066 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Spc_Cpu.h"
-
-#include <limits.h>
-
-#include "blargg_endian.h"
-#include "Snes_Spc.h"
-
-/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), 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] = {
-//  0 1 2 3 4 5 6 7 8 9 A B C D E F
-	2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0
-	2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1
-	2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2
-	2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3
-	2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4
-	2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5
-	2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6
-	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7
-	2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8
-	2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9
-	3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A
-	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B
-	3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C
-	2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D
-	2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E
-	2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3  // F
-};
-
-// 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;
-	
-	uint8_t* const ram = this->ram; // cache
-	
-	// 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;
-	
-	#define IS_NEG (nz & 0x880)
-	
-	#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 ( !(nz & 0xFF) ) 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) & 0x800;                     \
-		nz |= ~in & st_z;                           \
-		dp = (in << 3) & 0x100;                     \
-	} while ( 0 )
-	
-	uint8_t status;
-	int c;  // store C as 'c' & 0x100.
-	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
-	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
-	pc += (BOOST::int8_t) READ_PROG( pc );
-	remain_ -= 2;
-inc_pc_loop: // end of instruction with an operand
-	pc++;
-loop:
-	
-	check( (unsigned) pc < 0x10000 );
-	check( (unsigned) GET_SP() < 0x100 );
-	
-	assert( (unsigned) a < 0x100 );
-	assert( (unsigned) x < 0x100 );
-	assert( (unsigned) y < 0x100 );
-	
-	unsigned opcode = READ_PROG( pc );
-	pc++;
-	// to do: if pc is at end of memory, this will get wrong byte
-	data = READ_PROG( pc );
-	
-	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 ) {        \
-		pc++;                       \
-		int offset = (BOOST::int8_t) data; \
-		if ( cond ) {               \
-			pc += offset;           \
-			remain_ -= 2;           \
-		}                           \
-		goto 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;
-		nz &= 0xff;
-		goto inc_pc_loop;
-	
-	case 0x79: // CMP (X),(Y)
-		data = READ_DP( x );
-		nz = data - READ_DP( y );
-		c = ~nz;
-		nz &= 0xff;
-		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;
-		nz &= 0xff;
-		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;
-		nz &= 0xff;
-		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;
-		nz &= 0xff;
-		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 = (nz | c) & 0xff;
-		
-		// 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;
-		nz &= 0xff;
-		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 & 15) >= (x & 15) )
-			status |= st_h;
-		
-		if ( y >= x )
-			status |= st_v;
-		
-		unsigned ya = y * 0x100 + a;
-		if ( y < x * 2 )
-		{
-			a = ya / x;
-			y = ya - a * x;
-		}
-		else
-		{
-			a = 255 - (ya - x * 0x200) / (256 - x);
-			y = x   + (ya - x * 0x200) % (256 - x);
-		}
-		
-		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
-		pc += (BOOST::int8_t) data;
-		goto inc_pc_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);
-		BRANCH( y )
-	
-	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
-	
-	case 0x0F: // BRK
-		check( false ); // untested
-		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;
-	
-	case 0x6A: // AND1 C,/mem.bit
-		check( false ); // untested
-		c &= ~mem_bit( pc );
-		pc += 2;
-		goto loop;
-	
-	case 0x0A: // OR1 C,mem.bit
-		check( false ); // untested
-		c |= mem_bit( pc );
-		pc += 2;
-		goto loop;
-	
-	case 0x2A: // OR1 C,/mem.bit
-		check( false ); // untested
-		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 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) & 1) << bit);
-		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;
-	
-	case 0xA0: // EI
-		check( false ); // untested
-		status |= st_i;
-		goto loop;
-	
-	case 0xC0: // DI
-		check( false ); // untested
-		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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,1066 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Spc_Cpu.h"
+
+#include <limits.h>
+
+#include "blargg_endian.h"
+#include "Snes_Spc.h"
+
+/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), 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] = {
+//  0 1 2 3 4 5 6 7 8 9 A B C D E F
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1
+	2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2
+	2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4
+	2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5
+	2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7
+	2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9
+	3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A
+	2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B
+	3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C
+	2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D
+	2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E
+	2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3  // F
+};
+
+// 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;
+	
+	uint8_t* const ram = this->ram; // cache
+	
+	// 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;
+	
+	#define IS_NEG (nz & 0x880)
+	
+	#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 ( !(nz & 0xFF) ) 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) & 0x800;                     \
+		nz |= ~in & st_z;                           \
+		dp = (in << 3) & 0x100;                     \
+	} while ( 0 )
+	
+	uint8_t status;
+	int c;  // store C as 'c' & 0x100.
+	int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0
+	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
+	pc += (BOOST::int8_t) READ_PROG( pc );
+	remain_ -= 2;
+inc_pc_loop: // end of instruction with an operand
+	pc++;
+loop:
+	
+	check( (unsigned) pc < 0x10000 );
+	check( (unsigned) GET_SP() < 0x100 );
+	
+	assert( (unsigned) a < 0x100 );
+	assert( (unsigned) x < 0x100 );
+	assert( (unsigned) y < 0x100 );
+	
+	unsigned opcode = READ_PROG( pc );
+	pc++;
+	// to do: if pc is at end of memory, this will get wrong byte
+	data = READ_PROG( pc );
+	
+	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 ) {        \
+		pc++;                       \
+		int offset = (BOOST::int8_t) data; \
+		if ( cond ) {               \
+			pc += offset;           \
+			remain_ -= 2;           \
+		}                           \
+		goto 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;
+		nz &= 0xff;
+		goto inc_pc_loop;
+	
+	case 0x79: // CMP (X),(Y)
+		data = READ_DP( x );
+		nz = data - READ_DP( y );
+		c = ~nz;
+		nz &= 0xff;
+		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;
+		nz &= 0xff;
+		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;
+		nz &= 0xff;
+		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;
+		nz &= 0xff;
+		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 = (nz | c) & 0xff;
+		
+		// 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;
+		nz &= 0xff;
+		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 & 15) >= (x & 15) )
+			status |= st_h;
+		
+		if ( y >= x )
+			status |= st_v;
+		
+		unsigned ya = y * 0x100 + a;
+		if ( y < x * 2 )
+		{
+			a = ya / x;
+			y = ya - a * x;
+		}
+		else
+		{
+			a = 255 - (ya - x * 0x200) / (256 - x);
+			y = x   + (ya - x * 0x200) % (256 - x);
+		}
+		
+		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
+		pc += (BOOST::int8_t) data;
+		goto inc_pc_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);
+		BRANCH( y )
+	
+	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
+	
+	case 0x0F: // BRK
+		check( false ); // untested
+		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;
+	
+	case 0x6A: // AND1 C,/mem.bit
+		check( false ); // untested
+		c &= ~mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x0A: // OR1 C,mem.bit
+		check( false ); // untested
+		c |= mem_bit( pc );
+		pc += 2;
+		goto loop;
+	
+	case 0x2A: // OR1 C,/mem.bit
+		check( false ); // untested
+		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 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) & 1) << bit);
+		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;
+	
+	case 0xA0: // EI
+		check( false ); // untested
+		status |= st_i;
+		goto loop;
+	
+	case 0xC0: // DI
+		check( false ); // untested
+		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_;
+}
+
--- a/Plugins/Input/console/Spc_Dsp.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,666 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-// 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-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ )
-{
-	set_gain( 1.0 );
-	mute_voices( 0 );
-	disable_surround( false );
-	
-	BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count );
-}
-
-void Spc_Dsp::mute_voices( int mask )
-{
-	for ( int i = 0; i < voice_count; i++ )
-		voice_state [i].enabled = (mask >> i & 1) ? 31 : 7;
-}
-
-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_t& v = voice_state [i];
-		v.on_cnt = 0;
-		v.volume [0] = 0;
-		v.volume [1] = 0;
-		v.envstate = state_release;
-	}
-	
-	memset( fir_buf, 0, sizeof fir_buf );
-}
-
-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: {
-			short* volume = voice_state [high].volume;
-			int left  = (int8_t) reg [i & ~1];
-			int right = (int8_t) reg [i | 1];
-			volume [0] = left;
-			volume [1] = right;
-			// kill surround only if signs of volumes differ
-			if ( left * right < surround_threshold )
-			{
-				if ( left < 0 )
-					volume [0] = -left;
-				else
-					volume [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;
-
-inline 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;
-			
-			case state_release:
-				// handled above
-				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 so that this becomes a leaf function?
-	
-	// 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 left_volume  = g.left_volume;
-	int right_volume = g.right_volume;
-	if ( left_volume * right_volume < surround_threshold )
-		right_volume = -right_volume; // kill global surround
-	left_volume  *= emu_gain;
-	right_volume *= emu_gain;
-	
-	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;
-				
-				noise_amp = BOOST::int16_t (noise * 2);
-				
-				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;
-			
-			// Gaussian interpolation using most recent 4 samples
-			int index = voice.fraction >> 2 & 0x3FC;
-			voice.fraction = (voice.fraction & 0x0FFF) + rate;
-			const BOOST::int16_t* table  = (BOOST::int16_t*) ((char*) gauss + index);
-			const BOOST::int16_t* table2 = (BOOST::int16_t*) ((char*) gauss + (255*4 - index));
-			int s = ((table  [0] * voice.interp3) >> 12) +
-					((table  [1] * voice.interp2) >> 12);
-			s +=    ((table2 [1] * voice.interp1) >> 12) +
-			// to do: should clamp here
-					((table2 [0] * voice.interp0) >> 12);
-			int output = noise_amp; // noise is rarely used
-			if ( !(g.noise_enables & vbit) )
-				output = clamp_16( s * 2 );
-			
-			// scale output and set outx values
-			output = (output * envx) >> 11 & ~1;
-			
-			// output and apply muting (by setting voice.enabled to 31)
-			// if voice is externally disabled (not a SNES feature)
-			int l = (voice.volume [0] * output) >> voice.enabled;
-			int r = (voice.volume [1] * output) >> voice.enabled;
-			prev_outx = output;
-			raw_voice.outx = output >> 8;
-			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];
-		
-		left  += (fb_left  * g.left_echo_volume ) >> 14;
-		right += (fb_right * g.right_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 ) );
-		}
-		
-		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 almost exactly (with an error of 0 or -1 for each entry):
-// int normal_gauss [512];
-// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45
-
-// Interleved gauss table (to improve cache coherency).
-// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i]
-const BOOST::int16_t Spc_Dsp::gauss [512] =
-{
- 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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,666 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+// 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-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ )
+{
+	set_gain( 1.0 );
+	mute_voices( 0 );
+	disable_surround( false );
+	
+	BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count );
+}
+
+void Spc_Dsp::mute_voices( int mask )
+{
+	for ( int i = 0; i < voice_count; i++ )
+		voice_state [i].enabled = (mask >> i & 1) ? 31 : 7;
+}
+
+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_t& v = voice_state [i];
+		v.on_cnt = 0;
+		v.volume [0] = 0;
+		v.volume [1] = 0;
+		v.envstate = state_release;
+	}
+	
+	memset( fir_buf, 0, sizeof fir_buf );
+}
+
+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: {
+			short* volume = voice_state [high].volume;
+			int left  = (int8_t) reg [i & ~1];
+			int right = (int8_t) reg [i | 1];
+			volume [0] = left;
+			volume [1] = right;
+			// kill surround only if signs of volumes differ
+			if ( left * right < surround_threshold )
+			{
+				if ( left < 0 )
+					volume [0] = -left;
+				else
+					volume [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;
+
+inline 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;
+			
+			case state_release:
+				// handled above
+				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 so that this becomes a leaf function?
+	
+	// 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 left_volume  = g.left_volume;
+	int right_volume = g.right_volume;
+	if ( left_volume * right_volume < surround_threshold )
+		right_volume = -right_volume; // kill global surround
+	left_volume  *= emu_gain;
+	right_volume *= emu_gain;
+	
+	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;
+				
+				noise_amp = BOOST::int16_t (noise * 2);
+				
+				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;
+			
+			// Gaussian interpolation using most recent 4 samples
+			int index = voice.fraction >> 2 & 0x3FC;
+			voice.fraction = (voice.fraction & 0x0FFF) + rate;
+			const BOOST::int16_t* table  = (BOOST::int16_t*) ((char*) gauss + index);
+			const BOOST::int16_t* table2 = (BOOST::int16_t*) ((char*) gauss + (255*4 - index));
+			int s = ((table  [0] * voice.interp3) >> 12) +
+					((table  [1] * voice.interp2) >> 12);
+			s +=    ((table2 [1] * voice.interp1) >> 12) +
+			// to do: should clamp here
+					((table2 [0] * voice.interp0) >> 12);
+			int output = noise_amp; // noise is rarely used
+			if ( !(g.noise_enables & vbit) )
+				output = clamp_16( s * 2 );
+			
+			// scale output and set outx values
+			output = (output * envx) >> 11 & ~1;
+			
+			// output and apply muting (by setting voice.enabled to 31)
+			// if voice is externally disabled (not a SNES feature)
+			int l = (voice.volume [0] * output) >> voice.enabled;
+			int r = (voice.volume [1] * output) >> voice.enabled;
+			prev_outx = output;
+			raw_voice.outx = output >> 8;
+			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];
+		
+		left  += (fb_left  * g.left_echo_volume ) >> 14;
+		right += (fb_right * g.right_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 ) );
+		}
+		
+		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 almost exactly (with an error of 0 or -1 for each entry):
+// int normal_gauss [512];
+// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45
+
+// Interleved gauss table (to improve cache coherency).
+// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i]
+const BOOST::int16_t Spc_Dsp::gauss [512] =
+{
+ 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,
+};
+
--- a/Plugins/Input/console/Spc_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Spc_Emu.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include "blargg_endian.h"
-
-/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-Spc_Emu::Spc_Emu( double gain )
-{
-	apu.set_gain( gain );
-}
-
-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;
-}
-
-void Spc_Emu::mute_voices( int m )
-{
-	Music_Emu::mute_voices( m );
-	apu.mute_voices( m );
-}
-
-blargg_err_t Spc_Emu::set_sample_rate( long sample_rate )
-{
-	if ( sample_rate != native_sample_rate )
-	{
-		BLARGG_RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) );
-		resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 );
-	}
-	return Music_Emu::set_sample_rate( sample_rate );
-}
-
-blargg_err_t Spc_Emu::load( Data_Reader& in )
-{
-	header_t h;
-	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
-	return load( h, in );
-}
-
-blargg_err_t Spc_Emu::load( const header_t& h, Data_Reader& in )
-{
-	if ( in.remain() < Snes_Spc::spc_file_size - (int) sizeof h )
-		return "Not an SPC file";
-	
-	if ( strncmp( h.tag, "SNES-SPC700 Sound File Data", 27 ) != 0 )
-		return "Not an SPC file";
-	
-	long remain = in.remain();
-	long size = remain + sizeof h;
-	if ( size < trailer_offset )
-		size = trailer_offset;
-	BLARGG_RETURN_ERR( spc_data.resize( size ) );
-	
-	set_track_count( 1 );
-	set_voice_count( Snes_Spc::voice_count );
-	
-	memcpy( spc_data.begin(), &h, sizeof h );
-	return in.read( &spc_data [sizeof h], remain );
-}
-
-void Spc_Emu::start_track( int track )
-{
-	Music_Emu::start_track( track );
-	
-	resampler.clear();
-	if ( apu.load_spc( spc_data.begin(), spc_data.size() ) )
-		check( false );
-}
-
-void Spc_Emu::skip( long count )
-{
-	count = long (count * resampler.ratio()) & ~1;
-	
-	count -= resampler.skip_input( count );
-	if ( count > 0 )
-		apu.skip( count );
-	
-	// eliminate pop due to resampler
-	const int resampler_latency = 64;
-	sample_t buf [resampler_latency];
-	play( resampler_latency, buf );
-}
-
-void Spc_Emu::play( long count, sample_t* out )
-{
-	require( track_count() ); // file must be loaded
-	
-	if ( sample_rate() == native_sample_rate )
-	{
-		if ( apu.play( count, out ) )
-			log_error();
-		return;
-	}
-	
-	long remain = count;
-	while ( remain > 0 )
-	{
-		remain -= resampler.read( &out [count - remain], remain );
-		if ( remain > 0 )
-		{
-			long n = resampler.max_write();
-			if ( apu.play( n, resampler.buffer() ) )
-				log_error();
-			resampler.write( n );
-		}
-	}
-	
-	assert( remain == 0 );
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Spc_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,133 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Spc_Emu.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "blargg_endian.h"
+
+/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+Spc_Emu::Spc_Emu( double gain )
+{
+	apu.set_gain( gain );
+}
+
+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;
+}
+
+void Spc_Emu::mute_voices( int m )
+{
+	Music_Emu::mute_voices( m );
+	apu.mute_voices( m );
+}
+
+blargg_err_t Spc_Emu::set_sample_rate( long sample_rate )
+{
+	if ( sample_rate != native_sample_rate )
+	{
+		BLARGG_RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) );
+		resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 );
+	}
+	return Music_Emu::set_sample_rate( sample_rate );
+}
+
+blargg_err_t Spc_Emu::load( Data_Reader& in )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
+	return load( h, in );
+}
+
+blargg_err_t Spc_Emu::load( const header_t& h, Data_Reader& in )
+{
+	if ( in.remain() < Snes_Spc::spc_file_size - (int) sizeof h )
+		return "Not an SPC file";
+	
+	if ( strncmp( h.tag, "SNES-SPC700 Sound File Data", 27 ) != 0 )
+		return "Not an SPC file";
+	
+	long remain = in.remain();
+	long size = remain + sizeof h;
+	if ( size < trailer_offset )
+		size = trailer_offset;
+	BLARGG_RETURN_ERR( spc_data.resize( size ) );
+	
+	set_track_count( 1 );
+	set_voice_count( Snes_Spc::voice_count );
+	
+	memcpy( spc_data.begin(), &h, sizeof h );
+	return in.read( &spc_data [sizeof h], remain );
+}
+
+void Spc_Emu::start_track( int track )
+{
+	Music_Emu::start_track( track );
+	
+	resampler.clear();
+	if ( apu.load_spc( spc_data.begin(), spc_data.size() ) )
+		check( false );
+}
+
+void Spc_Emu::skip( long count )
+{
+	count = long (count * resampler.ratio()) & ~1;
+	
+	count -= resampler.skip_input( count );
+	if ( count > 0 )
+		apu.skip( count );
+	
+	// eliminate pop due to resampler
+	const int resampler_latency = 64;
+	sample_t buf [resampler_latency];
+	play( resampler_latency, buf );
+}
+
+void Spc_Emu::play( long count, sample_t* out )
+{
+	require( track_count() ); // file must be loaded
+	
+	if ( sample_rate() == native_sample_rate )
+	{
+		if ( apu.play( count, out ) )
+			log_error();
+		return;
+	}
+	
+	long remain = count;
+	while ( remain > 0 )
+	{
+		remain -= resampler.read( &out [count - remain], remain );
+		if ( remain > 0 )
+		{
+			long n = resampler.max_write();
+			if ( apu.play( n, resampler.buffer() ) )
+				log_error();
+			resampler.write( n );
+		}
+	}
+	
+	assert( remain == 0 );
+}
+
--- a/Plugins/Input/console/Track_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Track_Emu.h"
-
-#include <string.h>
-#include <math.h>
-
-/* Copyright (C) 2005-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-int const stereo = 2; // channels for a stereo signal
-int const fade_block_size = 512;
-int const fade_length = 8000; // msec
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-
-long Track_Emu::msec_to_samples( long msec ) const
-{
-	long rate = emu->sample_rate() * stereo;
-	return (msec / 1000L) * rate + (msec % 1000L) * rate / 1000;
-}
-
-void Track_Emu::sync( long time )
-{
-	buf_count = 0;
-	silence_count = 0;
-	emu_time = time;
-	out_time = time;
-	silence_time = time;
-	track_ended = time > fade_time + fade_length * stereo * emu->sample_rate();
-}
-
-void Track_Emu::restart_track()
-{
-	emu->start_track( track );
-	sync ( 0 );
-	
-	// skip initial silence
-	for ( int n = 40 * stereo * emu->sample_rate() / buf_size; n--; )
-	{
-		fill_buf( true );
-		if ( buf_count || track_ended )
-			break;
-	}
-	sync( 0 );
-}
-
-void Track_Emu::seek( long time )
-{
-	long pos = msec_to_samples( time ) & ~1;
-	if ( pos < out_time )
-		restart_track();
-	emu->skip( pos - emu_time );
-	sync( pos );
-}
-
-long Track_Emu::tell() const
-{
-	long rate = emu->sample_rate() * stereo;
-	return (out_time / rate * 1000) + (out_time % rate * 1000 / rate);
-}
-
-void Track_Emu::start_track( Music_Emu* e, int t, long length, bool ds )
-{
-// to do: remove
-//length = 50 * 1000;
-//ds = true;
-//t = 23;
-
-	emu = e;
-	track = t;
-	detect_silence = ds;
-	fade_factor = pow( 0.005, 1.0 / msec_to_samples( fade_length ) );
-	fade_time = msec_to_samples( length );
-	restart_track();
-}
-
-static bool is_silence( const Music_Emu::sample_t* p, int count )
-{
-	while ( count-- )
-	{
-		if ( (unsigned) (*p++ + silence_threshold / 2) > (unsigned) silence_threshold )
-			return false;
-	}
-	return true;
-}
-
-void Track_Emu::fill_buf( bool check_silence )
-{
-	emu->play( buf_size, buf );
-	emu_time += buf_size;
-	if ( (check_silence || emu_time > fade_time) && is_silence( buf, buf_size ) )
-	{
-		silence_count += buf_size;
-	}
-	else
-	{
-		silence_time = emu_time;
-		buf_count = buf_size;
-	}
-	if ( emu->track_ended() || emu->error_count() )
-		track_ended = true;
-}
-
-inline void Track_Emu::end_track()
-{
-	silence_count = 0;
-	buf_count = 0;
-	track_ended = true;
-}
-
-bool Track_Emu::play( int out_count, Music_Emu::sample_t* out )
-{
-	assert( out_count % 2 == 0 );
-	assert( emu );
-	
-	int pos = 0;
-	while ( pos < out_count )
-	{
-		// fill with any remaining silence
-		int count = min( silence_count, out_count - pos );
-		if ( count )
-		{
-			silence_count -= count;
-			memset( &out [pos], 0, count * sizeof *out );
-		}
-		else
-		{
-			// empty internal buffer
-			count = min( buf_count, out_count - pos );
-			if ( !count && track_ended )
-			{
-				memset( &out [pos], 0, (out_count - pos) * sizeof *out );
-				return true;
-			}
-			
-			memcpy( &out [pos], &buf [buf_size - buf_count], count * sizeof *out );
-			buf_count -= count;
-		}
-		pos += count;
-		
-		// keep internal buffer full and possibly run ahead
-		for ( int n = 6; n--; )
-		{
-			if ( buf_count || track_ended ||
-					emu_time - out_time > silence_max * stereo * emu->sample_rate() )
-				break;
-			fill_buf( detect_silence );
-		}
-	}
-	out_time += out_count;
-	
-	if ( detect_silence && 
-		( emu_time - silence_time > silence_max * stereo * emu->sample_rate() && silence_time ) )
-		end_track();
-	
-	// fade if track is ending
-	if ( out_time > fade_time )
-	{
-		for ( int i = 0; i < out_count; i += fade_block_size )
-		{
-			double gain = pow( fade_factor, (double) (out_time + i - fade_time) );
-			if ( gain < 0.005 )
-				end_track();
-			
-			int count = min( fade_block_size, out_count - i );
-			int igain = (unsigned int)((double)gain * (1 << 15));;
-			for ( int j = 0; j < count; j++ )
-				out [i + j] = (out [i + j] * igain) >> 15;
-		}
-	}
-	
-	return !silence_count && !buf_count && track_ended;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Track_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,186 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Track_Emu.h"
+
+#include <string.h>
+#include <math.h>
+
+/* Copyright (C) 2005-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+int const stereo = 2; // channels for a stereo signal
+int const fade_block_size = 512;
+int const fade_length = 8000; // msec
+int const silence_max = 6; // seconds
+int const silence_threshold = 0x10;
+
+long Track_Emu::msec_to_samples( long msec ) const
+{
+	long rate = emu->sample_rate() * stereo;
+	return (msec / 1000L) * rate + (msec % 1000L) * rate / 1000;
+}
+
+void Track_Emu::sync( long time )
+{
+	buf_count = 0;
+	silence_count = 0;
+	emu_time = time;
+	out_time = time;
+	silence_time = time;
+	track_ended = time > fade_time + fade_length * stereo * emu->sample_rate();
+}
+
+void Track_Emu::restart_track()
+{
+	emu->start_track( track );
+	sync ( 0 );
+	
+	// skip initial silence
+	for ( int n = 40 * stereo * emu->sample_rate() / buf_size; n--; )
+	{
+		fill_buf( true );
+		if ( buf_count || track_ended )
+			break;
+	}
+	sync( 0 );
+}
+
+void Track_Emu::seek( long time )
+{
+	long pos = msec_to_samples( time ) & ~1;
+	if ( pos < out_time )
+		restart_track();
+	emu->skip( pos - emu_time );
+	sync( pos );
+}
+
+long Track_Emu::tell() const
+{
+	long rate = emu->sample_rate() * stereo;
+	return (out_time / rate * 1000) + (out_time % rate * 1000 / rate);
+}
+
+void Track_Emu::start_track( Music_Emu* e, int t, long length, bool ds )
+{
+// to do: remove
+//length = 50 * 1000;
+//ds = true;
+//t = 23;
+
+	emu = e;
+	track = t;
+	detect_silence = ds;
+	fade_factor = pow( 0.005, 1.0 / msec_to_samples( fade_length ) );
+	fade_time = msec_to_samples( length );
+	restart_track();
+}
+
+static bool is_silence( const Music_Emu::sample_t* p, int count )
+{
+	while ( count-- )
+	{
+		if ( (unsigned) (*p++ + silence_threshold / 2) > (unsigned) silence_threshold )
+			return false;
+	}
+	return true;
+}
+
+void Track_Emu::fill_buf( bool check_silence )
+{
+	emu->play( buf_size, buf );
+	emu_time += buf_size;
+	if ( (check_silence || emu_time > fade_time) && is_silence( buf, buf_size ) )
+	{
+		silence_count += buf_size;
+	}
+	else
+	{
+		silence_time = emu_time;
+		buf_count = buf_size;
+	}
+	if ( emu->track_ended() || emu->error_count() )
+		track_ended = true;
+}
+
+inline void Track_Emu::end_track()
+{
+	silence_count = 0;
+	buf_count = 0;
+	track_ended = true;
+}
+
+bool Track_Emu::play( int out_count, Music_Emu::sample_t* out )
+{
+	assert( out_count % 2 == 0 );
+	assert( emu );
+	
+	int pos = 0;
+	while ( pos < out_count )
+	{
+		// fill with any remaining silence
+		int count = min( silence_count, out_count - pos );
+		if ( count )
+		{
+			silence_count -= count;
+			memset( &out [pos], 0, count * sizeof *out );
+		}
+		else
+		{
+			// empty internal buffer
+			count = min( buf_count, out_count - pos );
+			if ( !count && track_ended )
+			{
+				memset( &out [pos], 0, (out_count - pos) * sizeof *out );
+				return true;
+			}
+			
+			memcpy( &out [pos], &buf [buf_size - buf_count], count * sizeof *out );
+			buf_count -= count;
+		}
+		pos += count;
+		
+		// keep internal buffer full and possibly run ahead
+		for ( int n = 6; n--; )
+		{
+			if ( buf_count || track_ended ||
+					emu_time - out_time > silence_max * stereo * emu->sample_rate() )
+				break;
+			fill_buf( detect_silence );
+		}
+	}
+	out_time += out_count;
+	
+	if ( detect_silence && 
+		( emu_time - silence_time > silence_max * stereo * emu->sample_rate() && silence_time ) )
+		end_track();
+	
+	// fade if track is ending
+	if ( out_time > fade_time )
+	{
+		for ( int i = 0; i < out_count; i += fade_block_size )
+		{
+			double gain = pow( fade_factor, (double) (out_time + i - fade_time) );
+			if ( gain < 0.005 )
+				end_track();
+			
+			int count = min( fade_block_size, out_count - i );
+			int igain = (unsigned int)((double)gain * (1 << 15));;
+			for ( int j = 0; j < count; j++ )
+				out [i + j] = (out [i + j] * igain) >> 15;
+		}
+	}
+	
+	return !silence_count && !buf_count && track_ended;
+}
+
--- a/Plugins/Input/console/Vfs_File.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-
-#include "Vfs_File.h"
-
-#include "libaudacious/vfs.h"
-
-Vfs_File_Reader::Vfs_File_Reader() : file_( NULL ) { }
-
-Vfs_File_Reader::~Vfs_File_Reader() { close(); }
-
-Vfs_File_Reader::error_t Vfs_File_Reader::open( const char* path )
-{
-	file_ = vfs_fopen( path, "rb" );
-	if ( !file_ )
-		return "Couldn't open file";
-	return 0;
-}
-
-long Vfs_File_Reader::size() const
-{
-	long pos = tell();
-	vfs_fseek( (VFSFile*) file_, 0, SEEK_END );
-	long result = tell();
-	vfs_fseek( (VFSFile*) file_, pos, SEEK_SET );
-	return result;
-}
-
-long Vfs_File_Reader::read_avail( void* p, long s )
-{
-	return (long) vfs_fread( p, 1, s, (VFSFile*) file_ );
-}
-
-long Vfs_File_Reader::tell() const
-{
-	return vfs_ftell( (VFSFile*) file_ );
-}
-
-Vfs_File_Reader::error_t Vfs_File_Reader::seek( long n )
-{
-	if ( n == 0 ) // optimization
-		vfs_rewind( (VFSFile*) file_ );
-	else if ( vfs_fseek( (VFSFile*) file_, n, SEEK_SET ) != 0 )
-		return "Error seeking in file";
-	return 0;
-}
-
-void Vfs_File_Reader::close()
-{
-	if ( file_ )
-	{
-		vfs_fclose( (VFSFile*) file_ );
-		file_ = 0;
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Vfs_File.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,54 @@
+
+#include "Vfs_File.h"
+
+#include "libaudacious/vfs.h"
+
+Vfs_File_Reader::Vfs_File_Reader() : file_( NULL ) { }
+
+Vfs_File_Reader::~Vfs_File_Reader() { close(); }
+
+Vfs_File_Reader::error_t Vfs_File_Reader::open( const char* path )
+{
+	file_ = vfs_fopen( path, "rb" );
+	if ( !file_ )
+		return "Couldn't open file";
+	return 0;
+}
+
+long Vfs_File_Reader::size() const
+{
+	long pos = tell();
+	vfs_fseek( (VFSFile*) file_, 0, SEEK_END );
+	long result = tell();
+	vfs_fseek( (VFSFile*) file_, pos, SEEK_SET );
+	return result;
+}
+
+long Vfs_File_Reader::read_avail( void* p, long s )
+{
+	return (long) vfs_fread( p, 1, s, (VFSFile*) file_ );
+}
+
+long Vfs_File_Reader::tell() const
+{
+	return vfs_ftell( (VFSFile*) file_ );
+}
+
+Vfs_File_Reader::error_t Vfs_File_Reader::seek( long n )
+{
+	if ( n == 0 ) // optimization
+		vfs_rewind( (VFSFile*) file_ );
+	else if ( vfs_fseek( (VFSFile*) file_, n, SEEK_SET ) != 0 )
+		return "Error seeking in file";
+	return 0;
+}
+
+void Vfs_File_Reader::close()
+{
+	if ( file_ )
+	{
+		vfs_fclose( (VFSFile*) file_ );
+		file_ = 0;
+	}
+}
+
--- a/Plugins/Input/console/Vgm_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,298 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Vgm_Emu.h"
-
-#include <math.h>
-#include <string.h>
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-double const gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
-double const rolloff = 0.990;
-double const oversample_factor = 1.5;
-
-Vgm_Emu::Vgm_Emu( bool os, double tempo )
-{
-	oversample = os;
-	pos = NULL;
-	data = NULL;
-	uses_fm = false;
-	vgm_rate = (long) (header_t::time_rate * tempo + 0.5);
-	
-	static equalizer_t const eq = { -14.0, 80 };
-	set_equalizer( eq );
-	psg.volume( 1.0 );
-}
-
-Vgm_Emu::~Vgm_Emu()
-{
-	unload();
-}
-
-void Vgm_Emu::unload()
-{
-	data = NULL;
-	pos = NULL;
-	set_track_ended( false );
-	mem.clear();
-}
-
-blargg_err_t Vgm_Emu::set_sample_rate( long sample_rate )
-{
-	BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) );
-	return Classic_Emu::set_sample_rate( sample_rate );
-}
-
-BOOST::uint8_t const* Vgm_Emu::gd3_data( int* size ) const
-{
-	if ( size )
-		*size = 0;
-	
-	long gd3_offset = get_le32( header_.gd3_offset );
-	if ( !gd3_offset )
-		return NULL;
-	
-	gd3_offset -= 0x40 - offsetof (header_t,gd3_offset);
-	if ( gd3_offset < 0 )
-		return NULL;
-	
-	byte const* gd3 = data + gd3_offset;
-	if ( data_end - gd3 < 16 || 0 != memcmp( gd3, "Gd3 ", 4 ) || get_le32( gd3 + 4 ) >= 0x200 )
-		return NULL;
-	
-	long gd3_size = get_le32( gd3 + 8 );
-	if ( data_end - gd3 < gd3_size - 12 )
-		return NULL;
-	
-	if ( size )
-		*size = data_end - gd3;
-	return gd3;
-}
-
-void Vgm_Emu::update_eq( blip_eq_t const& eq )
-{
-	psg.treble_eq( eq );
-	dac_synth.treble_eq( eq );
-}
-
-void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
-{
-	if ( i < psg.osc_count )
-		psg.osc_output( i, c, l, r );
-}
-
-const char** Vgm_Emu::voice_names() const
-{
-	static const char* fm_names [] = {
-		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
-	};
-	if ( uses_fm )
-		return fm_names;
-	
-	static const char* psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
-	return psg_names;
-}
-
-void Vgm_Emu::mute_voices( int mask )
-{
-	Classic_Emu::mute_voices( mask );
-	dac_synth.output( &blip_buf );
-	if ( uses_fm )
-	{
-		psg.output( (mask & 0x80) ? 0 : &blip_buf );
-		if ( ym2612.enabled() )
-		{
-			dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * gain );
-			ym2612.mute_voices( mask );
-		}
-		
-		if ( ym2413.enabled() )
-		{
-			int m = mask & 0x3f;
-			if ( mask & 0x20 )
-				m |= 0x01e0; // channels 5-8
-			if ( mask & 0x40 )
-				m |= 0x3e00;
-			ym2413.mute_voices( m );
-		}
-	}
-}
-
-blargg_err_t Vgm_Emu::load_( const header_t& h, void const* new_data, long new_size )
-{
-	header_ = h;
-	
-	// compatibility
-	if ( 0 != memcmp( header_.tag, "Vgm ", 4 ) )
-		return "Not a VGM file";
-	check( get_le32( header_.version ) <= 0x150 );
-	
-	// psg rate
-	long psg_rate = get_le32( header_.psg_rate );
-	if ( !psg_rate )
-		psg_rate = 3579545;
-	blip_time_factor = (long) floor( (double) (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 );
-	blip_buf.clock_rate( psg_rate );
-	
-	data = (byte*) new_data;
-	data_end = data + new_size;
-	
-	// get loop
-	loop_begin = data_end;
-	if ( get_le32( header_.loop_offset ) )
-		loop_begin = &data [get_le32( header_.loop_offset ) + offsetof (header_t,loop_offset) - 0x40];
-	
-	set_voice_count( psg.osc_count );
-	set_track_count( 1 );
-	
-	BLARGG_RETURN_ERR( setup_fm() );
-	
-	// do after FM in case output buffer is changed
-	BLARGG_RETURN_ERR( Classic_Emu::setup_buffer( psg_rate ) );
-	
-	return blargg_success;
-}
-
-blargg_err_t Vgm_Emu::setup_fm()
-{
-	long ym2612_rate = get_le32( header_.ym2612_rate );
-	long ym2413_rate = get_le32( header_.ym2413_rate );
-	if ( ym2413_rate && get_le32( header_.version ) < 0x110 )
-		update_fm_rates( &ym2413_rate, &ym2612_rate );
-	
-	uses_fm = false;
-	
-	double fm_rate = blip_buf.sample_rate() * oversample_factor;
-	
-	if ( ym2612_rate )
-	{
-		uses_fm = true;
-		if ( !oversample )
-			fm_rate = ym2612_rate / 144.0;
-		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain );
-		BLARGG_RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) );
-		ym2612.enable( true );
-		set_voice_count( 8 );
-	}
-	
-	if ( !uses_fm && ym2413_rate )
-	{
-		uses_fm = true;
-		if ( !oversample )
-			fm_rate = ym2413_rate / 72.0;
-		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain );
-		int result = ym2413.set_rate( fm_rate, ym2413_rate );
-		if ( result == 2 )
-			return "YM2413 FM sound isn't supported";
-		BLARGG_CHECK_ALLOC( !result );
-		ym2413.enable( true );
-		set_voice_count( 8 );
-	}
-	
-	if ( uses_fm )
-	{
-		//dprintf( "fm_rate: %f\n", fm_rate );
-		fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
-		BLARGG_RETURN_ERR( Dual_Resampler::resize( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
-		psg.volume( 0.135 * gain );
-	}
-	else
-	{
-		ym2612.enable( false );
-		ym2413.enable( false );
-		psg.volume( 1.0 );
-	}
-	
-	return blargg_success;
-}
-
-blargg_err_t Vgm_Emu::load( Data_Reader& reader )
-{
-	header_t h;
-	BLARGG_RETURN_ERR( reader.read( &h, sizeof h ) );
-	return load( h, reader );
-}
-
-blargg_err_t Vgm_Emu::load( const header_t& h, Data_Reader& reader )
-{
-	unload();
-	
-	// allocate and read data
-	long data_size = reader.remain();
-	int const padding = 8;
-	BLARGG_RETURN_ERR( mem.resize( data_size + padding ) );
-	blargg_err_t err = reader.read( mem.begin(), data_size );
-	if ( err ) {
-		unload();
-		return err;
-	}
-	memset( &mem [data_size], 0x66, padding ); // pad with end command
-	
-	return load_( h, mem.begin(), data_size );
-}
-
-void Vgm_Emu::start_track( int track )
-{
-	require( data ); // file must have been loaded
-	
-	Classic_Emu::start_track( track );
-	psg.reset();
-	
-	dac_disabled = -1;
-	pcm_data = data;
-	pcm_pos = data;
-	dac_amp = -1;
-	vgm_time = 0;
-	pos = data;
-	if ( get_le32( header_.version ) >= 0x150 )
-	{
-		long data_offset = get_le32( header_.data_offset );
-		check( data_offset );
-		if ( data_offset )
-			pos += data_offset + offsetof (header_t,data_offset) - 0x40;
-	}
-	
-	if ( uses_fm )
-	{
-		if ( ym2413.enabled() )
-			ym2413.reset();
-		
-		if ( ym2612.enabled() )
-			ym2612.reset();
-		
-		fm_time_offset = 0;
-		blip_buf.clear();
-		Dual_Resampler::clear();
-	}
-}
-
-long Vgm_Emu::run( int msec, bool* added_stereo )
-{
-	blip_time_t psg_end = run_commands( msec * vgm_rate / 1000 );
-	*added_stereo = psg.end_frame( psg_end );
-	return psg_end;
-}
-
-void Vgm_Emu::play( long count, sample_t* out )
-{
-	require( pos ); // track must have been started
-	
-	if ( uses_fm )
-		Dual_Resampler::play( count, out, blip_buf );
-	else
-		Classic_Emu::play( count, out );
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Vgm_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,298 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Vgm_Emu.h"
+
+#include <math.h>
+#include <string.h>
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+double const gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
+double const rolloff = 0.990;
+double const oversample_factor = 1.5;
+
+Vgm_Emu::Vgm_Emu( bool os, double tempo )
+{
+	oversample = os;
+	pos = NULL;
+	data = NULL;
+	uses_fm = false;
+	vgm_rate = (long) (header_t::time_rate * tempo + 0.5);
+	
+	static equalizer_t const eq = { -14.0, 80 };
+	set_equalizer( eq );
+	psg.volume( 1.0 );
+}
+
+Vgm_Emu::~Vgm_Emu()
+{
+	unload();
+}
+
+void Vgm_Emu::unload()
+{
+	data = NULL;
+	pos = NULL;
+	set_track_ended( false );
+	mem.clear();
+}
+
+blargg_err_t Vgm_Emu::set_sample_rate( long sample_rate )
+{
+	BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) );
+	return Classic_Emu::set_sample_rate( sample_rate );
+}
+
+BOOST::uint8_t const* Vgm_Emu::gd3_data( int* size ) const
+{
+	if ( size )
+		*size = 0;
+	
+	long gd3_offset = get_le32( header_.gd3_offset );
+	if ( !gd3_offset )
+		return NULL;
+	
+	gd3_offset -= 0x40 - offsetof (header_t,gd3_offset);
+	if ( gd3_offset < 0 )
+		return NULL;
+	
+	byte const* gd3 = data + gd3_offset;
+	if ( data_end - gd3 < 16 || 0 != memcmp( gd3, "Gd3 ", 4 ) || get_le32( gd3 + 4 ) >= 0x200 )
+		return NULL;
+	
+	long gd3_size = get_le32( gd3 + 8 );
+	if ( data_end - gd3 < gd3_size - 12 )
+		return NULL;
+	
+	if ( size )
+		*size = data_end - gd3;
+	return gd3;
+}
+
+void Vgm_Emu::update_eq( blip_eq_t const& eq )
+{
+	psg.treble_eq( eq );
+	dac_synth.treble_eq( eq );
+}
+
+void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+	if ( i < psg.osc_count )
+		psg.osc_output( i, c, l, r );
+}
+
+const char** Vgm_Emu::voice_names() const
+{
+	static const char* fm_names [] = {
+		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
+	};
+	if ( uses_fm )
+		return fm_names;
+	
+	static const char* psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
+	return psg_names;
+}
+
+void Vgm_Emu::mute_voices( int mask )
+{
+	Classic_Emu::mute_voices( mask );
+	dac_synth.output( &blip_buf );
+	if ( uses_fm )
+	{
+		psg.output( (mask & 0x80) ? 0 : &blip_buf );
+		if ( ym2612.enabled() )
+		{
+			dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * gain );
+			ym2612.mute_voices( mask );
+		}
+		
+		if ( ym2413.enabled() )
+		{
+			int m = mask & 0x3f;
+			if ( mask & 0x20 )
+				m |= 0x01e0; // channels 5-8
+			if ( mask & 0x40 )
+				m |= 0x3e00;
+			ym2413.mute_voices( m );
+		}
+	}
+}
+
+blargg_err_t Vgm_Emu::load_( const header_t& h, void const* new_data, long new_size )
+{
+	header_ = h;
+	
+	// compatibility
+	if ( 0 != memcmp( header_.tag, "Vgm ", 4 ) )
+		return "Not a VGM file";
+	check( get_le32( header_.version ) <= 0x150 );
+	
+	// psg rate
+	long psg_rate = get_le32( header_.psg_rate );
+	if ( !psg_rate )
+		psg_rate = 3579545;
+	blip_time_factor = (long) floor( (double) (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 );
+	blip_buf.clock_rate( psg_rate );
+	
+	data = (byte*) new_data;
+	data_end = data + new_size;
+	
+	// get loop
+	loop_begin = data_end;
+	if ( get_le32( header_.loop_offset ) )
+		loop_begin = &data [get_le32( header_.loop_offset ) + offsetof (header_t,loop_offset) - 0x40];
+	
+	set_voice_count( psg.osc_count );
+	set_track_count( 1 );
+	
+	BLARGG_RETURN_ERR( setup_fm() );
+	
+	// do after FM in case output buffer is changed
+	BLARGG_RETURN_ERR( Classic_Emu::setup_buffer( psg_rate ) );
+	
+	return blargg_success;
+}
+
+blargg_err_t Vgm_Emu::setup_fm()
+{
+	long ym2612_rate = get_le32( header_.ym2612_rate );
+	long ym2413_rate = get_le32( header_.ym2413_rate );
+	if ( ym2413_rate && get_le32( header_.version ) < 0x110 )
+		update_fm_rates( &ym2413_rate, &ym2612_rate );
+	
+	uses_fm = false;
+	
+	double fm_rate = blip_buf.sample_rate() * oversample_factor;
+	
+	if ( ym2612_rate )
+	{
+		uses_fm = true;
+		if ( !oversample )
+			fm_rate = ym2612_rate / 144.0;
+		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain );
+		BLARGG_RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) );
+		ym2612.enable( true );
+		set_voice_count( 8 );
+	}
+	
+	if ( !uses_fm && ym2413_rate )
+	{
+		uses_fm = true;
+		if ( !oversample )
+			fm_rate = ym2413_rate / 72.0;
+		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain );
+		int result = ym2413.set_rate( fm_rate, ym2413_rate );
+		if ( result == 2 )
+			return "YM2413 FM sound isn't supported";
+		BLARGG_CHECK_ALLOC( !result );
+		ym2413.enable( true );
+		set_voice_count( 8 );
+	}
+	
+	if ( uses_fm )
+	{
+		//dprintf( "fm_rate: %f\n", fm_rate );
+		fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
+		BLARGG_RETURN_ERR( Dual_Resampler::resize( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
+		psg.volume( 0.135 * gain );
+	}
+	else
+	{
+		ym2612.enable( false );
+		ym2413.enable( false );
+		psg.volume( 1.0 );
+	}
+	
+	return blargg_success;
+}
+
+blargg_err_t Vgm_Emu::load( Data_Reader& reader )
+{
+	header_t h;
+	BLARGG_RETURN_ERR( reader.read( &h, sizeof h ) );
+	return load( h, reader );
+}
+
+blargg_err_t Vgm_Emu::load( const header_t& h, Data_Reader& reader )
+{
+	unload();
+	
+	// allocate and read data
+	long data_size = reader.remain();
+	int const padding = 8;
+	BLARGG_RETURN_ERR( mem.resize( data_size + padding ) );
+	blargg_err_t err = reader.read( mem.begin(), data_size );
+	if ( err ) {
+		unload();
+		return err;
+	}
+	memset( &mem [data_size], 0x66, padding ); // pad with end command
+	
+	return load_( h, mem.begin(), data_size );
+}
+
+void Vgm_Emu::start_track( int track )
+{
+	require( data ); // file must have been loaded
+	
+	Classic_Emu::start_track( track );
+	psg.reset();
+	
+	dac_disabled = -1;
+	pcm_data = data;
+	pcm_pos = data;
+	dac_amp = -1;
+	vgm_time = 0;
+	pos = data;
+	if ( get_le32( header_.version ) >= 0x150 )
+	{
+		long data_offset = get_le32( header_.data_offset );
+		check( data_offset );
+		if ( data_offset )
+			pos += data_offset + offsetof (header_t,data_offset) - 0x40;
+	}
+	
+	if ( uses_fm )
+	{
+		if ( ym2413.enabled() )
+			ym2413.reset();
+		
+		if ( ym2612.enabled() )
+			ym2612.reset();
+		
+		fm_time_offset = 0;
+		blip_buf.clear();
+		Dual_Resampler::clear();
+	}
+}
+
+long Vgm_Emu::run( int msec, bool* added_stereo )
+{
+	blip_time_t psg_end = run_commands( msec * vgm_rate / 1000 );
+	*added_stereo = psg.end_frame( psg_end );
+	return psg_end;
+}
+
+void Vgm_Emu::play( long count, sample_t* out )
+{
+	require( pos ); // track must have been started
+	
+	if ( uses_fm )
+		Dual_Resampler::play( count, out, blip_buf );
+	else
+		Classic_Emu::play( count, out );
+}
+
--- a/Plugins/Input/console/Vgm_Emu_Impl.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,312 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Vgm_Emu.h"
-
-#include <math.h>
-#include <string.h>
-#include "blargg_endian.h"
-
-/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include BLARGG_SOURCE_BEGIN
-
-enum {
-	cmd_gg_stereo       = 0x4F,
-	cmd_psg             = 0x50,
-	cmd_ym2413          = 0x51,
-	cmd_ym2612_port0    = 0x52,
-	cmd_ym2612_port1    = 0x53,
-	cmd_ym2151          = 0x54,
-	cmd_delay           = 0x61,
-	cmd_delay_735       = 0x62,
-	cmd_delay_882       = 0x63,
-	cmd_byte_delay      = 0x64,
-	cmd_end             = 0x66,
-	cmd_data_block      = 0x67,
-	cmd_short_delay     = 0x70,
-	cmd_pcm_delay       = 0x80,
-	cmd_pcm_seek        = 0xE0,
-	
-	pcm_block_type      = 0x00,
-	ym2612_dac_port     = 0x2A
-};
-
-inline int command_len( int command )
-{
-	switch ( command >> 4 )
-	{
-		case 0x03:
-		case 0x04:
-			return 2;
-		
-		case 0x05:
-		case 0x0A:
-		case 0x0B:
-			return 3;
-		
-		case 0x0C:
-		case 0x0D:
-			return 4;
-		
-		case 0x0E:
-		case 0x0F:
-			return 5;
-	}
-	
-	return 0;
-}
-
-template<class Emu>
-inline void Ym_Emu<Emu>::begin_frame( short* p )
-{
-	require( enabled() );
-	out = p;
-	last_time = 0;
-}
-
-template<class Emu>
-inline int Ym_Emu<Emu>::run_until( int time )
-{
-	int count = time - last_time;
-	if ( count > 0 )
-	{
-		if ( last_time < 0 )
-			return false;
-		last_time = time;
-		short* p = out;
-		out += count * Emu::out_chan_count;
-		Emu::run( count, p );
-	}
-	return true;
-}
-	
-inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const
-{
-	return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
-}
-
-inline blip_time_t Vgm_Emu_Impl::to_blip_time( vgm_time_t t ) const
-{
-	return (t * blip_time_factor) >> blip_time_bits;
-}
-
-void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp )
-{
-	blip_time_t blip_time = to_blip_time( vgm_time );
-	int old = dac_amp;
-	int delta = amp - old;
-	dac_amp = amp;
-	if ( old >= 0 )
-		dac_synth.offset_inline( blip_time, delta, &blip_buf );
-	else
-		dac_amp |= dac_disabled;
-}
-
-blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
-{
-	vgm_time_t vgm_time = this->vgm_time; 
-	byte const* pos = this->pos;
-	if ( pos >= data_end )
-	{
-		set_track_ended();
-		if ( pos > data_end )
-			log_error();
-	}
-	
-	while ( vgm_time < end_time && pos < data_end )
-	{
-		switch ( *pos++ )
-		{
-		case cmd_end:
-			pos = loop_begin; // if not looped, loop_begin == data_end
-			break;
-		
-		case cmd_delay_735:
-			vgm_time += 735;
-			break;
-		
-		case cmd_delay_882:
-			vgm_time += 882;
-			break;
-		
-		case cmd_gg_stereo:
-			psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ );
-			break;
-		
-		case cmd_psg:
-			psg.write_data( to_blip_time( vgm_time ), *pos++ );
-			break;
-		
-		case cmd_delay:
-			vgm_time += pos [1] * 0x100L + pos [0];
-			pos += 2;
-			break;
-		
-		case cmd_byte_delay:
-			vgm_time += *pos++;
-			break;
-		
-		case cmd_ym2413:
-			if ( ym2413.run_until( to_fm_time( vgm_time ) ) )
-				ym2413.write( pos [0], pos [1] );
-			pos += 2;
-			break;
-		
-		case cmd_ym2612_port0:
-			if ( pos [0] == ym2612_dac_port )
-			{
-				write_pcm( vgm_time, pos [1] );
-			}
-			else if ( ym2612.run_until( to_fm_time( vgm_time ) ) )
-			{
-				if ( pos [0] == 0x2B )
-				{
-					dac_disabled = (pos [1] >> 7 & 1) - 1;
-					dac_amp |= dac_disabled;
-				}
-				ym2612.write0( pos [0], pos [1] );
-			}
-			pos += 2;
-			break;
-		
-		case cmd_ym2612_port1:
-			if ( ym2612.run_until( to_fm_time( vgm_time ) ) )
-				ym2612.write1( pos [0], pos [1] );
-			pos += 2;
-			break;
-			
-		case cmd_data_block: {
-			check( *pos == cmd_end );
-			int type = pos [1];
-			long size = get_le32( pos + 2 );
-			pos += 6;
-			if ( type == pcm_block_type )
-				pcm_data = pos;
-			pos += size;
-			break;
-		}
-		
-		case cmd_pcm_seek:
-			pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L +
-					pos [1] * 0x100L + pos [0];
-			pos += 4;
-			break;
-		
-		default:
-			int cmd = pos [-1];
-			switch ( cmd & 0xf0 )
-			{
-				case cmd_pcm_delay:
-					vgm_time += cmd & 0x0f;
-					write_pcm( vgm_time, *pcm_pos++ );
-					break;
-				
-				case cmd_short_delay:
-					vgm_time += (cmd & 0x0f) + 1;
-					break;
-				
-				case 0x50:
-					pos += 2;
-					break;
-				
-				default:
-					pos += command_len( cmd ) - 1;
-					log_error();
-			}
-		}
-	}
-	vgm_time -= end_time;
-	this->pos = pos;
-	this->vgm_time = vgm_time;
-	
-	return to_blip_time( end_time );
-}
-
-int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
-{
-	// to do: timing is working mostly by luck
-	
-	int min_pairs = sample_count >> 1;
-	int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1;
-	assert( to_fm_time( vgm_time ) <= min_pairs );
-	int pairs = min_pairs;
-	while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
-		vgm_time++;
-	
-	if ( ym2612.enabled() )
-	{
-		ym2612.begin_frame( buf );
-		memset( buf, 0, pairs * stereo * sizeof *buf );
-	}
-	else if ( ym2413.enabled() )
-	{
-		ym2413.begin_frame( buf );
-	}
-	
-	run_commands( vgm_time );
-	ym2612.run_until( pairs );
-	ym2413.run_until( pairs );
-	
-	fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) -
-			((long) pairs << fm_time_bits);
-	
-	psg.end_frame( blip_time );
-	
-	return pairs * stereo;
-}
-
-// Update pre-1.10 header FM rates by scanning commands
-void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const
-{
-	byte const* p = data;
-	while ( p < data_end )
-	{
-		switch ( *p )
-		{
-		case cmd_end:
-			return;
-		
-		case cmd_psg:
-		case cmd_byte_delay:
-			p += 2;
-			break;
-		
-		case cmd_delay:
-			p += 3;
-			break;
-		
-		case cmd_data_block:
-			p += 7 + get_le32( p + 3 );
-			break;
-		
-		case cmd_ym2413:
-			*ym2612_rate = 0;
-			return;
-		
-		case cmd_ym2612_port0:
-		case cmd_ym2612_port1:
-			*ym2612_rate = *ym2413_rate;
-			*ym2413_rate = 0;
-			return;
-		
-		case cmd_ym2151:
-			*ym2413_rate = 0;
-			*ym2612_rate = 0;
-			return;
-		
-		default:
-			p += command_len( *p );
-		}
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Vgm_Emu_Impl.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,312 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Vgm_Emu.h"
+
+#include <math.h>
+#include <string.h>
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+enum {
+	cmd_gg_stereo       = 0x4F,
+	cmd_psg             = 0x50,
+	cmd_ym2413          = 0x51,
+	cmd_ym2612_port0    = 0x52,
+	cmd_ym2612_port1    = 0x53,
+	cmd_ym2151          = 0x54,
+	cmd_delay           = 0x61,
+	cmd_delay_735       = 0x62,
+	cmd_delay_882       = 0x63,
+	cmd_byte_delay      = 0x64,
+	cmd_end             = 0x66,
+	cmd_data_block      = 0x67,
+	cmd_short_delay     = 0x70,
+	cmd_pcm_delay       = 0x80,
+	cmd_pcm_seek        = 0xE0,
+	
+	pcm_block_type      = 0x00,
+	ym2612_dac_port     = 0x2A
+};
+
+inline int command_len( int command )
+{
+	switch ( command >> 4 )
+	{
+		case 0x03:
+		case 0x04:
+			return 2;
+		
+		case 0x05:
+		case 0x0A:
+		case 0x0B:
+			return 3;
+		
+		case 0x0C:
+		case 0x0D:
+			return 4;
+		
+		case 0x0E:
+		case 0x0F:
+			return 5;
+	}
+	
+	return 0;
+}
+
+template<class Emu>
+inline void Ym_Emu<Emu>::begin_frame( short* p )
+{
+	require( enabled() );
+	out = p;
+	last_time = 0;
+}
+
+template<class Emu>
+inline int Ym_Emu<Emu>::run_until( int time )
+{
+	int count = time - last_time;
+	if ( count > 0 )
+	{
+		if ( last_time < 0 )
+			return false;
+		last_time = time;
+		short* p = out;
+		out += count * Emu::out_chan_count;
+		Emu::run( count, p );
+	}
+	return true;
+}
+	
+inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const
+{
+	return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
+}
+
+inline blip_time_t Vgm_Emu_Impl::to_blip_time( vgm_time_t t ) const
+{
+	return (t * blip_time_factor) >> blip_time_bits;
+}
+
+void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp )
+{
+	blip_time_t blip_time = to_blip_time( vgm_time );
+	int old = dac_amp;
+	int delta = amp - old;
+	dac_amp = amp;
+	if ( old >= 0 )
+		dac_synth.offset_inline( blip_time, delta, &blip_buf );
+	else
+		dac_amp |= dac_disabled;
+}
+
+blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
+{
+	vgm_time_t vgm_time = this->vgm_time; 
+	byte const* pos = this->pos;
+	if ( pos >= data_end )
+	{
+		set_track_ended();
+		if ( pos > data_end )
+			log_error();
+	}
+	
+	while ( vgm_time < end_time && pos < data_end )
+	{
+		switch ( *pos++ )
+		{
+		case cmd_end:
+			pos = loop_begin; // if not looped, loop_begin == data_end
+			break;
+		
+		case cmd_delay_735:
+			vgm_time += 735;
+			break;
+		
+		case cmd_delay_882:
+			vgm_time += 882;
+			break;
+		
+		case cmd_gg_stereo:
+			psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ );
+			break;
+		
+		case cmd_psg:
+			psg.write_data( to_blip_time( vgm_time ), *pos++ );
+			break;
+		
+		case cmd_delay:
+			vgm_time += pos [1] * 0x100L + pos [0];
+			pos += 2;
+			break;
+		
+		case cmd_byte_delay:
+			vgm_time += *pos++;
+			break;
+		
+		case cmd_ym2413:
+			if ( ym2413.run_until( to_fm_time( vgm_time ) ) )
+				ym2413.write( pos [0], pos [1] );
+			pos += 2;
+			break;
+		
+		case cmd_ym2612_port0:
+			if ( pos [0] == ym2612_dac_port )
+			{
+				write_pcm( vgm_time, pos [1] );
+			}
+			else if ( ym2612.run_until( to_fm_time( vgm_time ) ) )
+			{
+				if ( pos [0] == 0x2B )
+				{
+					dac_disabled = (pos [1] >> 7 & 1) - 1;
+					dac_amp |= dac_disabled;
+				}
+				ym2612.write0( pos [0], pos [1] );
+			}
+			pos += 2;
+			break;
+		
+		case cmd_ym2612_port1:
+			if ( ym2612.run_until( to_fm_time( vgm_time ) ) )
+				ym2612.write1( pos [0], pos [1] );
+			pos += 2;
+			break;
+			
+		case cmd_data_block: {
+			check( *pos == cmd_end );
+			int type = pos [1];
+			long size = get_le32( pos + 2 );
+			pos += 6;
+			if ( type == pcm_block_type )
+				pcm_data = pos;
+			pos += size;
+			break;
+		}
+		
+		case cmd_pcm_seek:
+			pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L +
+					pos [1] * 0x100L + pos [0];
+			pos += 4;
+			break;
+		
+		default:
+			int cmd = pos [-1];
+			switch ( cmd & 0xf0 )
+			{
+				case cmd_pcm_delay:
+					vgm_time += cmd & 0x0f;
+					write_pcm( vgm_time, *pcm_pos++ );
+					break;
+				
+				case cmd_short_delay:
+					vgm_time += (cmd & 0x0f) + 1;
+					break;
+				
+				case 0x50:
+					pos += 2;
+					break;
+				
+				default:
+					pos += command_len( cmd ) - 1;
+					log_error();
+			}
+		}
+	}
+	vgm_time -= end_time;
+	this->pos = pos;
+	this->vgm_time = vgm_time;
+	
+	return to_blip_time( end_time );
+}
+
+int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
+{
+	// to do: timing is working mostly by luck
+	
+	int min_pairs = sample_count >> 1;
+	int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1;
+	assert( to_fm_time( vgm_time ) <= min_pairs );
+	int pairs = min_pairs;
+	while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
+		vgm_time++;
+	
+	if ( ym2612.enabled() )
+	{
+		ym2612.begin_frame( buf );
+		memset( buf, 0, pairs * stereo * sizeof *buf );
+	}
+	else if ( ym2413.enabled() )
+	{
+		ym2413.begin_frame( buf );
+	}
+	
+	run_commands( vgm_time );
+	ym2612.run_until( pairs );
+	ym2413.run_until( pairs );
+	
+	fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) -
+			((long) pairs << fm_time_bits);
+	
+	psg.end_frame( blip_time );
+	
+	return pairs * stereo;
+}
+
+// Update pre-1.10 header FM rates by scanning commands
+void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const
+{
+	byte const* p = data;
+	while ( p < data_end )
+	{
+		switch ( *p )
+		{
+		case cmd_end:
+			return;
+		
+		case cmd_psg:
+		case cmd_byte_delay:
+			p += 2;
+			break;
+		
+		case cmd_delay:
+			p += 3;
+			break;
+		
+		case cmd_data_block:
+			p += 7 + get_le32( p + 3 );
+			break;
+		
+		case cmd_ym2413:
+			*ym2612_rate = 0;
+			return;
+		
+		case cmd_ym2612_port0:
+		case cmd_ym2612_port1:
+			*ym2612_rate = *ym2413_rate;
+			*ym2413_rate = 0;
+			return;
+		
+		case cmd_ym2151:
+			*ym2413_rate = 0;
+			*ym2612_rate = 0;
+			return;
+		
+		default:
+			p += command_len( *p );
+		}
+	}
+}
+
--- a/Plugins/Input/console/Ym2413_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-
-// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-#include "Ym2413_Emu.h"
-
-Ym2413_Emu::Ym2413_Emu() { }
-
-Ym2413_Emu::~Ym2413_Emu() { }
-
-int Ym2413_Emu::set_rate( double, double ) { return 2; }
-
-void Ym2413_Emu::reset() { }
-
-void Ym2413_Emu::write( int, int ) { }
-
-void Ym2413_Emu::mute_voices( int ) { }
-
-void Ym2413_Emu::run( int, sample_t* ) { }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Ym2413_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,21 @@
+
+// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Ym2413_Emu.h"
+
+Ym2413_Emu::Ym2413_Emu() { }
+
+Ym2413_Emu::~Ym2413_Emu() { }
+
+int Ym2413_Emu::set_rate( double, double ) { return 2; }
+
+void Ym2413_Emu::reset() { }
+
+void Ym2413_Emu::write( int, int ) { }
+
+void Ym2413_Emu::mute_voices( int ) { }
+
+void Ym2413_Emu::run( int, sample_t* ) { }
+
--- a/Plugins/Input/console/Ym2612_Emu.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1321 +0,0 @@
-
-// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
-
-// Based on Gens 2.10 ym2612.c
-
-#include "Ym2612_Emu.h"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <stdio.h>
-#include <math.h>
-
-/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */
-/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-// 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 output_bits = 14;
-
-struct 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
-};
-
-struct 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
-};
-
-struct state_t
-{
-	int TimerBase;      // TimerBase calculation
-	int Status;         // YM2612 Status (timer overflow)
-	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
-	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
-};
-
-#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;
-	}
-}
-
-struct tables_t
-{
-	short SIN_TAB [SIN_LENGHT];                 // SINUS TABLE (offset into TL TABLE)
-	int LFOcnt;         // LFO counter = compteur-frequence pour le LFO
-	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
-	
-	short ENV_TAB [2 * ENV_LENGHT + 8];         // ENV CURVE TABLE (attack & decay)
-	
-	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)
-	unsigned int DECAY_TO_ATTACK [ENV_LENGHT];  // Conversion from decay to attack phase
-	unsigned int FINC_TAB [2048];               // Frequency step table
-};
-
-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 char 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 );
-	
-	void set_rate( double sample_rate, double clock_factor );
-	void reset();
-	void write0( int addr, int data );
-	void write1( int addr, int data );
-	void run_timer( int );
-	void run( int pair_count, Ym2612_Emu::sample_t* );
-};
-
-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 = g.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;
-}
-
-void Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
-{
-	assert( sample_rate );
-	assert( clock_rate > sample_rate );
-	
-	int i;
-
-	// 144 = 12 * (prescale * 2) = 12 * 6 * 2
-	// prescale set to 6 by default
-	
-	double Frequence = clock_rate / sample_rate / 144.0;
-	//dprintf( "Frequence: %.40f\n", Frequence );
-	if ( fabs( Frequence - 1.0 ) < 0.0000001 )
-		Frequence = 1.0;
-	YM2612.TimerBase = int (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
-		{
-			double 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++)
-	{
-		double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT));    // Sinus
-		x = 20 * log10(1 / x);                                      // convert to dB
-
-		int 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++)
-	{
-		double 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)
-		double 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;
-	}
-	for ( i = 0; i < 8; i++ )
-		g.ENV_TAB [i + ENV_LENGHT * 2] = 0;
-	
-	g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1;      // for the stopped state
-	
-	// Tableau pour la conversion Attack -> Decay and Decay -> Attack
-	
-	int j = ENV_LENGHT - 1;
-	for ( i = 0; i < ENV_LENGHT; i++ )
-	{
-		while ( j && g.ENV_TAB [j] < i )
-			j--;
-
-		g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
-	}
-
-	// Tableau pour le Substain Level
-	
-	for(i = 0; i < 15; i++)
-	{
-		double x = i * 3;           // 3 and not 6 (Mickey Mania first music for test)
-		x /= ENV_STEP;
-
-		int j = (int) x;
-		j <<= ENV_LBITS;
-
-		g.SL_TAB [i] = j + ENV_DECAY;
-	}
-
-	g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off
-
-	// Tableau Frequency Step
-
-	for(i = 0; i < 2048; i++)
-	{
-		double x = (double) (i) * 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++)
-	{
-		double x = 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;
-	}
-	
-	for ( i = 96; i < 128; i++ )
-		g.AR_TAB [i] = 0;
-	
-	// Tableau Detune
-
-	for(i = 0; i < 4; i++)
-	{
-		for (int j = 0; j < 32; j++)
-		{
-#if ((SIN_LBITS + SIN_HBITS - 21) < 0)
-			double x = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS));
-#else
-			double x = (double) DT_DEF_TAB [(i << 5) + j] * 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
-	g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
-	
-	reset();
-}
-
-const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
-{
-	if ( !impl )
-	{
-		impl = (Ym2612_Impl*) malloc( sizeof *impl );
-		if ( !impl )
-			return "Out of memory";
-		impl->mute_mask = 0;
-	}
-	memset( &impl->YM2612, 0, sizeof impl->YM2612 );
-	
-	impl->set_rate( sample_rate, clock_rate );
-	
-	return 0;
-}
-
-Ym2612_Emu::~Ym2612_Emu()
-{
-	free( impl );
-}
-
-inline void Ym2612_Impl::write0( int opn_addr, int data )
-{
-	assert( (unsigned) data <= 0xFF );
-	
-	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 );
-	}
-}
-
-inline void Ym2612_Impl::write1( int opn_addr, int data )
-{
-	assert( (unsigned) data <= 0xFF );
-	
-	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 );
-	}
-}
-
-void Ym2612_Emu::reset()
-{
-	impl->reset();
-}
-
-void Ym2612_Impl::reset()
-{
-	g.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;
-
-	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-- )
-	{
-		write0( i, 0xC0 );
-		write1( i, 0xC0 );
-	}
-
-	for ( i = 0xB2; i >= 0x22; i-- )
-	{
-		write0( i, 0 );
-		write1( i, 0 );
-	}
-	
-	write0( 0x2A, 0x80 );
-}
-
-void Ym2612_Emu::write0( int addr, int data )
-{
-	impl->write0( addr, data );
-}
-
-void Ym2612_Emu::write1( int addr, int data )
-{
-	impl->write1( addr, data );
-}
-
-void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; }
-
-static void update_envelope_( slot_t* sl )
-{
-	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
-	}
-}
-
-inline void update_envelope( slot_t& sl )
-{
-	int ecmp = sl.Ecmp;
-	if ( (sl.Ecnt += sl.Einc) >= ecmp )
-		update_envelope_( &sl );
-}
-
-template<int algo>
-struct ym2612_update_chan {
-	static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
-};
-
-typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
-
-template<int algo>
-void ym2612_update_chan<algo>::func( 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;
-	
-	int CH_S0_OUT_1 = ch.S0_OUT [1];
-	
-	int in0 = ch.SLOT [S0].Fcnt;
-	int in1 = ch.SLOT [S1].Fcnt;
-	int in2 = ch.SLOT [S2].Fcnt;
-	int in3 = ch.SLOT [S3].Fcnt;
-	
-	int YM2612_LFOinc = g.LFOinc;
-	int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc;
-	
-	if ( !not_end )
-		return;
-	
-	do
-	{
-		// envelope
-		int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK];
-		
-		short const* const ENV_TAB = g.ENV_TAB;
-		
-	#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 )
-		
-		int const* const TL_TAB = g.TL_TAB;
-		
-	#define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)])
-		
-		// 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
-		unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] *
-				ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1));
-		YM2612_LFOcnt += YM2612_LFOinc;
-		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);
-		
-		update_envelope( ch.SLOT [0] );
-		update_envelope( ch.SLOT [1] );
-		update_envelope( ch.SLOT [2] );
-		update_envelope( ch.SLOT [3] );
-		
-		ch.S0_OUT [0] = CH_S0_OUT_0;
-		buf [0] = t0;
-		buf [1] = t1;
-		buf += 2;
-	}
-	while ( --length );
-	
-	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;
-}
-
-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_timer( int length )
-{
-	int const step = 6;
-	int remain = length;
-	do
-	{
-		int n = step;
-		if ( n > remain )
-			n = remain;
-		remain -= n;
-		
-		long i = n * YM2612.TimerBase;
-		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;
-			}
-		}
-	}
-	while ( remain > 0 );
-}
-
-void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out )
-{
-	if ( pair_count <= 0 )
-		return;
-	
-	if ( YM2612.Mode & 3 )
-		run_timer( pair_count );
-	
-	// 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);
-		}
-	}
-	
-	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], out, pair_count );
-	}
-	
-	g.LFOcnt += g.LFOinc * pair_count;
-}
-
-void Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/console/Ym2612_Emu.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,1321 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+// Based on Gens 2.10 ym2612.c
+
+#include "Ym2612_Emu.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */
+/* Copyright (C) 2004-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// 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 output_bits = 14;
+
+struct 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
+};
+
+struct 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
+};
+
+struct state_t
+{
+	int TimerBase;      // TimerBase calculation
+	int Status;         // YM2612 Status (timer overflow)
+	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
+	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
+};
+
+#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;
+	}
+}
+
+struct tables_t
+{
+	short SIN_TAB [SIN_LENGHT];                 // SINUS TABLE (offset into TL TABLE)
+	int LFOcnt;         // LFO counter = compteur-frequence pour le LFO
+	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
+	
+	short ENV_TAB [2 * ENV_LENGHT + 8];         // ENV CURVE TABLE (attack & decay)
+	
+	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)
+	unsigned int DECAY_TO_ATTACK [ENV_LENGHT];  // Conversion from decay to attack phase
+	unsigned int FINC_TAB [2048];               // Frequency step table
+};
+
+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 char 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 );
+	
+	void set_rate( double sample_rate, double clock_factor );
+	void reset();
+	void write0( int addr, int data );
+	void write1( int addr, int data );
+	void run_timer( int );
+	void run( int pair_count, Ym2612_Emu::sample_t* );
+};
+
+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 = g.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;
+}
+
+void Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
+{
+	assert( sample_rate );
+	assert( clock_rate > sample_rate );
+	
+	int i;
+
+	// 144 = 12 * (prescale * 2) = 12 * 6 * 2
+	// prescale set to 6 by default
+	
+	double Frequence = clock_rate / sample_rate / 144.0;
+	//dprintf( "Frequence: %.40f\n", Frequence );
+	if ( fabs( Frequence - 1.0 ) < 0.0000001 )
+		Frequence = 1.0;
+	YM2612.TimerBase = int (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
+		{
+			double 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++)
+	{
+		double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT));    // Sinus
+		x = 20 * log10(1 / x);                                      // convert to dB
+
+		int 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++)
+	{
+		double 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)
+		double 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;
+	}
+	for ( i = 0; i < 8; i++ )
+		g.ENV_TAB [i + ENV_LENGHT * 2] = 0;
+	
+	g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1;      // for the stopped state
+	
+	// Tableau pour la conversion Attack -> Decay and Decay -> Attack
+	
+	int j = ENV_LENGHT - 1;
+	for ( i = 0; i < ENV_LENGHT; i++ )
+	{
+		while ( j && g.ENV_TAB [j] < i )
+			j--;
+
+		g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
+	}
+
+	// Tableau pour le Substain Level
+	
+	for(i = 0; i < 15; i++)
+	{
+		double x = i * 3;           // 3 and not 6 (Mickey Mania first music for test)
+		x /= ENV_STEP;
+
+		int j = (int) x;
+		j <<= ENV_LBITS;
+
+		g.SL_TAB [i] = j + ENV_DECAY;
+	}
+
+	g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off
+
+	// Tableau Frequency Step
+
+	for(i = 0; i < 2048; i++)
+	{
+		double x = (double) (i) * 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++)
+	{
+		double x = 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;
+	}
+	
+	for ( i = 96; i < 128; i++ )
+		g.AR_TAB [i] = 0;
+	
+	// Tableau Detune
+
+	for(i = 0; i < 4; i++)
+	{
+		for (int j = 0; j < 32; j++)
+		{
+#if ((SIN_LBITS + SIN_HBITS - 21) < 0)
+			double x = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS));
+#else
+			double x = (double) DT_DEF_TAB [(i << 5) + j] * 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
+	g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	
+	reset();
+}
+
+const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
+{
+	if ( !impl )
+	{
+		impl = (Ym2612_Impl*) malloc( sizeof *impl );
+		if ( !impl )
+			return "Out of memory";
+		impl->mute_mask = 0;
+	}
+	memset( &impl->YM2612, 0, sizeof impl->YM2612 );
+	
+	impl->set_rate( sample_rate, clock_rate );
+	
+	return 0;
+}
+
+Ym2612_Emu::~Ym2612_Emu()
+{
+	free( impl );
+}
+
+inline void Ym2612_Impl::write0( int opn_addr, int data )
+{
+	assert( (unsigned) data <= 0xFF );
+	
+	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 );
+	}
+}
+
+inline void Ym2612_Impl::write1( int opn_addr, int data )
+{
+	assert( (unsigned) data <= 0xFF );
+	
+	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 );
+	}
+}
+
+void Ym2612_Emu::reset()
+{
+	impl->reset();
+}
+
+void Ym2612_Impl::reset()
+{
+	g.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;
+
+	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-- )
+	{
+		write0( i, 0xC0 );
+		write1( i, 0xC0 );
+	}
+
+	for ( i = 0xB2; i >= 0x22; i-- )
+	{
+		write0( i, 0 );
+		write1( i, 0 );
+	}
+	
+	write0( 0x2A, 0x80 );
+}
+
+void Ym2612_Emu::write0( int addr, int data )
+{
+	impl->write0( addr, data );
+}
+
+void Ym2612_Emu::write1( int addr, int data )
+{
+	impl->write1( addr, data );
+}
+
+void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; }
+
+static void update_envelope_( slot_t* sl )
+{
+	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
+	}
+}
+
+inline void update_envelope( slot_t& sl )
+{
+	int ecmp = sl.Ecmp;
+	if ( (sl.Ecnt += sl.Einc) >= ecmp )
+		update_envelope_( &sl );
+}
+
+template<int algo>
+struct ym2612_update_chan {
+	static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
+};
+
+typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
+
+template<int algo>
+void ym2612_update_chan<algo>::func( 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;
+	
+	int CH_S0_OUT_1 = ch.S0_OUT [1];
+	
+	int in0 = ch.SLOT [S0].Fcnt;
+	int in1 = ch.SLOT [S1].Fcnt;
+	int in2 = ch.SLOT [S2].Fcnt;
+	int in3 = ch.SLOT [S3].Fcnt;
+	
+	int YM2612_LFOinc = g.LFOinc;
+	int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc;
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		// envelope
+		int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK];
+		
+		short const* const ENV_TAB = g.ENV_TAB;
+		
+	#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 )
+		
+		int const* const TL_TAB = g.TL_TAB;
+		
+	#define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)])
+		
+		// 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
+		unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] *
+				ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1));
+		YM2612_LFOcnt += YM2612_LFOinc;
+		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);
+		
+		update_envelope( ch.SLOT [0] );
+		update_envelope( ch.SLOT [1] );
+		update_envelope( ch.SLOT [2] );
+		update_envelope( ch.SLOT [3] );
+		
+		ch.S0_OUT [0] = CH_S0_OUT_0;
+		buf [0] = t0;
+		buf [1] = t1;
+		buf += 2;
+	}
+	while ( --length );
+	
+	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;
+}
+
+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_timer( int length )
+{
+	int const step = 6;
+	int remain = length;
+	do
+	{
+		int n = step;
+		if ( n > remain )
+			n = remain;
+		remain -= n;
+		
+		long i = n * YM2612.TimerBase;
+		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;
+			}
+		}
+	}
+	while ( remain > 0 );
+}
+
+void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out )
+{
+	if ( pair_count <= 0 )
+		return;
+	
+	if ( YM2612.Mode & 3 )
+		run_timer( pair_count );
+	
+	// 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);
+		}
+	}
+	
+	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], out, pair_count );
+	}
+	
+	g.LFOcnt += g.LFOinc * pair_count;
+}
+
+void Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); }
+
--- a/Plugins/Input/console/abstract_file.cpp	Thu Sep 14 13:27:38 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-
-#include "abstract_file.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-/* Copyright (C) 2005 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. */
-
-// to do: remove?
-#ifndef RAISE_ERROR
-	#define RAISE_ERROR( str ) return str
-#endif
-
-typedef Data_Reader::error_t error_t;
-
-error_t Data_Writer::write( const void*, long ) { return NULL; }
-
-void Data_Writer::satisfy_lame_linker_() { }
-
-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;
-}
-
-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;
-}
-
-long File_Reader::remain() const
-{
-	return size() - tell();
-}
-
-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 (long) 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 = (long) 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 )
-{
-	data_ = (char*) p;
-	size_ = 0;
-	allocated = s;
-	mode = b ? ignore_excess : fixed;
-}
-
-Mem_Writer::Mem_Writer()
-{
-	data_ = NULL;
-	size_ = 0;
-	allocated = 0;
-	mode = expanding;
-}
-
-Mem_Writer::~Mem_Writer()
-{
-	if ( mode == expanding )
-		free( data_ );
-}
-
-error_t Mem_Writer::write( const void* p, long s )
-{
-	long remain = allocated - size_;
-	if ( s > remain )
-	{
-		if ( mode == fixed )
-			RAISE_ERROR( "Tried to write more data than expected" );
-		
-		if ( mode == ignore_excess )
-		{
-			s = remain;
-		}
-		else // expanding
-		{
-			long new_allocated = size_ + s;
-			new_allocated += (new_allocated >> 1) + 2048;
-			void* p = realloc( data_, new_allocated );
-			if ( !p )
-				RAISE_ERROR( "Out of memory" );
-			data_ = (char*) p;
-			allocated = new_allocated;
-		}
-	}
-	
-	assert( size_ + s <= allocated );
-	memcpy( data_ + size_, p, s );
-	size_ += s;
-	
-	return NULL;
-}
-
-// 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.cxx	Thu Sep 14 13:33:10 2006 -0700
@@ -0,0 +1,282 @@
+
+#include "abstract_file.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+/* Copyright (C) 2005 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. */
+
+// to do: remove?
+#ifndef RAISE_ERROR
+	#define RAISE_ERROR( str ) return str
+#endif
+
+typedef Data_Reader::error_t error_t;
+
+error_t Data_Writer::write( const void*, long ) { return NULL; }
+
+void Data_Writer::satisfy_lame_linker_() { }
+
+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;
+}
+
+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;
+}
+
+long File_Reader::remain() const
+{
+	return size() - tell();
+}
+
+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 (long) 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 = (long) 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 )
+{
+	data_ = (char*) p;
+	size_ = 0;
+	allocated = s;
+	mode = b ? ignore_excess : fixed;
+}
+
+Mem_Writer::Mem_Writer()
+{
+	data_ = NULL;
+	size_ = 0;
+	allocated = 0;
+	mode = expanding;
+}
+
+Mem_Writer::~Mem_Writer()
+{
+	if ( mode == expanding )
+		free( data_ );
+}
+
+error_t Mem_Writer::write( const void* p, long s )
+{
+	long remain = allocated - size_;
+	if ( s > remain )
+	{
+		if ( mode == fixed )
+			RAISE_ERROR( "Tried to write more data than expected" );
+		
+		if ( mode == ignore_excess )
+		{
+			s = remain;
+		}
+		else // expanding
+		{
+			long new_allocated = size_ + s;
+			new_allocated += (new_allocated >> 1) + 2048;
+			void* p = realloc( data_, new_allocated );
+			if ( !p )
+				RAISE_ERROR( "Out of memory" );
+			data_ = (char*) p;
+			allocated = new_allocated;
+		}
+	}
+	
+	assert( size_ + s <= allocated );
+	memcpy( data_ + size_, p, s );
+	size_ += s;
+	
+	return NULL;
+}
+
+// Null_Writer
+
+error_t Null_Writer::write( const void*, long )
+{
+	return NULL;
+}
+