? .pc
? patches
? examples/seeking/chained
? examples/seeking/playbin
? ext/ivorbis/libgstivorbis_la-vorbis.loT
? ext/mad/gstmad.c.change
? gst/nsf
? gst/spectrum/demo-osssrc
? gst-libs/gst/profiles
? testsuite/embed/embed
Index: configure.ac
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/configure.ac,v
retrieving revision 1.524.2.12
diff -u -r1.524.2.12 configure.ac
--- configure.ac	26 Mar 2005 15:59:25 -0000	1.524.2.12
+++ configure.ac	26 Mar 2005 20:50:50 -0000
@@ -401,6 +401,7 @@
 	monoscope \
 	multifilesink \
 	multipart \
+	nsf \
 	overlay \
 	passthrough \
 	playback \
@@ -2011,6 +2012,7 @@
 gst/monoscope/Makefile
 gst/multifilesink/Makefile
 gst/multipart/Makefile
+gst/nsf/Makefile
 gst/overlay/Makefile
 gst/passthrough/Makefile
 gst/playback/Makefile
Index: gst/typefind/gsttypefindfunctions.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/typefind/gsttypefindfunctions.c,v
retrieving revision 1.68.2.5
diff -u -r1.68.2.5 gsttypefindfunctions.c
--- gst/typefind/gsttypefindfunctions.c	26 Mar 2005 15:18:02 -0000	1.68.2.5
+++ gst/typefind/gsttypefindfunctions.c	26 Mar 2005 20:50:52 -0000
@@ -1755,6 +1755,7 @@
   static gchar *q3gp_exts[] = { "3gp", NULL };
   static gchar *aac_exts[] = { "aac", NULL };
   static gchar *spc_exts[] = { "spc", NULL };
+  static gchar *nsf_exts[] = { "nsf", NULL };
 
   GST_DEBUG_CATEGORY_INIT (type_find_debug, "typefindfunctions",
       GST_DEBUG_FG_GREEN | GST_DEBUG_BG_RED, "generic type find functions");
@@ -1903,6 +1904,8 @@
       aac_type_find, aac_exts, AAC_CAPS, NULL);
   TYPE_FIND_REGISTER_START_WITH (plugin, "audio/x-spc", GST_RANK_SECONDARY,
       spc_exts, "SNES-SPC700 Sound File Data", 27, GST_TYPE_FIND_MAXIMUM);
+  TYPE_FIND_REGISTER_START_WITH (plugin, "audio/x-nsf", GST_RANK_SECONDARY,
+      nsf_exts, "NESM", 4, GST_TYPE_FIND_LIKELY);
 
   return TRUE;
 }
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/fds_snd.c	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,89 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** fds_snd.c
+**
+** Famicom Disk System sound emulation
+** $Id: fds_snd.c,v 1.3 2000/07/03 02:18:53 matt Exp $
+*/
+
+#include "types.h"
+#include "nes_apu.h"
+#include "fds_snd.h"
+
+static int32 fds_incsize = 0;
+
+/* mix sound channels together */
+static int32 fds_process(void)
+{
+   int32 output;
+   output = 0;
+
+   return output;
+}
+
+/* write to registers */
+static void fds_write(uint32 address, uint8 value)
+{
+}
+
+/* reset state of vrcvi sound channels */
+static void fds_reset(void)
+{
+   fds_incsize = apu_getcyclerate();
+}
+
+static void fds_init(void)
+{
+}
+
+/* TODO: bleh */
+static void fds_shutdown(void)
+{
+}
+
+static apu_memwrite fds_memwrite[] =
+{
+   { 0x4040, 0x4092, fds_write }, 
+   {     -1,     -1, NULL }
+};
+
+apuext_t fds_ext = 
+{
+   fds_init,
+   fds_shutdown,
+   fds_reset,
+   fds_process,
+   NULL, /* no reads */
+   fds_memwrite
+};
+
+/*
+** $Log: fds_snd.c,v $
+** Revision 1.3  2000/07/03 02:18:53  matt
+** much better external module exporting
+**
+** Revision 1.2  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.1  2000/06/20 00:06:47  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/fmopl.c	2005-03-26 21:49:13.824571824 +0100
@@ -0,0 +1,1338 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+**
+** Version 0.36f
+**
+*/
+
+/*
+	preliminary :
+	Problem :
+	note:
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+/* #include "driver.h" */		/* use M.A.M.E. */
+#include "fmopl.h"
+
+/* MPC - hacks */
+#include "types.h"
+#include "log.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE     141280  /* RATE 4 =  2826.24ms @ 3.6MHz */
+#define OPL_DRRATE    1956000  /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24			/* frequency turn          */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE   (1<<(FREQ_BITS-20))
+#define TL_BITS    (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB   (TL_BITS+3-16)		/* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT   4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static  memory = EG_ENT*4 (byte)                     */
+
+#define EG_OFF   ((2*EG_ENT)<<ENV_BITS)  /* OFF          */
+#define EG_DED   EG_OFF
+#define EG_DST   (EG_ENT<<ENV_BITS)      /* DECAY  START */
+#define EG_AED   EG_DST
+#define EG_AST   0                       /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step  */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR  0x00
+#define ENV_MOD_DR  0x01
+#define ENV_MOD_AR  0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+	 0, 2, 4, 1, 3, 5,-1,-1,
+	 6, 8,10, 7, 9,11,-1,-1,
+	12,14,16,13,15,17,-1,-1,
+	-1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+#define ML(x) ((UINT32)((x)*0.1875*2/EG_STEP))
+static const UINT32 KSL_TABLE[8*16]=
+{
+	/* OCT 0 */
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	/* OCT 1 */
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	 ML(0.000), ML(0.750), ML(1.125), ML(1.500),
+	 ML(1.875), ML(2.250), ML(2.625), ML(3.000),
+	/* OCT 2 */
+	 ML(0.000), ML(0.000), ML(0.000), ML(0.000),
+	 ML(0.000), ML(1.125), ML(1.875), ML(2.625),
+	 ML(3.000), ML(3.750), ML(4.125), ML(4.500),
+	 ML(4.875), ML(5.250), ML(5.625), ML(6.000),
+	/* OCT 3 */
+	 ML(0.000), ML(0.000), ML(0.000), ML(1.875),
+	 ML(3.000), ML(4.125), ML(4.875), ML(5.625),
+	 ML(6.000), ML(6.750), ML(7.125), ML(7.500),
+	 ML(7.875), ML(8.250), ML(8.625), ML(9.000),
+	/* OCT 4 */
+	 ML(0.000), ML(0.000), ML(3.000), ML(4.875),
+	 ML(6.000), ML(7.125), ML(7.875), ML(8.625),
+	 ML(9.000), ML(9.750),ML(10.125),ML(10.500),
+	ML(10.875),ML(11.250),ML(11.625),ML(12.000),
+	/* OCT 5 */
+	 ML(0.000), ML(3.000), ML(6.000), ML(7.875),
+	 ML(9.000),ML(10.125),ML(10.875),ML(11.625),
+	ML(12.000),ML(12.750),ML(13.125),ML(13.500),
+	ML(13.875),ML(14.250),ML(14.625),ML(15.000),
+	/* OCT 6 */
+	 ML(0.000), ML(6.000), ML(9.000),ML(10.875),
+	ML(12.000),ML(13.125),ML(13.875),ML(14.625),
+	ML(15.000),ML(15.750),ML(16.125),ML(16.500),
+	ML(16.875),ML(17.250),ML(17.625),ML(18.000),
+	/* OCT 7 */
+	 ML(0.000), ML(9.000),ML(12.000),ML(13.875),
+	ML(15.000),ML(16.125),ML(16.875),ML(17.625),
+	ML(18.000),ML(18.750),ML(19.125),ML(19.500),
+	ML(19.875),ML(20.250),ML(20.625),ML(21.000)
+};
+#undef ML
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) ((INT32) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST)
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12  6  3 1.5 0.75 (dB) */
+/* TL.TABLE[ 0      to TL_MAX          ] : plus  section */
+/* TL.TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static union {
+    INT32 *TABLE;
+    void *TABLE_PTR;
+} TL;
+
+/* pointers to TL.TABLE with sinwave output offset */
+static union {
+  INT32 **TABLE;
+  void *TABLE_PTR;
+} SIN;
+
+/* LFO table */
+static union {
+  INT32 *TABLE;
+  void *TABLE_PTR;
+} AMS;
+
+static union {
+  INT32 *TABLE;
+  void *TABLE_PTR;
+} VIB;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML(x) ((UINT32) (2*(x)))
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+   ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00),
+   ML(8.00), ML(9.00),ML(10.00),ML(10.00),ML(12.00),ML(12.00),ML(15.00),ML(15.00)
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL;	/* current chip point */
+/* currenct chip state */
+/* static FMSAMPLE  *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32  *ams_table;
+INT32  *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2;		/* connect for SLOT 2 */
+
+/* log output level */
+#define LOG_ERR  3      /* ERROR       */
+#define LOG_WAR  2      /* WARNING     */
+#define LOG_INF  1      /* INFORMATION */
+
+#define LOG_LEVEL LOG_INF
+
+/* #define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x */
+#define LOG(n,x) if( (n)>=LOG_LEVEL ) log_printf x
+
+/* --------------------- subroutines  --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+	if ( val > max )
+		val = max;
+	else if ( val < min )
+		val = min;
+
+	return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+	/* set status flag */
+	OPL->status |= flag;
+	if(!(OPL->status & 0x80))
+	{
+		if(OPL->status & OPL->statusmask)
+		{	/* IRQ on */
+			OPL->status |= 0x80;
+			/* callback user interrupt handler (IRQ is OFF to ON) */
+			if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+		}
+	}
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+	/* reset status flag */
+	OPL->status &=~flag;
+	if((OPL->status & 0x80))
+	{
+		if (!(OPL->status & OPL->statusmask) )
+		{
+			OPL->status &= 0x7f;
+			/* callback user interrupt handler (IRQ is ON to OFF) */
+			if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+		}
+	}
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+	OPL->statusmask = flag;
+	/* IRQ handling check */
+	OPL_STATUS_SET(OPL,0);
+	OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on  ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+	/* sin wave restart */
+	SLOT->Cnt = 0;
+	/* set attack */
+	SLOT->evm = ENV_MOD_AR;
+	SLOT->evs = SLOT->evsa;
+	SLOT->evc = EG_AST;
+	SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+	if( SLOT->evm > ENV_MOD_RR)
+	{
+		/* set envelope counter from envleope output */
+		SLOT->evm = ENV_MOD_RR;
+		if( !(SLOT->evc&EG_DST) )
+		  /* SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST; */
+			SLOT->evc = EG_DST;
+		SLOT->eve = EG_DED;
+		SLOT->evs = SLOT->evsr;
+	}
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+	/* calcrate envelope generator */
+	if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+	{
+		switch( SLOT->evm ){
+		case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+			/* next DR */
+			SLOT->evm = ENV_MOD_DR;
+			SLOT->evc = EG_DST;
+			SLOT->eve = SLOT->SL;
+			SLOT->evs = SLOT->evsd;
+			break;
+		case ENV_MOD_DR: /* DECAY -> SL or RR */
+			SLOT->evc = SLOT->SL;
+			SLOT->eve = EG_DED;
+			if(SLOT->eg_typ)
+			{
+				SLOT->evs = 0;
+			}
+			else
+			{
+				SLOT->evm = ENV_MOD_RR;
+				SLOT->evs = SLOT->evsr;
+			}
+			break;
+		case ENV_MOD_RR: /* RR -> OFF */
+			SLOT->evc = EG_OFF;
+			SLOT->eve = EG_OFF+1;
+			SLOT->evs = 0;
+			break;
+		}
+	}
+	/* calcrate envelope */
+	return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm( OPL_CH *CH)
+{
+	INT32 *carrier = &outd[0];
+	CH->connect1 = CH->CON ? carrier : &feedback2;
+	CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+	int ksr;
+
+	/* frequency step counter */
+	SLOT->Incr = CH->fc * SLOT->mul;
+	ksr = CH->kcode >> SLOT->KSR;
+
+	if( SLOT->ksr != ksr )
+	{
+		SLOT->ksr = ksr;
+		/* attack , decay rate recalcration */
+		SLOT->evsa = SLOT->AR[ksr];
+		SLOT->evsd = SLOT->DR[ksr];
+		SLOT->evsr = SLOT->RR[ksr];
+	}
+	SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+	OPL_CH   *CH   = &OPL->P_CH[slot/2];
+	OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+	SLOT->mul    = MUL_TABLE[v&0x0f];
+	SLOT->KSR    = (v&0x10) ? 0 : 2;
+	SLOT->eg_typ = (v&0x20)>>5;
+	SLOT->vib    = (v&0x40);
+	SLOT->ams    = (v&0x80);
+	CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+	OPL_CH   *CH   = &OPL->P_CH[slot/2];
+	OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+	int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+	SLOT->ksl = ksl ? 3-ksl : 31;
+	SLOT->TL  = (INT32) (((v&0x3f)*(0.75/EG_STEP))); /* 0.75db step */
+
+	if( !(OPL->mode&0x80) )
+	{	/* not CSM latch total level */
+		SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+	}
+}
+
+/* set attack rate & decay rate  */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+	OPL_CH   *CH   = &OPL->P_CH[slot/2];
+	OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+	int ar = v>>4;
+	int dr = v&0x0f;
+
+	SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+	SLOT->evsa = SLOT->AR[SLOT->ksr];
+	if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+	SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+	SLOT->evsd = SLOT->DR[SLOT->ksr];
+	if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+	OPL_CH   *CH   = &OPL->P_CH[slot/2];
+	OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+	int sl = v>>4;
+	int rr = v & 0x0f;
+
+	SLOT->SL = SL_TABLE[sl];
+	if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+	SLOT->RR = &OPL->DR_TABLE[rr<<2];
+	SLOT->evsr = SLOT->RR[SLOT->ksr];
+	if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con)   slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+	UINT32 env_out;
+	OPL_SLOT *SLOT;
+
+	feedback2 = 0;
+	/* SLOT 1 */
+	SLOT = &CH->SLOT[SLOT1];
+	env_out=OPL_CALC_SLOT(SLOT);
+	if( env_out < EG_ENT-1 )
+	{
+		/* PG */
+		if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+		else          SLOT->Cnt += SLOT->Incr;
+		/* connectoion */
+		if(CH->FB)
+		{
+			int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+			CH->op1_out[1] = CH->op1_out[0];
+			*CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+		}
+		else
+		{
+			*CH->connect1 += OP_OUT(SLOT,env_out,0);
+		}
+	}else
+	{
+		CH->op1_out[1] = CH->op1_out[0];
+		CH->op1_out[0] = 0;
+	}
+	/* SLOT 2 */
+	SLOT = &CH->SLOT[SLOT2];
+	env_out=OPL_CALC_SLOT(SLOT);
+	if( env_out < EG_ENT-1 )
+	{
+		/* PG */
+		if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+		else          SLOT->Cnt += SLOT->Incr;
+		/* connectoion */
+		outd[0] += OP_OUT(SLOT,env_out, feedback2);
+	}
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+	UINT32 env_tam,env_sd,env_top,env_hh;
+	int whitenoise = (rand()&1)*((int)(WHITE_NOISE_db/EG_STEP));
+	INT32 tone8;
+
+	OPL_SLOT *SLOT;
+	int env_out;
+
+	/* BD : same as FM serial mode and output level is large */
+	feedback2 = 0;
+	/* SLOT 1 */
+	SLOT = &CH[6].SLOT[SLOT1];
+	env_out=OPL_CALC_SLOT(SLOT);
+	if( env_out < EG_ENT-1 )
+	{
+		/* PG */
+		if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+		else          SLOT->Cnt += SLOT->Incr;
+		/* connectoion */
+		if(CH[6].FB)
+		{
+			int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+			CH[6].op1_out[1] = CH[6].op1_out[0];
+			feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+		}
+		else
+		{
+			feedback2 = OP_OUT(SLOT,env_out,0);
+		}
+	}else
+	{
+		feedback2 = 0;
+		CH[6].op1_out[1] = CH[6].op1_out[0];
+		CH[6].op1_out[0] = 0;
+	}
+	/* SLOT 2 */
+	SLOT = &CH[6].SLOT[SLOT2];
+	env_out=OPL_CALC_SLOT(SLOT);
+	if( env_out < EG_ENT-1 )
+	{
+		/* PG */
+		if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+		else          SLOT->Cnt += SLOT->Incr;
+		/* connectoion */
+		outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+	}
+
+	/* SD  (17) = mul14[fnum7] + white noise
+	 * TAM (15) = mul15[fnum8]
+	 * TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+	 * HH  (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+	 */
+	env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+	env_tam=OPL_CALC_SLOT(SLOT8_1);
+	env_top=OPL_CALC_SLOT(SLOT8_2);
+	env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+	/* PG */
+	if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+	else             SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+	if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+	else             SLOT7_2->Cnt += (CH[7].fc*8);
+	if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+	else             SLOT8_1->Cnt += SLOT8_1->Incr;
+	if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+	else             SLOT8_2->Cnt += (CH[8].fc*48);
+
+	tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+	/* SD */
+	if( env_sd < EG_ENT-1 )
+		outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+	/* TAM */
+	if( env_tam < EG_ENT-1 )
+		outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+	/* TOP-CY */
+	if( env_top < EG_ENT-1 )
+		outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+	/* HH */
+	if( env_hh  < EG_ENT-1 )
+		outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+	int i;
+	double rate;
+
+	/* make attack rate & decay rate tables */
+	for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+	for (i = 4;i <= 60;i++){
+		rate  = OPL->freqbase;						/* frequency rate */
+		if( i < 60 ) rate *= 1.0+(i&3)*0.25;		/* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+		rate *= 1<<((i>>2)-1);						/* b2-5 : shift bit */
+		rate *= (double)(EG_ENT<<ENV_BITS);
+		OPL->AR_TABLE[i] = (INT32) (rate / ARRATE);
+		OPL->DR_TABLE[i] = (INT32) (rate / DRRATE);
+	}
+	for (i = 60;i < 76;i++)
+	{
+		OPL->AR_TABLE[i] = EG_AED-1;
+		OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+	}
+#if 0
+	for (i = 0;i < 64 ;i++){	/* make for overflow area */
+		LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i,
+			((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
+			((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
+	}
+#endif
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+	int s,t;
+	double rate;
+	int i,j;
+	double pom;
+
+	/* allocate dynamic tables */
+	if( (TL.TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+		return 0;
+	if( (SIN.TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+	{
+		free(TL.TABLE);
+		return 0;
+	}
+	if( (AMS.TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+	{
+		free(TL.TABLE);
+		free(SIN.TABLE);
+		return 0;
+	}
+	if( (VIB.TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+	{
+		free(TL.TABLE_PTR);
+		free(SIN.TABLE);
+		free(AMS.TABLE);
+		return 0;
+	}
+	/* make total level table */
+	for (t = 0;t < EG_ENT-1 ;t++){
+		rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20);	/* dB -> voltage */
+		TL.TABLE[       t] =  (int)rate;
+		TL.TABLE[TL_MAX+t] = -TL.TABLE[t];
+/*		LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL.TABLE[t]));*/
+	}
+	/* fill volume off area */
+	for ( t = EG_ENT-1; t < TL_MAX ;t++){
+		TL.TABLE[t] = TL.TABLE[TL_MAX+t] = 0;
+	}
+
+	/* make sinwave table (total level offet) */
+	/* degree 0 = degree 180                   = off */
+	SIN.TABLE[0] = SIN.TABLE[SIN_ENT/2]         = &TL.TABLE[EG_ENT-1];
+	for (s = 1;s <= SIN_ENT/4;s++){
+		pom = sin(2*PI*s/SIN_ENT); /* sin     */
+		pom = 20*log10(1/pom);	   /* decibel */
+		j = (int) (pom / EG_STEP);         /* TL.TABLE steps */
+
+        /* degree 0   -  90    , degree 180 -  90 : plus section */
+		SIN.TABLE[          s] = SIN.TABLE[SIN_ENT/2-s] = &TL.TABLE[j];
+        /* degree 180 - 270    , degree 360 - 270 : minus section */
+		SIN.TABLE[SIN_ENT/2+s] = SIN.TABLE[SIN_ENT  -s] = &TL.TABLE[TL_MAX+j];
+/*		LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+	}
+	for (s = 0;s < SIN_ENT;s++)
+	{
+		SIN.TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN.TABLE[s] : &TL.TABLE[EG_ENT];
+		SIN.TABLE[SIN_ENT*2+s] = SIN.TABLE[s % (SIN_ENT/2)];
+		SIN.TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL.TABLE[EG_ENT] : SIN.TABLE[SIN_ENT*2+s];
+	}
+
+	/* envelope counter -> envelope output table */
+	for (i=0; i<EG_ENT; i++)
+	{
+		/* ATTACK curve */
+		pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+		/* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+		ENV_CURVE[i] = (int)pom;
+		/* DECAY ,RELEASE curve */
+		ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+	}
+	/* off */
+	ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+	/* make LFO ams table */
+	for (i=0; i<AMS_ENT; i++)
+	{
+		pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+		AMS.TABLE[i]         = (INT32) ((1.0/EG_STEP)*pom); /* 1dB   */
+		AMS.TABLE[AMS_ENT+i] = (INT32) ((4.8/EG_STEP)*pom); /* 4.8dB */
+	}
+	/* make LFO vibrate table */
+	for (i=0; i<VIB_ENT; i++)
+	{
+		/* 100cent = 1seminote = 6% ?? */
+		pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+		VIB.TABLE[i]         = VIB_RATE + (INT32) (pom*0.07); /* +- 7cent */
+		VIB.TABLE[VIB_ENT+i] = VIB_RATE + (INT32) (pom*0.14); /* +-14cent */
+		/* LOG(LOG_INF,("vib %d=%d\n",i,VIB.TABLE[VIB_ENT+i])); */
+	}
+	return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+	free(TL.TABLE);
+	free(SIN.TABLE);
+	free(AMS.TABLE);
+	free(VIB.TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+	OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+	OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+	/* all key off */
+	OPL_KEYOFF(slot1);
+	OPL_KEYOFF(slot2);
+	/* total level latch */
+	slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+	slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+	/* key on */
+	CH->op1_out[0] = CH->op1_out[1] = 0;
+	OPL_KEYON(slot1);
+	OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL)
+{
+	int fn;
+
+	/* frequency base */
+	OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72  : 0;
+	/* Timer base time */
+	OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+	/* make time tables */
+	init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+	/* make fnumber -> increment counter table */
+	for( fn=0 ; fn < 1024 ; fn++ )
+	{
+		OPL->FN_TABLE[fn] = (UINT32) (OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2);
+	}
+	/* LFO freq.table */
+	OPL->amsIncr = (INT32) (OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0);
+	OPL->vibIncr = (INT32) (OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0);
+}
+
+/* ---------- write a OPL registers ---------- */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+	OPL_CH *CH;
+	int slot;
+	unsigned int block_fnum;
+
+	switch(r&0xe0)
+	{
+	case 0x00: /* 00-1f:controll */
+		switch(r&0x1f)
+		{
+		case 0x01:
+			/* wave selector enable */
+			if(OPL->type&OPL_TYPE_WAVESEL)
+			{
+				OPL->wavesel = v&0x20;
+				if(!OPL->wavesel)
+				{
+					/* preset compatible mode */
+					int c;
+					for(c=0;c<OPL->max_ch;c++)
+					{
+						OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN.TABLE[0];
+						OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN.TABLE[0];
+					}
+				}
+			}
+			return;
+		case 0x02:	/* Timer 1 */
+			OPL->T[0] = (256-v)*4;
+			break;
+		case 0x03:	/* Timer 2 */
+			OPL->T[1] = (256-v)*16;
+			return;
+		case 0x04:	/* IRQ clear / mask and Timer enable */
+			if(v&0x80)
+			{	/* IRQ flag clear */
+				OPL_STATUS_RESET(OPL,0x7f);
+			}
+			else
+			{	/* set IRQ mask ,timer enable*/
+				UINT8 st1 = v&1;
+				UINT8 st2 = (v>>1)&1;
+				/* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+				OPL_STATUS_RESET(OPL,v&0x78);
+				OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+				/* timer 2 */
+				if(OPL->st[1] != st2)
+				{
+					double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+					OPL->st[1] = st2;
+					if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+				}
+				/* timer 1 */
+				if(OPL->st[0] != st1)
+				{
+					double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+					OPL->st[0] = st1;
+					if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+				}
+			}
+			return;
+#if BUILD_Y8950
+		case 0x06:		/* Key Board OUT */
+			if(OPL->type&OPL_TYPE_KEYBOARD)
+			{
+				if(OPL->keyboardhandler_w)
+					OPL->keyboardhandler_w(OPL->keyboard_param,v);
+				else
+					LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
+			}
+			return;
+		case 0x07:	/* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+			if(OPL->type&OPL_TYPE_ADPCM)
+				YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+			return;
+		case 0x08:	/* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+			OPL->mode = v;
+			v&=0x1f;	/* for DELTA-T unit */
+		case 0x09:		/* START ADD */
+		case 0x0a:
+		case 0x0b:		/* STOP ADD  */
+		case 0x0c:
+		case 0x0d:		/* PRESCALE   */
+		case 0x0e:
+		case 0x0f:		/* ADPCM data */
+		case 0x10: 		/* DELTA-N    */
+		case 0x11: 		/* DELTA-N    */
+		case 0x12: 		/* EG-CTRL    */
+			if(OPL->type&OPL_TYPE_ADPCM)
+				YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+			return;
+#if 0
+		case 0x15:		/* DAC data    */
+		case 0x16:
+		case 0x17:		/* SHIFT    */
+			return;
+		case 0x18:		/* I/O CTRL (Direction) */
+			if(OPL->type&OPL_TYPE_IO)
+				OPL->portDirection = v&0x0f;
+			return;
+		case 0x19:		/* I/O DATA */
+			if(OPL->type&OPL_TYPE_IO)
+			{
+				OPL->portLatch = v;
+				if(OPL->porthandler_w)
+					OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+			}
+			return;
+		case 0x1a:		/* PCM data */
+			return;
+#endif
+#endif
+		}
+		break;
+	case 0x20:	/* am,vib,ksr,eg type,mul */
+		slot = slot_array[r&0x1f];
+		if(slot == -1) return;
+		set_mul(OPL,slot,v);
+		return;
+	case 0x40:
+		slot = slot_array[r&0x1f];
+		if(slot == -1) return;
+		set_ksl_tl(OPL,slot,v);
+		return;
+	case 0x60:
+		slot = slot_array[r&0x1f];
+		if(slot == -1) return;
+		set_ar_dr(OPL,slot,v);
+		return;
+	case 0x80:
+		slot = slot_array[r&0x1f];
+		if(slot == -1) return;
+		set_sl_rr(OPL,slot,v);
+		return;
+	case 0xa0:
+		switch(r)
+		{
+		case 0xbd:
+			/* amsep,vibdep,r,bd,sd,tom,tc,hh */
+			{
+			UINT8 rkey = OPL->rythm^v;
+			OPL->ams_table = &AMS.TABLE[v&0x80 ? AMS_ENT : 0];
+			OPL->vib_table = &VIB.TABLE[v&0x40 ? VIB_ENT : 0];
+			OPL->rythm  = v&0x3f;
+			if(OPL->rythm&0x20)
+			{
+#if 0
+				usrintf_showmessage("OPL Rythm mode select");
+#endif
+				/* BD key on/off */
+				if(rkey&0x10)
+				{
+					if(v&0x10)
+					{
+						OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+						OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+						OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+					}
+					else
+					{
+						OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+						OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+					}
+				}
+				/* SD key on/off */
+				if(rkey&0x08)
+				{
+					if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+					else       OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+				}/* TAM key on/off */
+				if(rkey&0x04)
+				{
+					if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+					else       OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+				}
+				/* TOP-CY key on/off */
+				if(rkey&0x02)
+				{
+					if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+					else       OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+				}
+				/* HH key on/off */
+				if(rkey&0x01)
+				{
+					if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+					else       OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+				}
+			}
+			}
+			return;
+		}
+		/* keyon,block,fnum */
+		if( (r&0x0f) > 8) return;
+		CH = &OPL->P_CH[r&0x0f];
+		if(!(r&0x10))
+		{	/* a0-a8 */
+			block_fnum  = (CH->block_fnum&0x1f00) | v;
+		}
+		else
+		{	/* b0-b8 */
+			int keyon = (v>>5)&1;
+			block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+			if(CH->keyon != keyon)
+			{
+				if( (CH->keyon=keyon) )
+				{
+					CH->op1_out[0] = CH->op1_out[1] = 0;
+					OPL_KEYON(&CH->SLOT[SLOT1]);
+					OPL_KEYON(&CH->SLOT[SLOT2]);
+				}
+				else
+				{
+					OPL_KEYOFF(&CH->SLOT[SLOT1]);
+					OPL_KEYOFF(&CH->SLOT[SLOT2]);
+				}
+			}
+		}
+		/* update */
+		if(CH->block_fnum != block_fnum)
+		{
+			int blockRv = 7-(block_fnum>>10);
+			int fnum   = block_fnum&0x3ff;
+			CH->block_fnum = block_fnum;
+
+			CH->ksl_base = KSL_TABLE[block_fnum>>6];
+			CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+			CH->kcode = CH->block_fnum>>9;
+			if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+			CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+			CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+		}
+		return;
+	case 0xc0:
+		/* FB,C */
+		if( (r&0x0f) > 8) return;
+		CH = &OPL->P_CH[r&0x0f];
+		{
+		int feedback = (v>>1)&7;
+		CH->FB   = feedback ? (8+1) - feedback : 0;
+		CH->CON = v&1;
+		set_algorythm(CH);
+		}
+		return;
+	case 0xe0: /* wave type */
+		slot = slot_array[r&0x1f];
+		if(slot == -1) return;
+		CH = &OPL->P_CH[slot/2];
+		if(OPL->wavesel)
+		{
+			/* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+			CH->SLOT[slot&1].wavetable = &SIN.TABLE[(v&0x03)*SIN_ENT];
+		}
+		return;
+	}
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+	num_lock++;
+	if(num_lock>1) return 0;
+	/* first time */
+	cur_chip = NULL;
+	/* allocate total level table (128kb space) */
+	if( !OPLOpenTable() )
+	{
+		num_lock--;
+		return -1;
+	}
+	return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+	if(num_lock) num_lock--;
+	if(num_lock) return;
+	/* last time */
+	cur_chip = NULL;
+	OPLCloseTable();
+}
+
+#if (BUILD_YM3812 || BUILD_YM3526)
+/*******************************************************************************/
+/*		YM3812 local section                                                   */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+    int i;
+	int data;
+	FMSAMPLE *buf = buffer;
+	UINT32 amsCnt  = OPL->amsCnt;
+	UINT32 vibCnt  = OPL->vibCnt;
+	UINT8 rythm = OPL->rythm&0x20;
+	OPL_CH *CH,*R_CH;
+
+	if( (void *)OPL != cur_chip ){
+		cur_chip = (void *)OPL;
+		/* channel pointers */
+		S_CH = OPL->P_CH;
+		E_CH = &S_CH[9];
+		/* rythm slot */
+		SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+		SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+		SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+		SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+		/* LFO state */
+		amsIncr = OPL->amsIncr;
+		vibIncr = OPL->vibIncr;
+		ams_table = OPL->ams_table;
+		vib_table = OPL->vib_table;
+	}
+	R_CH = rythm ? &S_CH[6] : E_CH;
+    for( i=0; i < length ; i++ )
+	{
+		/*            channel A         channel B         channel C      */
+		/* LFO */
+		ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+		vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+		outd[0] = 0;
+		/* FM part */
+		for(CH=S_CH ; CH < R_CH ; CH++)
+			OPL_CALC_CH(CH);
+		/* Rythn part */
+		if(rythm)
+			OPL_CALC_RH(S_CH);
+		/* limit check */
+		data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+		/* store to sound buffer */
+		buf[i] = data >> OPL_OUTSB;
+	}
+
+	OPL->amsCnt = amsCnt;
+	OPL->vibCnt = vibCnt;
+}
+#endif /* (BUILD_YM3812 || BUILD_YM3526) */
+
+#if BUILD_Y8950
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+    int i;
+	int data;
+	FMSAMPLE *buf = buffer;
+	UINT32 amsCnt  = OPL->amsCnt;
+	UINT32 vibCnt  = OPL->vibCnt;
+	UINT8 rythm = OPL->rythm&0x20;
+	OPL_CH *CH,*R_CH;
+	YM_DELTAT *DELTAT = OPL->deltat;
+
+	/* setup DELTA-T unit */
+	YM_DELTAT_DECODE_PRESET(DELTAT);
+
+	if( (void *)OPL != cur_chip ){
+		cur_chip = (void *)OPL;
+		/* channel pointers */
+		S_CH = OPL->P_CH;
+		E_CH = &S_CH[9];
+		/* rythm slot */
+		SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+		SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+		SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+		SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+		/* LFO state */
+		amsIncr = OPL->amsIncr;
+		vibIncr = OPL->vibIncr;
+		ams_table = OPL->ams_table;
+		vib_table = OPL->vib_table;
+	}
+	R_CH = rythm ? &S_CH[6] : E_CH;
+    for( i=0; i < length ; i++ )
+	{
+		/*            channel A         channel B         channel C      */
+		/* LFO */
+		ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+		vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+		outd[0] = 0;
+		/* deltaT ADPCM */
+		if( DELTAT->flag )
+			YM_DELTAT_ADPCM_CALC(DELTAT);
+		/* FM part */
+		for(CH=S_CH ; CH < R_CH ; CH++)
+			OPL_CALC_CH(CH);
+		/* Rythn part */
+		if(rythm)
+			OPL_CALC_RH(S_CH);
+		/* limit check */
+		data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+		/* store to sound buffer */
+		buf[i] = data >> OPL_OUTSB;
+	}
+	OPL->amsCnt = amsCnt;
+	OPL->vibCnt = vibCnt;
+	/* deltaT START flag */
+	if( !DELTAT->flag )
+		OPL->status &= 0xfe;
+}
+#endif
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+	int c,s;
+	int i;
+
+	/* reset chip */
+	OPL->mode   = 0;	/* normal mode */
+	OPL_STATUS_RESET(OPL,0x7f);
+	/* reset with register write */
+	OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+	OPLWriteReg(OPL,0x02,0); /* Timer1 */
+	OPLWriteReg(OPL,0x03,0); /* Timer2 */
+	OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+	for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+	/* reset OPerator paramater */
+	for( c = 0 ; c < OPL->max_ch ; c++ )
+	{
+		OPL_CH *CH = &OPL->P_CH[c];
+		/* OPL->P_CH[c].PAN = OPN_CENTER; */
+		for(s = 0 ; s < 2 ; s++ )
+		{
+			/* wave table */
+			CH->SLOT[s].wavetable = &SIN.TABLE[0];
+			/* CH->SLOT[s].evm = ENV_MOD_RR; */
+			CH->SLOT[s].evc = EG_OFF;
+			CH->SLOT[s].eve = EG_OFF+1;
+			CH->SLOT[s].evs = 0;
+		}
+	}
+#if BUILD_Y8950
+	if(OPL->type&OPL_TYPE_ADPCM)
+	{
+		YM_DELTAT *DELTAT = OPL->deltat;
+
+		DELTAT->freqbase = OPL->freqbase;
+		DELTAT->output_pointer = outd;
+		DELTAT->portshift = 5;
+		DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
+		YM_DELTAT_ADPCM_Reset(DELTAT,0);
+	}
+#endif
+}
+
+/* ----------  Create one of vietual YM3812 ----------       */
+/* 'rate'  is sampling rate and 'bufsiz' is the size of the  */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+	char *ptr;
+	FM_OPL *OPL;
+	int state_size;
+	int max_ch = 9; /* normaly 9 channels */
+
+	if( OPL_LockTable() ==-1) return NULL;
+	/* allocate OPL state space */
+	state_size  = sizeof(FM_OPL);
+	state_size += sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+	if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+	/* allocate memory block */
+	ptr = malloc(state_size);
+	if(ptr==NULL) return NULL;
+	/* clear */
+	memset(ptr,0,state_size);
+	OPL        = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+	OPL->P_CH  = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+	if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
+#endif
+	/* set channel state pointer */
+	OPL->type  = type;
+	OPL->clock = clock;
+	OPL->rate  = rate;
+	OPL->max_ch = max_ch;
+	/* init grobal tables */
+	OPL_initalize(OPL);
+	/* reset chip */
+	OPLResetChip(OPL);
+	return OPL;
+}
+
+/* ----------  Destroy one of vietual YM3812 ----------       */
+void OPLDestroy(FM_OPL *OPL)
+{
+	void *t = OPL;
+	OPL_UnLockTable();
+	free(t);
+}
+
+/* ----------  Option handlers ----------       */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+	OPL->TimerHandler   = TimerHandler;
+	OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+	OPL->IRQHandler     = IRQHandler;
+	OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+	OPL->UpdateHandler = UpdateHandler;
+	OPL->UpdateParam = param;
+}
+#if BUILD_Y8950
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
+{
+	OPL->porthandler_w = PortHandler_w;
+	OPL->porthandler_r = PortHandler_r;
+	OPL->port_param = param;
+}
+
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
+{
+	OPL->keyboardhandler_w = KeyboardHandler_w;
+	OPL->keyboardhandler_r = KeyboardHandler_r;
+	OPL->keyboard_param = param;
+}
+#endif
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+	if( !(a&1) )
+	{	/* address port */
+		OPL->address = v & 0xff;
+	}
+	else
+	{	/* data port */
+		if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+		OPLWriteReg(OPL,OPL->address,v);
+	}
+	return OPL->status>>7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+	if( !(a&1) )
+	{	/* status port */
+		return OPL->status & (OPL->statusmask|0x80);
+	}
+	/* data port */
+	switch(OPL->address)
+	{
+	case 0x05: /* KeyBoard IN */
+		if(OPL->type&OPL_TYPE_KEYBOARD)
+		{
+			if(OPL->keyboardhandler_r)
+				return OPL->keyboardhandler_r(OPL->keyboard_param);
+			else
+				LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+		}
+		return 0;
+#if 0
+	case 0x0f: /* ADPCM-DATA  */
+		return 0;
+#endif
+	case 0x19: /* I/O DATA    */
+		if(OPL->type&OPL_TYPE_IO)
+		{
+			if(OPL->porthandler_r)
+				return OPL->porthandler_r(OPL->port_param);
+			else
+				LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+		}
+		return 0;
+	case 0x1a: /* PCM-DATA    */
+		return 0;
+	}
+	return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+	if( c )
+	{	/* Timer B */
+		OPL_STATUS_SET(OPL,0x20);
+	}
+	else
+	{	/* Timer A */
+		OPL_STATUS_SET(OPL,0x40);
+		/* CSM mode key,TL controll */
+		if( OPL->mode & 0x80 )
+		{	/* CSM mode total level latch and auto key on */
+			int ch;
+			if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+			for(ch=0;ch<9;ch++)
+				CSMKeyControll( &OPL->P_CH[ch] );
+		}
+	}
+	/* reload timer */
+	if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+	return OPL->status>>7;
+}
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/gstnsf.c	2005-03-26 21:38:41.247738120 +0100
@@ -0,0 +1,390 @@
+/* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2004 Johan Dahlin <johan@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include <gst/gst.h>
+#include <gst/gstelement.h>
+
+#include "gstnsf.h"
+
+#define VERSION "0.1"
+#define DEFAULT_BLOCKSIZE	4096
+
+enum {
+  ARG_0,
+  ARG_TUNE,
+  ARG_FILTER
+  /* FILL ME */
+};
+
+static GstStaticPadTemplate sink_templ =
+GST_STATIC_PAD_TEMPLATE("sink",
+			GST_PAD_SINK,
+			GST_PAD_ALWAYS,
+			GST_STATIC_CAPS ("audio/x-nsf"));
+
+static GstStaticPadTemplate src_templ =
+GST_STATIC_PAD_TEMPLATE("src",
+			GST_PAD_SRC,
+			GST_PAD_ALWAYS,
+			GST_STATIC_CAPS ("audio/x-raw-int, "
+					 "endianness = (int) LITTLE_ENDIAN, "
+					 "depth = (int) 8, "
+                                         "width = (int) 8, "
+					 "rate = (int) 44100, "
+					 "channels = (int) 1, "
+					 "signed = (boolean) FALSE"));
+
+enum {
+  NSF_STATE_NEED_TUNE = 1,
+  NSF_STATE_LOAD_TUNE = 2,
+  NSF_STATE_PLAY_TUNE = 3
+};
+
+static void     gst_nsfdec_base_init            (gpointer g_class);
+static void 	gst_nsfdec_class_init		(GstNsfDec *klass);
+static void 	gst_nsfdec_init			(GstNsfDec *nsfdec);
+
+static gboolean gst_nsfdec_query (GstPad * pad,
+    GstQueryType query, GstFormat * format, gint64 * value);
+static void 	gst_nsfdec_loop 		(GstElement *element);
+static GstElementStateReturn gst_nsfdec_change_state (GstElement * element);
+
+static void     gst_nsfdec_get_property         (GObject *object, guint prop_id, 
+						 GValue *value, GParamSpec *pspec);
+static void     gst_nsfdec_set_property         (GObject *object, guint prop_id, 
+						 const GValue *value, GParamSpec *pspec);
+
+static GstElementClass *parent_class = NULL;
+
+GType
+gst_nsfdec_get_type (void) 
+{
+  static GType nsfdec_type = 0;
+
+  if (!nsfdec_type) {
+    static const GTypeInfo nsfdec_info = {
+      sizeof(GstNsfDecClass),      
+      gst_nsfdec_base_init,
+      NULL,
+      (GClassInitFunc) gst_nsfdec_class_init,
+      NULL,
+      NULL,
+      sizeof(GstNsfDec),
+      0,
+      (GInstanceInitFunc) gst_nsfdec_init,
+      NULL
+    };
+    nsfdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstNsfDec",
+					  &nsfdec_info, (GTypeFlags)0);
+  }
+
+  return nsfdec_type;
+}
+
+#define GST_TYPE_NSF_FILTER (gst_nsf_filter_get_type())
+static GType
+gst_nsf_filter_get_type (void)
+{
+  static GType nsf_filter_type = 0;
+  static GEnumValue nsf_filter[] = {
+    {NSF_FILTER_NONE, "0", "None"},
+    {NSF_FILTER_LOWPASS, "1", "Lowpass"},
+    {NSF_FILTER_WEIGHTED, "2", "Weighted"},
+    {0, NULL, NULL},
+  };
+
+  if (!nsf_filter_type) {
+    nsf_filter_type = g_enum_register_static ("GstNsfFilter", nsf_filter);
+  }
+  return nsf_filter_type;
+}
+
+static void
+gst_nsfdec_base_init (gpointer g_class)
+{
+  static GstElementDetails gst_nsfdec_details =
+    GST_ELEMENT_DETAILS ("Nsf decoder",
+			 "Codec/Audio/Decoder",
+			 "Using nosefart to decode NSF audio tunes",
+			 "Johan Dahlin <johan@gnome.org>");
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_set_details (element_class, &gst_nsfdec_details);
+  gst_element_class_add_pad_template (element_class,
+				      gst_static_pad_template_get (&src_templ));
+  gst_element_class_add_pad_template (element_class,
+				      gst_static_pad_template_get (&sink_templ));
+
+}
+
+static void
+gst_nsfdec_class_init (GstNsfDec *klass) 
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass*)klass;
+  gstelement_class = (GstElementClass*)klass;
+
+  parent_class = GST_ELEMENT_CLASS (g_type_class_ref (GST_TYPE_ELEMENT));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TUNE,
+				   g_param_spec_int ("tune", "tune", "tune",
+						     1, 100, 1,
+						     (GParamFlags)G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILTER,
+      g_param_spec_enum ("filter", "filter", "filter", GST_TYPE_NSF_FILTER,
+          NSF_FILTER_NONE, (GParamFlags) G_PARAM_WRITABLE));
+
+  gobject_class->set_property = gst_nsfdec_set_property;
+  gobject_class->get_property = gst_nsfdec_get_property;
+
+  gstelement_class->change_state = gst_nsfdec_change_state;
+
+  nsf_init ();
+}
+
+static const GstFormat *
+gst_nsfdec_get_formats (GstPad * pad)
+{
+  static GstFormat formats[] = {
+    GST_FORMAT_DEFAULT,
+    GST_FORMAT_TIME,
+    0
+  };
+
+  return formats;
+}
+
+static const GstQueryType *
+gst_nsfdec_get_query_types (GstPad * pad)
+{
+  static const GstQueryType query_types[] = {
+    GST_QUERY_POSITION,
+    0
+  };
+
+  return query_types;
+}
+
+static void 
+gst_nsfdec_init (GstNsfDec *nsfdec) 
+{
+  /* Sink */
+  nsfdec->sinkpad = gst_pad_new_from_template (
+  		gst_static_pad_template_get (&sink_templ), "sink");
+  gst_element_add_pad (GST_ELEMENT (nsfdec), nsfdec->sinkpad);
+
+  /* Source */
+  nsfdec->srcpad = gst_pad_new_from_template (
+               gst_static_pad_template_get (&src_templ), "src");
+  gst_pad_set_query_type_function (nsfdec->srcpad, gst_nsfdec_get_query_types);
+  gst_pad_set_query_function (nsfdec->srcpad, gst_nsfdec_query);
+  gst_pad_set_formats_function (nsfdec->srcpad, gst_nsfdec_get_formats);
+  gst_element_add_pad (GST_ELEMENT (nsfdec), nsfdec->srcpad);
+
+  gst_element_set_loop_function (GST_ELEMENT (nsfdec), gst_nsfdec_loop);
+
+  nsfdec->nsf = NULL;
+  nsfdec->state = NSF_STATE_NEED_TUNE;
+  nsfdec->dataSize = 0;
+  nsfdec->samples = 0;
+  nsfdec->tune_buffer = NULL;
+  nsfdec->tune_number = 1;
+  nsfdec->frequency = 44100;
+  nsfdec->bits = 8;
+  nsfdec->stereo = FALSE;
+  nsfdec->channels = 1;
+}
+
+static gboolean
+gst_nsfdec_query (GstPad * pad,
+    GstQueryType query, GstFormat * format, gint64 * value)
+{
+  GstNsfDec *nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+  switch (query) {
+    case GST_QUERY_POSITION:
+      switch (*format) {
+        case GST_FORMAT_TIME:
+          *value = nsfdec->samples * GST_SECOND / nsfdec->frequency;
+          break;
+        case GST_FORMAT_DEFAULT:
+          *value = nsfdec->samples;
+          break;
+        default:
+          return FALSE;
+      }
+      break;
+    default:
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+static void 
+gst_nsfdec_loop (GstElement *element)
+{
+  GstNsfDec *nsfdec;
+  
+  nsfdec = GST_NSFDEC (element);
+
+  if (nsfdec->state == NSF_STATE_NEED_TUNE) {
+    GstData *data = gst_pad_pull (nsfdec->sinkpad);
+
+    g_assert (data != NULL);
+      
+    if (GST_IS_EVENT (data)) {
+      GstEvent *event = GST_EVENT (data);
+      switch (GST_EVENT_TYPE (event)) {
+	case GST_EVENT_EOS:
+          nsfdec->state = NSF_STATE_LOAD_TUNE;
+	  break;
+        case GST_EVENT_INTERRUPT:
+          break;
+        default:
+          gst_pad_event_default (nsfdec->sinkpad, event);
+	  return;
+      }
+      gst_event_unref (event);
+      return;
+    } else {
+      if (nsfdec->tune_buffer) {
+        nsfdec->tune_buffer = gst_buffer_join (nsfdec->tune_buffer,
+            GST_BUFFER (data));
+      } else {
+        nsfdec->tune_buffer = GST_BUFFER (data);
+      }
+    }
+  } else if (nsfdec->state == NSF_STATE_LOAD_TUNE) {
+    nsfdec->nsf = nsf_load (NULL, GST_BUFFER_DATA (nsfdec->tune_buffer),
+        GST_BUFFER_SIZE (nsfdec->tune_buffer));
+    if (!nsfdec->nsf) {
+      GST_ELEMENT_ERROR (nsfdec, LIBRARY, INIT, (NULL), (NULL));
+      return;
+    }
+
+    nsf_playtrack (nsfdec->nsf,
+		   nsfdec->tune_number,
+		   nsfdec->frequency,
+		   nsfdec->bits,
+		   nsfdec->stereo);
+
+    nsfdec->dataSize = nsfdec->frequency / nsfdec->nsf->playback_rate;
+    
+    nsfdec->state = NSF_STATE_PLAY_TUNE;
+  } else if (nsfdec->state == NSF_STATE_PLAY_TUNE) {
+    GstBuffer *buffer;
+
+    buffer = gst_buffer_new_and_alloc (nsfdec->dataSize);
+    GST_BUFFER_TIMESTAMP (buffer) = GST_SECOND * nsfdec->samples /
+        nsfdec->frequency;
+    GST_BUFFER_DURATION (buffer) =
+        GST_SECOND * nsfdec->dataSize / nsfdec->frequency;
+    nsfdec->samples += nsfdec->dataSize;
+
+    nsf_frame (nsfdec->nsf);
+    apu_process (GST_BUFFER_DATA (buffer), nsfdec->dataSize);
+
+    gst_pad_push (nsfdec->srcpad, GST_DATA (buffer));
+  }
+}
+
+static GstElementStateReturn
+gst_nsfdec_change_state (GstElement * element)
+{
+  GstNsfDec *nsfdec = GST_NSFDEC (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_PAUSED_TO_READY:
+      nsfdec->state = NSF_STATE_NEED_TUNE;
+      if (nsfdec->tune_buffer) {
+        gst_buffer_unref (nsfdec->tune_buffer);
+        nsfdec->tune_buffer = NULL;
+      }
+      nsfdec->samples = 0;
+      break;
+    default:
+      break;
+  }
+
+  return parent_class->change_state (element);
+}
+
+static void 
+gst_nsfdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GstNsfDec *nsfdec;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(GST_IS_NSFDEC(object));
+
+  nsfdec = GST_NSFDEC(object);
+
+  switch(prop_id) {
+    case ARG_TUNE:
+      nsfdec->tune_number = g_value_get_int (value);
+      break;
+    case ARG_FILTER:
+      nsf_setfilter (nsfdec->nsf, g_value_get_enum (value));
+      break;
+    default:
+      /* G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); */
+      return;
+  }
+}
+
+static void 
+gst_nsfdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  GstNsfDec *nsfdec;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(GST_IS_NSFDEC(object));
+  
+  nsfdec = GST_NSFDEC(object);
+  switch(prop_id) {
+    case ARG_TUNE:
+      g_value_set_int (value, nsfdec->tune_number);
+      break;
+    default:
+      /* G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); */
+      break;
+  }
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+  return gst_element_register (plugin, "nsfdec", GST_RANK_PRIMARY,
+			       GST_TYPE_NSFDEC);
+}
+
+GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR,
+		  "nsfdec",
+		  "Uses nosefart to decode .nsf files",
+		  plugin_init,
+		  VERSION,
+		  "GPL",
+		  "nsfdec",
+		  "????")
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/log.c	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,113 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** log.c
+**
+** Error logging functions
+** $Id: log.c,v 1.5 2000/06/26 04:55:33 matt Exp $
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "types.h"
+#include "log.h"
+
+
+#ifdef OSD_LOG
+#include "osd.h"
+#endif
+
+#if defined(OSD_LOG) && !defined(NOFRENDO_DEBUG)
+#error NOFRENDO_DEBUG must be defined as well as OSD_LOG
+#endif
+
+/* Note that all of these functions will be empty if
+** debugging is not enabled.
+*/
+#ifdef NOFRENDO_DEBUG
+static FILE *errorlog;
+#endif
+
+int log_init(void)
+{
+#ifdef NOFRENDO_DEBUG
+#ifdef OSD_LOG
+   /* Initialize an OSD logging system */
+   osd_loginit();
+#endif /* OSD_LOG */
+   errorlog = fopen("errorlog.txt", "wt");
+   if (NULL == errorlog)
+      return (-1);
+#endif /* NOFRENDO_DEBUG */
+   return 0;
+}
+
+void log_shutdown(void)
+{
+#ifdef NOFRENDO_DEBUG
+   /* Snoop around for unallocated blocks */
+   mem_checkblocks();
+   mem_checkleaks();
+#ifdef OSD_LOG
+   osd_logshutdown();
+#endif /* OSD_LOG */
+   fclose(errorlog);
+#endif /* NOFRENDO_DEBUG */
+}
+
+void log_print(const char *string)
+{
+#ifdef NOFRENDO_DEBUG
+#ifdef OSD_LOG
+   osd_logprint(string);
+#endif /* OSD_LOG */
+   /* Log it to disk, as well */
+   fputs(string, errorlog);
+#endif /* NOFRENDO_DEBUG */
+}
+
+void log_printf(const char *format, ... )
+{
+#ifdef NOFRENDO_DEBUG
+#ifdef OSD_LOG
+   char buffer[1024 + 1];
+#endif /* OSD_LOG */
+   va_list arg;
+
+   va_start(arg, format);
+
+#ifdef OSD_LOG
+   vsprintf(buffer, format, arg);
+   osd_logprint(buffer);
+#endif /* OSD_LOG */
+   vfprintf(errorlog, format, arg);
+   va_end(arg);
+#endif /* NOFRENDO_DEBUG */
+}
+
+/*
+** $Log: log.c,v $
+** Revision 1.5  2000/06/26 04:55:33  matt
+** minor change
+**
+** Revision 1.4  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/memguard.c	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,376 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** memguard.c
+**
+** memory allocation wrapper routines
+**
+** NOTE: based on code (c) 1998 the Retrocade group
+** $Id: memguard.c,v 1.8 2000/06/26 04:54:48 matt Exp $
+*/
+
+#include "types.h"
+
+/* undefine macro definitions, so we get real calls */
+#undef malloc
+#undef free
+
+#include <string.h>
+#include <stdlib.h>
+#include "memguard.h"
+#include "log.h"
+
+
+/* Maximum number of allocated blocks at any one time */
+#define  MAX_BLOCKS        16384
+
+/* Memory block structure */
+typedef struct memblock_s
+{
+   void  *block_addr;
+   int   block_size;
+   char  *file_name;
+   int   line_num;
+} memblock_t;
+
+boolean mem_debug = TRUE;           /* debugging flag */
+
+
+#ifdef NOFRENDO_DEBUG
+
+static int mem_blockcount = 0;   /* allocated block count */
+static memblock_t *mem_record = NULL;
+
+#define  GUARD_STRING   "GgUuAaRrDdSsTtRrIiNnGgBbLlOoCcKk"
+#define  GUARD_LENGTH   64          /* before and after allocated block */
+
+
+/*
+** Check the memory guard to make sure out of bounds writes have not
+** occurred.
+*/
+static boolean mem_checkguardblock(void *data, int guard_size)
+{
+   uint8 *orig, *chk, *blk;
+   int i, alloc_size;
+
+   /* get the original pointer */
+   orig = (((uint8 *) data) - guard_size);
+
+   /* get the size */
+   alloc_size = *((uint32 *) orig);
+
+   /* now skip past the size */
+   blk = orig + sizeof(uint32);
+
+   /* check leading guard string */
+   chk = GUARD_STRING;
+   for (i = sizeof(uint32); i < guard_size; i++)
+   {
+      if (0 == *chk)
+         chk = GUARD_STRING;
+      if (*blk != *chk)
+         return FALSE;
+      chk++;
+      blk++;
+   }
+
+   /* check end of block */
+   chk = GUARD_STRING;
+   blk = ((uint8 *) data) + alloc_size;
+   for (i = 0; i < guard_size; i++)
+   {
+      if (0 == *chk)
+         chk = GUARD_STRING;
+      if (*blk != *chk)
+         return FALSE;
+      chk++;
+      blk++;
+   }
+
+   /* we're okay! */
+   return TRUE;
+}
+
+/* free a guard block */
+static void mem_freeguardblock(void *data, int guard_size)
+{
+   uint8 *orig = (((uint8 *) data) - guard_size);
+
+   free(orig);
+}
+
+/* fill in the memory guard, advance the pointer to the 'real' memory */
+static void *mem_guardblock(int alloc_size, int guard_size)
+{
+   void *orig;
+   uint8 *blk, *chk;
+   int i;
+
+   /* allocate memory */
+   orig = calloc(alloc_size + (guard_size * 2), 1);
+   if (NULL == orig)
+      return NULL;
+
+   blk = ((uint8 *) orig);
+   
+   /* store the size of the newly allocated block*/
+   *((uint32 *) blk) = alloc_size;
+
+   /* skip past the size */
+   blk += sizeof(uint32);
+
+   /* put guard string at beginning of block */
+   chk = GUARD_STRING;
+   for (i = sizeof(uint32); i < guard_size; i++)
+   {
+      if (0 == *chk)
+         chk = GUARD_STRING;
+      *blk++ = *chk++;
+   }
+
+   /* check end of block */
+   chk = GUARD_STRING;
+   blk = guard_size + (uint8 *) orig + alloc_size;
+   for (i = 0; i < guard_size; i++)
+   {
+      if (0 == *chk)
+         chk = GUARD_STRING;
+      *blk++ = *chk++;
+   }
+
+   return (void *) (guard_size + (uint8 *) orig);
+}
+
+
+/* Allocate a bunch of memory to keep track of all memory blocks */
+static void mem_init(void)
+{
+   if (mem_record)
+   {
+      free(mem_record);
+      mem_record = NULL;
+   }
+
+   mem_record = calloc(MAX_BLOCKS * sizeof(memblock_t), 1);
+   ASSERT(mem_record);
+}
+
+/* add a block of memory to the master record */
+static void mem_addblock(void *data, int block_size, char *file, int line)
+{
+   int i;
+
+   for (i = 0; i < MAX_BLOCKS; i++)
+   {
+      if (NULL == mem_record[i].block_addr)
+      {
+         mem_record[i].block_addr = data;
+         mem_record[i].block_size = block_size;
+         mem_record[i].file_name = file;
+         mem_record[i].line_num = line;
+         return;
+      }
+   }
+
+   ASSERT_MSG("out of memory blocks.");
+}
+
+/* find an entry in the block record and delete it */
+static void mem_deleteblock(void *data, char *file, int line)
+{
+   int i;
+   char fail[256];
+
+   for (i = 0; i < MAX_BLOCKS; i++)
+   {
+      if (data == mem_record[i].block_addr)
+      {
+         if (FALSE == mem_checkguardblock(mem_record[i].block_addr, GUARD_LENGTH))
+         {
+            sprintf(fail, "mem_deleteblock 0x%08X at line %d of %s -- block corrupt",
+                    (uint32) data, line, file);
+            ASSERT_MSG(fail);
+         }
+
+         memset(&mem_record[i], 0, sizeof(memblock_t));
+         return;
+      }
+   }
+
+   sprintf(fail, "mem_deleteblock 0x%08X at line %d of %s -- block not found",
+           (uint32) data, line, file);
+   ASSERT_MSG(fail);
+}
+#endif /* NOFRENDO_DEBUG */
+
+/* allocates memory and clears it */
+#ifdef NOFRENDO_DEBUG
+void *_my_malloc(int size, char *file, int line)
+#else
+void *_my_malloc(int size)
+#endif
+{
+   void *temp;
+   char fail[256];
+
+#ifdef NOFRENDO_DEBUG
+   if (NULL == mem_record && FALSE != mem_debug)
+      mem_init();
+
+   if (FALSE != mem_debug)
+      temp = mem_guardblock(size, GUARD_LENGTH);
+   else
+#endif /* NOFRENDO_DEBUG */
+      temp = calloc(sizeof(uint8), size);
+
+   if (NULL == temp)
+   {
+#ifdef NOFRENDO_DEBUG
+      sprintf(fail, "malloc: out of memory at line %d of %s.  block size: %d\n",
+              line, file, size);
+#else
+      sprintf(fail, "malloc: out of memory.  block size: %d\n", size);
+#endif
+      ASSERT_MSG(fail);
+   }
+
+#ifdef NOFRENDO_DEBUG
+   if (FALSE != mem_debug)
+      mem_addblock(temp, size, file, line);
+
+   mem_blockcount++;
+#endif
+
+   return temp;
+}
+
+/* free a pointer allocated with my_malloc */
+#ifdef NOFRENDO_DEBUG
+void _my_free(void **data, char *file, int line)
+#else
+void _my_free(void **data)
+#endif
+{
+   char fail[256];
+
+   if (NULL == data || NULL == *data
+       || 0xFFFFFFFF == (uint32) *data || 0xFFFFFFFF == (uint32) data)
+   {
+#ifdef NOFRENDO_DEBUG
+      sprintf(fail, "free: attempted to free NULL pointer at line %d of %s\n",
+              line, file);
+#else
+      sprintf(fail, "free: attempted to free NULL pointer.\n");
+#endif
+      ASSERT_MSG(fail);
+   }
+
+#ifdef NOFRENDO_DEBUG
+   /* if this is true, we are in REAL trouble */
+   if (0 == mem_blockcount)
+   {
+      ASSERT_MSG("free: attempted to free memory when no blocks available");
+   }
+
+   if (FALSE != mem_debug)
+      mem_deleteblock(*data, file, line);
+
+   mem_blockcount--; /* dec our block count */
+
+   if (FALSE != mem_debug)
+      mem_freeguardblock(*data, GUARD_LENGTH);
+   else
+#endif /* NOFRENDO_DEBUG */
+      free(*data);
+
+   *data = NULL; /* NULL our source */
+}
+
+/* check for orphaned memory handles */
+void mem_checkleaks(void)
+{
+#ifdef NOFRENDO_DEBUG
+   int i;
+
+   if (FALSE == mem_debug)
+      return;
+
+   if (mem_blockcount)
+   {
+      log_printf("memory leak - %d unfreed block%s\n\n", mem_blockcount, 
+         mem_blockcount == 1 ? "" : "s");
+
+      for (i = 0; i < MAX_BLOCKS; i++)
+      {
+         if (mem_record[i].block_addr)
+         {
+            log_printf("addr: 0x%08X, size: %d, line %d of %s%s\n",
+                    (uint32) mem_record[i].block_addr,
+                    mem_record[i].block_size,
+                    mem_record[i].line_num,
+                    mem_record[i].file_name,
+                    (FALSE == mem_checkguardblock(mem_record[i].block_addr, GUARD_LENGTH))
+                    ? " -- block corrupt" : "");
+         }
+      }
+   }
+   else
+      log_printf("no memory leaks\n");
+#endif
+}
+
+void mem_checkblocks(void)
+{
+#ifdef NOFRENDO_DEBUG
+   int i;
+
+   if (FALSE == mem_debug)
+      return;
+
+   for (i = 0; i < MAX_BLOCKS; i++)
+   {
+      if (mem_record[i].block_addr)
+      {
+         if (FALSE == mem_checkguardblock(mem_record[i].block_addr, GUARD_LENGTH))
+         {
+            log_printf("addr: 0x%08X, size: %d, line %d of %s -- block corrupt\n",
+                    (uint32) mem_record[i].block_addr,
+                    mem_record[i].block_size,
+                    mem_record[i].line_num,
+                    mem_record[i].file_name);
+         }
+      }
+   }
+#endif /* NOFRENDO_DEBUG */
+}
+
+/*
+** $Log: memguard.c,v $
+** Revision 1.8  2000/06/26 04:54:48  matt
+** simplified and made more robust
+**
+** Revision 1.7  2000/06/12 01:11:41  matt
+** cleaned up some error output for win32
+**
+** Revision 1.6  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/mmc5_snd.c	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,355 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** mmc5_snd.c
+**
+** Nintendo MMC5 sound emulation
+** $Id: mmc5_snd.c,v 1.6 2000/07/04 04:51:41 matt Exp $
+*/
+
+#include <string.h>
+#include "types.h"
+#include "mmc5_snd.h"
+#include "nes_apu.h"
+
+/* TODO: encapsulate apu/mmc5 rectangle */
+
+#define  APU_OVERSAMPLE
+#define  APU_VOLUME_DECAY(x)  ((x) -= ((x) >> 7))
+
+
+typedef struct mmc5dac_s
+{
+   int32 output;
+   boolean enabled;
+} mmc5dac_t;
+
+
+/* look up table madness */
+static int32 decay_lut[16];
+static int vbl_lut[32];
+
+/* various sound constants for sound emulation */
+/* vblank length table used for rectangles, triangle, noise */
+static const uint8 vbl_length[32] =
+{
+   5, 127, 10, 1, 19,  2, 40,  3, 80,  4, 30,  5, 7,  6, 13,  7,
+   6,   8, 12, 9, 24, 10, 48, 11, 96, 12, 36, 13, 8, 14, 16, 15
+};
+
+/* ratios of pos/neg pulse for rectangle waves
+** 2/16 = 12.5%, 4/16 = 25%, 8/16 = 50%, 12/16 = 75% 
+** (4-bit adder in rectangles, hence the 16)
+*/
+static const int duty_lut[4] =
+{
+   2, 4, 8, 12
+};
+
+
+static int32 mmc5_incsize;
+static uint8 mul[2];
+static mmc5rectangle_t mmc5rect[2];
+static mmc5dac_t mmc5dac;
+
+#define  MMC5_RECTANGLE_OUTPUT   chan->output_vol
+static int32 mmc5_rectangle(mmc5rectangle_t *chan)
+{
+   int32 output;
+
+#ifdef APU_OVERSAMPLE
+   int num_times;
+   int32 total;
+#endif /* APU_OVERSAMPLE */
+
+   /* reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle
+   ** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on
+   ** reg2: 8 bits of freq
+   ** reg3: 0-2=high freq, 7-4=vbl length counter
+   */
+
+   APU_VOLUME_DECAY(chan->output_vol);
+
+   if (FALSE == chan->enabled || 0 == chan->vbl_length)
+      return MMC5_RECTANGLE_OUTPUT;
+
+   /* vbl length counter */
+   if (FALSE == chan->holdnote)
+      chan->vbl_length--;
+
+   /* envelope decay at a rate of (env_delay + 1) / 240 secs */
+   chan->env_phase -= 4; /* 240/60 */
+   while (chan->env_phase < 0)
+   {
+      chan->env_phase += chan->env_delay;
+
+      if (chan->holdnote)
+         chan->env_vol = (chan->env_vol + 1) & 0x0F;
+      else if (chan->env_vol < 0x0F)
+         chan->env_vol++;
+   }
+
+   if (chan->freq < APU_TO_FIXED(4))
+      return MMC5_RECTANGLE_OUTPUT;
+
+   chan->phaseacc -= mmc5_incsize; /* # of cycles per sample */
+   if (chan->phaseacc >= 0)
+      return MMC5_RECTANGLE_OUTPUT;
+
+#ifdef APU_OVERSAMPLE
+   num_times = total = 0;
+
+   if (chan->fixed_envelope)
+      output = chan->volume << 8; /* fixed volume */
+   else
+      output = (chan->env_vol ^ 0x0F) << 8;
+#endif
+
+   while (chan->phaseacc < 0)
+   {
+      chan->phaseacc += chan->freq;
+      chan->adder = (chan->adder + 1) & 0x0F;
+
+#ifdef APU_OVERSAMPLE
+      if (chan->adder < chan->duty_flip)
+         total += output;
+      else
+         total -= output;
+
+      num_times++;
+#endif
+   }
+
+#ifdef APU_OVERSAMPLE
+   chan->output_vol = total / num_times;
+#else
+   if (chan->fixed_envelope)
+      output = chan->volume << 8; /* fixed volume */
+   else
+      output = (chan->env_vol ^ 0x0F) << 8;
+
+   if (0 == chan->adder)
+      chan->output_vol = output;
+   else if (chan->adder == chan->duty_flip)
+      chan->output_vol = -output;
+#endif
+
+   return MMC5_RECTANGLE_OUTPUT;
+}
+
+static uint8 mmc5_read(uint32 address)
+{
+   uint32 retval;
+
+   retval = (uint32) (mul[0] * mul[1]);
+
+   switch (address)
+   {
+   case 0x5205:
+      return (uint8) retval;
+
+   case 0x5206:
+      return (uint8) (retval >> 8);
+
+   default:
+      return 0xFF;
+   }
+}
+
+/* mix vrcvi sound channels together */
+static int32 mmc5_process(void)
+{
+   int32 accum;
+
+   accum = mmc5_rectangle(&mmc5rect[0]);
+   accum += mmc5_rectangle(&mmc5rect[1]);
+   if (mmc5dac.enabled)
+      accum += mmc5dac.output;
+
+   return accum;
+}
+
+/* write to registers */
+static void mmc5_write(uint32 address, uint8 value)
+{
+   int chan;
+
+   switch (address)
+   {
+   /* rectangles */
+   case MMC5_WRA0:
+   case MMC5_WRB0:
+      chan = (address & 4) ? 1 : 0;
+      mmc5rect[chan].regs[0] = value;
+
+      mmc5rect[chan].volume = value & 0x0F;
+      mmc5rect[chan].env_delay = decay_lut[value & 0x0F];
+      mmc5rect[chan].holdnote = (value & 0x20) ? TRUE : FALSE;
+      mmc5rect[chan].fixed_envelope = (value & 0x10) ? TRUE : FALSE;
+      mmc5rect[chan].duty_flip = duty_lut[value >> 6];
+      break;
+
+   case MMC5_WRA1:
+   case MMC5_WRB1:
+      break;
+
+   case MMC5_WRA2:
+   case MMC5_WRB2:
+      chan = (address & 4) ? 1 : 0;
+      mmc5rect[chan].regs[2] = value;
+      if (mmc5rect[chan].enabled)
+         mmc5rect[chan].freq = APU_TO_FIXED((((mmc5rect[chan].regs[3] & 7) << 8) + value) + 1);
+      break;
+
+   case MMC5_WRA3:
+   case MMC5_WRB3:
+      chan = (address & 4) ? 1 : 0;
+      mmc5rect[chan].regs[3] = value;
+
+      if (mmc5rect[chan].enabled)
+      {
+         mmc5rect[chan].vbl_length = vbl_lut[value >> 3];
+         mmc5rect[chan].env_vol = 0;
+         mmc5rect[chan].freq = APU_TO_FIXED((((value & 7) << 8) + mmc5rect[chan].regs[2]) + 1);
+         mmc5rect[chan].adder = 0;
+      }
+      break;
+   
+   case MMC5_SMASK:
+      if (value & 0x01)
+         mmc5rect[0].enabled = TRUE;
+      else
+      {
+         mmc5rect[0].enabled = FALSE;
+         mmc5rect[0].vbl_length = 0;
+      }
+
+      if (value & 0x02)
+         mmc5rect[1].enabled = TRUE;
+      else
+      {
+         mmc5rect[1].enabled = FALSE;
+         mmc5rect[1].vbl_length = 0;
+      }
+
+      break;
+
+   case 0x5010:
+      if (value & 0x01)
+         mmc5dac.enabled = TRUE;
+      else
+         mmc5dac.enabled = FALSE;
+      break;
+
+   case 0x5011:
+      mmc5dac.output = (value ^ 0x80) << 8;
+      break;
+
+   case 0x5205:
+      mul[0] = value;
+      break;
+
+   case 0x5206:
+      mul[1] = value;
+      break;
+
+   default:
+      break;
+   }
+}
+
+/* reset state of vrcvi sound channels */
+static void mmc5_reset(void)
+{
+   int i;
+
+   /* get the phase period from the apu */
+   mmc5_incsize = apu_getcyclerate();
+
+   for (i = 0x5000; i < 0x5008; i++)
+      mmc5_write(i, 0);
+
+   mmc5_write(0x5010, 0);
+   mmc5_write(0x5011, 0);
+}
+
+static void mmc5_init(void)
+{
+   int i;
+   int num_samples = apu_getcontext()->num_samples;
+
+   /* lut used for enveloping and frequency sweeps */
+   for (i = 0; i < 16; i++)
+      decay_lut[i] = num_samples * (i + 1);
+
+   /* used for note length, based on vblanks and size of audio buffer */
+   for (i = 0; i < 32; i++)
+      vbl_lut[i] = vbl_length[i] * num_samples;
+}
+
+/* TODO: bleh */
+static void mmc5_shutdown(void)
+{
+}
+
+static apu_memread mmc5_memread[] =
+{
+   { 0x5205, 0x5206, mmc5_read },
+   {     -1,     -1, NULL }
+};
+
+static apu_memwrite mmc5_memwrite[] =
+{
+   { 0x5000, 0x5015, mmc5_write },
+   { 0x5205, 0x5206, mmc5_write },
+   {     -1,     -1, NULL }
+};
+
+apuext_t mmc5_ext =
+{
+   mmc5_init,
+   mmc5_shutdown,
+   mmc5_reset,
+   mmc5_process,
+   mmc5_memread,
+   mmc5_memwrite
+};
+
+/*
+** $Log: mmc5_snd.c,v $
+** Revision 1.6  2000/07/04 04:51:41  matt
+** cleanups
+**
+** Revision 1.5  2000/07/03 02:18:53  matt
+** much better external module exporting
+**
+** Revision 1.4  2000/06/28 22:03:51  matt
+** fixed stupid oversight
+**
+** Revision 1.3  2000/06/20 20:46:58  matt
+** minor cleanups
+**
+** Revision 1.2  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.1  2000/06/20 00:06:47  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/nes6502.c	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,2398 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** nes6502.c
+**
+** NES custom 6502 (2A03) CPU implementation
+** $Id: nes6502.c,v 1.6 2000/07/04 04:50:07 matt Exp $
+*/
+
+
+#include "types.h"
+#include "nes6502.h"
+#include "dis6502.h"
+#include <stdio.h>
+
+
+#define  ADD_CYCLES(x)     instruction_cycles += (x)
+#define  INC_CYCLES()      instruction_cycles++
+/* #define  ADD_CYCLES(x)  remaining_cycles -= (x) */
+/* #define  INC_CYCLES()   remaining_cycles-- */
+
+/*
+** Check to see if an index reg addition overflowed to next page
+*/
+#define CHECK_INDEX_OVERFLOW(addr, reg) \
+{ \
+   if ((uint8) (addr) < (reg)) \
+      INC_CYCLES(); \
+}
+
+/*
+** Addressing mode macros
+*/
+
+#define NO_READ(value)        /* empty */
+
+#define IMMEDIATE_BYTE(value) \
+{ \
+   value = bank_readbyte(PC++); \
+}
+
+
+#define ABSOLUTE_ADDR(address) \
+{ \
+   address = bank_readaddress(PC); \
+   PC += 2; \
+}
+
+#define ABSOLUTE(address, value) \
+{ \
+   ABSOLUTE_ADDR(address); \
+   value = mem_read(address); \
+}
+
+#define ABSOLUTE_BYTE(value) \
+{ \
+   ABSOLUTE(temp, value); \
+}
+
+#define ABS_IND_X_ADDR(address) \
+{ \
+   address = (bank_readaddress(PC) + X) & 0xFFFF; \
+   PC += 2; \
+   CHECK_INDEX_OVERFLOW(address, X); \
+}
+
+#define ABS_IND_X(address, value) \
+{ \
+   ABS_IND_X_ADDR(address); \
+   value = mem_read(address); \
+}
+
+#define ABS_IND_X_BYTE(value) \
+{ \
+   ABS_IND_X(temp, value); \
+}
+
+#define ABS_IND_Y_ADDR(address) \
+{ \
+   address = (bank_readaddress(PC) + Y) & 0xFFFF; \
+   PC += 2; \
+   CHECK_INDEX_OVERFLOW(address, Y); \
+}
+
+#define ABS_IND_Y(address, value) \
+{ \
+   ABS_IND_Y_ADDR(address); \
+   value = mem_read(address); \
+}
+
+#define ABS_IND_Y_BYTE(value) \
+{ \
+   ABS_IND_Y(temp, value); \
+}
+
+#define ZERO_PAGE_ADDR(address) \
+{ \
+   IMMEDIATE_BYTE(address); \
+}
+
+#define ZERO_PAGE(address, value) \
+{ \
+   ZERO_PAGE_ADDR(address); \
+   value = ZP_READ(address); \
+}
+
+#define ZERO_PAGE_BYTE(value) \
+{ \
+   ZERO_PAGE(btemp, value); \
+}
+
+/* Zero-page indexed Y doesn't really exist, just for LDX / STX */
+#define ZP_IND_X_ADDR(address) \
+{ \
+   address = bank_readbyte(PC++) + X; \
+}
+
+#define ZP_IND_X(bAddr, value) \
+{ \
+   ZP_IND_X_ADDR(bAddr); \
+   value = ZP_READ(bAddr); \
+}
+
+#define ZP_IND_X_BYTE(value) \
+{ \
+   ZP_IND_X(btemp, value); \
+}
+
+#define ZP_IND_Y_ADDR(address) \
+{ \
+   address = bank_readbyte(PC++) + Y; \
+}
+
+#define ZP_IND_Y(address, value) \
+{ \
+   ZP_IND_Y_ADDR(address); \
+   value = ZP_READ(address); \
+}
+
+#define ZP_IND_Y_BYTE(value) \
+{ \
+   ZP_IND_Y(btemp, value); \
+}  
+
+/*
+** For conditional jump relative instructions
+** (BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS)
+*/
+#define RELATIVE_JUMP(cond) \
+{ \
+   if (cond) \
+   { \
+      IMMEDIATE_BYTE(btemp); \
+      if (((int8) btemp + (uint8) PC) & 0xFF00) \
+         ADD_CYCLES(4); \
+      else \
+         ADD_CYCLES(3); \
+      PC += ((int8) btemp); \
+   } \
+   else \
+   { \
+      PC++; \
+      ADD_CYCLES(2); \
+   } \
+}
+
+/*
+** This is actually indexed indirect, but I call it
+** indirect X to avoid confusion
+*/
+#define INDIR_X_ADDR(address) \
+{ \
+   btemp = bank_readbyte(PC++) + X; \
+   address = zp_address(btemp); \
+}
+
+#define INDIR_X(address, value) \
+{ \
+   INDIR_X_ADDR(address); \
+   value = mem_read(address); \
+} 
+
+#define INDIR_X_BYTE(value) \
+{ \
+   INDIR_X(temp, value); \
+}
+
+/*
+** This is actually indirect indexed, but I call it
+** indirect y to avoid confusion
+*/
+#define INDIR_Y_ADDR(address) \
+{ \
+   IMMEDIATE_BYTE(btemp); \
+   address = (zp_address(btemp) + Y) & 0xFFFF; \
+   /* ???? */ \
+   CHECK_INDEX_OVERFLOW(address, Y); \
+}
+
+#define INDIR_Y(address, value) \
+{ \
+   INDIR_Y_ADDR(address); \
+   value = mem_read(address); \
+} 
+
+#define INDIR_Y_BYTE(value) \
+{ \
+   /*IMMEDIATE_BYTE(btemp); \
+   temp = zp_address(btemp) + Y; \
+   CHECK_INDEX_OVERFLOW(temp, Y); \
+   value = mem_read(temp);*/ \
+   INDIR_Y(temp, value); \
+}
+
+
+#define  JUMP(address)  PC = bank_readaddress((address))
+
+/*
+** Interrupt macros
+*/
+#define NMI() \
+{ \
+   PUSH(PC >> 8); \
+   PUSH(PC & 0xFF); \
+   CLEAR_FLAG(B_FLAG); \
+   PUSH(P); \
+   SET_FLAG(I_FLAG); \
+   JUMP(NMI_VECTOR); \
+   int_pending &= ~NMI_MASK; \
+   ADD_CYCLES(INT_CYCLES); \
+}
+
+#define IRQ() \
+{ \
+   PUSH(PC >> 8); \
+   PUSH(PC & 0xFF); \
+   CLEAR_FLAG(B_FLAG); \
+   PUSH(P); \
+   SET_FLAG(I_FLAG); \
+   JUMP(IRQ_VECTOR); \
+   int_pending &= ~IRQ_MASK; \
+   ADD_CYCLES(INT_CYCLES); \
+}
+
+/*
+** Instruction macros
+*/
+
+/* Warning! NES CPU has no decimal mode, so by default this does no BCD! */
+#ifdef NES6502_DECIMAL
+#define ADC(cycles, read_func) \
+{ \
+   read_func(data); \
+   if (P & D_FLAG) \
+   { \
+      temp = (A & 0x0F) + (data & 0x0F) + (P & C_FLAG); \
+      if (temp >= 10) \
+         temp = (temp - 10) | 0x10; \
+      temp += (A & 0xF0) + (data & 0xF0); \
+      TEST_AND_FLAG(0 == ((A + data + (P & C_FLAG)) & 0xFF), Z_FLAG); \
+      TEST_AND_FLAG(temp & 0x80, N_FLAG); \
+      TEST_AND_FLAG(((~(A ^ data)) & (A ^ temp) & 0x80), V_FLAG); \
+      if (temp > 0x9F) \
+         temp += 0x60; \
+      TEST_AND_FLAG(temp > 0xFF, C_FLAG); \
+      A = (uint8) temp; \
+   } \
+   else \
+   { \
+      temp = A + data + (P & C_FLAG); \
+      /* Set C on carry */ \
+      TEST_AND_FLAG(temp > 0xFF, C_FLAG); \
+      /* Set V on overflow */ \
+      TEST_AND_FLAG(((~(A ^ data)) & (A ^ temp) & 0x80), V_FLAG); \
+      A = (uint8) temp; \
+      SET_NZ_FLAGS(A); \
+   }\
+   ADD_CYCLES(cycles); \
+}
+#else
+#define ADC(cycles, read_func) \
+{ \
+   read_func(data); \
+   temp = A + data + (P & C_FLAG); \
+   /* Set C on carry */ \
+   TEST_AND_FLAG(temp > 0xFF, C_FLAG); \
+   /* Set V on overflow */ \
+   TEST_AND_FLAG(((~(A ^ data)) & (A ^ temp) & 0x80), V_FLAG); \
+   A = (uint8) temp; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+#endif /* NES6502_DECIMAL */
+
+/* undocumented */
+#define ANC(cycles, read_func) \
+{ \
+   read_func(data); \
+   A &= data; \
+   SET_NZ_FLAGS(A); \
+   TEST_AND_FLAG(P & N_FLAG, C_FLAG); \
+   ADD_CYCLES(cycles); \
+}
+
+#define AND(cycles, read_func) \
+{ \
+   read_func(data); \
+   A &= data; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define ANE(cycles, read_func) \
+{ \
+   read_func(data); \
+   A = (A | 0xEE) & X & data; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#ifdef NES6502_DECIMAL
+#define ARR(cycles, read_func) \
+{ \
+   read_func(data); \
+   data &= A; \
+   if (P & D_FLAG) \
+   { \
+      temp = (data >> 1) | ((P & C_FLAG) << 7); \
+      SET_NZ_FLAGS(temp); \
+      TEST_AND_FLAG((temp ^ data) & 0x40, V_FLAG); \
+      if (((data & 0x0F) + (data & 0x01)) > 5) \
+         temp = (temp & 0xF0) | ((temp + 0x6) & 0x0F); \
+      if (((data & 0xF0) + (data & 0x10)) > 0x50) \
+      { \
+         temp = (temp & 0x0F) | ((temp + 0x60) & 0xF0); \
+         SET_FLAG(C_FLAG); \
+      } \
+      else \
+         CLEAR_FLAG(C_FLAG); \
+      A = (uint8) temp; \
+   } \
+   else \
+   { \
+      A = (data >> 1) | ((P & C_FLAG) << 7); \
+      SET_NZ_FLAGS(A); \
+      TEST_AND_FLAG(A & 0x40, C_FLAG); \
+      TEST_AND_FLAG(((A >> 6) ^ (A >> 5)) & 1, V_FLAG); \
+   }\
+   ADD_CYCLES(cycles); \
+}
+#else
+#define ARR(cycles, read_func) \
+{ \
+   read_func(data); \
+   data &= A; \
+   A = (data >> 1) | ((P & C_FLAG) << 7); \
+   SET_NZ_FLAGS(A); \
+   TEST_AND_FLAG(A & 0x40, C_FLAG); \
+   TEST_AND_FLAG((A >> 6) ^ (A >> 5), V_FLAG); \
+   ADD_CYCLES(cycles); \
+}
+#endif /* NES6502_DECIMAL */
+
+#define ASL(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   TEST_AND_FLAG(data & 0x80, C_FLAG); \
+   data <<= 1; \
+   write_func(addr, data); \
+   SET_NZ_FLAGS(data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define ASL_A() \
+{ \
+   TEST_AND_FLAG(A & 0x80, C_FLAG); \
+   A <<= 1; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(2); \
+}
+
+/* undocumented */
+#define ASR(cycles, read_func) \
+{ \
+   read_func(data); \
+   data &= A; \
+   TEST_AND_FLAG(data & 0x01, C_FLAG); \
+   A = data >> 1; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define BCC() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_CLEAR(C_FLAG))); \
+}
+
+#define BCS() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_SET(C_FLAG))); \
+}
+
+#define BEQ() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_SET(Z_FLAG))); \
+}
+
+#define BIT(cycles, read_func) \
+{ \
+   read_func(data); \
+   TEST_AND_FLAG(0 == (data & A), Z_FLAG);\
+   CLEAR_FLAG(N_FLAG | V_FLAG); \
+   /* move bit 7/6 of data into N/V flags */ \
+   SET_FLAG(data & (N_FLAG | V_FLAG)); \
+   ADD_CYCLES(cycles); \
+}
+
+#define BMI() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_SET(N_FLAG))); \
+}
+
+#define BNE() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_CLEAR(Z_FLAG))); \
+}
+
+#define BPL() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_CLEAR(N_FLAG))); \
+}
+
+/* Software interrupt type thang */
+#define BRK() \
+{ \
+   PC++; \
+   PUSH(PC >> 8); \
+   PUSH(PC & 0xFF); \
+   SET_FLAG(B_FLAG); \
+   PUSH(P); \
+   SET_FLAG(I_FLAG); \
+   JUMP(IRQ_VECTOR); \
+   ADD_CYCLES(7); \
+}
+
+#define BVC() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_CLEAR(V_FLAG))); \
+}
+
+#define BVS() \
+{ \
+   RELATIVE_JUMP((IS_FLAG_SET(V_FLAG))); \
+}
+
+#define CLC() \
+{ \
+   CLEAR_FLAG(C_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+#define CLD() \
+{ \
+   CLEAR_FLAG(D_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+#define CLI() \
+{ \
+   CLEAR_FLAG(I_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+#define CLV() \
+{ \
+   CLEAR_FLAG(V_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+/* TODO: ick! */
+#define _COMPARE(reg, value) \
+{ \
+   temp = (reg) - (value); \
+   /* C is clear when data > A */ \
+   TEST_AND_FLAG(0 == (temp & 0x8000), C_FLAG); \
+   SET_NZ_FLAGS((uint8) temp); /* handles Z flag */ \
+}
+
+#define CMP(cycles, read_func) \
+{ \
+   read_func(data); \
+   _COMPARE(A, data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define CPX(cycles, read_func) \
+{ \
+   read_func(data); \
+   _COMPARE(X, data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define CPY(cycles, read_func) \
+{ \
+   read_func(data); \
+   _COMPARE(Y, data); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define DCP(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   data--; \
+   write_func(addr, data); \
+   CMP(cycles, NO_READ); \
+}
+
+#define DEC(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   data--; \
+   write_func(addr, data); \
+   SET_NZ_FLAGS(data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define DEX() \
+{ \
+   X--; \
+   SET_NZ_FLAGS(X); \
+   ADD_CYCLES(2); \
+}
+
+#define DEY() \
+{ \
+   Y--; \
+   SET_NZ_FLAGS(Y); \
+   ADD_CYCLES(2); \
+}
+
+/* undocumented (double-NOP) */
+#define DOP(cycles) \
+{ \
+   PC++; \
+   ADD_CYCLES(cycles); \
+}
+
+#define EOR(cycles, read_func) \
+{ \
+   read_func(data); \
+   A ^= data; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define INC(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   data++; \
+   write_func(addr, data); \
+   SET_NZ_FLAGS(data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define INX() \
+{ \
+   X++; \
+   SET_NZ_FLAGS(X); \
+   ADD_CYCLES(2); \
+}
+
+#define INY() \
+{ \
+   Y++; \
+   SET_NZ_FLAGS(Y); \
+   ADD_CYCLES(2); \
+}
+
+/* undocumented */
+#define ISB(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   data++; \
+   write_func(addr, data); \
+   SBC(cycles, NO_READ); \
+}
+
+#ifdef NES6502_TESTOPS
+#define JAM() \
+{ \
+   cpu_Jam(); \
+}
+#elif defined(NSF_PLAYER)
+#define JAM() \
+{ \
+}
+#else
+#define JAM() \
+{ \
+   char jambuf[20]; \
+   sprintf(jambuf, "JAM: PC=$%04X", PC); \
+   ASSERT_MSG(jambuf); \
+   ADD_CYCLES(2); \
+}
+#endif /* NES6502_TESTOPS */
+
+#define JMP_INDIRECT() \
+{ \
+   temp = bank_readaddress(PC); \
+   /* bug in crossing page boundaries */ \
+   if (0xFF == (uint8) temp) \
+      PC = (bank_readbyte(temp & ~0xFF) << 8) | bank_readbyte(temp); \
+   else \
+      JUMP(temp); \
+   ADD_CYCLES(5); \
+}
+
+#define JMP_ABSOLUTE() \
+{ \
+   JUMP(PC); \
+   ADD_CYCLES(3); \
+}
+
+#define JSR() \
+{ \
+   PC++; \
+   PUSH(PC >> 8); \
+   PUSH(PC & 0xFF); \
+   JUMP(PC - 1); \
+   ADD_CYCLES(6); \
+}
+
+/* undocumented */
+#define LAS(cycles, read_func) \
+{ \
+   read_func(data); \
+   A = X = S = (S & data); \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define LAX(cycles, read_func) \
+{ \
+   read_func(A); \
+   X = A; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define LDA(cycles, read_func) \
+{ \
+   read_func(A); \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define LDX(cycles, read_func) \
+{ \
+   read_func(X); \
+   SET_NZ_FLAGS(X);\
+   ADD_CYCLES(cycles); \
+}
+
+#define LDY(cycles, read_func) \
+{ \
+   read_func(Y); \
+   SET_NZ_FLAGS(Y);\
+   ADD_CYCLES(cycles); \
+}
+
+#define LSR(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   TEST_AND_FLAG(data & 0x01, C_FLAG); \
+   data >>= 1; \
+   write_func(addr, data); \
+   SET_NZ_FLAGS(data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define LSR_A() \
+{ \
+   TEST_AND_FLAG(A & 0x01, C_FLAG); \
+   A >>= 1; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(2); \
+}
+
+/* undocumented */
+#define LXA(cycles, read_func) \
+{ \
+   read_func(data); \
+   A = X = ((A | 0xEE) & data); \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define NOP() \
+{ \
+   ADD_CYCLES(2); \
+}
+
+#define ORA(cycles, read_func) \
+{ \
+   read_func(data); \
+   A |= data; \
+   SET_NZ_FLAGS(A);\
+   ADD_CYCLES(cycles); \
+}
+
+#define PHA() \
+{ \
+   PUSH(A); \
+   ADD_CYCLES(3); \
+}
+
+#define PHP() \
+{ \
+   /* B flag is pushed on stack as well */ \
+   PUSH((P | B_FLAG)); \
+   ADD_CYCLES(3); \
+}
+
+#define PLA() \
+{ \
+   A = PULL(); \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(4); \
+}
+
+#define PLP() \
+{ \
+   P = PULL(); \
+   SET_FLAG(R_FLAG); /* ensure reserved flag is set */ \
+   ADD_CYCLES(4); \
+}
+
+/* undocumented */
+#define RLA(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   if (P & C_FLAG) \
+   { \
+      TEST_AND_FLAG(data & 0x80, C_FLAG); \
+      data = (data << 1) | 1; \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(data & 0x80, C_FLAG); \
+      data <<= 1; \
+   } \
+   write_func(addr, data); \
+   A &= data; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+/* 9-bit rotation (carry flag used for rollover) */
+#define ROL(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   if (P & C_FLAG) \
+   { \
+      TEST_AND_FLAG(data & 0x80, C_FLAG); \
+      data = (data << 1) | 1; \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(data & 0x80, C_FLAG); \
+      data <<= 1; \
+   } \
+   write_func(addr, data); \
+   SET_NZ_FLAGS(data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define ROL_A() \
+{ \
+   if (P & C_FLAG) \
+   { \
+      TEST_AND_FLAG(A & 0x80, C_FLAG); \
+      A = (A << 1) | 1; \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(A & 0x80, C_FLAG); \
+      A <<= 1; \
+   } \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(2); \
+}
+
+#define ROR(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   if (P & C_FLAG) \
+   { \
+      TEST_AND_FLAG(data & 1, C_FLAG); \
+      data = (data >> 1) | 0x80; \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(data & 1, C_FLAG); \
+      data >>= 1; \
+   } \
+   write_func(addr, data); \
+   SET_NZ_FLAGS(data); \
+   ADD_CYCLES(cycles); \
+}
+
+#define ROR_A() \
+{ \
+   if (P & C_FLAG) \
+   { \
+      TEST_AND_FLAG(A & 1, C_FLAG); \
+      A = (A >> 1) | 0x80; \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(A & 1, C_FLAG); \
+      A >>= 1; \
+   } \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(2); \
+}
+
+/* undocumented */
+#define RRA(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   if (P & C_FLAG) \
+   { \
+      TEST_AND_FLAG(data & 1, C_FLAG); \
+      data = (data >> 1) | 0x80; \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(data & 1, C_FLAG); \
+      data >>= 1; \
+   } \
+   write_func(addr, data); \
+   ADC(cycles, NO_READ); \
+}
+
+#define RTI() \
+{ \
+   P = PULL(); \
+   SET_FLAG(R_FLAG); /* ensure reserved flag is set */ \
+   PC = PULL(); \
+   PC |= PULL() << 8; \
+   ADD_CYCLES(6); \
+}
+
+#define RTS() \
+{ \
+   PC = PULL(); \
+   PC = (PC | (PULL() << 8)) + 1; \
+   ADD_CYCLES(6); \
+}
+
+/* undocumented */
+#define SAX(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   data = A & X; \
+   write_func(addr, data); \
+   ADD_CYCLES(cycles); \
+}
+
+/* Warning! NES CPU has no decimal mode, so by default this does no BCD! */
+#ifdef NES6502_DECIMAL
+#define SBC(cycles, read_func) \
+{ \
+   read_func(data); \
+   /* NOT(C) is considered borrow */ \
+   temp = A - data - ((P & C_FLAG) ^ C_FLAG); \
+   if (P & D_FLAG) \
+   { \
+      uint8 al, ah; \
+      al = (A & 0x0F) - (data & 0x0F) - ((P & C_FLAG) ^ C_FLAG); \
+      ah = (A >> 4) - (data >> 4); \
+      if (al & 0x10) \
+      { \
+         al -= 6; \
+         ah--; \
+      } \
+      if (ah & 0x10) \
+         ah -= 6; \
+      TEST_AND_FLAG(temp < 0x100, C_FLAG); \
+      TEST_AND_FLAG(((A ^ temp) & 0x80) && ((A ^ data) & 0x80), V_FLAG); \
+      SET_NZ_FLAGS(temp & 0xFF); \
+      A = (ah << 4) | (al & 0x0F); \
+   } \
+   else \
+   { \
+      TEST_AND_FLAG(((A ^ temp) & 0x80) && ((A ^ data) & 0x80), V_FLAG); \
+      TEST_AND_FLAG(temp < 0x100, C_FLAG); \
+      A = (uint8) temp; \
+      SET_NZ_FLAGS(A & 0xFF); \
+   } \
+   ADD_CYCLES(cycles); \
+}
+#else
+#define SBC(cycles, read_func) \
+{ \
+   read_func(data); \
+   temp = A - data - ((P & C_FLAG) ^ C_FLAG); \
+   TEST_AND_FLAG(((A ^ data) & (A ^ temp) & 0x80), V_FLAG); \
+   TEST_AND_FLAG(temp < 0x100, C_FLAG); \
+   A = (uint8) temp; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+#endif /* NES6502_DECIMAL */
+
+/* undocumented */
+#define SBX(cycles, read_func) \
+{ \
+   read_func(data); \
+   temp = (A & X) - data; \
+   TEST_AND_FLAG(temp < 0x100, C_FLAG); \
+   X = temp & 0xFF; \
+   SET_NZ_FLAGS(X); \
+   ADD_CYCLES(cycles); \
+}
+
+#define SEC() \
+{ \
+   SET_FLAG(C_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+#define SED() \
+{ \
+   SET_FLAG(D_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+#define SEI() \
+{ \
+   SET_FLAG(I_FLAG); \
+   ADD_CYCLES(2); \
+}
+
+/* undocumented */
+#define SHA(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   data = A & X & ((uint8) ((addr >> 8) + 1)); \
+   write_func(addr, data); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define SHS(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   S = A & X; \
+   data = S & ((uint8) ((addr >> 8) + 1)); \
+   write_func(addr, data); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define SHX(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   data = X & ((uint8) ((addr >> 8) + 1)); \
+   write_func(addr, data); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define SHY(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   data = Y & ((uint8) ((addr >> 8 ) + 1)); \
+   write_func(addr, data); \
+   ADD_CYCLES(cycles); \
+}
+
+/* undocumented */
+#define SLO(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   TEST_AND_FLAG(data & 0x80, C_FLAG); \
+   data <<= 1; \
+   write_func(addr, data); \
+   A |= data; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+/* unoffical */
+#define SRE(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr, data); \
+   TEST_AND_FLAG(data & 1, C_FLAG); \
+   data >>= 1; \
+   write_func(addr, data); \
+   A ^= data; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define STA(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   write_func(addr, A); \
+   ADD_CYCLES(cycles); \
+}
+
+#define STX(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   write_func(addr, X); \
+   ADD_CYCLES(cycles); \
+}
+
+#define STY(cycles, read_func, write_func, addr) \
+{ \
+   read_func(addr); \
+   write_func(addr, Y); \
+   ADD_CYCLES(cycles); \
+}
+
+#define TAX() \
+{ \
+   X = A; \
+   SET_NZ_FLAGS(X);\
+   ADD_CYCLES(2); \
+}
+
+#define TAY() \
+{ \
+   Y = A; \
+   SET_NZ_FLAGS(Y);\
+   ADD_CYCLES(2); \
+}
+
+/* undocumented (triple-NOP) */
+#define TOP() \
+{ \
+   PC += 2; \
+   ADD_CYCLES(4); \
+}
+
+#define TSX() \
+{ \
+   X = S; \
+   SET_NZ_FLAGS(X);\
+   ADD_CYCLES(2); \
+}
+
+#define TXA() \
+{ \
+   A = X; \
+   SET_NZ_FLAGS(A);\
+   ADD_CYCLES(2); \
+}
+
+#define TXS() \
+{ \
+   S = X; \
+   ADD_CYCLES(2); \
+}
+
+#define TYA() \
+{ \
+   A = Y; \
+   SET_NZ_FLAGS(A); \
+   ADD_CYCLES(2); \
+}
+
+
+/*
+** Stack and data fetching macros
+*/
+
+/* Set/clear/test bits in the flags register */
+#define  SET_FLAG(mask)          P |= (mask)
+#define  CLEAR_FLAG(mask)        P &= ~(mask)
+#define  IS_FLAG_SET(mask)       (P & (mask))
+#define  IS_FLAG_CLEAR(mask)     (0 == IS_FLAG_SET((mask)))
+
+#define  TEST_AND_FLAG(test, mask) \
+{ \
+   if ((test)) \
+      SET_FLAG((mask)); \
+   else \
+      CLEAR_FLAG((mask)); \
+}
+
+
+/*
+** flag register helper macros
+*/
+
+/* register push/pull */
+#define  PUSH(value)             stack_page[S--] = (uint8) (value)
+#define  PULL()                  stack_page[++S]
+
+/* Sets the Z and N flags based on given data, taken from precomputed table */
+#define  SET_NZ_FLAGS(value)     P &= ~(N_FLAG | Z_FLAG); \
+                                 P |= flag_table[(value)]
+
+#define  GET_GLOBAL_REGS() \
+{ \
+   PC = reg_PC; \
+   A = reg_A; \
+   X = reg_X; \
+   Y = reg_Y; \
+   P = reg_P; \
+   S = reg_S; \
+}
+
+#define SET_LOCAL_REGS() \
+{ \
+   reg_PC = PC; \
+   reg_A = A; \
+   reg_X = X; \
+   reg_Y = Y; \
+   reg_P = P; \
+   reg_S = S; \
+}
+
+
+/* static data */
+static nes6502_memread *pmem_read, *pmr;
+static nes6502_memwrite *pmem_write, *pmw;
+
+/* lookup table for N/Z flags */
+static uint8 flag_table[256];
+
+/* internal critical CPU vars */
+static uint32 reg_PC;
+static uint8 reg_A, reg_P, reg_X, reg_Y, reg_S;
+static uint8 int_pending;
+static int dma_cycles;
+
+/* execution cycle count (can be reset by user) */
+static uint32 total_cycles = 0;
+
+/* memory region pointers */
+static uint8 *nes6502_banks[NES6502_NUMBANKS];
+static uint8 *ram = NULL;
+static uint8 *stack_page = NULL;
+
+
+/*
+** Zero-page helper macros
+*/
+
+#define  ZP_READ(addr)           ram[(addr)]
+#define  ZP_WRITE(addr, value)   ram[(addr)] = (uint8) (value)
+
+
+INLINE uint8 bank_readbyte(register uint32 address)
+{
+   ASSERT(nes6502_banks[address >> NES6502_BANKSHIFT]);
+   return nes6502_banks[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK];
+}
+
+INLINE void bank_writebyte(register uint32 address, register uint8 value)
+{
+   ASSERT(nes6502_banks[address >> NES6502_BANKSHIFT]);
+   nes6502_banks[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK] = value;
+}
+
+INLINE uint32 zp_address(register uint8 address)
+{
+#ifdef HOST_LITTLE_ENDIAN
+   /* TODO: this fails if src address is $xFFF */
+   /* TODO: this fails if host architecture doesn't support byte alignment */
+   return (uint32) (*(uint16 *)(ram + address));
+#else
+#ifdef TARGET_CPU_PPC
+   return __lhbrx(ram, address);
+#else
+   uint32 x = (uint32) *(uint16 *)(ram + address);
+   return (x << 8) | (x >> 8);
+#endif /* TARGET_CPU_PPC */
+#endif /* HOST_LITTLE_ENDIAN */
+}
+
+INLINE uint32 bank_readaddress(register uint32 address)
+{
+#ifdef HOST_LITTLE_ENDIAN
+   /* TODO: this fails if src address is $xFFF */
+   /* TODO: this fails if host architecture doesn't support byte alignment */
+   return (uint32) (*(uint16 *)(nes6502_banks[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK)));
+#else
+#ifdef TARGET_CPU_PPC
+   return __lhbrx(nes6502_banks[address >> NES6502_BANKSHIFT], address & NES6502_BANKMASK);
+#else
+   uint32 x = (uint32) *(uint16 *)(nes6502_banks[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK));
+   return (x << 8) | (x >> 8);
+#endif /* TARGET_CPU_PPC */
+#endif /* HOST_LITTLE_ENDIAN */
+}
+
+/* read a byte of 6502 memory */
+static uint8 mem_read(uint32 address)
+{
+   /* TODO: following cases are N2A03-specific */
+   /* RAM */
+   if (address < 0x800)
+      return ram[address];
+   /* always paged memory */
+   /*   else if (address >= 0x6000) */
+   else if (address >= 0x8000)
+      return bank_readbyte(address);
+   /* check memory range handlers */
+   else
+   {
+      for (pmr = pmem_read; pmr->min_range != 0xFFFFFFFF; pmr++)
+      {
+         if ((address >= pmr->min_range) && (address <= pmr->max_range))
+            return pmr->read_func(address);
+      }
+   }
+
+   /* return paged memory */
+   return bank_readbyte(address);
+}
+
+/* write a byte of data to 6502 memory */
+static void mem_write(uint32 address, uint8 value)
+{
+   /* RAM */
+   if (address < 0x800)
+   {
+      ram[address] = value;
+      return;
+   }
+   /* check memory range handlers */
+   else
+   {
+      for (pmw = pmem_write; pmw->min_range != 0xFFFFFFFF; pmw++)
+      {
+         if ((address >= pmw->min_range) && (address <= pmw->max_range))
+         {
+            pmw->write_func(address, value);
+            return;
+         }
+      }
+   }
+
+   /* write to paged memory */
+   bank_writebyte(address, value);
+}
+
+/* set the current context */
+void nes6502_setcontext(nes6502_context *cpu)
+{
+   int loop;
+
+   ASSERT(cpu);
+   
+   /* Set the page pointers */
+   for (loop = 0; loop < NES6502_NUMBANKS; loop++)
+      nes6502_banks[loop] = cpu->mem_page[loop];
+
+   ram = nes6502_banks[0];  /* quicker zero-page/RAM references */
+   stack_page = ram + STACK_OFFSET;
+
+   pmem_read = cpu->read_handler;
+   pmem_write = cpu->write_handler;
+
+   reg_PC = cpu->pc_reg;
+   reg_A = cpu->a_reg;
+   reg_P = cpu->p_reg;
+   reg_X = cpu->x_reg;
+   reg_Y = cpu->y_reg;
+   reg_S = cpu->s_reg;
+   int_pending = cpu->int_pending;
+   dma_cycles = cpu->dma_cycles;
+}
+
+/* get the current context */
+void nes6502_getcontext(nes6502_context *cpu)
+{
+   int loop;
+
+   /* Set the page pointers */
+   for (loop = 0; loop < NES6502_NUMBANKS; loop++)
+      cpu->mem_page[loop] = nes6502_banks[loop];
+
+   cpu->read_handler = pmem_read;
+   cpu->write_handler = pmem_write;
+
+   cpu->pc_reg = reg_PC;
+   cpu->a_reg = reg_A;
+   cpu->p_reg = reg_P;
+   cpu->x_reg = reg_X;
+   cpu->y_reg = reg_Y;
+   cpu->s_reg = reg_S;
+   cpu->int_pending = int_pending;
+   cpu->dma_cycles = dma_cycles;
+}
+
+/* DMA a byte of data from ROM */
+uint8 nes6502_getbyte(uint32 address)
+{
+   return bank_readbyte(address);
+}
+
+/* get number of elapsed cycles */
+uint32 nes6502_getcycles(boolean reset_flag)
+{
+   uint32 cycles = total_cycles;
+
+   if (reset_flag)
+      total_cycles = 0;
+
+   return cycles;
+}
+
+
+/* Execute instructions until count expires
+**
+** Returns the number of cycles *actually* executed
+** (note that this can be from 0-6 cycles more than you wanted)
+*/
+int nes6502_execute(int remaining_cycles)
+{
+   int instruction_cycles, old_cycles = total_cycles;
+   uint32 temp, addr; /* for macros */
+   uint32 PC;
+   uint8 A, X, Y, P, S;
+   uint8 opcode, data;
+   uint8 btemp, baddr; /* for macros */
+
+   GET_GLOBAL_REGS();
+
+   /* Continue until we run out of cycles */
+   while (remaining_cycles > 0)
+   {
+      instruction_cycles = 0;
+
+      /* check for DMA cycle burning */
+      if (dma_cycles)
+      {
+         if (remaining_cycles <= dma_cycles)
+         {
+            dma_cycles -= remaining_cycles;
+            total_cycles += remaining_cycles;
+            goto _execute_done;
+         }
+         else
+         {
+            remaining_cycles -= dma_cycles;
+            total_cycles += dma_cycles;
+            dma_cycles = 0;
+         }
+      }
+
+      if (int_pending)
+      {
+         /* NMI has highest priority */
+         if (int_pending & NMI_MASK)
+         {
+            NMI();
+         }
+         /* IRQ has lowest priority */
+         else /* if (int_pending & IRQ_MASK) */
+         {
+            if (IS_FLAG_CLEAR(I_FLAG))
+               IRQ();
+         }
+      }
+
+      /* Fetch instruction */
+      /* nes6502_disasm(PC, P, A, X, Y, S); */
+
+      opcode = bank_readbyte(PC++);
+
+      /* Execute instruction */
+      switch (opcode)
+      {
+      case 0x00:  /* BRK */
+         BRK();
+         break;
+
+      case 0x01:  /* ORA ($nn,X) */
+         ORA(6, INDIR_X_BYTE);
+         break;
+
+      /* JAM */
+      case 0x02:  /* JAM */
+      case 0x12:  /* JAM */
+      case 0x22:  /* JAM */
+      case 0x32:  /* JAM */
+      case 0x42:  /* JAM */
+      case 0x52:  /* JAM */
+      case 0x62:  /* JAM */
+      case 0x72:  /* JAM */
+      case 0x92:  /* JAM */
+      case 0xB2:  /* JAM */
+      case 0xD2:  /* JAM */
+      case 0xF2:  /* JAM */
+         JAM();
+         /* kill switch for CPU emulation */
+         goto _execute_done;
+
+      case 0x03:  /* SLO ($nn,X) */
+         SLO(8, INDIR_X, mem_write, addr);
+         break;
+
+      case 0x04:  /* NOP $nn */
+      case 0x44:  /* NOP $nn */
+      case 0x64:  /* NOP $nn */
+         DOP(3);
+         break;
+
+      case 0x05:  /* ORA $nn */
+         ORA(3, ZERO_PAGE_BYTE); 
+         break;
+
+      case 0x06:  /* ASL $nn */
+         ASL(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x07:  /* SLO $nn */
+         SLO(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x08:  /* PHP */
+         PHP(); 
+         break;
+
+      case 0x09:  /* ORA #$nn */
+         ORA(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x0A:  /* ASL A */
+         ASL_A();
+         break;
+
+      case 0x0B:  /* ANC #$nn */
+         ANC(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x0C:  /* NOP $nnnn */
+         TOP(); 
+         break;
+
+      case 0x0D:  /* ORA $nnnn */
+         ORA(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0x0E:  /* ASL $nnnn */
+         ASL(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x0F:  /* SLO $nnnn */
+         SLO(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x10:  /* BPL $nnnn */
+         BPL();
+         break;
+
+      case 0x11:  /* ORA ($nn),Y */
+         ORA(5, INDIR_Y_BYTE);
+         break;
+      
+      case 0x13:  /* SLO ($nn),Y */
+         SLO(8, INDIR_Y, mem_write, addr);
+         break;
+
+      case 0x14:  /* NOP $nn,X */
+      case 0x34:  /* NOP */
+      case 0x54:  /* NOP $nn,X */
+      case 0x74:  /* NOP $nn,X */
+      case 0xD4:  /* NOP $nn,X */
+      case 0xF4:  /* NOP ($nn,X) */
+         DOP(4);
+         break;
+
+      case 0x15:  /* ORA $nn,X */
+         ORA(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0x16:  /* ASL $nn,X */
+         ASL(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x17:  /* SLO $nn,X */
+         SLO(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x18:  /* CLC */
+         CLC();
+         break;
+
+      case 0x19:  /* ORA $nnnn,Y */
+         ORA(4, ABS_IND_Y_BYTE);
+         break;
+      
+      case 0x1A:  /* NOP */
+      case 0x3A:  /* NOP */
+      case 0x5A:  /* NOP */
+      case 0x7A:  /* NOP */
+      case 0xDA:  /* NOP */
+      case 0xFA:  /* NOP */
+         NOP();
+         break;
+
+      case 0x1B:  /* SLO $nnnn,Y */
+         SLO(7, ABS_IND_Y, mem_write, addr);
+         break;
+
+      case 0x1C:  /* NOP $nnnn,X */
+      case 0x3C:  /* NOP $nnnn,X */
+      case 0x5C:  /* NOP $nnnn,X */
+      case 0x7C:  /* NOP $nnnn,X */
+      case 0xDC:  /* NOP $nnnn,X */
+      case 0xFC:  /* NOP $nnnn,X */
+         TOP();
+         break;
+
+      case 0x1D:  /* ORA $nnnn,X */
+         ORA(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0x1E:  /* ASL $nnnn,X */
+         ASL(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x1F:  /* SLO $nnnn,X */
+         SLO(7, ABS_IND_X, mem_write, addr);
+         break;
+      
+      case 0x20:  /* JSR $nnnn */
+         JSR();
+         break;
+
+      case 0x21:  /* AND ($nn,X) */
+         AND(6, INDIR_X_BYTE);
+         break;
+
+      case 0x23:  /* RLA ($nn,X) */
+         RLA(8, INDIR_X, mem_write, addr);
+         break;
+
+      case 0x24:  /* BIT $nn */
+         BIT(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0x25:  /* AND $nn */
+         AND(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0x26:  /* ROL $nn */
+         ROL(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x27:  /* RLA $nn */
+         RLA(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x28:  /* PLP */
+         PLP();
+         break;
+
+      case 0x29:  /* AND #$nn */
+         AND(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x2A:  /* ROL A */
+         ROL_A();
+         break;
+
+      case 0x2B:  /* ANC #$nn */
+         ANC(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x2C:  /* BIT $nnnn */
+         BIT(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0x2D:  /* AND $nnnn */
+         AND(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0x2E:  /* ROL $nnnn */
+         ROL(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x2F:  /* RLA $nnnn */
+         RLA(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x30:  /* BMI $nnnn */
+         BMI();
+         break;
+
+      case 0x31:  /* AND ($nn),Y */
+         AND(5, INDIR_Y_BYTE);
+         break;
+
+      case 0x33:  /* RLA ($nn),Y */
+         RLA(8, INDIR_Y, mem_write, addr);
+         break;
+
+      case 0x35:  /* AND $nn,X */
+         AND(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0x36:  /* ROL $nn,X */
+         ROL(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x37:  /* RLA $nn,X */
+         RLA(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x38:  /* SEC */
+         SEC();
+         break;
+
+      case 0x39:  /* AND $nnnn,Y */
+         AND(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0x3B:  /* RLA $nnnn,Y */
+         RLA(7, ABS_IND_Y, mem_write, addr);
+         break;
+
+      case 0x3D:  /* AND $nnnn,X */
+         AND(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0x3E:  /* ROL $nnnn,X */
+         ROL(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x3F:  /* RLA $nnnn,X */
+         RLA(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x40:  /* RTI */
+         RTI();
+         break;
+
+      case 0x41:  /* EOR ($nn,X) */
+         EOR(6, INDIR_X_BYTE);
+         break;
+
+      case 0x43:  /* SRE ($nn,X) */
+         SRE(8, INDIR_X, mem_write, addr);
+         break;
+
+      case 0x45:  /* EOR $nn */
+         EOR(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0x46:  /* LSR $nn */
+         LSR(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x47:  /* SRE $nn */
+         SRE(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x48:  /* PHA */
+         PHA();
+         break;
+
+      case 0x49:  /* EOR #$nn */
+         EOR(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x4A:  /* LSR A */
+         LSR_A();
+         break;
+
+      case 0x4B:  /* ASR #$nn */
+         ASR(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x4C:  /* JMP $nnnn */
+         JMP_ABSOLUTE();
+         break;
+
+      case 0x4D:  /* EOR $nnnn */
+         EOR(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0x4E:  /* LSR $nnnn */
+         LSR(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x4F:  /* SRE $nnnn */
+         SRE(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x50:  /* BVC $nnnn */
+         BVC();
+         break;
+
+      case 0x51:  /* EOR ($nn),Y */
+         EOR(5, INDIR_Y_BYTE);
+         break;
+
+      case 0x53:  /* SRE ($nn),Y */
+         SRE(8, INDIR_Y, mem_write, addr);
+         break;
+
+      case 0x55:  /* EOR $nn,X */
+         EOR(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0x56:  /* LSR $nn,X */
+         LSR(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x57:  /* SRE $nn,X */
+         SRE(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x58:  /* CLI */
+         CLI();
+         break;
+
+      case 0x59:  /* EOR $nnnn,Y */
+         EOR(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0x5B:  /* SRE $nnnn,Y */
+         SRE(7, ABS_IND_Y, mem_write, addr);
+         break;
+
+      case 0x5D:  /* EOR $nnnn,X */
+         EOR(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0x5E:  /* LSR $nnnn,X */
+         LSR(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x5F:  /* SRE $nnnn,X */
+         SRE(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x60:  /* RTS */
+         RTS();
+         break;
+
+      case 0x61:  /* ADC ($nn,X) */
+         ADC(6, INDIR_X_BYTE);
+         break;
+
+      case 0x63:  /* RRA ($nn,X) */
+         RRA(8, INDIR_X, mem_write, addr);
+         break;
+
+      case 0x65:  /* ADC $nn */
+         ADC(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0x66:  /* ROR $nn */
+         ROR(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x67:  /* RRA $nn */
+         RRA(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0x68:  /* PLA */
+         PLA();
+         break;
+
+      case 0x69:  /* ADC #$nn */
+         ADC(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x6A:  /* ROR A */
+         ROR_A();
+         break;
+
+      case 0x6B:  /* ARR #$nn */
+         ARR(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x6C:  /* JMP ($nnnn) */
+         JMP_INDIRECT();
+         break;
+
+      case 0x6D:  /* ADC $nnnn */
+         ADC(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0x6E:  /* ROR $nnnn */
+         ROR(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x6F:  /* RRA $nnnn */
+         RRA(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0x70:  /* BVS $nnnn */
+         BVS();
+         break;
+
+      case 0x71:  /* ADC ($nn),Y */
+         ADC(5, INDIR_Y_BYTE);
+         break;
+
+      case 0x73:  /* RRA ($nn),Y */
+         RRA(8, INDIR_Y, mem_write, addr);
+         break;
+
+      case 0x75:  /* ADC $nn,X */
+         ADC(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0x76:  /* ROR $nn,X */
+         ROR(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x77:  /* RRA $nn,X */
+         RRA(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0x78:  /* SEI */
+         SEI();
+         break;
+
+      case 0x79:  /* ADC $nnnn,Y */
+         ADC(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0x7B:  /* RRA $nnnn,Y */
+         RRA(7, ABS_IND_Y, mem_write, addr);
+         break;
+
+      case 0x7D:  /* ADC $nnnn,X */
+         ADC(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0x7E:  /* ROR $nnnn,X */
+         ROR(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x7F:  /* RRA $nnnn,X */
+         RRA(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0x80:  /* NOP #$nn */
+      case 0x82:  /* NOP #$nn */
+      case 0x89:  /* NOP #$nn */
+      case 0xC2:  /* NOP #$nn */
+      case 0xE2:  /* NOP #$nn */
+         DOP(2);
+         break;
+
+      case 0x81:  /* STA ($nn,X) */
+         STA(6, INDIR_X_ADDR, mem_write, addr);
+         break;
+
+      case 0x83:  /* SAX ($nn,X) */
+         SAX(6, INDIR_X_ADDR, mem_write, addr);
+         break;
+
+      case 0x84:  /* STY $nn */
+         STY(3, ZERO_PAGE_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x85:  /* STA $nn */
+         STA(3, ZERO_PAGE_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x86:  /* STX $nn */
+         STX(3, ZERO_PAGE_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x87:  /* SAX $nn */
+         SAX(3, ZERO_PAGE_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x88:  /* DEY */
+         DEY();
+         break;
+
+      case 0x8A:  /* TXA */
+         TXA();
+         break;
+
+      case 0x8B:  /* ANE #$nn */
+         ANE(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0x8C:  /* STY $nnnn */
+         STY(4, ABSOLUTE_ADDR, mem_write, addr);
+         break;
+
+      case 0x8D:  /* STA $nnnn */
+         STA(4, ABSOLUTE_ADDR, mem_write, addr);
+         break;
+
+      case 0x8E:  /* STX $nnnn */
+         STX(4, ABSOLUTE_ADDR, mem_write, addr);
+         break;
+      
+      case 0x8F:  /* SAX $nnnn */
+         SAX(4, ABSOLUTE_ADDR, mem_write, addr);
+         break;
+
+      case 0x90:  /* BCC $nnnn */
+         BCC();
+         break;
+
+      case 0x91:  /* STA ($nn),Y */
+         STA(6, INDIR_Y_ADDR, mem_write, addr);
+         break;
+
+      case 0x93:  /* SHA ($nn),Y */
+         SHA(6, INDIR_Y_ADDR, mem_write, addr);
+         break;
+
+      case 0x94:  /* STY $nn,X */
+         STY(4, ZP_IND_X_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x95:  /* STA $nn,X */
+         STA(4, ZP_IND_X_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x96:  /* STX $nn,Y */
+         STX(4, ZP_IND_Y_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x97:  /* SAX $nn,Y */
+         SAX(4, ZP_IND_Y_ADDR, ZP_WRITE, baddr);
+         break;
+
+      case 0x98:  /* TYA */
+         TYA();
+         break;
+
+      case 0x99:  /* STA $nnnn,Y */
+         STA(5, ABS_IND_Y_ADDR, mem_write, addr);
+         break;
+
+      case 0x9A:  /* TXS */
+         TXS();
+         break;
+
+      case 0x9B:  /* SHS $nnnn,Y */
+         SHS(5, ABS_IND_Y_ADDR, mem_write, addr);
+         break;
+
+      case 0x9C:  /* SHY $nnnn,X */
+         SHY(5, ABS_IND_X_ADDR, mem_write, addr);
+         break;
+
+      case 0x9D:  /* STA $nnnn,X */
+         STA(5, ABS_IND_X_ADDR, mem_write, addr);
+         break;
+
+      case 0x9E:  /* SHX $nnnn,Y */
+         SHX(5, ABS_IND_Y_ADDR, mem_write, addr);
+         break;
+
+      case 0x9F:  /* SHA $nnnn,Y */
+         SHA(5, ABS_IND_Y_ADDR, mem_write, addr);
+         break;
+      
+      case 0xA0:  /* LDY #$nn */
+         LDY(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xA1:  /* LDA ($nn,X) */
+         LDA(6, INDIR_X_BYTE);
+         break;
+
+      case 0xA2:  /* LDX #$nn */
+         LDX(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xA3:  /* LAX ($nn,X) */
+         LAX(6, INDIR_X_BYTE);
+         break;
+
+      case 0xA4:  /* LDY $nn */
+         LDY(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xA5:  /* LDA $nn */
+         LDA(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xA6:  /* LDX $nn */
+         LDX(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xA7:  /* LAX $nn */
+         LAX(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xA8:  /* TAY */
+         TAY();
+         break;
+
+      case 0xA9:  /* LDA #$nn */
+         LDA(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xAA:  /* TAX */
+         TAX();
+         break;
+
+      case 0xAB:  /* LXA #$nn */
+         LXA(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xAC:  /* LDY $nnnn */
+         LDY(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xAD:  /* LDA $nnnn */
+         LDA(4, ABSOLUTE_BYTE);
+         break;
+      
+      case 0xAE:  /* LDX $nnnn */
+         LDX(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xAF:  /* LAX $nnnn */
+         LAX(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xB0:  /* BCS $nnnn */
+         BCS();
+         break;
+
+      case 0xB1:  /* LDA ($nn),Y */
+         LDA(5, INDIR_Y_BYTE);
+         break;
+
+      case 0xB3:  /* LAX ($nn),Y */
+         LAX(5, INDIR_Y_BYTE);
+         break;
+
+      case 0xB4:  /* LDY $nn,X */
+         LDY(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0xB5:  /* LDA $nn,X */
+         LDA(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0xB6:  /* LDX $nn,Y */
+         LDX(4, ZP_IND_Y_BYTE);
+         break;
+
+      case 0xB7:  /* LAX $nn,Y */
+         LAX(4, ZP_IND_Y_BYTE);
+         break;
+
+      case 0xB8:  /* CLV */
+         CLV();
+         break;
+
+      case 0xB9:  /* LDA $nnnn,Y */
+         LDA(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0xBA:  /* TSX */
+         TSX();
+         break;
+
+      case 0xBB:  /* LAS $nnnn,Y */
+         LAS(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0xBC:  /* LDY $nnnn,X */
+         LDY(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0xBD:  /* LDA $nnnn,X */
+         LDA(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0xBE:  /* LDX $nnnn,Y */
+         LDX(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0xBF:  /* LAX $nnnn,Y */
+         LAX(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0xC0:  /* CPY #$nn */
+         CPY(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xC1:  /* CMP ($nn,X) */
+         CMP(6, INDIR_X_BYTE);
+         break;
+
+      case 0xC3:  /* DCP ($nn,X) */
+         DCP(8, INDIR_X, mem_write, addr);
+         break;
+
+      case 0xC4:  /* CPY $nn */
+         CPY(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xC5:  /* CMP $nn */
+         CMP(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xC6:  /* DEC $nn */
+         DEC(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0xC7:  /* DCP $nn */
+         DCP(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0xC8:  /* INY */
+         INY();
+         break;
+
+      case 0xC9:  /* CMP #$nn */
+         CMP(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xCA:  /* DEX */
+         DEX();
+         break;
+
+      case 0xCB:  /* SBX #$nn */
+         SBX(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xCC:  /* CPY $nnnn */
+         CPY(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xCD:  /* CMP $nnnn */
+         CMP(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xCE:  /* DEC $nnnn */
+         DEC(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0xCF:  /* DCP $nnnn */
+         DCP(6, ABSOLUTE, mem_write, addr);
+         break;
+      
+      case 0xD0:  /* BNE $nnnn */
+         BNE();
+         break;
+
+      case 0xD1:  /* CMP ($nn),Y */
+         CMP(5, INDIR_Y_BYTE);
+         break;
+
+      case 0xD3:  /* DCP ($nn),Y */
+         DCP(8, INDIR_Y, mem_write, addr);
+         break;
+
+      case 0xD5:  /* CMP $nn,X */
+         CMP(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0xD6:  /* DEC $nn,X */
+         DEC(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0xD7:  /* DCP $nn,X */
+         DCP(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0xD8:  /* CLD */
+         CLD();
+         break;
+
+      case 0xD9:  /* CMP $nnnn,Y */
+         CMP(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0xDB:  /* DCP $nnnn,Y */
+         DCP(7, ABS_IND_Y, mem_write, addr);
+         break;                  
+
+      case 0xDD:  /* CMP $nnnn,X */
+         CMP(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0xDE:  /* DEC $nnnn,X */
+         DEC(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0xDF:  /* DCP $nnnn,X */
+         DCP(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0xE0:  /* CPX #$nn */
+         CPX(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xE1:  /* SBC ($nn,X) */
+         SBC(6, INDIR_X_BYTE);
+         break;
+
+      case 0xE3:  /* ISB ($nn,X) */
+         ISB(8, INDIR_X, mem_write, addr);
+         break;
+
+      case 0xE4:  /* CPX $nn */
+         CPX(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xE5:  /* SBC $nn */
+         SBC(3, ZERO_PAGE_BYTE);
+         break;
+
+      case 0xE6:  /* INC $nn */
+         INC(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0xE7:  /* ISB $nn */
+         ISB(5, ZERO_PAGE, ZP_WRITE, baddr);
+         break;
+
+      case 0xE8:  /* INX */
+         INX();
+         break;
+
+      case 0xE9:  /* SBC #$nn */
+      case 0xEB:  /* USBC #$nn */
+         SBC(2, IMMEDIATE_BYTE);
+         break;
+
+      case 0xEA:  /* NOP */
+         NOP();
+         break;
+
+      case 0xEC:  /* CPX $nnnn */
+         CPX(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xED:  /* SBC $nnnn */
+         SBC(4, ABSOLUTE_BYTE);
+         break;
+
+      case 0xEE:  /* INC $nnnn */
+         INC(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0xEF:  /* ISB $nnnn */
+         ISB(6, ABSOLUTE, mem_write, addr);
+         break;
+
+      case 0xF0:  /* BEQ $nnnn */
+         BEQ();
+         break;
+
+      case 0xF1:  /* SBC ($nn),Y */
+         SBC(5, INDIR_Y_BYTE);
+         break;
+
+      case 0xF3:  /* ISB ($nn),Y */
+         ISB(8, INDIR_Y, mem_write, addr);
+         break;
+
+      case 0xF5:  /* SBC $nn,X */
+         SBC(4, ZP_IND_X_BYTE);
+         break;
+
+      case 0xF6:  /* INC $nn,X */
+         INC(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0xF7:  /* ISB $nn,X */
+         ISB(6, ZP_IND_X, ZP_WRITE, baddr);
+         break;
+
+      case 0xF8:  /* SED */
+         SED();
+         break;
+
+      case 0xF9:  /* SBC $nnnn,Y */
+         SBC(4, ABS_IND_Y_BYTE);
+         break;
+
+      case 0xFB:  /* ISB $nnnn,Y */
+         ISB(7, ABS_IND_Y, mem_write, addr);
+         break;
+
+      case 0xFD:  /* SBC $nnnn,X */
+         SBC(4, ABS_IND_X_BYTE);
+         break;
+
+      case 0xFE:  /* INC $nnnn,X */
+         INC(7, ABS_IND_X, mem_write, addr);
+         break;
+
+      case 0xFF:  /* ISB $nnnn,X */
+         ISB(7, ABS_IND_X, mem_write, addr);
+         break;
+      }
+
+      /* Calculate remaining/elapsed clock cycles */
+      remaining_cycles -= instruction_cycles;
+      total_cycles += instruction_cycles;
+   }
+
+_execute_done:
+
+   /* restore local copy of regs */
+   SET_LOCAL_REGS();
+
+   /* Return our actual amount of executed cycles */
+   return (total_cycles - old_cycles);
+}
+
+/* Initialize tables, etc. */
+void nes6502_init(void)
+{
+   int index;
+
+   /* Build the N / Z flag lookup table */
+   flag_table[0] = Z_FLAG;
+
+   for (index = 1; index < 256; index++)
+      flag_table[index] = (index & 0x80) ? N_FLAG : 0;
+
+   reg_A = reg_X = reg_Y = 0;
+   reg_S = 0xFF;                             /* Stack grows down */
+}
+
+
+/* Issue a CPU Reset */
+void nes6502_reset(void)
+{
+   reg_P = Z_FLAG | R_FLAG | I_FLAG;         /* Reserved bit always 1 */
+   int_pending = dma_cycles = 0;             /* No pending interrupts */
+   reg_PC = bank_readaddress(RESET_VECTOR);  /* Fetch reset vector */
+   /* TODO: 6 cycles for RESET? */
+}
+
+/* Non-maskable interrupt */
+void nes6502_nmi(void)
+{
+   int_pending |= NMI_MASK;
+}
+
+/* Interrupt request */
+void nes6502_irq(void)
+{
+   int_pending |= IRQ_MASK;
+}
+
+/* Set dma period (in cycles) */
+void nes6502_setdma(int cycles)
+{
+   dma_cycles += cycles;
+}
+
+/*
+** $Log: nes6502.c,v $
+** Revision 1.6  2000/07/04 04:50:07  matt
+** minor change to includes
+**
+** Revision 1.5  2000/07/03 02:18:16  matt
+** added a few notes about potential failure cases
+**
+** Revision 1.4  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/nes_apu.c	2005-03-26 21:43:34.435166856 +0100
@@ -0,0 +1,1202 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** nes_apu.c
+**
+** NES APU emulation
+** $Id: nes_apu.c,v 1.19 2000/07/04 04:53:26 matt Exp $
+*/
+
+#include <string.h>
+#include "types.h"
+#include "log.h"
+#include "nes_apu.h"
+#include "nes6502.h"
+
+#ifdef NSF_PLAYER
+#include "nsf.h"
+#else
+#include "nes.h"
+#include "nes_ppu.h"
+#include "nes_mmc.h"
+#include "nesinput.h"
+#endif /* !NSF_PLAYER */
+
+
+#define  APU_OVERSAMPLE
+#define  APU_VOLUME_DECAY(x)  ((x) -= ((x) >> 7))
+
+
+/* pointer to active APU */
+static apu_t *apu;
+
+/* look up table madness */
+static int32 decay_lut[16];
+static int vbl_lut[32];
+static int trilength_lut[128];
+
+/* noise lookups for both modes */
+#ifndef REALTIME_NOISE
+static int8 noise_long_lut[APU_NOISE_32K];
+static int8 noise_short_lut[APU_NOISE_93];
+#endif /* !REALTIME_NOISE */
+
+
+/* vblank length table used for rectangles, triangle, noise */
+static const uint8 vbl_length[32] =
+{
+    5, 127,
+   10,   1,
+   19,   2,
+   40,   3,
+   80,   4,
+   30,   5,
+    7,   6,
+   13,   7,
+    6,   8,
+   12,   9,
+   24,  10,
+   48,  11,
+   96,  12,
+   36,  13,
+    8,  14,
+   16,  15
+};
+
+/* frequency limit of rectangle channels */
+static const int freq_limit[8] =
+{
+   0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0
+};
+
+/* noise frequency lookup table */
+static const int noise_freq[16] =
+{
+     4,    8,   16,   32,   64,   96,  128,  160,
+   202,  254,  380,  508,  762, 1016, 2034, 4068
+};
+
+/* DMC transfer freqs */
+const int dmc_clocks[16] =
+{
+   428, 380, 340, 320, 286, 254, 226, 214,
+   190, 160, 142, 128, 106,  85,  72,  54
+};
+
+/* ratios of pos/neg pulse for rectangle waves */
+static const int duty_lut[4] = { 2, 4, 8, 12 };
+
+
+void apu_setcontext(apu_t *src_apu)
+{
+   apu = src_apu;
+}
+
+
+/*
+** Simple queue routines
+*/
+#define  APU_QEMPTY()   (apu->q_head == apu->q_tail)
+
+static void apu_enqueue(apudata_t *d)
+{
+   ASSERT(apu);
+   apu->queue[apu->q_head] = *d;
+
+   apu->q_head = (apu->q_head + 1) & APUQUEUE_MASK;
+
+   if (APU_QEMPTY())
+      log_printf("apu: queue overflow\n");      
+}
+
+static apudata_t *apu_dequeue(void)
+{
+   int loc;
+
+   ASSERT(apu);
+
+   if (APU_QEMPTY())
+      log_printf("apu: queue empty\n");
+
+   loc = apu->q_tail;
+   apu->q_tail = (apu->q_tail + 1) & APUQUEUE_MASK;
+
+   return &apu->queue[loc];
+}
+
+void apu_setchan(int chan, boolean enabled)
+{
+   ASSERT(apu);
+   apu->mix_enable[chan] = enabled;
+}
+
+/* emulation of the 15-bit shift register the
+** NES uses to generate pseudo-random series
+** for the white noise channel
+*/
+#ifdef REALTIME_NOISE
+INLINE int8 shift_register15(uint8 xor_tap)
+{
+   static int sreg = 0x4000;
+   int bit0, tap, bit14;
+
+   bit0 = sreg & 1;
+   tap = (sreg & xor_tap) ? 1 : 0;
+   bit14 = (bit0 ^ tap);
+   sreg >>= 1;
+   sreg |= (bit14 << 14);
+   return (bit0 ^ 1);
+}
+#else
+static void shift_register15(int8 *buf, int count)
+{
+   static int sreg = 0x4000;
+   int bit0, bit1, bit6, bit14;
+
+   if (count == APU_NOISE_93)
+   {
+      while (count--)
+      {
+         bit0 = sreg & 1;
+         bit6 = (sreg & 0x40) >> 6;
+         bit14 = (bit0 ^ bit6);
+         sreg >>= 1;
+         sreg |= (bit14 << 14);
+         *buf++ = bit0 ^ 1;
+      }
+   }
+   else /* 32K noise */
+   {
+      while (count--)
+      {
+         bit0 = sreg & 1;
+         bit1 = (sreg & 2) >> 1;
+         bit14 = (bit0 ^ bit1);
+         sreg >>= 1;
+         sreg |= (bit14 << 14);
+         *buf++ = bit0 ^ 1;
+      }
+   }
+}
+#endif
+
+/* RECTANGLE WAVE
+** ==============
+** reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle
+** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on
+** reg2: 8 bits of freq
+** reg3: 0-2=high freq, 7-4=vbl length counter
+*/
+#define  APU_RECTANGLE_OUTPUT chan->output_vol
+static int32 apu_rectangle(rectangle_t *chan)
+{
+   int32 output;
+
+#ifdef APU_OVERSAMPLE
+   int num_times;
+   int32 total;
+#endif
+
+   APU_VOLUME_DECAY(chan->output_vol);
+
+   if (FALSE == chan->enabled || 0 == chan->vbl_length)
+      return APU_RECTANGLE_OUTPUT;
+
+   /* vbl length counter */
+   if (FALSE == chan->holdnote)
+      chan->vbl_length--;
+
+   /* envelope decay at a rate of (env_delay + 1) / 240 secs */
+   chan->env_phase -= 4; /* 240/60 */
+   while (chan->env_phase < 0)
+   {
+      chan->env_phase += chan->env_delay;
+
+      if (chan->holdnote)
+         chan->env_vol = (chan->env_vol + 1) & 0x0F;
+      else if (chan->env_vol < 0x0F)
+         chan->env_vol++;
+   }
+
+   if ((FALSE == chan->sweep_inc && chan->freq > chan->freq_limit)
+       || chan->freq < APU_TO_FIXED(4))
+      return APU_RECTANGLE_OUTPUT;
+
+   /* frequency sweeping at a rate of (sweep_delay + 1) / 120 secs */
+   if (chan->sweep_on && chan->sweep_shifts)
+   {
+      chan->sweep_phase -= 2; /* 120/60 */
+      while (chan->sweep_phase < 0)
+      {
+         chan->sweep_phase += chan->sweep_delay;
+         if (chan->sweep_inc) /* ramp up */
+            chan->freq -= chan->freq >> (chan->sweep_shifts);
+         else /* ramp down */
+            chan->freq += chan->freq >> (chan->sweep_shifts);
+      }
+   }
+
+   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
+   if (chan->phaseacc >= 0)
+      return APU_RECTANGLE_OUTPUT;
+
+#ifdef APU_OVERSAMPLE
+   num_times = total = 0;
+
+   if (chan->fixed_envelope)
+      output = chan->volume << 8; /* fixed volume */
+   else
+      output = (chan->env_vol ^ 0x0F) << 8;
+#endif
+
+   while (chan->phaseacc < 0)
+   {
+      chan->phaseacc += chan->freq;
+      chan->adder = (chan->adder + 1) & 0x0F;
+
+#ifdef APU_OVERSAMPLE
+      if (chan->adder < chan->duty_flip)
+         total += output;
+      else
+         total -= output;
+
+      num_times++;
+#endif
+   }
+
+#ifdef APU_OVERSAMPLE
+   chan->output_vol = total / num_times;
+#else
+   if (chan->fixed_envelope)
+      output = chan->volume << 8; /* fixed volume */
+   else
+      output = (chan->env_vol ^ 0x0F) << 8;
+
+   if (0 == chan->adder)
+      chan->output_vol = output;
+   else if (chan->adder == chan->duty_flip)
+      chan->output_vol = -output;
+#endif
+
+   return APU_RECTANGLE_OUTPUT;
+}
+
+/* TRIANGLE WAVE
+** =============
+** reg0: 7=holdnote, 6-0=linear length counter
+** reg2: low 8 bits of frequency
+** reg3: 7-3=length counter, 2-0=high 3 bits of frequency
+*/
+#define  APU_TRIANGLE_OUTPUT  (chan->output_vol + (chan->output_vol >> 2))
+static int32 apu_triangle(triangle_t *chan)
+{
+   APU_VOLUME_DECAY(chan->output_vol);
+
+   if (FALSE == chan->enabled || 0 == chan->vbl_length)
+      return APU_TRIANGLE_OUTPUT;
+
+   if (chan->counter_started)
+   {
+      if (chan->linear_length > 0)
+         chan->linear_length--;
+      if (chan->vbl_length && FALSE == chan->holdnote)
+         chan->vbl_length--;
+   }
+   else if (FALSE == chan->holdnote && chan->write_latency)
+   {
+      if (--chan->write_latency == 0)
+         chan->counter_started = TRUE;
+   }
+/*
+   if (chan->countmode == COUNTMODE_COUNT)
+   {
+      if (chan->linear_length > 0)
+         chan->linear_length--;
+      if (chan->vbl_length)
+         chan->vbl_length--;
+   }
+*/
+   if (0 == chan->linear_length || chan->freq < APU_TO_FIXED(4)) /* inaudible */
+      return APU_TRIANGLE_OUTPUT;
+
+   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
+   while (chan->phaseacc < 0)
+   {
+      chan->phaseacc += chan->freq;
+      chan->adder = (chan->adder + 1) & 0x1F;
+
+      if (chan->adder & 0x10)
+         chan->output_vol -= (2 << 8);
+      else
+         chan->output_vol += (2 << 8);
+   }
+
+   return APU_TRIANGLE_OUTPUT;
+}
+
+
+/* WHITE NOISE CHANNEL
+** ===================
+** reg0: 0-3=volume, 4=envelope, 5=hold
+** reg2: 7=small(93 byte) sample,3-0=freq lookup
+** reg3: 7-4=vbl length counter
+*/
+#define  APU_NOISE_OUTPUT  ((chan->output_vol + chan->output_vol + chan->output_vol) >> 2)
+
+static int32 apu_noise(noise_t *chan)
+{
+   int32 outvol;
+
+#if defined(APU_OVERSAMPLE) && defined(REALTIME_NOISE)
+#else
+   int32 noise_bit;
+#endif
+#ifdef APU_OVERSAMPLE
+   int num_times;
+   int32 total;
+#endif
+
+   APU_VOLUME_DECAY(chan->output_vol);
+
+   if (FALSE == chan->enabled || 0 == chan->vbl_length)
+      return APU_NOISE_OUTPUT;
+
+   /* vbl length counter */
+   if (FALSE == chan->holdnote)
+      chan->vbl_length--;
+
+   /* envelope decay at a rate of (env_delay + 1) / 240 secs */
+   chan->env_phase -= 4; /* 240/60 */
+   while (chan->env_phase < 0)
+   {
+      chan->env_phase += chan->env_delay;
+
+      if (chan->holdnote)
+         chan->env_vol = (chan->env_vol + 1) & 0x0F;
+      else if (chan->env_vol < 0x0F)
+         chan->env_vol++;
+   }
+
+   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
+   if (chan->phaseacc >= 0)
+      return APU_NOISE_OUTPUT;
+   
+#ifdef APU_OVERSAMPLE
+   num_times = total = 0;
+
+   if (chan->fixed_envelope)
+      outvol = chan->volume << 8; /* fixed volume */
+   else
+      outvol = (chan->env_vol ^ 0x0F) << 8;
+#endif
+
+   while (chan->phaseacc < 0)
+   {
+      chan->phaseacc += chan->freq;
+
+#ifdef REALTIME_NOISE
+
+#ifdef APU_OVERSAMPLE
+      if (shift_register15(chan->xor_tap))
+         total += outvol;
+      else
+         total -= outvol;
+
+      num_times++;
+#else
+      noise_bit = shift_register15(chan->xor_tap);
+#endif
+
+#else
+      chan->cur_pos++;
+
+      if (chan->short_sample)
+      {
+         if (APU_NOISE_93 == chan->cur_pos)
+            chan->cur_pos = 0;
+      }
+      else
+      {
+         if (APU_NOISE_32K == chan->cur_pos)
+            chan->cur_pos = 0;
+      }
+
+#ifdef APU_OVERSAMPLE
+      if (chan->short_sample)
+         noise_bit = noise_short_lut[chan->cur_pos];
+      else
+         noise_bit = noise_long_lut[chan->cur_pos];
+
+      if (noise_bit)
+         total += outvol;
+      else
+         total -= outvol;
+
+      num_times++;
+#endif
+#endif /* REALTIME_NOISE */
+   }
+
+#ifdef APU_OVERSAMPLE
+   chan->output_vol = total / num_times;
+#else
+   if (chan->fixed_envelope)
+      outvol = chan->volume << 8; /* fixed volume */
+   else
+      outvol = (chan->env_vol ^ 0x0F) << 8;
+
+#ifndef REALTIME_NOISE
+   if (chan->short_sample)
+      noise_bit = noise_short_lut[chan->cur_pos];
+   else
+      noise_bit = noise_long_lut[chan->cur_pos];
+#endif /* !REALTIME_NOISE */
+
+   if (noise_bit)
+      chan->output_vol = outvol;
+   else
+      chan->output_vol = -outvol;
+#endif
+
+   return APU_NOISE_OUTPUT;
+}
+
+
+INLINE void apu_dmcreload(dmc_t *chan)
+{
+   chan->address = chan->cached_addr;
+   chan->dma_length = chan->cached_dmalength;
+   chan->irq_occurred = FALSE;
+}
+
+/* DELTA MODULATION CHANNEL
+** =========================
+** reg0: 7=irq gen, 6=looping, 3-0=pointer to clock table
+** reg1: output dc level, 6 bits unsigned
+** reg2: 8 bits of 64-byte aligned address offset : $C000 + (value * 64)
+** reg3: length, (value * 16) + 1
+*/
+#define  APU_DMC_OUTPUT ((chan->output_vol + chan->output_vol + chan->output_vol) >> 2)
+static int32 apu_dmc(dmc_t *chan)
+{
+   int delta_bit;
+
+   APU_VOLUME_DECAY(chan->output_vol);
+
+   /* only process when channel is alive */
+   if (chan->dma_length)
+   {
+      chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
+      
+      while (chan->phaseacc < 0)
+      {
+         chan->phaseacc += chan->freq;
+         
+         delta_bit = (chan->dma_length & 7) ^ 7;
+         
+         if (7 == delta_bit)
+         {
+            chan->cur_byte = nes6502_getbyte(chan->address);
+            
+            /* steal a cycle from CPU*/
+            nes6502_setdma(1);
+
+            if (0xFFFF == chan->address)
+               chan->address = 0x8000;
+            else
+               chan->address++;
+         }
+
+         if (--chan->dma_length == 0)
+         {
+            /* if loop bit set, we're cool to retrigger sample */
+            if (chan->looping)
+               apu_dmcreload(chan);
+            else
+            {
+               /* check to see if we should generate an irq */
+               if (chan->irq_gen)
+               {
+                  chan->irq_occurred = TRUE;
+                  nes6502_irq();
+               }
+
+               /* bodge for timestamp queue */
+               chan->enabled = FALSE;
+               break;
+            }
+         }
+
+         /* positive delta */
+         if (chan->cur_byte & (1 << delta_bit))
+         {
+            if (chan->regs[1] < 0x7D)
+            {
+               chan->regs[1] += 2;
+               chan->output_vol += (2 << 8);
+            }
+/*
+            if (chan->regs[1] < 0x3F)
+               chan->regs[1]++;
+
+            chan->output_vol &= ~(0x7E << 8);
+            chan->output_vol |= ((chan->regs[1] << 1) << 8);
+*/
+         }
+         /* negative delta */
+         else            
+         {
+            if (chan->regs[1] > 1)
+            {
+               chan->regs[1] -= 2;
+               chan->output_vol -= (2 << 8);
+            }
+
+/*
+            if (chan->regs[1] > 0)
+               chan->regs[1]--;
+
+            chan->output_vol &= ~(0x7E << 8);
+            chan->output_vol |= ((chan->regs[1] << 1) << 8);
+*/
+         }
+      }
+   }
+
+   return APU_DMC_OUTPUT;
+}
+
+
+static void apu_regwrite(uint32 address, uint8 value)
+{  
+   int chan;
+
+   ASSERT(apu);
+   switch (address)
+   {
+   /* rectangles */
+   case APU_WRA0:
+   case APU_WRB0:
+      chan = (address & 4) ? 1 : 0;
+      apu->rectangle[chan].regs[0] = value;
+
+      apu->rectangle[chan].volume = value & 0x0F;
+      apu->rectangle[chan].env_delay = decay_lut[value & 0x0F];
+      apu->rectangle[chan].holdnote = (value & 0x20) ? TRUE : FALSE;
+      apu->rectangle[chan].fixed_envelope = (value & 0x10) ? TRUE : FALSE;
+      apu->rectangle[chan].duty_flip = duty_lut[value >> 6];
+      break;
+
+   case APU_WRA1:
+   case APU_WRB1:
+      chan = (address & 4) ? 1 : 0;
+      apu->rectangle[chan].regs[1] = value;
+      apu->rectangle[chan].sweep_on = (value & 0x80) ? TRUE : FALSE;
+      apu->rectangle[chan].sweep_shifts = value & 7;
+      apu->rectangle[chan].sweep_delay = decay_lut[(value >> 4) & 7];
+      
+      apu->rectangle[chan].sweep_inc = (value & 0x08) ? TRUE : FALSE;
+      apu->rectangle[chan].freq_limit = APU_TO_FIXED(freq_limit[value & 7]);
+      break;
+
+   case APU_WRA2:
+   case APU_WRB2:
+      chan = (address & 4) ? 1 : 0;
+      apu->rectangle[chan].regs[2] = value;
+//      if (apu->rectangle[chan].enabled)
+         apu->rectangle[chan].freq = APU_TO_FIXED((((apu->rectangle[chan].regs[3] & 7) << 8) + value) + 1);
+      break;
+
+   case APU_WRA3:
+   case APU_WRB3:
+      chan = (address & 4) ? 1 : 0;
+      apu->rectangle[chan].regs[3] = value;
+
+//      if (apu->rectangle[chan].enabled)
+      {
+         apu->rectangle[chan].vbl_length = vbl_lut[value >> 3];
+         apu->rectangle[chan].env_vol = 0;
+         apu->rectangle[chan].freq = APU_TO_FIXED((((value & 7) << 8) + apu->rectangle[chan].regs[2]) + 1);
+         apu->rectangle[chan].adder = 0;
+      }
+      break;
+
+   /* triangle */
+   case APU_WRC0:
+/*
+      if (0 == (apu->triangle.regs[0] & 0x80))
+         apu->triangle.countmode = COUNTMODE_COUNT;
+      else
+      {
+         if (apu->triangle.countmode == COUNTMODE_LOAD && apu->triangle.vbl_length)
+            apu->triangle.linear_length = trilength_lut[value & 0x7F];
+
+         if (0 == (value & 0x80))
+            apu->triangle.countmode = COUNTMODE_COUNT;
+      }
+*/
+      apu->triangle.regs[0] = value;
+
+      apu->triangle.holdnote = (value & 0x80) ? TRUE : FALSE;
+
+
+//      if (apu->triangle.enabled)
+      {
+         if (FALSE == apu->triangle.counter_started && apu->triangle.vbl_length)
+            apu->triangle.linear_length = trilength_lut[value & 0x7F];
+      }
+
+      break;
+
+   case APU_WRC2:
+
+      apu->triangle.regs[1] = value;
+
+//      if (apu->triangle.enabled)
+         apu->triangle.freq = APU_TO_FIXED((((apu->triangle.regs[2] & 7) << 8) + value) + 1);
+      break;
+
+   case APU_WRC3:
+
+      apu->triangle.regs[2] = value;
+  
+      /* this is somewhat of a hack.  there appears to be some latency on 
+      ** the Real Thing between when trireg0 is written to and when the 
+      ** linear length counter actually begins its countdown.  we want to 
+      ** prevent the case where the program writes to the freq regs first, 
+      ** then to reg 0, and the counter accidentally starts running because 
+      ** of the sound queue's timestamp processing.
+      **
+      ** set latency to a couple scanlines -- should be plenty of time for 
+      ** the 6502 code to do a couple of table dereferences and load up the
+      ** other triregs
+      */
+
+      /* 06/13/00 MPC -- seems to work OK */
+      apu->triangle.write_latency = (int) (2 * NES_SCANLINE_CYCLES / APU_FROM_FIXED(apu->cycle_rate));
+/*
+      apu->triangle.linear_length = trilength_lut[apu->triangle.regs[0] & 0x7F];
+      if (0 == (apu->triangle.regs[0] & 0x80))
+         apu->triangle.countmode = COUNTMODE_COUNT;
+      else
+         apu->triangle.countmode = COUNTMODE_LOAD;
+*/
+//      if (apu->triangle.enabled)
+      {
+         apu->triangle.freq = APU_TO_FIXED((((value & 7) << 8) + apu->triangle.regs[1]) + 1);
+         apu->triangle.vbl_length = vbl_lut[value >> 3];
+         apu->triangle.counter_started = FALSE;
+         apu->triangle.linear_length = trilength_lut[apu->triangle.regs[0] & 0x7F];
+      }
+
+      break;
+
+   /* noise */
+   case APU_WRD0:
+      apu->noise.regs[0] = value;
+      apu->noise.env_delay = decay_lut[value & 0x0F];
+      apu->noise.holdnote = (value & 0x20) ? TRUE : FALSE;
+      apu->noise.fixed_envelope = (value & 0x10) ? TRUE : FALSE;
+      apu->noise.volume = value & 0x0F;
+      break;
+
+   case APU_WRD2:
+      apu->noise.regs[1] = value;
+      apu->noise.freq = APU_TO_FIXED(noise_freq[value & 0x0F]);
+
+#ifdef REALTIME_NOISE
+      apu->noise.xor_tap = (value & 0x80) ? 0x40: 0x02;
+#else
+      /* detect transition from long->short sample */
+      if ((value & 0x80) && FALSE == apu->noise.short_sample)
+      {
+         /* recalculate short noise buffer */
+         shift_register15(noise_short_lut, APU_NOISE_93);
+         apu->noise.cur_pos = 0;
+      }
+      apu->noise.short_sample = (value & 0x80) ? TRUE : FALSE;
+#endif
+      break;
+
+   case APU_WRD3:
+      apu->noise.regs[2] = value;
+
+//      if (apu->noise.enabled)
+      {
+         apu->noise.vbl_length = vbl_lut[value >> 3];
+         apu->noise.env_vol = 0; /* reset envelope */
+      }
+      break;
+
+   /* DMC */
+   case APU_WRE0:
+      apu->dmc.regs[0] = value;
+
+      apu->dmc.freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]);
+      apu->dmc.looping = (value & 0x40) ? TRUE : FALSE;
+
+      if (value & 0x80)
+         apu->dmc.irq_gen = TRUE;
+      else
+      {
+         apu->dmc.irq_gen = FALSE;
+         apu->dmc.irq_occurred = FALSE;
+      }
+      break;
+
+   case APU_WRE1: /* 7-bit DAC */
+      /* add the _delta_ between written value and
+      ** current output level of the volume reg
+      */
+      value &= 0x7F; /* bit 7 ignored */
+      apu->dmc.output_vol += ((value - apu->dmc.regs[1]) << 8);
+      apu->dmc.regs[1] = value;
+/*      
+      apu->dmc.output_vol = (value & 0x7F) << 8;
+      apu->dmc.regs[1] = (value & 0x7E) >> 1;
+*/
+      break;
+
+   case APU_WRE2:
+      apu->dmc.regs[2] = value;
+      apu->dmc.cached_addr = 0xC000 + (uint16) (value << 6);
+      break;
+
+   case APU_WRE3:
+      apu->dmc.regs[3] = value;
+      apu->dmc.cached_dmalength = ((value << 4) + 1) << 3;
+      break;
+
+   case APU_SMASK:
+      /* bodge for timestamp queue */
+      apu->dmc.enabled = (value & 0x10) ? TRUE : FALSE;
+
+      apu->enable_reg = value;
+
+      for (chan = 0; chan < 2; chan++)
+      {
+         if (value & (1 << chan))
+            apu->rectangle[chan].enabled = TRUE;
+         else
+         {
+            apu->rectangle[chan].enabled = FALSE;
+            apu->rectangle[chan].vbl_length = 0;
+         }
+      }
+
+      if (value & 0x04)
+         apu->triangle.enabled = TRUE;
+      else
+      {
+         apu->triangle.enabled = FALSE;
+         apu->triangle.vbl_length = 0;
+         apu->triangle.linear_length = 0;
+         apu->triangle.counter_started = FALSE;
+         apu->triangle.write_latency = 0;
+      }
+
+      if (value & 0x08)
+         apu->noise.enabled = TRUE;
+      else
+      {
+         apu->noise.enabled = FALSE;
+         apu->noise.vbl_length = 0;
+      }
+
+      if (value & 0x10)
+      {
+         if (0 == apu->dmc.dma_length)
+            apu_dmcreload(&apu->dmc);
+      }
+      else
+         apu->dmc.dma_length = 0;
+
+      apu->dmc.irq_occurred = FALSE;
+      break;
+
+      /* unused, but they get hit in some mem-clear loops */
+   case 0x4009:
+   case 0x400D:
+      break;
+   
+   default:
+      break;
+   }
+}
+
+/* Read from $4000-$4017 */
+uint8 apu_read(uint32 address)
+{
+   uint8 value;
+
+   ASSERT(apu);
+
+   switch (address)
+   {
+   case APU_SMASK:
+      /* seems that bit 6 denotes vblank -- return 1 for now */
+      value = 0x40;
+
+      /* Return 1 in 0-5 bit pos if a channel is playing */
+      if (apu->rectangle[0].enabled && apu->rectangle[0].vbl_length)
+         value |= 0x01;
+      if (apu->rectangle[1].enabled && apu->rectangle[1].vbl_length)
+         value |= 0x02;
+      if (apu->triangle.enabled && apu->triangle.vbl_length)
+         value |= 0x04;
+      if (apu->noise.enabled && apu->noise.vbl_length)
+         value |= 0x08;
+
+      //if (apu->dmc.dma_length)
+      /* bodge for timestamp queue */
+      if (apu->dmc.enabled)
+         value |= 0x10;
+
+      if (apu->dmc.irq_occurred)
+         value |= 0x80;
+
+      break;
+
+#ifndef NSF_PLAYER
+   case APU_JOY0:
+      value = input_get(INP_JOYPAD0);
+      break;
+
+   case APU_JOY1:
+      value = input_get(INP_ZAPPER | INP_JOYPAD1 /*| INP_ARKANOID*/ /*| INP_POWERPAD*/);
+      break;
+#endif /* !NSF_PLAYER */
+
+   default:
+      value = (address >> 8); /* heavy capacitance on data bus */
+      break;
+   }
+
+   return value;
+}
+
+
+void apu_write(uint32 address, uint8 value)
+{
+#ifndef NSF_PLAYER
+   static uint8 last_write;
+#endif /* !NSF_PLAYER */
+   apudata_t d;
+
+   switch (address)
+   {
+   case 0x4015:
+      /* bodge for timestamp queue */
+      apu->dmc.enabled = (value & 0x10) ? TRUE : FALSE;
+
+   case 0x4000: case 0x4001: case 0x4002: case 0x4003:
+   case 0x4004: case 0x4005: case 0x4006: case 0x4007:
+   case 0x4008: case 0x4009: case 0x400A: case 0x400B:
+   case 0x400C: case 0x400D: case 0x400E: case 0x400F:
+   case 0x4010: case 0x4011: case 0x4012: case 0x4013:
+      d.timestamp = nes6502_getcycles(FALSE);
+      d.address = address;
+      d.value = value;
+      apu_enqueue(&d);
+      break;
+
+#ifndef NSF_PLAYER
+   case APU_OAMDMA:
+      ppu_oamdma(address, value);
+      break;
+
+   case APU_JOY0:
+      /* VS system VROM switching */
+      mmc_vsvrom(value & 4);
+
+      /* see if we need to strobe them joypads */
+      value &= 1;
+      if ((0 == value) && last_write)
+         input_strobe();
+      last_write = value;
+      break;
+
+   case APU_JOY1: /* Some kind of IRQ control business */
+      break;
+
+#endif /* !NSF_PLAYER */
+
+   default:
+      break;
+   }
+}
+
+void apu_getpcmdata(void **data, int *num_samples, int *sample_bits)
+{
+   ASSERT(apu);
+   *data = apu->buffer;
+   *num_samples = apu->num_samples;
+   *sample_bits = apu->sample_bits;
+}
+
+
+void apu_process(void *buffer, int num_samples)
+{
+   apudata_t *d;
+   uint32 elapsed_cycles;
+   static int32 prev_sample = 0;
+   int32 next_sample, accum;
+
+   ASSERT(apu);
+
+   /* grab it, keep it local for speed */
+   elapsed_cycles = (uint32) apu->elapsed_cycles;
+
+   /* BLEH */
+   apu->buffer = buffer; 
+
+   while (num_samples--)
+   {
+      while ((FALSE == APU_QEMPTY()) && (apu->queue[apu->q_tail].timestamp <= elapsed_cycles))
+      {
+         d = apu_dequeue();
+         apu_regwrite(d->address, d->value);
+      }
+
+      elapsed_cycles += APU_FROM_FIXED(apu->cycle_rate);
+
+      accum = 0;
+      if (apu->mix_enable[0]) accum += apu_rectangle(&apu->rectangle[0]);
+      if (apu->mix_enable[1]) accum += apu_rectangle(&apu->rectangle[1]);
+      if (apu->mix_enable[2]) accum += apu_triangle(&apu->triangle);
+      if (apu->mix_enable[3]) accum += apu_noise(&apu->noise);
+      if (apu->mix_enable[4]) accum += apu_dmc(&apu->dmc);
+
+      if (apu->ext && apu->mix_enable[5]) accum += apu->ext->process();
+
+      /* do any filtering */
+      if (APU_FILTER_NONE != apu->filter_type)
+      {
+         next_sample = accum;
+
+         if (APU_FILTER_LOWPASS == apu->filter_type)
+         {
+            accum += prev_sample;
+            accum >>= 1;
+         }
+         else
+            accum = (accum + accum + accum + prev_sample) >> 2;
+
+         prev_sample = next_sample;
+      }
+
+      /* little extra kick for the kids */
+      accum <<= 1;
+
+      /* prevent clipping */
+      if (accum > 0x7FFF)
+         accum = 0x7FFF;
+      else if (accum < -0x8000)
+         accum = -0x8000;
+
+      /* signed 16-bit output, unsigned 8-bit */
+      if (16 == apu->sample_bits) {
+         int16 *t = buffer;
+         *t++ = (int16) accum;
+         buffer = t;
+      } else {
+         uint8 *t = buffer;
+         *t++ = (accum >> 8) ^ 0x80;
+         buffer = t;
+      }
+   }
+
+   /* resync cycle counter */
+   apu->elapsed_cycles = nes6502_getcycles(FALSE);
+}
+
+/* set the filter type */
+void apu_setfilter(int filter_type)
+{
+   ASSERT(apu);
+   apu->filter_type = filter_type;
+}
+
+void apu_reset(void)
+{
+   uint32 address;
+
+   ASSERT(apu);
+
+   apu->elapsed_cycles = 0;
+   memset(&apu->queue, 0, APUQUEUE_SIZE * sizeof(apudata_t));
+   apu->q_head = 0;
+   apu->q_tail = 0;
+
+   /* use to avoid bugs =) */
+   for (address = 0x4000; address <= 0x4013; address++)
+      apu_regwrite(address, 0);
+
+#ifdef NSF_PLAYER
+   apu_regwrite(0x400C, 0x10); /* silence noise channel on NSF start */
+   apu_regwrite(0x4015, 0x0F);
+#else
+   apu_regwrite(0x4015, 0);
+#endif /* NSF_PLAYER */
+
+   if (apu->ext)
+      apu->ext->reset();
+}
+
+void apu_build_luts(int num_samples)
+{
+   int i;
+
+   /* lut used for enveloping and frequency sweeps */
+   for (i = 0; i < 16; i++)
+      decay_lut[i] = num_samples * (i + 1);
+
+   /* used for note length, based on vblanks and size of audio buffer */
+   for (i = 0; i < 32; i++)
+      vbl_lut[i] = vbl_length[i] * num_samples;
+
+   /* triangle wave channel's linear length table */
+   for (i = 0; i < 128; i++)
+      trilength_lut[i] = (i * num_samples) / 4;
+
+#ifndef REALTIME_NOISE
+   /* generate noise samples */
+   shift_register15(noise_long_lut, APU_NOISE_32K);
+   shift_register15(noise_short_lut, APU_NOISE_93);
+#endif /* !REALTIME_NOISE */
+}
+
+static void apu_setactive(apu_t *active)
+{
+   ASSERT(active);
+   apu = active;
+}
+
+/* Initializes emulated sound hardware, creates waveforms/voices */
+apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean stereo)
+{
+   apu_t *temp_apu;
+   int channel;
+
+   temp_apu = malloc(sizeof(apu_t));
+   if (NULL == temp_apu)
+      return NULL;
+
+   temp_apu->sample_rate = sample_rate;
+   temp_apu->refresh_rate = refresh_rate;
+   temp_apu->sample_bits = sample_bits;
+
+   temp_apu->num_samples = sample_rate / refresh_rate;
+   /* turn into fixed point! */
+   temp_apu->cycle_rate = (int32) (APU_BASEFREQ * 65536.0 / (float) sample_rate);
+
+   /* build various lookup tables for apu */
+   apu_build_luts(temp_apu->num_samples);
+
+   /* set the update routine */
+   temp_apu->process = apu_process;
+   temp_apu->ext = NULL;
+
+   apu_setactive(temp_apu);
+   apu_reset();
+
+   for (channel = 0; channel < 6; channel++)
+      apu_setchan(channel, TRUE);
+
+   apu_setfilter(APU_FILTER_LOWPASS);
+
+   return temp_apu;
+}
+
+apu_t *apu_getcontext(void)
+{
+   return apu;
+}
+
+void apu_destroy(apu_t *src_apu)
+{
+   if (src_apu)
+   {
+      void *t = src_apu;
+      if (src_apu->ext)
+         src_apu->ext->shutdown();
+      free (t);
+   }
+}
+
+void apu_setext(apu_t *src_apu, apuext_t *ext)
+{
+   ASSERT(src_apu);
+
+   src_apu->ext = ext;
+
+   /* initialize it */
+   if (src_apu->ext)
+      src_apu->ext->init();
+}
+
+/* this exists for external mixing routines */
+int32 apu_getcyclerate(void)
+{
+   ASSERT(apu);
+   return apu->cycle_rate;
+}
+
+/*
+** $Log: nes_apu.c,v $
+** Revision 1.19  2000/07/04 04:53:26  matt
+** minor changes, sound amplification
+**
+** Revision 1.18  2000/07/03 02:18:53  matt
+** much better external module exporting
+**
+** Revision 1.17  2000/06/26 11:01:55  matt
+** made triangle a tad quieter
+**
+** Revision 1.16  2000/06/26 05:10:33  matt
+** fixed cycle rate generation accuracy
+**
+** Revision 1.15  2000/06/26 05:00:37  matt
+** cleanups
+**
+** Revision 1.14  2000/06/23 11:06:24  matt
+** more faithful mixing of channels
+**
+** Revision 1.13  2000/06/23 03:29:27  matt
+** cleaned up external sound inteface
+**
+** Revision 1.12  2000/06/20 00:08:39  matt
+** bugfix to rectangle wave
+**
+** Revision 1.11  2000/06/13 13:48:58  matt
+** fixed triangle write latency for fixed point apu cycle rate
+**
+** Revision 1.10  2000/06/12 01:14:36  matt
+** minor change to clipping extents
+**
+** Revision 1.9  2000/06/09 20:00:56  matt
+** fixed noise hiccup in NSF player mode
+**
+** Revision 1.8  2000/06/09 16:49:02  matt
+** removed all floating point from sound generation
+**
+** Revision 1.7  2000/06/09 15:12:28  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/nsf.c	2005-03-26 21:47:46.080910888 +0100
@@ -0,0 +1,622 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** nsf.c
+**
+** NSF loading/saving related functions
+** $Id: nsf.c,v 1.14 2000/07/05 14:54:45 matt Exp $
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "types.h"
+#include "nsf.h"
+#include "log.h"
+#include "nes6502.h"
+#include "nes_apu.h"
+#include "vrcvisnd.h"
+#include "vrc7_snd.h"
+#include "mmc5_snd.h"
+#include "fds_snd.h"
+
+/* TODO: bleh! should encapsulate in NSF */
+#define  MAX_ADDRESS_HANDLERS    32
+static nes6502_memread nsf_readhandler[MAX_ADDRESS_HANDLERS];
+static nes6502_memwrite nsf_writehandler[MAX_ADDRESS_HANDLERS];
+
+static nsf_t *cur_nsf = NULL;
+
+static void nsf_setcontext(nsf_t *nsf)
+{
+   ASSERT(nsf);
+   cur_nsf = nsf;
+}
+
+static uint8 read_mirrored_ram(uint32 address)
+{
+   return cur_nsf->cpu->mem_page[0][address & 0x7FF];
+}
+
+static void write_mirrored_ram(uint32 address, uint8 value)
+{
+   cur_nsf->cpu->mem_page[0][address & 0x7FF] = value;
+}
+
+/* can be used for both banked and non-bankswitched NSFs */
+static void nsf_bankswitch(uint32 address, uint8 value)
+{
+   int cpu_page;
+   uint8 *offset;
+
+   cpu_page = address & 0x0F;
+   offset = (cur_nsf->data - (cur_nsf->load_addr & 0x0FFF)) + (value << 12);
+
+   nes6502_getcontext(cur_nsf->cpu);
+   cur_nsf->cpu->mem_page[cpu_page] = offset;
+   nes6502_setcontext(cur_nsf->cpu);
+}
+
+static nes6502_memread default_readhandler[] =
+{
+   { 0x0800, 0x1FFF, read_mirrored_ram },
+   { 0x4000, 0x4017, apu_read },
+   { -1,     -1,     NULL }
+};
+
+static nes6502_memwrite default_writehandler[] =
+{
+   { 0x0800, 0x1FFF, write_mirrored_ram },
+   { 0x4000, 0x4017, apu_write },
+   { 0x5FF6, 0x5FFF, nsf_bankswitch },
+   { -1,     -1,     NULL}
+};
+
+static uint8 invalid_read(uint32 address)
+{
+#ifdef NOFRENDO_DEBUG
+   log_printf("filthy NSF read from $%04X\n", address);
+#endif /* NOFRENDO_DEBUG */
+
+   return 0xFF;
+}
+
+static void invalid_write(uint32 address, uint8 value)
+{
+#ifdef NOFRENDO_DEBUG
+   log_printf("filthy NSF tried to write $%02X to $%04X\n", value, address);
+#endif /* NOFRENDO_DEBUG */
+}
+
+/* set up the address handlers that the CPU uses */
+static void build_address_handlers(nsf_t *nsf)
+{
+   int count, num_handlers;
+
+   memset(nsf_readhandler, 0, sizeof(nsf_readhandler));
+   memset(nsf_writehandler, 0, sizeof(nsf_writehandler));
+ 
+   num_handlers = 0;
+   for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++)
+   {
+      if (NULL == default_readhandler[count].read_func)
+         break;
+
+      memcpy(&nsf_readhandler[num_handlers], &default_readhandler[count],
+             sizeof(nes6502_memread));
+   }
+
+   if (nsf->apu->ext)
+   {
+      if (NULL != nsf->apu->ext->mem_read)
+      {
+         for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++)
+         {
+            if (NULL == nsf->apu->ext->mem_read[count].read_func)
+               break;
+
+            memcpy(&nsf_readhandler[num_handlers], &nsf->apu->ext->mem_read[count],
+                   sizeof(nes6502_memread));
+         }
+      }
+   }
+
+   /* catch-all for bad reads */
+   nsf_readhandler[num_handlers].min_range = 0x2000; /* min address */
+   nsf_readhandler[num_handlers].max_range = 0x5BFF; /* max address */
+   nsf_readhandler[num_handlers].read_func = invalid_read; /* handler */
+   num_handlers++;
+   nsf_readhandler[num_handlers].min_range = -1;
+   nsf_readhandler[num_handlers].max_range = -1;
+   nsf_readhandler[num_handlers].read_func = NULL;
+   num_handlers++;
+   ASSERT(num_handlers <= MAX_ADDRESS_HANDLERS);
+
+   num_handlers = 0;
+   for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++)
+   {
+      if (NULL == default_writehandler[count].write_func)
+         break;
+
+      memcpy(&nsf_writehandler[num_handlers], &default_writehandler[count],
+             sizeof(nes6502_memwrite));
+   }
+
+   if (nsf->apu->ext)
+   {
+      if (NULL != nsf->apu->ext->mem_write)
+      {
+         for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++)
+         {
+            if (NULL == nsf->apu->ext->mem_write[count].write_func)
+               break;
+
+            memcpy(&nsf_writehandler[num_handlers], &nsf->apu->ext->mem_write[count],
+                   sizeof(nes6502_memwrite));
+         }
+      }
+   }
+
+   /* catch-all for bad writes */
+   nsf_writehandler[num_handlers].min_range = 0x2000; /* min address */
+   nsf_writehandler[num_handlers].max_range = 0x5BFF; /* max address */
+   nsf_writehandler[num_handlers].write_func = invalid_write; /* handler */
+   num_handlers++;
+   /* protect region at $8000-$FFFF */
+   nsf_writehandler[num_handlers].min_range = 0x8000; /* min address */
+   nsf_writehandler[num_handlers].max_range = 0xFFFF; /* max address */
+   nsf_writehandler[num_handlers].write_func = invalid_write; /* handler */
+   num_handlers++;
+   nsf_writehandler[num_handlers].min_range = -1;
+   nsf_writehandler[num_handlers].max_range = -1;
+   nsf_writehandler[num_handlers].write_func = NULL;
+   num_handlers++;
+   ASSERT(num_handlers <= MAX_ADDRESS_HANDLERS);
+}
+
+#define  NSF_ROUTINE_LOC   0x5000
+
+/* sets up a simple loop that calls the desired routine and spins */
+static void nsf_setup_routine(uint32 address, uint8 a_reg, uint8 x_reg)
+{
+   uint8 *mem;
+
+   nes6502_getcontext(cur_nsf->cpu);
+   mem = cur_nsf->cpu->mem_page[NSF_ROUTINE_LOC >> 12] + (NSF_ROUTINE_LOC & 0x0FFF);
+
+   /* our lovely 4-byte 6502 NSF player */
+   mem[0] = 0x20;            /* JSR address */
+   mem[1] = address & 0xFF;
+   mem[2] = address >> 8;
+   mem[3] = 0xF2;            /* JAM (cpu kill op) */
+
+   cur_nsf->cpu->pc_reg = NSF_ROUTINE_LOC;
+   cur_nsf->cpu->a_reg = a_reg;
+   cur_nsf->cpu->x_reg = x_reg;
+   cur_nsf->cpu->y_reg = 0;
+   cur_nsf->cpu->s_reg = 0xFF;
+
+   nes6502_setcontext(cur_nsf->cpu);
+}
+
+/* retrieve any external soundchip driver */
+static apuext_t *nsf_getext(nsf_t *nsf)
+{
+   switch (nsf->ext_sound_type)
+   {
+   case EXT_SOUND_VRCVI:
+      return &vrcvi_ext;
+
+   case EXT_SOUND_VRCVII:
+      return &vrc7_ext;
+
+   case EXT_SOUND_FDS:
+      return &fds_ext;
+
+   case EXT_SOUND_MMC5:
+      return &mmc5_ext;
+
+   case EXT_SOUND_NAMCO106:
+   case EXT_SOUND_SUNSOFT_FME07:
+   case EXT_SOUND_NONE:
+   default:
+      return NULL;
+   }
+}
+
+static void nsf_inittune(nsf_t *nsf)
+{
+   uint8 bank, x_reg;
+   uint8 start_bank, num_banks;
+
+   memset(nsf->cpu->mem_page[0], 0, 0x800);
+   memset(nsf->cpu->mem_page[6], 0, 0x1000);
+   memset(nsf->cpu->mem_page[7], 0, 0x1000);
+
+   if (nsf->bankswitched)
+   {
+      /* the first hack of the NSF spec! */
+      if (EXT_SOUND_FDS == nsf->ext_sound_type)
+      {
+         nsf_bankswitch(0x5FF6, nsf->bankswitch_info[6]);
+         nsf_bankswitch(0x5FF7, nsf->bankswitch_info[7]);
+      }
+
+      for (bank = 0; bank < 8; bank++)
+         nsf_bankswitch(0x5FF8 + bank, nsf->bankswitch_info[bank]);
+   }
+   else
+   {
+      /* not bankswitched, just page in our standard stuff */
+      ASSERT(nsf->load_addr + nsf->length <= 0x10000);
+
+      /* avoid ripper filth */
+      for (bank = 0; bank < 8; bank++)
+         nsf_bankswitch(0x5FF8 + bank, bank);
+
+      start_bank = nsf->load_addr >> 12;
+      num_banks = ((nsf->load_addr + nsf->length - 1) >> 12) - start_bank + 1;
+
+      for (bank = 0; bank < num_banks; bank++)
+         nsf_bankswitch(0x5FF0 + start_bank + bank, bank);
+   }
+
+   /* determine PAL/NTSC compatibility shite */
+   if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL)
+      x_reg = 1;
+   else   
+      x_reg = 0;
+
+   /* execute 1 frame or so; let init routine run free */
+   nsf_setup_routine(nsf->init_addr, (uint8) (nsf->current_song - 1), x_reg);
+   nes6502_execute((int) NES_FRAME_CYCLES);
+}
+
+void nsf_frame(nsf_t *nsf)
+{
+   /* future expansion =) */
+  /*nsf_setcontext(nsf); */
+
+   /* one frame of NES processing */
+   nsf_setup_routine(nsf->play_addr, 0, 0);
+   nes6502_execute((int) NES_FRAME_CYCLES);
+}
+
+/* Deallocate memory */
+void nes_shutdown(nsf_t *nsf)
+{
+   int i;
+
+   ASSERT(nsf);
+   
+   if (nsf->cpu)
+   {
+      if (nsf->cpu->mem_page[0])
+         free(nsf->cpu->mem_page[0]);
+      for (i = 5; i <= 7; i++)
+      {
+         if (nsf->cpu->mem_page[i])
+            free(nsf->cpu->mem_page[i]);
+      }
+      free(nsf->cpu);
+   }
+}
+
+void nsf_init(void)
+{
+   nes6502_init();
+}
+
+/* Initialize NES CPU, hardware, etc. */
+static int nsf_cpuinit(nsf_t *nsf)
+{
+   int i;
+   
+   nsf->cpu = malloc(sizeof(nes6502_context));
+   if (NULL == nsf->cpu)
+      return -1;
+
+   memset(nsf->cpu, 0, sizeof(nes6502_context));
+
+   nsf->cpu->mem_page[0] = malloc(0x800);
+   if (NULL == nsf->cpu->mem_page[0])
+      return -1;
+
+   /* allocate some space for the NSF "player" MMC5 EXRAM, and WRAM */
+   for (i = 5; i <= 7; i++)
+   {
+      nsf->cpu->mem_page[i] = malloc(0x1000);
+      if (NULL == nsf->cpu->mem_page[i])
+         return -1;
+   }
+
+   nsf->cpu->read_handler = nsf_readhandler;
+   nsf->cpu->write_handler = nsf_writehandler;
+
+   return 0;
+}
+
+static void nsf_setup(nsf_t *nsf)
+{
+   int i;
+
+   nsf->current_song = nsf->start_song;
+
+   if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL)
+   {
+      if (nsf->pal_speed)
+         nsf->playback_rate = 1000000 / nsf->pal_speed;
+      else
+         nsf->playback_rate = 50; /* 50 Hz */
+   }
+   else
+   {
+      if (nsf->ntsc_speed)
+         nsf->playback_rate = 1000000 / nsf->ntsc_speed;
+      else
+         nsf->playback_rate = 60; /* 60 Hz */
+   }
+
+   nsf->bankswitched = FALSE;
+
+   for (i = 0; i < 8; i++)
+   {
+      if (nsf->bankswitch_info[i])
+      {
+         nsf->bankswitched = TRUE;
+         break;
+      }
+   }
+}
+
+#ifdef HOST_LITTLE_ENDIAN
+#define  SWAP_16(x)  (x)
+#else /* !HOST_LITTLE_ENDIAN */
+#define  SWAP_16(x)  (((uint16) x >> 8) | (((uint16) x & 0xFF) << 8))
+#endif /* !HOST_LITTLE_ENDIAN */
+
+/* Load a ROM image into memory */
+nsf_t *nsf_load(char *filename, void *source, int length)
+{
+   FILE *fp = NULL;
+   char *new_fn = NULL;
+   nsf_t *temp_nsf;
+
+   if (NULL == filename && NULL == source)
+      return NULL;
+ 
+   if (NULL == source)
+   {
+      fp = fopen(filename, "rb");
+
+      /* Didn't find the file?  Maybe the .NSF extension was omitted */
+      if (NULL == fp)
+      {
+         new_fn = malloc(strlen(filename) + 5);
+         if (NULL == new_fn)
+            return NULL;
+         strcpy(new_fn, filename);
+
+         if (NULL == strrchr(new_fn, '.'))
+            strcat(new_fn, ".nsf");
+
+         fp = fopen(new_fn, "rb");
+
+         if (NULL == fp)
+         {
+            void *t = new_fn;
+            log_printf("could not find file '%s'\n", new_fn);
+            free(t);
+            return NULL;
+         }
+      }
+   }
+
+   temp_nsf = malloc(sizeof(nsf_t));
+   if (NULL == temp_nsf)
+      return NULL;
+
+   /* Read in the header */
+   if (NULL == source)
+      fread(temp_nsf, 1, NSF_HEADER_SIZE, fp);
+   else
+      memcpy(temp_nsf, source, NSF_HEADER_SIZE);
+
+   if (memcmp(temp_nsf->id, NSF_MAGIC, 5))
+   {
+      if (NULL == source)
+      {
+         void *t = new_fn;
+         log_printf("%s is not an NSF format file\n", new_fn);
+         fclose(fp);
+         free(t);
+      }
+      nsf_free(&temp_nsf);
+      return NULL;
+   }
+
+   /* fixup endianness */
+   temp_nsf->load_addr = SWAP_16(temp_nsf->load_addr);
+   temp_nsf->init_addr = SWAP_16(temp_nsf->init_addr);
+   temp_nsf->play_addr = SWAP_16(temp_nsf->play_addr);
+   temp_nsf->ntsc_speed = SWAP_16(temp_nsf->ntsc_speed);
+   temp_nsf->pal_speed = SWAP_16(temp_nsf->pal_speed);
+
+   /* we're now at position 80h */
+   if (NULL == source)
+   {
+      fseek(fp, 0, SEEK_END);
+      temp_nsf->length = ftell(fp) - NSF_HEADER_SIZE;
+   }
+   else
+   {
+      temp_nsf->length = length - NSF_HEADER_SIZE;
+   }
+
+   /* Allocate NSF space, and load it up! */
+   temp_nsf->data = malloc(temp_nsf->length);
+   if (NULL == temp_nsf->data)
+   {
+      log_printf("error allocating memory for NSF data\n");
+      nsf_free(&temp_nsf);
+      return NULL;
+   }
+
+   /* seek to end of header, read in data */
+   if (NULL == source)
+   {
+      fseek(fp, NSF_HEADER_SIZE, SEEK_SET);
+      fread(temp_nsf->data, temp_nsf->length, 1, fp);
+
+      fclose(fp);
+
+      
+      if (new_fn) {
+        void *t = new_fn;
+	free(t);
+      }
+   }
+   else
+      memcpy(temp_nsf->data, (uint8 *) source + NSF_HEADER_SIZE, 
+		temp_nsf->length);
+
+   /* Set up some variables */
+   nsf_setup(temp_nsf);
+
+   temp_nsf->apu = NULL; /* just make sure */
+
+   if (nsf_cpuinit(temp_nsf))
+   {
+      nsf_free(&temp_nsf);
+      return NULL;
+   }
+
+   return temp_nsf;
+}
+
+/* Free an NSF */
+void nsf_free(nsf_t **nsf)
+{
+   if (*nsf)
+   {
+      if ((*nsf)->apu)
+         apu_destroy((*nsf)->apu);
+
+      nes_shutdown(*nsf);
+
+      if ((*nsf)->data)
+         free((*nsf)->data);
+
+      free(*nsf);
+   }
+}
+
+void nsf_setchan(nsf_t *nsf, int chan, boolean enabled)
+{
+   if (nsf)
+   {
+      nsf_setcontext(nsf);
+      apu_setchan(chan, enabled);
+   }
+}
+
+void nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits, boolean stereo)
+{
+   ASSERT(nsf);
+
+   /* make this NSF the current context */
+   nsf_setcontext(nsf);
+
+   /* create the APU */
+   if (nsf->apu)
+      apu_destroy(nsf->apu);
+
+   nsf->apu = apu_create(sample_rate, nsf->playback_rate, sample_bits, stereo);
+   if (NULL == nsf->apu)
+   {
+      nsf_free(&nsf);
+      return;
+   }
+
+   apu_setext(nsf->apu, nsf_getext(nsf));
+
+   /* go ahead and init all the read/write handlers */
+   build_address_handlers(nsf);
+
+   /* convenience? */
+   nsf->process = nsf->apu->process;
+
+   nes6502_setcontext(nsf->cpu);
+
+   if (track > nsf->num_songs)
+      track = nsf->num_songs;
+   else if (track < 1)
+      track = 1;
+
+   nsf->current_song = track;
+   
+   apu_reset();
+
+   nsf_inittune(nsf);
+}
+
+void nsf_setfilter(nsf_t *nsf, int filter_type)
+{
+   if (nsf)
+   {
+      nsf_setcontext(nsf);
+      apu_setfilter(filter_type);
+   }
+}
+
+/*
+** $Log: nsf.c,v $
+** Revision 1.14  2000/07/05 14:54:45  matt
+** fix for naughty Crystalis rip
+**
+** Revision 1.13  2000/07/04 04:59:38  matt
+** removed DOS-specific stuff, fixed bug in address handlers
+**
+** Revision 1.12  2000/07/03 02:19:36  matt
+** dynamic address range handlers, cleaner and faster
+**
+** Revision 1.11  2000/06/23 03:27:58  matt
+** cleaned up external sound inteface
+**
+** Revision 1.10  2000/06/20 20:42:47  matt
+** accuracy changes
+**
+** Revision 1.9  2000/06/20 00:05:58  matt
+** changed to driver-based external sound generation
+**
+** Revision 1.8  2000/06/13 03:51:54  matt
+** update API to take freq/sample data on nsf_playtrack
+**
+** Revision 1.7  2000/06/12 03:57:14  matt
+** more robust checking for winamp plugin
+**
+** Revision 1.6  2000/06/12 01:13:00  matt
+** added CPU/APU as members of the nsf struct
+**
+** Revision 1.5  2000/06/11 16:09:21  matt
+** nsf_free is more robust
+**
+** Revision 1.4  2000/06/09 15:12:26  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/vrc7_snd.c	2005-03-26 21:49:29.166239536 +0100
@@ -0,0 +1,342 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** vrc7_snd.c
+**
+** VRCVII sound hardware emulation
+** Thanks to Charles MacDonald (cgfm2@hooked.net) for donating code.
+** $Id: vrc7_snd.c,v 1.5 2000/07/04 04:51:02 matt Exp $
+*/
+
+#include <stdio.h>
+#include "types.h"
+#include "vrc7_snd.h"
+#include "fmopl.h"
+
+
+static int buflen;
+static int16 *buffer;
+
+#define OPL_WRITE(opl, r, d) \
+{ \
+   OPLWrite((opl)->ym3812, 0, (r)); \
+   OPLWrite((opl)->ym3812, 1, (d)); \
+}
+
+static vrc7_t vrc7;
+
+/* Fixed instrument settings, from MAME's YM2413 emulation */
+/* This might need some tweaking... */
+unsigned char table[16][11] =
+{
+  /*   20    23    40    43    60    63    80    83    E0    E3    C0 */
+   { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+
+   /* MAME */  
+   { 0x01, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0x07, 0x18, 0x00, 0x00, 0x00 }, /* Violin */
+   { 0x23, 0x01, 0x68, 0x05, 0xF2, 0x74, 0x6C, 0x89, 0x00, 0x00, 0x00 }, /* Acoustic Guitar(steel) */
+   { 0x13, 0x11, 0x25, 0x00, 0xD2, 0xB2, 0xF4, 0xF4, 0x00, 0x00, 0x00 }, /* Acoustic Grand */
+   { 0x22, 0x21, 0x1B, 0x05, 0xC0, 0xA1, 0x18, 0x08, 0x00, 0x00, 0x00 }, /* Flute */
+   { 0x22, 0x21, 0x2C, 0x03, 0xD2, 0xA1, 0x18, 0x57, 0x00, 0x00, 0x00 }, /* Clarinet */
+   { 0x01, 0x22, 0xBA, 0x01, 0xF1, 0xF1, 0x1E, 0x04, 0x00, 0x00, 0x00 }, /* Oboe */
+   { 0x21, 0x21, 0x28, 0x06, 0xF1, 0xF1, 0x6B, 0x3E, 0x00, 0x00, 0x00 }, /* Trumpet */
+   { 0x27, 0x21, 0x60, 0x00, 0xF0, 0xF0, 0x0D, 0x0F, 0x00, 0x00, 0x00 }, /* Church Organ */
+   { 0x20, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x6D, 0x89, 0x00, 0x00, 0x00 }, /* French Horn */
+   { 0x01, 0x21, 0xBF, 0x02, 0x53, 0x62, 0x5F, 0xAE, 0x01, 0x00, 0x00 }, /* Synth Voice */
+   { 0x23, 0x21, 0x70, 0x07, 0xD4, 0xA3, 0x4E, 0x64, 0x01, 0x00, 0x00 }, /* Harpsichord */
+   { 0x2B, 0x21, 0xA4, 0x07, 0xF6, 0x93, 0x5C, 0x4D, 0x00, 0x00, 0x00 }, /* Vibraphone */
+   { 0x21, 0x23, 0xAD, 0x07, 0x77, 0xF1, 0x18, 0x37, 0x00, 0x00, 0x00 }, /* Synth Bass 1 */
+   { 0x21, 0x21, 0x2A, 0x03, 0xF3, 0xE2, 0x29, 0x46, 0x00, 0x00, 0x00 }, /* Acoustic Bass */
+   { 0x21, 0x23, 0x37, 0x03, 0xF3, 0xE2, 0x29, 0x46, 0x00, 0x00, 0x00 }, /* Electric Guitar(clean) */
+
+
+#if 0
+   /* Horton, try 1 */
+   { 0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4, 0x00, 0x00, 0x00 },
+   { 0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5, 0x00, 0x00, 0x00 },
+   { 0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95, 0x00, 0x00, 0x00 },
+   { 0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95, 0x00, 0x00, 0x00 },
+   { 0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94, 0x00, 0x00, 0x00 },
+   { 0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95, 0x00, 0x00, 0x00 },
+   { 0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10, 0x00, 0x00, 0x00 },
+   { 0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35, 0x00, 0x00, 0x00 },
+   { 0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4, 0x00, 0x00, 0x00 },
+   { 0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5, 0x00, 0x00, 0x00 },
+   { 0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03, 0x00, 0x00, 0x00 },
+   { 0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95, 0x00, 0x00, 0x00 },
+   { 0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75, 0x00, 0x00, 0x00 },
+   { 0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15, 0x00, 0x00, 0x00 },
+   { 0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15, 0x00, 0x00, 0x00 },
+#endif
+
+#if 0
+   /* Horton try 2 */
+   { 0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7, 0x00, 0x00, 0x00 },
+   { 0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C, 0x00, 0x00, 0x00 },
+   { 0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B, 0x00, 0x00, 0x00 },
+   { 0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8, 0x00, 0x00, 0x00 },
+   { 0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8, 0x00, 0x00, 0x00 },
+   { 0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE, 0x00, 0x00, 0x00 },
+   { 0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B, 0x00, 0x00, 0x00 },
+   { 0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD, 0x00, 0x00, 0x00 },
+   { 0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D, 0x00, 0x00, 0x00 },
+   { 0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF, 0x00, 0x00, 0x00 },
+   { 0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE, 0x00, 0x00, 0x00 },
+   { 0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC, 0x00, 0x00, 0x00 },
+   { 0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8, 0x00, 0x00, 0x00 },
+   { 0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, 0x00, 0x00, 0x00 },
+   { 0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, 0x00, 0x00, 0x00 },
+#endif
+};
+
+static void vrc7_reset(void)
+{
+   int n;
+
+   /* Point to current VRC7 context */
+   vrc7_t *opll = &vrc7;
+
+   /* Clear all YM3812 registers */
+   for (n = 0; n < 0x100; n++)
+      OPL_WRITE(opll, n, 0x00);
+
+   /* Turn off rhythm mode and key-on bits */
+   OPL_WRITE(opll, 0xBD, 0xC0);
+
+   /* Enable waveform select */
+   OPL_WRITE(opll, 0x01, 0x20);
+}
+
+static void vrc7_init(void)
+{
+   vrc7.ym3812 = OPLCreate(OPL_TYPE_YM3812, 3579545, apu_getcontext()->sample_rate);
+   ASSERT(vrc7.ym3812);
+   buflen = apu_getcontext()->num_samples;
+   buffer = malloc(buflen * 2);
+   ASSERT(buffer);
+   vrc7_reset();
+}
+
+static void vrc7_shutdown(void)
+{
+   void *t = buffer;
+   vrc7_reset();
+   OPLDestroy(vrc7.ym3812);
+   free(t);
+}
+
+/* channel (0-9), instrument (0-F), volume (0-3F, YM3812 format) */
+static void load_instrument(uint8 ch, uint8 inst, uint8 vol)
+{
+   /* Point to current VRC7 context */
+   vrc7_t *opll = &vrc7;
+
+   /* Point to fixed instrument or user table */
+   uint8 *param = (inst == 0) ? &opll->user[0] : &table[inst][0];
+
+   /* Maps channels to operator registers */
+   uint8 ch2op[] = {0, 1, 2, 8, 9, 10, 16, 17, 18};
+
+   /* Make operator offset from requested channel */
+   uint8 op = ch2op[ch];
+
+   /* Store volume level */
+   opll->channel[ch].volume = (vol & 0x3F);
+
+   /* Store instrument number */
+   opll->channel[ch].instrument = (inst & 0x0F);
+
+   /* Update instrument settings, except frequency registers */
+   OPL_WRITE(opll, 0x20 + op,  param[0]);
+   OPL_WRITE(opll, 0x23 + op,  param[1]);
+   OPL_WRITE(opll, 0x40 + op,  param[2]);
+   OPL_WRITE(opll, 0x43 + op, (param[3] & 0xC0) | opll->channel[ch].volume);
+   OPL_WRITE(opll, 0x60 + op,  param[4]);
+   OPL_WRITE(opll, 0x63 + op,  param[5]);
+   OPL_WRITE(opll, 0x80 + op,  param[6]);
+   OPL_WRITE(opll, 0x83 + op,  param[7]);
+   OPL_WRITE(opll, 0xE0 + op,  param[8]);
+   OPL_WRITE(opll, 0xE3 + op,  param[9]);
+   OPL_WRITE(opll, 0xC0 + ch,  param[10]);
+}
+
+static void vrc7_write(uint32 address, uint8 data)
+{
+   /* Point to current VRC7 context */
+   vrc7_t *opll = &vrc7;
+
+   if (address & 0x0020) /* data port */
+   {
+      /* Store register data */
+      opll->reg[opll->latch] = data;
+
+      switch (opll->latch & 0x30)
+      {
+      case 0x00: /* User instrument registers */
+         switch (opll->latch & 0x0F)
+         {
+         case 0x00: /* Misc. ctrl. (modulator) */
+         case 0x01: /* Misc. ctrl. (carrier) */
+         case 0x02: /* Key scale level and total level (modulator) */
+         case 0x04: /* Attack / Decay (modulator) */
+         case 0x05: /* Attack / Decay (carrier) */
+         case 0x06: /* Sustain / Release (modulator) */
+         case 0x07: /* Sustain / Release (carrier) */
+            opll->user[(opll->latch & 0x07)] = data;
+            break;
+    
+         case 0x03: /* Key scale level, carrier/modulator waveform, feedback */
+    
+            /* Key scale level (carrier) */
+            /* Don't touch the total level (channel volume) */
+            opll->user[3] = (opll->user[3] & 0x3F) | (data & 0xC0);
+    
+            /* Waveform select for the modulator */
+            opll->user[8] = (data >> 3) & 1;
+    
+            /* Waveform select for the carrier */
+            opll->user[9] = (data >> 4) & 1;
+    
+            /* Store feedback level in YM3812 format */
+            opll->user[10] = ((data & 0x07) << 1) & 0x0E;
+            break;
+         }
+    
+         /* If the user instrument registers were accessed, then
+            go through each channel and update the ones that were
+            currently using the user instrument. We can skip the
+            last three channels in rhythm mode since they can
+            only use percussion sounds anyways. */
+         if (opll->latch <= 0x05)
+         {
+            uint8 x;
+
+            for (x = 0; x < 6; x++)
+               if (opll->channel[x].instrument == 0x00)
+                  load_instrument(x, 0x00, opll->channel[x].volume);
+         }
+         break;
+    
+      case 0x10: /* Channel Frequency (LSB) */
+      case 0x20: /* Channel Frequency (MSB) + key-on and sustain control */
+         {
+            uint8 block;
+            uint16 frequency;
+            uint8 ch = (opll->latch & 0x0F);
+
+            /* Ensure proper channel range */
+            if (ch > 0x05)
+               break;
+ 
+            /* Get VRC7 channel frequency */
+            frequency = ((opll->reg[0x10 + ch] & 0xFF) | ((opll->reg[0x20 + ch] & 0x01) << 8));
+
+            /* Scale 9 bit frequency to 10 bits */
+            frequency = (frequency << 1) & 0x1FFF;
+
+            /* Get VRC7 block */
+            block = (opll->reg[0x20 + ch] >> 1) & 7;
+
+            /* Add in block */
+            frequency |= (block << 10);
+
+            /* Add key-on flag */
+            if (opll->reg[0x20 + ch] & 0x10)
+               frequency |= 0x2000;
+
+            /* Save current frequency/block/key-on setting */
+            opll->channel[ch].frequency = (frequency & 0x3FFF);
+
+            /* Write changes to YM3812 */
+            OPL_WRITE(opll, 0xA0 + ch, (opll->channel[ch].frequency >> 0) & 0xFF);
+            OPL_WRITE(opll, 0xB0 + ch, (opll->channel[ch].frequency >> 8) & 0xFF);
+         }
+         break;
+    
+      case 0x30: /* Channel Volume Level and Instrument Select */
+
+         /* Ensure proper channel range */
+         if (opll->latch > 0x35) 
+            break;
+         
+         {
+            uint8 ch = (opll->latch & 0x0F);
+            uint8 inst = (data >> 4) & 0x0F;
+            uint8 vol = (data & 0x0F) << 2;
+            load_instrument(ch, inst, vol);
+         }
+
+         break;
+      }
+   }
+   else /* Register latch */
+   {
+      opll->latch = (data & 0x3F);
+   }
+}
+
+static int32 vrc7_process(void)
+{
+   static int sample = 0;
+
+   /* update a large chunk at once */
+   if (sample >= buflen)
+   {
+      sample -= buflen;
+      YM3812UpdateOne(vrc7.ym3812, buffer, buflen);
+   }
+
+   return (int32) ((int16 *) buffer)[sample++];
+}
+
+static apu_memwrite vrc7_memwrite[] =
+{
+   { 0x9010, 0x9010, vrc7_write },
+   { 0x9030, 0x9030, vrc7_write }, 
+   {     -1,     -1, NULL }
+};
+
+apuext_t vrc7_ext = 
+{
+   vrc7_init,
+   vrc7_shutdown,
+   vrc7_reset,
+   vrc7_process,
+   NULL, /* no reads */
+   vrc7_memwrite
+};
+
+/*
+** $Log: vrc7_snd.c,v $
+** Revision 1.5  2000/07/04 04:51:02  matt
+** made data types stricter
+**
+** Revision 1.4  2000/07/03 02:18:53  matt
+** much better external module exporting
+**
+** Revision 1.3  2000/06/20 20:45:09  matt
+** minor cleanups
+**
+** Revision 1.2  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.1  2000/06/20 00:06:47  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/vrcvisnd.c	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,205 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** vrcvisnd.c
+**
+** VRCVI sound hardware emulation
+** $Id: vrcvisnd.c,v 1.9 2000/07/04 04:51:41 matt Exp $
+*/
+
+#include "types.h"
+#include "vrcvisnd.h"
+#include "nes_apu.h"
+
+
+static vrcvisnd_t vrcvi;
+static int32 vrcvi_incsize;
+
+/* VRCVI rectangle wave generation */
+static int32 vrcvi_rectangle(vrcvirectangle_t *chan)
+{
+   /* reg0: 0-3=volume, 4-6=duty cycle
+   ** reg1: 8 bits of freq
+   ** reg2: 0-3=high freq, 7=enable
+   */
+
+   chan->phaseacc -= vrcvi_incsize; /* # of clocks per wave cycle */
+   while (chan->phaseacc < 0)
+   {
+      chan->phaseacc += chan->freq;
+      chan->adder = (chan->adder + 1) & 0x0F;
+   }
+
+   /* return if not enabled */
+   if (FALSE == chan->enabled)
+      return 0;
+
+   if (chan->adder < chan->duty_flip)
+      return -(chan->volume);
+   else
+      return chan->volume;
+}
+
+/* VRCVI sawtooth wave generation */
+static int32 vrcvi_sawtooth(vrcvisawtooth_t *chan)
+{
+   /* reg0: 0-5=phase accumulator bits
+   ** reg1: 8 bits of freq
+   ** reg2: 0-3=high freq, 7=enable
+   */
+
+   chan->phaseacc -= vrcvi_incsize; /* # of clocks per wav cycle */
+   while (chan->phaseacc < 0)
+   {
+      chan->phaseacc += chan->freq;
+      chan->output_acc += chan->volume;
+      
+      if (7 == ++chan->adder)
+      {
+         chan->adder = 0;
+         chan->output_acc = 0;
+      }
+   }
+
+   /* return if not enabled */
+   if (FALSE == chan->enabled)
+      return 0;
+   else
+      return (chan->output_acc >> 3) << 9;
+}
+
+/* mix vrcvi sound channels together */
+static int32 vrcvi_process(void)
+{
+   int32 output;
+
+   output = vrcvi_rectangle(&vrcvi.rectangle[0]);
+   output += vrcvi_rectangle(&vrcvi.rectangle[1]);
+   output += vrcvi_sawtooth(&vrcvi.saw);
+
+   return output;
+}
+
+/* write to registers */
+static void vrcvi_write(uint32 address, uint8 value)
+{
+   int chan;
+
+   switch (address & 0xB003)
+   {
+   case 0x9000:
+   case 0xA000:
+      chan = (address >> 12) - 9;
+      vrcvi.rectangle[chan].reg[0] = value;
+      vrcvi.rectangle[chan].volume = (value & 0x0F) << 8;
+      vrcvi.rectangle[chan].duty_flip = (value >> 4) + 1;
+      break;
+   case 0x9001:
+   case 0xA001:
+      chan = (address >> 12) - 9;
+      vrcvi.rectangle[chan].reg[1] = value;
+      vrcvi.rectangle[chan].freq = APU_TO_FIXED(((vrcvi.rectangle[chan].reg[2] & 0x0F) << 8) + value + 1);
+      break;
+   case 0x9002:
+   case 0xA002:
+      chan = (address >> 12) - 9;
+      vrcvi.rectangle[chan].reg[2] = value;
+      vrcvi.rectangle[chan].freq = APU_TO_FIXED(((value & 0x0F) << 8) + vrcvi.rectangle[chan].reg[1] + 1);
+      vrcvi.rectangle[chan].enabled = (value & 0x80) ? TRUE : FALSE;
+      break;
+   case 0xB000:
+      vrcvi.saw.reg[0] = value;
+      vrcvi.saw.volume = value & 0x3F;
+      break;
+   case 0xB001:
+      vrcvi.saw.reg[1] = value;
+      vrcvi.saw.freq = APU_TO_FIXED((((vrcvi.saw.reg[2] & 0x0F) << 8) + value + 1) << 1);
+      break;
+   case 0xB002:
+      vrcvi.saw.reg[2] = value;
+      vrcvi.saw.freq = APU_TO_FIXED((((value & 0x0F) << 8) + vrcvi.saw.reg[1] + 1) << 1);
+      vrcvi.saw.enabled = (value & 0x80) ? TRUE : FALSE;
+      break;
+   default:
+      break;
+   }
+}
+
+/* reset state of vrcvi sound channels */
+static void vrcvi_reset(void)
+{
+   int i;
+
+   /* preload regs */
+   for (i = 0; i < 3; i++)
+   {
+      vrcvi_write(0x9000 + i, 0);
+      vrcvi_write(0xA000 + i, 0);
+      vrcvi_write(0xB000 + i, 0);
+   }
+
+   /* get the phase period from the apu */
+   vrcvi_incsize = apu_getcyclerate();
+}
+
+static void vrcvi_dummy(void)
+{
+}
+
+static apu_memwrite vrcvi_memwrite[] =
+{
+  /*   { 0x4040, 0x4092, ext_write },*/ /* FDS sound regs */
+   { 0x9000, 0x9002, vrcvi_write }, /* vrc6 */
+   { 0xA000, 0xA002, vrcvi_write },
+   { 0xB000, 0xB002, vrcvi_write },
+   {     -1,     -1, NULL }
+};
+
+apuext_t vrcvi_ext =
+{
+   vrcvi_dummy, /* no init */
+   vrcvi_dummy, /* no shutdown */
+   vrcvi_reset,
+   vrcvi_process,
+   NULL, /* no reads */
+   vrcvi_memwrite
+};
+
+/*
+** $Log: vrcvisnd.c,v $
+** Revision 1.9  2000/07/04 04:51:41  matt
+** cleanups
+**
+** Revision 1.8  2000/07/03 02:18:53  matt
+** much better external module exporting
+**
+** Revision 1.7  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.6  2000/06/20 00:08:58  matt
+** changed to driver based API
+**
+** Revision 1.5  2000/06/09 16:49:02  matt
+** removed all floating point from sound generation
+**
+** Revision 1.4  2000/06/09 15:12:28  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/dis6502.h	2004-06-30 14:29:21.000000000 +0200
@@ -0,0 +1,47 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** dis6502.h
+**
+** 6502 disassembler header
+** $Id: dis6502.h,v 1.4 2000/06/09 15:12:25 matt Exp $
+*/
+
+#ifndef _DIS6502_H_
+#define _DIS6502_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern void nes6502_disasm(uint32 PC, uint8 P, uint8 A, uint8 X, uint8 Y, uint8 S);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* !_DIS6502_H_ */
+
+/*
+** $Log: dis6502.h,v $
+** Revision 1.4  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/fds_snd.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,45 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** fds_snd.h
+**
+** Famicom Disk System sound emulation
+** $Id: fds_snd.h,v 1.2 2000/06/20 04:06:16 matt Exp $
+*/
+
+#ifndef _FDS_SND_H_
+#define _FDS_SND_H_
+
+#include "nes_apu.h"
+
+extern apuext_t fds_ext;
+
+
+#endif /* _VRCVISND_H_ */
+
+/*
+** $Log: fds_snd.h,v $
+** Revision 1.2  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.1  2000/06/20 00:06:47  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/fmopl.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,164 @@
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+#define HAS_YM3812 1
+typedef signed short int FMSAMPLE;
+
+
+#define BUILD_YM3812 (HAS_YM3812)
+#define BUILD_YM3526 (HAS_YM3526)
+#define BUILD_Y8950  (HAS_Y8950)
+
+/* compiler dependence */
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+typedef unsigned char	UINT8;   /* unsigned  8bit */
+typedef unsigned short	UINT16;  /* unsigned 16bit */
+typedef unsigned int	UINT32;  /* unsigned 32bit */
+typedef signed char		INT8;    /* signed  8bit   */
+typedef signed short	INT16;   /* signed 16bit   */
+typedef signed int		INT32;   /* signed 32bit   */
+#endif
+
+#if BUILD_Y8950
+#include "ymdeltat.h"
+#endif
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
+
+/* !!!!! here is private section , do not access there member direct !!!!! */
+
+#define OPL_TYPE_WAVESEL   0x01  /* waveform select    */
+#define OPL_TYPE_ADPCM     0x02  /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD  0x04  /* keyboard interface */
+#define OPL_TYPE_IO        0x08  /* I/O port */
+
+/* ---------- OPL one of slot  ---------- */
+typedef struct fm_opl_slot {
+	INT32 TL;		/* total level     :TL << 8            */
+	INT32 TLL;		/* adjusted now TL                     */
+	UINT8  KSR;		/* key scale rate  :(shift down bit)   */
+	INT32 *AR;		/* attack rate     :&AR_TABLE[AR<<2]   */
+	INT32 *DR;		/* decay rate      :&DR_TALBE[DR<<2]   */
+	INT32 SL;		/* sustin level    :SL_TALBE[SL]       */
+	INT32 *RR;		/* release rate    :&DR_TABLE[RR<<2]   */
+	UINT8 ksl;		/* keyscale level  :(shift down bits)  */
+	UINT8 ksr;		/* key scale rate  :kcode>>KSR         */
+	UINT32 mul;		/* multiple        :ML_TABLE[ML]       */
+	UINT32 Cnt;		/* frequency count :                   */
+	UINT32 Incr;	/* frequency step  :                   */
+	/* envelope generator state */
+	UINT8 eg_typ;	/* envelope type flag                  */
+	UINT8 evm;		/* envelope phase                      */
+	INT32 evc;		/* envelope counter                    */
+	INT32 eve;		/* envelope counter end point          */
+	INT32 evs;		/* envelope counter step               */
+	INT32 evsa;	/* envelope step for AR :AR[ksr]       */
+	INT32 evsd;	/* envelope step for DR :DR[ksr]       */
+	INT32 evsr;	/* envelope step for RR :RR[ksr]       */
+	/* LFO */
+	UINT8 ams;		/* ams flag                            */
+	UINT8 vib;		/* vibrate flag                        */
+	/* wave selector */
+	INT32 **wavetable;
+}OPL_SLOT;
+
+/* ---------- OPL one of channel  ---------- */
+typedef struct fm_opl_channel {
+	OPL_SLOT SLOT[2];
+	UINT8 CON;			/* connection type                     */
+	UINT8 FB;			/* feed back       :(shift down bit)   */
+	INT32 *connect1;	/* slot1 output pointer                */
+	INT32 *connect2;	/* slot2 output pointer                */
+	INT32 op1_out[2];	/* slot1 output for selfeedback        */
+	/* phase generator state */
+	UINT32  block_fnum;	/* block+fnum      :                   */
+	UINT8 kcode;		/* key code        : KeyScaleCode      */
+	UINT32  fc;			/* Freq. Increment base                */
+	UINT32  ksl_base;	/* KeyScaleLevel Base step             */
+	UINT8 keyon;		/* key on/off flag                     */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+	UINT8 type;			/* chip type                        */
+	int clock;			/* master clock  (Hz)                */
+	int rate;			/* sampling rate (Hz)                */
+	double freqbase;	/* frequency base                    */
+	double TimerBase;	/* Timer base time (==sampling time) */
+	UINT8 address;		/* address register                  */
+	UINT8 status;		/* status flag                       */
+	UINT8 statusmask;	/* status mask                       */
+	UINT32 mode;		/* Reg.08 : CSM , notesel,etc.       */
+	/* Timer */
+	int T[2];			/* timer counter       */
+	UINT8 st[2];		/* timer enable        */
+	/* FM channel slots */
+	OPL_CH *P_CH;		/* pointer of CH       */
+	int	max_ch;			/* maximum channel     */
+	/* Rythm sention */
+	UINT8 rythm;		/* Rythm mode , key flag */
+#if BUILD_Y8950
+	/* Delta-T ADPCM unit (Y8950) */
+	YM_DELTAT *deltat;			/* DELTA-T ADPCM       */
+#endif
+	/* Keyboard / I/O interface unit (Y8950) */
+	UINT8 portDirection;
+	UINT8 portLatch;
+	OPL_PORTHANDLER_R porthandler_r;
+	OPL_PORTHANDLER_W porthandler_w;
+	int port_param;
+	OPL_PORTHANDLER_R keyboardhandler_r;
+	OPL_PORTHANDLER_W keyboardhandler_w;
+	int keyboard_param;
+	/* time tables */
+	INT32 AR_TABLE[75];	/* atttack rate tables */
+	INT32 DR_TABLE[75];	/* decay rate tables   */
+	UINT32 FN_TABLE[1024];  /* fnumber -> increment counter */
+	/* LFO */
+	INT32 *ams_table;
+	INT32 *vib_table;
+	INT32 amsCnt;
+	INT32 amsIncr;
+	INT32 vibCnt;
+	INT32 vibIncr;
+	/* wave selector enable flag */
+	UINT8 wavesel;
+	/* external event callback handler */
+	OPL_TIMERHANDLER  TimerHandler;		/* TIMER handler   */
+	int TimerParam;						/* TIMER parameter */
+	OPL_IRQHANDLER    IRQHandler;		/* IRQ handler    */
+	int IRQParam;						/* IRQ parameter  */
+	OPL_UPDATEHANDLER UpdateHandler;	/* stream update handler   */
+	int UpdateParam;					/* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950  (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
+/* Y8950 port handlers */
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL,int a,int v);
+unsigned char OPLRead(FM_OPL *OPL,int a);
+int OPLTimerOver(FM_OPL *OPL,int c);
+
+/* YM3626/YM3812 local section */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+#endif
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/gstnsf.h	2005-03-26 16:43:16.000000000 +0100
@@ -0,0 +1,81 @@
+/* Copyright (C) 2003 Johan Dahlin <johan@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_NSFDEC_H__
+#define __GST_NSFDEC_H__
+
+#include <stdlib.h>
+
+#include <gst/gst.h>
+
+#include "types.h"
+#include "nsf.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_NSFDEC \
+  (gst_nsfdec_get_type())
+#define GST_NSFDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NSFDEC,GstNsfDec))
+#define GST_NSFDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NSFDEC,GstNsfDec))
+#define GST_IS_NSFDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NSFDEC))
+#define GST_IS_NSFDEC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NSFDEC))
+
+typedef struct _GstNsfDec GstNsfDec;
+typedef struct _GstNsfDecClass GstNsfDecClass;
+
+struct _GstNsfDec {
+  GstElement 	 element;
+
+  /* pads */
+  GstPad 	*sinkpad, 
+  		*srcpad;
+
+  gint 		 state;
+  guint64	 samples;
+  GstBuffer	*tune_buffer;
+  gint 		 tune_number;
+  guint64 	 total_bytes;
+
+  gulong	 blocksize;
+
+  GstCaps	*metadata;
+
+  nsf_t         *nsf;
+  int            frequency;
+  int            bits;
+  gboolean       stereo;
+  int            channels;
+  
+  int            dataSize;
+  int            bufferSize;
+};
+
+struct _GstNsfDecClass {
+  GstElementClass parent_class;
+};
+
+GType gst_nsfdec_get_type (void);
+	
+G_END_DECLS
+
+#endif /* __GST_NSFDEC_H__ */
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/log.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,44 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** log.h
+**
+** Error logging header file
+** $Id: log.h,v 1.4 2000/06/09 15:12:25 matt Exp $
+*/
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <stdio.h>
+
+extern int log_init(void);
+extern void log_shutdown(void);
+extern void log_print(const char *string);
+extern void log_printf(const char *format, ...);
+
+#endif /* _LOG_H_ */
+
+/*
+** $Log: log.h,v $
+** Revision 1.4  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/memguard.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,63 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** memguard.h
+**
+** memory allocation wrapper routines
+** $Id: memguard.h,v 1.5 2000/06/26 04:54:48 matt Exp $
+*/
+
+#ifndef  _MEMGUARD_H_
+#define  _MEMGUARD_H_
+
+#ifdef NOFRENDO_DEBUG
+
+#define  malloc(s)   _my_malloc((s), __FILE__, __LINE__)
+#define  free(d)     _my_free((void **) &(d), __FILE__, __LINE__)
+
+extern void *_my_malloc(int size, char *file, int line);
+extern void _my_free(void **data, char *file, int line);
+
+#else /* Non-debugging versions of calls */
+
+#define  malloc(s)   _my_malloc((s))
+#define  free(d)     _my_free((void **) &(d))
+
+extern void *_my_malloc(int size);
+extern void _my_free(void **data);
+
+#endif /* NOFRENDO_DEBUG */
+
+
+extern void mem_checkblocks(void);
+extern void mem_checkleaks(void);
+
+extern boolean mem_debug;
+
+#endif   /* _MEMGUARD_H_ */
+
+/*
+** $Log: memguard.h,v $
+** Revision 1.5  2000/06/26 04:54:48  matt
+** simplified and made more robust
+**
+** Revision 1.4  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/mmc5_snd.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,77 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** mmc5_snd.h
+**
+** Nintendo MMC5 sound emulation header
+** $Id: mmc5_snd.h,v 1.2 2000/06/20 04:06:16 matt Exp $
+*/
+
+#ifndef _MMC5_SND_H_
+#define _MMC5_SND_H_
+
+#define  MMC5_WRA0    0x5000
+#define  MMC5_WRA1    0x5001
+#define  MMC5_WRA2    0x5002
+#define  MMC5_WRA3    0x5003
+#define  MMC5_WRB0    0x5004
+#define  MMC5_WRB1    0x5005
+#define  MMC5_WRB2    0x5006
+#define  MMC5_WRB3    0x5007
+#define  MMC5_SMASK   0x5015
+
+typedef struct mmc5rectangle_s
+{
+   uint8 regs[4];
+
+   boolean enabled;
+   
+   int32 phaseacc;
+   int32 freq;
+   int32 output_vol;
+   boolean fixed_envelope;
+   boolean holdnote;
+   uint8 volume;
+
+   int32 env_phase;
+   int32 env_delay;
+   uint8 env_vol;
+
+   int vbl_length;
+   uint8 adder;
+   int duty_flip;
+} mmc5rectangle_t;
+
+
+#include "nes_apu.h"
+
+extern apuext_t mmc5_ext;
+
+#endif /* !_MMC5_SND_H_ */
+
+/*
+** $Log: mmc5_snd.h,v $
+** Revision 1.2  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.1  2000/06/20 00:06:47  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/nes6502.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,128 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** nes6502.h
+**
+** NES custom 6502 CPU definitions / prototypes
+** $Id: nes6502.h,v 1.4 2000/06/09 15:12:25 matt Exp $
+*/
+
+/* NOTE: 16-bit addresses avoided like the plague: use 32-bit values
+**       wherever humanly possible
+*/
+#ifndef _NES6502_H_
+#define _NES6502_H_
+
+/* Define this to enable decimal mode in ADC / SBC (not needed in NES) */
+/*#define  NES6502_DECIMAL*/
+
+/* number of bank pointers the CPU emulation core handles */
+#ifdef NSF_PLAYER
+#define  NES6502_4KBANKS
+#endif
+
+#ifdef NES6502_4KBANKS
+#define  NES6502_NUMBANKS  16
+#define  NES6502_BANKSHIFT 12
+#else
+#define  NES6502_NUMBANKS  8
+#define  NES6502_BANKSHIFT 13
+#endif
+
+#define  NES6502_BANKMASK  ((0x10000 / NES6502_NUMBANKS) - 1)
+
+
+/* P (flag) register bitmasks */
+#define  N_FLAG         0x80
+#define  V_FLAG         0x40
+#define  R_FLAG         0x20  /* Reserved, always 1 */
+#define  B_FLAG         0x10
+#define  D_FLAG         0x08
+#define  I_FLAG         0x04
+#define  Z_FLAG         0x02
+#define  C_FLAG         0x01
+
+/* Vector addresses */
+#define  NMI_VECTOR     0xFFFA
+#define  RESET_VECTOR   0xFFFC
+#define  IRQ_VECTOR     0xFFFE
+
+/* cycle counts for interrupts */
+#define  INT_CYCLES     7
+#define  RESET_CYCLES   6
+
+#define  NMI_MASK       0x01
+#define  IRQ_MASK       0x02
+
+/* Stack is located on 6502 page 1 */
+#define  STACK_OFFSET   0x0100
+
+typedef struct
+{
+   uint32 min_range, max_range;
+   uint8 (*read_func)(uint32 address);
+} nes6502_memread;
+
+typedef struct
+{
+   uint32 min_range, max_range;
+   void (*write_func)(uint32 address, uint8 value);
+} nes6502_memwrite;
+
+typedef struct
+{
+   uint8 *mem_page[NES6502_NUMBANKS];  /* memory page pointers */
+   nes6502_memread *read_handler;
+   nes6502_memwrite *write_handler;
+   int dma_cycles;
+   uint32 pc_reg;
+   uint8 a_reg, p_reg, x_reg, y_reg, s_reg;
+   uint8 int_pending;
+} nes6502_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Functions which govern the 6502's execution */
+extern void nes6502_init(void);
+extern void nes6502_reset(void);
+extern int nes6502_execute(int total_cycles);
+extern void nes6502_nmi(void);
+extern void nes6502_irq(void);
+extern uint8 nes6502_getbyte(uint32 address);
+extern uint32 nes6502_getcycles(boolean reset_flag);
+extern void nes6502_setdma(int cycles);
+
+/* Context get/set */
+extern void nes6502_setcontext(nes6502_context *cpu);
+extern void nes6502_getcontext(nes6502_context *cpu);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _NES6502_H_ */
+
+/*
+** $Log: nes6502.h,v $
+** Revision 1.4  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/nes_apu.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,322 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** nes_apu.h
+**
+** NES APU emulation header file
+** $Id: nes_apu.h,v 1.12 2000/07/04 04:54:48 matt Exp $
+*/
+
+#ifndef _NES_APU_H_
+#define _NES_APU_H_
+
+#ifdef __GNUC__
+#define  INLINE      static inline
+#elif defined(WIN32)
+#define  INLINE      static __inline
+#else
+#define  INLINE      static
+#endif
+
+/* define this for realtime generated noise */
+#define  REALTIME_NOISE
+
+#define  APU_WRA0       0x4000
+#define  APU_WRA1       0x4001
+#define  APU_WRA2       0x4002
+#define  APU_WRA3       0x4003
+#define  APU_WRB0       0x4004
+#define  APU_WRB1       0x4005
+#define  APU_WRB2       0x4006
+#define  APU_WRB3       0x4007
+#define  APU_WRC0       0x4008
+#define  APU_WRC2       0x400A
+#define  APU_WRC3       0x400B
+#define  APU_WRD0       0x400C
+#define  APU_WRD2       0x400E
+#define  APU_WRD3       0x400F
+#define  APU_WRE0       0x4010
+#define  APU_WRE1       0x4011
+#define  APU_WRE2       0x4012
+#define  APU_WRE3       0x4013
+
+#define  APU_OAMDMA     0x4014
+#define  APU_SMASK      0x4015
+#define  APU_JOY0       0x4016
+#define  APU_JOY1       0x4017
+
+/* length of generated noise */
+#define  APU_NOISE_32K  0x7FFF
+#define  APU_NOISE_93   93
+
+#define  APU_BASEFREQ   (NES_MASTER_CLOCK / 12)
+
+/* to/from 16.16 fixed point */
+#define  APU_TO_FIXED(x)    ((x) << 16)
+#define  APU_FROM_FIXED(x)  ((x) >> 16)
+
+
+/* channel structures */
+/* As much data as possible is precalculated,
+** to keep the sample processing as lean as possible
+*/
+ 
+typedef struct rectangle_s
+{
+   uint8 regs[4];
+
+   boolean enabled;
+   
+   int32 phaseacc;
+   int32 freq;
+   int32 output_vol;
+   boolean fixed_envelope;
+   boolean holdnote;
+   uint8 volume;
+
+   int32 sweep_phase;
+   int32 sweep_delay;
+   boolean sweep_on;
+   uint8 sweep_shifts;
+   uint8 sweep_length;
+   boolean sweep_inc;
+   int32 freq_limit;
+
+   int32 env_phase;
+   int32 env_delay;
+   uint8 env_vol;
+
+   int vbl_length;
+   uint8 adder;
+   int duty_flip;
+} rectangle_t;
+
+/*
+enum
+{
+   COUNTMODE_LOAD,
+   COUNTMODE_COUNT
+};
+*/
+
+typedef struct triangle_s
+{
+   uint8 regs[3];
+
+   boolean enabled;
+
+   int32 freq;
+   int32 phaseacc;
+   int32 output_vol;
+
+   uint8 adder;
+
+   boolean holdnote;
+   boolean counter_started;
+   /* quasi-hack */
+   int write_latency;
+
+  /* boolean countmode; */
+
+   int vbl_length;
+   int linear_length;
+
+} triangle_t;
+
+
+typedef struct noise_s
+{
+   uint8 regs[3];
+
+   boolean enabled;
+
+   int32 freq;
+   int32 phaseacc;
+   int32 output_vol;
+
+   int32 env_phase;
+   int32 env_delay;
+   uint8 env_vol;
+   boolean fixed_envelope;
+   boolean holdnote;
+
+   uint8 volume;
+
+   int vbl_length;
+
+#ifdef REALTIME_NOISE
+   uint8 xor_tap;
+#else
+   boolean short_sample;
+   int cur_pos;
+#endif /* REALTIME_NOISE */
+} noise_t;
+
+typedef struct dmc_s
+{
+   uint8 regs[4];
+
+   /* bodge for timestamp queue */
+   boolean enabled;
+   
+   int32 freq;
+   int32 phaseacc;
+   int32 output_vol;
+
+   uint32 address;
+   uint32 cached_addr;
+   int dma_length;
+   int cached_dmalength;
+   uint8 cur_byte;
+
+   boolean looping;
+   boolean irq_gen;
+   boolean irq_occurred;
+
+} dmc_t;
+
+enum
+{
+   APU_FILTER_NONE,
+   APU_FILTER_LOWPASS,
+   APU_FILTER_WEIGHTED
+};
+
+typedef struct
+{
+   uint32 min_range, max_range;
+   uint8 (*read_func)(uint32 address);
+} apu_memread;
+
+typedef struct
+{
+   uint32 min_range, max_range;
+   void (*write_func)(uint32 address, uint8 value);
+} apu_memwrite;
+
+/* external sound chip stuff */
+typedef struct apuext_s
+{
+   void  (*init)(void);
+   void  (*shutdown)(void);
+   void  (*reset)(void);
+   int32 (*process)(void);
+   apu_memread *mem_read;
+   apu_memwrite *mem_write;
+} apuext_t;
+
+
+/* APU queue structure */
+#define  APUQUEUE_SIZE  4096
+#define  APUQUEUE_MASK  (APUQUEUE_SIZE - 1)
+
+/* apu ring buffer member */
+typedef struct apudata_s
+{
+   uint32 timestamp, address;
+   uint8 value;
+} apudata_t;
+
+
+typedef struct apu_s
+{
+   rectangle_t rectangle[2];
+   triangle_t triangle;
+   noise_t noise;
+   dmc_t dmc;
+   uint8 enable_reg;
+
+   apudata_t queue[APUQUEUE_SIZE];
+   int q_head, q_tail;
+   uint32 elapsed_cycles;
+
+   void *buffer; /* pointer to output buffer */
+   int num_samples;
+
+   boolean mix_enable[6];
+   int filter_type;
+
+   int32 cycle_rate;
+
+   int sample_rate;
+   int sample_bits;
+   int refresh_rate;
+
+   void (*process)(void *buffer, int num_samples);
+
+   /* external sound chip */
+   apuext_t *ext;
+} apu_t;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Function prototypes */
+extern apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean stereo);
+extern void apu_destroy(apu_t *apu);
+extern void apu_setext(apu_t *apu, apuext_t *ext);
+extern void apu_setfilter(int filter_type);
+extern void apu_process(void *buffer, int num_samples);
+extern void apu_reset(void);
+extern void apu_setchan(int chan, boolean enabled);
+extern int32 apu_getcyclerate(void);
+extern apu_t *apu_getcontext(void);
+
+extern uint8 apu_read(uint32 address);
+extern void apu_write(uint32 address, uint8 value);
+
+/* for visualization */
+extern void apu_getpcmdata(void **data, int *num_samples, int *sample_bits);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _NES_APU_H_ */
+
+/*
+** $Log: nes_apu.h,v $
+** Revision 1.12  2000/07/04 04:54:48  matt
+** minor changes that helped with MAME
+**
+** Revision 1.11  2000/07/03 02:18:53  matt
+** much better external module exporting
+**
+** Revision 1.10  2000/06/26 05:00:37  matt
+** cleanups
+**
+** Revision 1.9  2000/06/23 03:29:28  matt
+** cleaned up external sound inteface
+**
+** Revision 1.8  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.7  2000/06/20 00:07:35  matt
+** added convenience members to apu_t struct
+**
+** Revision 1.6  2000/06/09 16:49:02  matt
+** removed all floating point from sound generation
+**
+** Revision 1.5  2000/06/09 15:12:28  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/nsf.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,141 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** nsf.h
+**
+** NSF loading/saving related defines / prototypes
+** $Id: nsf.h,v 1.11 2000/07/04 04:59:24 matt Exp $
+*/
+
+#ifndef _NSF_H_
+#define _NSF_H_
+
+#include "osd.h"
+#include "nes6502.h"
+#include "nes_apu.h"
+
+#define  NSF_MAGIC   "NESM\x1A"
+
+#define  NSF_DEDICATED_PAL       0x01
+#define  NSF_DUAL_PAL_NTSC       0x02
+
+#define  EXT_SOUND_NONE          0x00
+#define  EXT_SOUND_VRCVI         0x01
+#define  EXT_SOUND_VRCVII        0x02
+#define  EXT_SOUND_FDS           0x04
+#define  EXT_SOUND_MMC5          0x08
+#define  EXT_SOUND_NAMCO106      0x10
+#define  EXT_SOUND_SUNSOFT_FME07 0x20
+/* bits 6,7: future expansion */
+
+#define  NSF_HEADER_SIZE         0x80
+
+/* 60 Hertz refresh (NTSC) */
+#define  NES_MASTER_CLOCK     21477272.7272
+#define  NTSC_REFRESH         60
+#define  NTSC_SUBCARRIER_DIV  12
+#define  NTSC_SCANLINES       262
+
+#define  NES_FRAME_CYCLES     ((NES_MASTER_CLOCK / NTSC_SUBCARRIER_DIV) / NTSC_REFRESH)
+#define  NES_SCANLINE_CYCLES  (NES_FRAME_CYCLES / NTSC_SCANLINES)
+
+/* filter levels */
+enum
+{
+   NSF_FILTER_NONE,
+   NSF_FILTER_LOWPASS,
+   NSF_FILTER_WEIGHTED
+};
+
+typedef struct nsf_s
+{
+   /* NESM header */
+   uint8  id[5]               __PACKED__; /* NESM\x1A */
+   uint8  version             __PACKED__; /* spec version */
+   uint8  num_songs           __PACKED__; /* total num songs */
+   uint8  start_song          __PACKED__; /* first song */
+   uint16 load_addr           __PACKED__; /* loc to load code */
+   uint16 init_addr           __PACKED__; /* init call address */
+   uint16 play_addr           __PACKED__; /* play call address */
+   uint8  song_name[32]       __PACKED__; /* name of song */
+   uint8  artist_name[32]     __PACKED__; /* artist name */
+   uint8  copyright[32]       __PACKED__; /* copyright info */
+   uint16 ntsc_speed          __PACKED__; /* playback speed (if NTSC) */
+   uint8  bankswitch_info[8]  __PACKED__; /* initial code banking */
+   uint16 pal_speed           __PACKED__; /* playback speed (if PAL) */
+   uint8  pal_ntsc_bits       __PACKED__; /* NTSC/PAL determination bits */
+   uint8  ext_sound_type      __PACKED__; /* type of external sound gen. */
+   uint8  reserved[4]         __PACKED__; /* reserved */
+
+   /* things that the NSF player needs */
+   uint8  *data;              /* actual NSF data */
+   uint32 length;             /* length of data */
+   uint32 playback_rate;      /* current playback rate */
+   uint8  current_song;       /* current song */
+   boolean bankswitched;      /* is bankswitched? */
+
+   /* CPU and APU contexts */
+   nes6502_context *cpu;
+   apu_t *apu;
+
+   /* our main processing routine, calls all external mixing routines */
+   void (*process)(void *buffer, int num_samples);
+} nsf_t;
+
+/* Function prototypes */
+extern void nsf_init(void);
+
+extern nsf_t *nsf_load(char *filename, void *source, int length);
+extern void nsf_free(nsf_t **nsf_info);
+
+extern void nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits, 
+                          boolean stereo);
+extern void nsf_frame(nsf_t *nsf);
+extern void nsf_setchan(nsf_t *nsf, int chan, boolean enabled);
+extern void nsf_setfilter(nsf_t *nsf, int filter_type);
+
+#endif /* _NSF_H_ */
+
+/*
+** $Log: nsf.h,v $
+** Revision 1.11  2000/07/04 04:59:24  matt
+** removed DOS-specific stuff
+**
+** Revision 1.10  2000/07/03 02:19:36  matt
+** dynamic address range handlers, cleaner and faster
+**
+** Revision 1.9  2000/06/23 03:27:58  matt
+** cleaned up external sound inteface
+**
+** Revision 1.8  2000/06/20 04:04:37  matt
+** moved external soundchip struct to apu module
+**
+** Revision 1.7  2000/06/20 00:05:45  matt
+** changed to driver-based external sound generation
+**
+** Revision 1.6  2000/06/13 03:51:54  matt
+** update API to take freq/sample data on nsf_playtrack
+**
+** Revision 1.5  2000/06/12 01:13:00  matt
+** added CPU/APU as members of the nsf struct
+**
+** Revision 1.4  2000/06/09 15:12:26  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/osd.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,91 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** osd.h
+**
+** O/S dependent routine defintions (must be customized)
+** $Id: osd.h,v 1.7 2000/07/04 04:45:33 matt Exp $
+*/
+
+#ifndef _OSD_H_
+#define _OSD_H_
+
+
+#ifdef __GNUC__
+#define  __PACKED__  __attribute__ ((packed))
+#define  PATH_SEP    '/'
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include "dos_ints.h"
+#endif
+#elif defined(WIN32)
+#define  __PACKED__
+#define  PATH_SEP    '\\'
+#else /* crapintosh? */
+#define  __PACKED__
+#define  PATH_SEP    ':'
+#endif
+
+extern void osd_loginit(void);
+extern void osd_logshutdown(void);
+extern void osd_logprint(const char *string);
+
+extern int osd_startsound(void (*playfunc)(void *buffer, int size));
+extern int osd_getsoundbps(void);
+extern int osd_getsamplerate(void);
+
+
+#ifndef NSF_PLAYER
+#include "rgb.h"
+#include "bitmap.h"
+
+extern bitmap_t *osd_getvidbuf(void);
+typedef void (*blitproc_t)(bitmap_t *bmp, int x_pos, int y_pos, int width, int height);
+extern blitproc_t osd_blit;
+extern void osd_copytoscreen(void);
+
+extern void osd_showusage(char *filename);
+extern void osd_fullname(char *fullname, const char *shortname);
+extern char *osd_newextension(char *string, char *ext);
+
+extern void osd_setpalette(rgb_t *pal);
+extern void osd_restorepalette(void);
+
+extern void osd_getinput(void);
+extern int osd_gethostinput(void);
+extern void osd_getmouse(int *x, int *y, int *button);
+
+extern int osd_init(void);
+extern void osd_shutdown(void);
+#endif /* !NSF_PLAYER */
+
+#endif /* _OSD_H_ */
+
+/*
+** $Log: osd.h,v $
+** Revision 1.7  2000/07/04 04:45:33  matt
+** moved INLINE define into types.h
+**
+** Revision 1.6  2000/06/29 16:06:18  neil
+** Wrapped DOS-specific headers in an ifdef
+**
+** Revision 1.5  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/types.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** types.h
+**
+** Data type definitions
+** $Id: types.h,v 1.7 2000/07/04 04:46:44 matt Exp $
+*/
+
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+/* Define this if running on little-endian (x86) systems */
+#define  HOST_LITTLE_ENDIAN
+
+#ifdef __GNUC__
+#define  INLINE      static inline
+#elif defined(WIN32)
+#define  INLINE      static __inline
+#else /* crapintosh? */
+#define  INLINE      static
+#endif
+
+/* These should be changed depending on the platform */
+typedef  char     int8;
+typedef  short    int16;
+typedef  int      int32;
+
+typedef  unsigned char  uint8;
+typedef  unsigned short uint16;
+typedef  unsigned int   uint32;
+
+typedef  uint8    boolean;
+
+#ifndef  TRUE
+#define  TRUE     1
+#endif
+#ifndef  FALSE
+#define  FALSE    0
+#endif
+
+#ifndef  NULL
+#define  NULL     ((void *) 0)
+#endif
+
+#ifdef NOFRENDO_DEBUG
+#include <stdlib.h>
+#include "memguard.h"
+#include "log.h"
+#define  ASSERT(expr)      if (FALSE == (expr))\
+                           {\
+                             log_printf("ASSERT: line %d of %s\n", __LINE__, __FILE__);\
+                             log_shutdown();\
+                             exit(1);\
+                           }
+#define  ASSERT_MSG(msg)   {\
+                             log_printf("ASSERT: %s\n", msg);\
+                             log_shutdown();\
+                             exit(1);\
+                           }
+#else /* Not debugging */
+#include "memguard.h"
+#define  ASSERT(expr)
+#define  ASSERT_MSG(msg)
+#endif
+
+#endif /* _TYPES_H_ */
+
+/*
+** $Log: types.h,v $
+** Revision 1.7  2000/07/04 04:46:44  matt
+** moved INLINE define from osd.h
+**
+** Revision 1.6  2000/06/09 15:12:25  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/vrc7_snd.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,67 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** vrc7_snd.h
+**
+** VRCVII (Konami MMC) sound hardware emulation header
+** Thanks to Charles MacDonald (cgfm2@hooked.net) for donating code.
+**
+** $Id: vrc7_snd.h,v 1.3 2000/07/04 04:51:02 matt Exp $
+*/
+
+#ifndef _VRC7_SND_H_
+#define _VRC7_SND_H_
+
+#include "fmopl.h"
+
+/* VRC7 context */
+typedef struct vrc7_s
+{
+   uint8 reg[0x40];        /* 64 registers */
+   uint8 latch;            /* Register latch */
+   uint8 user[0x10];       /* User instrument settings */
+   struct
+   {
+      uint16 frequency;    /* Channel frequency */
+      uint8 volume;        /* Channel volume */
+      uint8 instrument;    /* Channel instrument */
+   } channel[9];
+
+   FM_OPL *ym3812;
+} vrc7_t;
+
+
+#include "nes_apu.h"
+
+extern apuext_t vrc7_ext;
+
+#endif /* !_VRC7_SND_H_ */
+
+/*
+** $Log: vrc7_snd.h,v $
+** Revision 1.3  2000/07/04 04:51:02  matt
+** made data types stricter
+**
+** Revision 1.2  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.1  2000/06/20 00:06:47  matt
+** initial revision
+**
+*/
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/vrcvisnd.h	2004-06-30 13:32:06.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
+**
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of version 2 of the GNU Library General 
+** Public License as published by the Free Software Foundation.
+**
+** This program 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 
+** Library General Public License for more details.  To obtain a 
+** copy of the GNU Library General Public License, write to the Free 
+** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Any permitted reproduction of these routines, in whole or in part,
+** must bear this legend.
+**
+**
+** vrcvisnd.h
+**
+** VRCVI (Konami MMC) sound hardware emulation header
+** $Id: vrcvisnd.h,v 1.7 2000/06/20 04:06:16 matt Exp $
+*/
+
+#ifndef _VRCVISND_H_
+#define _VRCVISND_H_
+
+typedef struct vrcvirectangle_s
+{
+   uint8 reg[3];
+   int32 phaseacc;
+   uint8 adder;
+
+   int32 freq;
+   int32 volume;
+   uint8 duty_flip;
+   boolean enabled;
+} vrcvirectangle_t;
+
+typedef struct vrcvisawtooth_s
+{
+   uint8 reg[3];
+   int32 phaseacc;
+   uint8 adder;
+   uint8 output_acc;
+
+   int32 freq;
+   uint8 volume;
+   boolean enabled;
+} vrcvisawtooth_t;
+
+typedef struct vrcvisnd_s
+{
+   vrcvirectangle_t rectangle[2];
+   vrcvisawtooth_t saw;
+} vrcvisnd_t;
+
+#include "nes_apu.h"
+
+extern apuext_t vrcvi_ext;
+
+#endif /* _VRCVISND_H_ */
+
+/*
+** $Log: vrcvisnd.h,v $
+** Revision 1.7  2000/06/20 04:06:16  matt
+** migrated external sound definition to apu module
+**
+** Revision 1.6  2000/06/20 00:08:58  matt
+** changed to driver based API
+**
+** Revision 1.5  2000/06/09 16:49:02  matt
+** removed all floating point from sound generation
+**
+** Revision 1.4  2000/06/09 15:12:28  matt
+** initial revision
+**
+*/
+
--- /dev/null	2005-03-26 11:33:10.278634432 +0100
+++ gst/nsf/Makefile.am	2005-03-26 21:35:33.000000000 +0100
@@ -0,0 +1,36 @@
+
+plugin_LTLIBRARIES = libgstnsf.la
+
+NOSEFART_SOURCES=fmopl.c \
+	log.c \
+	mmc5_snd.c \
+	nes_apu.c \
+	vrc7_snd.c \
+	fds_snd.c \
+	memguard.c \
+	nes6502.c \
+	nsf.c \
+	vrcvisnd.c
+
+NOSEFART_INCLUDES=fmopl.h \
+	log.h \
+	mmc5_snd.h \
+	nes_apu.h \
+	osd.h \
+	vrc7_snd.h \
+	fds_snd.h \
+	memguard.h \
+	nes6502.h \
+	nsf.h \
+	types.h \
+	vrcvisnd.h
+
+libgstnsf_la_SOURCES = gstnsf.c $(NOSEFART_SOURCES)
+
+libgstnsf_la_CFLAGS = $(GST_CFLAGS) -DNSF_PLAYER
+libgstnsf_la_LIBADD =
+libgstnsf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = gstnsf.h $(NOSEFART_INCLUDES)
+
+EXTRA_DIST =
