summaryrefslogtreecommitdiffstats
path: root/gst/nsf
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@gmail.com>2006-07-13 15:07:28 +0000
committerWim Taymans <wim.taymans@gmail.com>2006-07-13 15:07:28 +0000
commitaae22fa1c92df5ee70fa478f831c64287344879c (patch)
tree82d1b83aff375d2ab6b0d09427560550c1b7db1e /gst/nsf
parent9d2c04267dec2eda43a150b2541d5c5a0f9eaf18 (diff)
downloadgst-plugins-bad-aae22fa1c92df5ee70fa478f831c64287344879c.tar.gz
gst-plugins-bad-aae22fa1c92df5ee70fa478f831c64287344879c.tar.bz2
gst-plugins-bad-aae22fa1c92df5ee70fa478f831c64287344879c.zip
Added NSF decoder plugin. Fixes 151192.
Original commit message from CVS: Based on patches by: Johan Dahlin <johan at gnome dot org> Ronald Bultje <rbultje at ronald dot bitfreak dot net> * configure.ac: * gst/nsf/Makefile.am: * gst/nsf/dis6502.h: * gst/nsf/fds_snd.c: * gst/nsf/fds_snd.h: * gst/nsf/fmopl.c: * gst/nsf/fmopl.h: * gst/nsf/gstnsf.c: * gst/nsf/gstnsf.h: * gst/nsf/log.c: * gst/nsf/log.h: * gst/nsf/memguard.c: * gst/nsf/memguard.h: * gst/nsf/mmc5_snd.c: * gst/nsf/mmc5_snd.h: * gst/nsf/nes6502.c: * gst/nsf/nes6502.h: * gst/nsf/nes_apu.c: * gst/nsf/nes_apu.h: * gst/nsf/nsf.c: * gst/nsf/nsf.h: * gst/nsf/osd.h: * gst/nsf/types.h: * gst/nsf/vrc7_snd.c: * gst/nsf/vrc7_snd.h: * gst/nsf/vrcvisnd.c: * gst/nsf/vrcvisnd.h: Added NSF decoder plugin. Fixes 151192.
Diffstat (limited to 'gst/nsf')
-rw-r--r--gst/nsf/Makefile.am36
-rw-r--r--gst/nsf/dis6502.h79
-rw-r--r--gst/nsf/fds_snd.c124
-rw-r--r--gst/nsf/fds_snd.h77
-rw-r--r--gst/nsf/fmopl.c1392
-rw-r--r--gst/nsf/fmopl.h164
-rw-r--r--gst/nsf/gstnsf.c635
-rw-r--r--gst/nsf/gstnsf.h86
-rw-r--r--gst/nsf/log.c148
-rw-r--r--gst/nsf/log.h76
-rw-r--r--gst/nsf/memguard.c398
-rw-r--r--gst/nsf/memguard.h95
-rw-r--r--gst/nsf/mmc5_snd.c383
-rw-r--r--gst/nsf/mmc5_snd.h109
-rw-r--r--gst/nsf/nes6502.c2438
-rw-r--r--gst/nsf/nes6502.h160
-rw-r--r--gst/nsf/nes_apu.c1246
-rw-r--r--gst/nsf/nes_apu.h354
-rw-r--r--gst/nsf/nsf.c648
-rw-r--r--gst/nsf/nsf.h173
-rw-r--r--gst/nsf/osd.h123
-rw-r--r--gst/nsf/types.h125
-rw-r--r--gst/nsf/vrc7_snd.c376
-rw-r--r--gst/nsf/vrc7_snd.h99
-rw-r--r--gst/nsf/vrcvisnd.c241
-rw-r--r--gst/nsf/vrcvisnd.h112
26 files changed, 9897 insertions, 0 deletions
diff --git a/gst/nsf/Makefile.am b/gst/nsf/Makefile.am
new file mode 100644
index 00000000..bff272a2
--- /dev/null
+++ b/gst/nsf/Makefile.am
@@ -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 =
diff --git a/gst/nsf/dis6502.h b/gst/nsf/dis6502.h
new file mode 100644
index 00000000..578defe6
--- /dev/null
+++ b/gst/nsf/dis6502.h
@@ -0,0 +1,79 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** Revision 1.4 2000/06/09 15:12:25 matt
+** initial revision
+**
+*/
+
diff --git a/gst/nsf/fds_snd.c b/gst/nsf/fds_snd.c
new file mode 100644
index 00000000..d2cfccb7
--- /dev/null
+++ b/gst/nsf/fds_snd.c
@@ -0,0 +1,124 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/fds_snd.h b/gst/nsf/fds_snd.h
new file mode 100644
index 00000000..4ec90f15
--- /dev/null
+++ b/gst/nsf/fds_snd.h
@@ -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.
+**
+**
+** fds_snd.h
+**
+** Famicom Disk System sound emulation
+** $Id$
+*/
+
+#ifndef _FDS_SND_H_
+#define _FDS_SND_H_
+
+#include "nes_apu.h"
+
+extern apuext_t fds_ext;
+
+
+#endif /* _VRCVISND_H_ */
+
+/*
+** $Log$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
+
diff --git a/gst/nsf/fmopl.c b/gst/nsf/fmopl.c
new file mode 100644
index 00000000..c1e321f3
--- /dev/null
+++ b/gst/nsf/fmopl.c
@@ -0,0 +1,1392 @@
+/*
+**
+** 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_PTR);
+ return 0;
+ }
+ if ((AMS.TABLE = malloc (AMS_ENT * 2 * sizeof (INT32))) == NULL) {
+ free (TL.TABLE_PTR);
+ free (SIN.TABLE_PTR);
+ return 0;
+ }
+ if ((VIB.TABLE = malloc (VIB_ENT * 2 * sizeof (INT32))) == NULL) {
+ free (TL.TABLE_PTR);
+ free (SIN.TABLE_PTR);
+ free (AMS.TABLE_PTR);
+ 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_PTR);
+ free (SIN.TABLE_PTR);
+ free (AMS.TABLE_PTR);
+ free (VIB.TABLE_PTR);
+}
+
+/* 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;
+}
diff --git a/gst/nsf/fmopl.h b/gst/nsf/fmopl.h
new file mode 100644
index 00000000..92a2859d
--- /dev/null
+++ b/gst/nsf/fmopl.h
@@ -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
diff --git a/gst/nsf/gstnsf.c b/gst/nsf/gstnsf.c
new file mode 100644
index 00000000..8f3b53ee
--- /dev/null
+++ b/gst/nsf/gstnsf.c
@@ -0,0 +1,635 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * (C) <2004> Johan Dahlin <johan@gnome.org>
+ * (C) <2006> Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "gstnsf.h"
+
+#define GST_CAT_DEFAULT nsfdec_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+static const GstElementDetails gst_nsfdec_details =
+GST_ELEMENT_DETAILS ("Nsf decoder",
+ "Codec/Audio/Decoder",
+ "Using nosefart to decode NSF audio tunes",
+ "Johan Dahlin <johan@gnome.org>");
+
+/* Nsfdec signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+#define DEFAULT_TUNE 0
+#define DEFAULT_FILTER NSF_FILTER_NONE
+
+enum
+{
+ PROP_0,
+ PROP_TUNE,
+ PROP_FILTER,
+};
+
+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) BYTE_ORDER, "
+ "signed = (boolean) TRUE, "
+ "width = (int) { 8, 16 }, "
+ "depth = (int) { 8, 16 }, "
+ "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]")
+ );
+
+#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, "NSF_FILTER_NONE", "None"},
+ {NSF_FILTER_LOWPASS, "NSF_FILTER_LOWPASS", "Lowpass"},
+ {NSF_FILTER_WEIGHTED, "NSF_FILTER_WEIGHTED", "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 void gst_nsfdec_class_init (GstNsfDec * klass);
+static void gst_nsfdec_init (GstNsfDec * nsfdec);
+static void gst_nsfdec_finalize (GObject * object);
+
+static GstFlowReturn gst_nsfdec_chain (GstPad * pad, GstBuffer * buffer);
+static gboolean gst_nsfdec_sink_event (GstPad * pad, GstEvent * event);
+
+static gboolean gst_nsfdec_src_convert (GstPad * pad, GstFormat src_format,
+ gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
+static gboolean gst_nsfdec_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_nsfdec_src_query (GstPad * pad, GstQuery * query);
+
+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;
+
+//static guint gst_nsfdec_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_nsfdec_get_type (void)
+{
+ static GType nsfdec_type = 0;
+
+ if (G_UNLIKELY (nsfdec_type == 0)) {
+ 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;
+}
+
+static void
+gst_nsfdec_base_init (gpointer g_class)
+{
+ 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_peek_parent (klass));
+
+ gobject_class->finalize = gst_nsfdec_finalize;
+ gobject_class->set_property = gst_nsfdec_set_property;
+ gobject_class->get_property = gst_nsfdec_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_TUNE,
+ g_param_spec_int ("tune", "tune", "tune", 1, 100, 1, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_FILTER,
+ g_param_spec_enum ("filter", "filter", "filter", GST_TYPE_NSF_FILTER,
+ NSF_FILTER_NONE, G_PARAM_WRITABLE));
+
+ GST_DEBUG_CATEGORY_INIT (nsfdec_debug, "nsfdec", 0,
+ "NES sound file (nsf) decoder");
+
+ nsf_init ();
+}
+
+static void
+gst_nsfdec_init (GstNsfDec * nsfdec)
+{
+ nsfdec->sinkpad =
+ gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
+ "sink");
+ gst_pad_set_query_function (nsfdec->sinkpad, NULL);
+ gst_pad_set_event_function (nsfdec->sinkpad, gst_nsfdec_sink_event);
+ gst_pad_set_chain_function (nsfdec->sinkpad, gst_nsfdec_chain);
+ gst_element_add_pad (GST_ELEMENT (nsfdec), nsfdec->sinkpad);
+
+ nsfdec->srcpad =
+ gst_pad_new_from_template (gst_static_pad_template_get (&src_templ),
+ "src");
+ gst_pad_set_event_function (nsfdec->srcpad, gst_nsfdec_src_event);
+ gst_pad_set_query_function (nsfdec->srcpad, gst_nsfdec_src_query);
+ gst_pad_use_fixed_caps (nsfdec->srcpad);
+ gst_element_add_pad (GST_ELEMENT (nsfdec), nsfdec->srcpad);
+
+ nsfdec->nsf = NULL;
+ nsfdec->state = NSF_STATE_NEED_TUNE;
+ nsfdec->tune_buffer = NULL;
+
+ nsfdec->blocksize = 0;
+ nsfdec->frequency = 44100;
+ nsfdec->bits = 8;
+ nsfdec->stereo = FALSE;
+ nsfdec->channels = 1;
+
+ nsfdec->tune_number = DEFAULT_TUNE;
+ nsfdec->filter = DEFAULT_FILTER;
+}
+
+static void
+gst_nsfdec_finalize (GObject * object)
+{
+ GstNsfDec *nsfdec = GST_NSFDEC (object);
+
+ if (nsfdec->tune_buffer)
+ gst_buffer_unref (nsfdec->tune_buffer);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+nsfdec_negotiate (GstNsfDec * nsfdec)
+{
+ GstCaps *allowed;
+ gboolean sign = TRUE;
+ gint width = 16, depth = 16;
+ GstStructure *structure;
+ int rate = 44100;
+ int channels = 1;
+ GstCaps *caps;
+
+ allowed = gst_pad_get_allowed_caps (nsfdec->srcpad);
+ if (!allowed)
+ goto nothing_allowed;
+
+ GST_DEBUG_OBJECT (nsfdec, "allowed caps: %" GST_PTR_FORMAT, allowed);
+
+ structure = gst_caps_get_structure (allowed, 0);
+
+ gst_structure_get_int (structure, "width", &width);
+ gst_structure_get_int (structure, "depth", &depth);
+
+ if (width && depth && width != depth)
+ goto wrong_width;
+
+ width = width | depth;
+ if (width) {
+ nsfdec->bits = width;
+ }
+
+ gst_structure_get_boolean (structure, "signed", &sign);
+ gst_structure_get_int (structure, "rate", &rate);
+ nsfdec->frequency = rate;
+ gst_structure_get_int (structure, "channels", &channels);
+ nsfdec->channels = channels;
+ nsfdec->stereo = (channels == 2);
+
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "endianness", G_TYPE_INT, G_BYTE_ORDER,
+ "signed", G_TYPE_BOOLEAN, TRUE,
+ "width", G_TYPE_INT, nsfdec->bits,
+ "depth", G_TYPE_INT, nsfdec->bits,
+ "rate", G_TYPE_INT, nsfdec->frequency,
+ "channels", G_TYPE_INT, nsfdec->channels, NULL);
+ gst_pad_set_caps (nsfdec->srcpad, caps);
+ gst_caps_unref (caps);
+
+ gst_caps_unref (allowed);
+
+ return TRUE;
+
+nothing_allowed:
+ {
+ GST_DEBUG_OBJECT (nsfdec, "could not get allowed caps");
+ return FALSE;
+ }
+wrong_width:
+ {
+ GST_DEBUG_OBJECT (nsfdec, "width %d and depth %d are different",
+ width, depth);
+ gst_caps_unref (allowed);
+ return FALSE;
+ }
+}
+
+static void
+play_loop (GstPad * pad)
+{
+ GstFlowReturn ret;
+ GstNsfDec *nsfdec;
+ GstBuffer *out;
+ gint64 value, offset, time;
+ GstFormat format;
+
+ nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+ out = gst_buffer_new_and_alloc (nsfdec->blocksize);
+ gst_buffer_set_caps (out, GST_PAD_CAPS (pad));
+
+ nsf_frame (nsfdec->nsf);
+ apu_process (GST_BUFFER_DATA (out), nsfdec->blocksize / nsfdec->bps);
+
+ /* get offset in samples */
+ format = GST_FORMAT_DEFAULT;
+ gst_nsfdec_src_convert (nsfdec->srcpad,
+ GST_FORMAT_BYTES, nsfdec->total_bytes, &format, &offset);
+ GST_BUFFER_OFFSET (out) = offset;
+
+ /* get current timestamp */
+ format = GST_FORMAT_TIME;
+ gst_nsfdec_src_convert (nsfdec->srcpad,
+ GST_FORMAT_BYTES, nsfdec->total_bytes, &format, &time);
+ GST_BUFFER_TIMESTAMP (out) = time;
+
+ /* update position and get new timestamp to calculate duration */
+ nsfdec->total_bytes += nsfdec->blocksize;
+
+ /* get offset in samples */
+ format = GST_FORMAT_DEFAULT;
+ gst_nsfdec_src_convert (nsfdec->srcpad,
+ GST_FORMAT_BYTES, nsfdec->total_bytes, &format, &value);
+ GST_BUFFER_OFFSET_END (out) = value;
+
+ format = GST_FORMAT_TIME;
+ gst_nsfdec_src_convert (nsfdec->srcpad,
+ GST_FORMAT_BYTES, nsfdec->total_bytes, &format, &value);
+ GST_BUFFER_DURATION (out) = value - time;
+
+ if ((ret = gst_pad_push (nsfdec->srcpad, out)) != GST_FLOW_OK)
+ goto pause;
+
+done:
+ gst_object_unref (nsfdec);
+
+ return;
+
+ /* ERRORS */
+pause:
+ {
+ const gchar *reason = gst_flow_get_name (ret);
+
+ GST_DEBUG_OBJECT (nsfdec, "pausing task, reason %s", reason);
+ gst_pad_pause_task (pad);
+
+ if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
+ if (ret == GST_FLOW_UNEXPECTED) {
+ /* perform EOS logic, FIXME, segment seek? */
+ gst_pad_push_event (pad, gst_event_new_eos ());
+ } else {
+ /* for fatal errors we post an error message */
+ GST_ELEMENT_ERROR (nsfdec, STREAM, FAILED,
+ (NULL), ("streaming task paused, reason %s", reason));
+ gst_pad_push_event (pad, gst_event_new_eos ());
+ }
+ }
+ goto done;
+ }
+}
+
+static gboolean
+start_play_tune (GstNsfDec * nsfdec)
+{
+ gboolean res;
+
+ nsfdec->nsf = nsf_load (NULL, GST_BUFFER_DATA (nsfdec->tune_buffer),
+ GST_BUFFER_SIZE (nsfdec->tune_buffer));
+
+ if (!nsfdec->nsf)
+ goto could_not_load;
+
+ if (!nsfdec_negotiate (nsfdec))
+ goto could_not_negotiate;
+
+ nsf_playtrack (nsfdec->nsf,
+ nsfdec->tune_number, nsfdec->frequency, nsfdec->bits, nsfdec->stereo);
+ nsf_setfilter (nsfdec->nsf, nsfdec->filter);
+
+ nsfdec->bps = (nsfdec->bits >> 3) * nsfdec->channels;
+ /* calculate the number of bytes we need to output after each call to
+ * nsf_frame(). */
+ nsfdec->blocksize =
+ nsfdec->bps * nsfdec->frequency / nsfdec->nsf->playback_rate;
+
+ gst_pad_push_event (nsfdec->srcpad,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
+
+ res = gst_pad_start_task (nsfdec->srcpad,
+ (GstTaskFunction) play_loop, nsfdec->srcpad);
+
+ return res;
+
+ /* ERRORS */
+could_not_load:
+ {
+ GST_ELEMENT_ERROR (nsfdec, LIBRARY, INIT,
+ ("Could not load tune"), ("Could not load tune"));
+ return FALSE;
+ }
+could_not_negotiate:
+ {
+ GST_ELEMENT_ERROR (nsfdec, CORE, NEGOTIATION,
+ ("Could not negotiate format"), ("Could not negotiate format"));
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_nsfdec_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstNsfDec *nsfdec;
+ gboolean res;
+
+ nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ res = start_play_tune (nsfdec);
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ res = FALSE;
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+ gst_event_unref (event);
+ gst_object_unref (nsfdec);
+
+ return res;
+}
+
+static GstFlowReturn
+gst_nsfdec_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstNsfDec *nsfdec;
+
+ nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+ /* collect all data, we start doing something when we get an EOS
+ * event */
+ if (nsfdec->tune_buffer) {
+ nsfdec->tune_buffer = gst_buffer_join (nsfdec->tune_buffer, buffer);
+ } else {
+ nsfdec->tune_buffer = buffer;
+ }
+
+ gst_object_unref (nsfdec);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_nsfdec_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ guint scale = 1;
+ GstNsfDec *nsfdec;
+
+ nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+ if (src_format == *dest_format) {
+ *dest_value = src_value;
+ return TRUE;
+ }
+
+ switch (src_format) {
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_DEFAULT:
+ if (nsfdec->bps == 0)
+ return FALSE;
+ *dest_value = src_value / nsfdec->bps;
+ break;
+ case GST_FORMAT_TIME:
+ {
+ gint byterate = nsfdec->bps * nsfdec->frequency;
+
+ if (byterate == 0)
+ return FALSE;
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
+ break;
+ }
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * nsfdec->bps;
+ break;
+ case GST_FORMAT_TIME:
+ if (nsfdec->frequency == 0)
+ return FALSE;
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND,
+ nsfdec->frequency);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ scale = nsfdec->bps;
+ /* fallthrough */
+ case GST_FORMAT_DEFAULT:
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, scale * nsfdec->frequency,
+ GST_SECOND);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+
+ return res;
+}
+
+static gboolean
+gst_nsfdec_src_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = FALSE;
+ GstNsfDec *nsfdec;
+
+ nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ default:
+ break;
+ }
+ gst_event_unref (event);
+
+ gst_object_unref (nsfdec);
+
+ return res;
+}
+
+static gboolean
+gst_nsfdec_src_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = TRUE;
+ GstNsfDec *nsfdec;
+
+ nsfdec = GST_NSFDEC (gst_pad_get_parent (pad));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ GstFormat format;
+ gint64 current;
+
+ gst_query_parse_position (query, &format, NULL);
+
+ /* we only know about our bytes, convert to requested format */
+ res &= gst_nsfdec_src_convert (pad,
+ GST_FORMAT_BYTES, nsfdec->total_bytes, &format, &current);
+ if (res) {
+ gst_query_set_position (query, format, current);
+ }
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+ gst_object_unref (nsfdec);
+
+ return res;
+}
+
+static void
+gst_nsfdec_set_property (GObject * object, guint prop_id, const GValue * value,
+ GParamSpec * pspec)
+{
+ GstNsfDec *nsfdec;
+
+ nsfdec = GST_NSFDEC (object);
+
+ switch (prop_id) {
+ case PROP_TUNE:
+ nsfdec->tune_number = g_value_get_int (value);
+ break;
+ case PROP_FILTER:
+ nsfdec->filter = g_value_get_enum (value);
+ if (nsfdec->nsf)
+ nsf_setfilter (nsfdec->nsf, nsfdec->filter);
+ 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;
+
+ nsfdec = GST_NSFDEC (object);
+
+ switch (prop_id) {
+ case PROP_TUNE:
+ g_value_set_int (value, nsfdec->tune_number);
+ break;
+ case PROP_FILTER:
+ g_value_set_enum (value, nsfdec->filter);
+ 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", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
diff --git a/gst/nsf/gstnsf.h b/gst/nsf/gstnsf.h
new file mode 100644
index 00000000..c80f4e98
--- /dev/null
+++ b/gst/nsf/gstnsf.h
@@ -0,0 +1,86 @@
+/* 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;
+
+enum
+{
+ NSF_STATE_NEED_TUNE = 1,
+ NSF_STATE_LOAD_TUNE = 2,
+ NSF_STATE_PLAY_TUNE = 3
+};
+
+struct _GstNsfDec {
+ GstElement element;
+
+ /* pads */
+ GstPad *sinkpad,
+ *srcpad;
+
+ gint state;
+ GstBuffer *tune_buffer;
+ guint64 total_bytes;
+
+ /* properties */
+ gint tune_number;
+ gint filter;
+
+ nsf_t *nsf;
+ gulong blocksize;
+
+ int frequency;
+ int bits;
+ gboolean stereo;
+ int channels;
+ int bps;
+};
+
+struct _GstNsfDecClass {
+ GstElementClass parent_class;
+};
+
+GType gst_nsfdec_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_NSFDEC_H__ */
diff --git a/gst/nsf/log.c b/gst/nsf/log.c
new file mode 100644
index 00000000..5d0d168a
--- /dev/null
+++ b/gst/nsf/log.c
@@ -0,0 +1,148 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** Revision 1.5 2000/06/26 04:55:33 matt
+** minor change
+**
+** Revision 1.4 2000/06/09 15:12:25 matt
+** initial revision
+**
+*/
diff --git a/gst/nsf/log.h b/gst/nsf/log.h
new file mode 100644
index 00000000..96c37ea4
--- /dev/null
+++ b/gst/nsf/log.h
@@ -0,0 +1,76 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** Revision 1.4 2000/06/09 15:12:25 matt
+** initial revision
+**
+*/
+
diff --git a/gst/nsf/memguard.c b/gst/nsf/memguard.c
new file mode 100644
index 00000000..8ad9749d
--- /dev/null
+++ b/gst/nsf/memguard.c
@@ -0,0 +1,398 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/memguard.h b/gst/nsf/memguard.h
new file mode 100644
index 00000000..2074a9e6
--- /dev/null
+++ b/gst/nsf/memguard.h
@@ -0,0 +1,95 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/mmc5_snd.c b/gst/nsf/mmc5_snd.c
new file mode 100644
index 00000000..7c39af44
--- /dev/null
+++ b/gst/nsf/mmc5_snd.c
@@ -0,0 +1,383 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/mmc5_snd.h b/gst/nsf/mmc5_snd.h
new file mode 100644
index 00000000..5107c58a
--- /dev/null
+++ b/gst/nsf/mmc5_snd.h
@@ -0,0 +1,109 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
+
diff --git a/gst/nsf/nes6502.c b/gst/nsf/nes6502.c
new file mode 100644
index 00000000..c677a5ae
--- /dev/null
+++ b/gst/nsf/nes6502.c
@@ -0,0 +1,2438 @@
+/*
+** 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$
+*/
+
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/nes6502.h b/gst/nsf/nes6502.h
new file mode 100644
index 00000000..e8ccffd4
--- /dev/null
+++ b/gst/nsf/nes6502.h
@@ -0,0 +1,160 @@
+/*
+** 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$
+*/
+
+/* 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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** Revision 1.4 2000/06/09 15:12:25 matt
+** initial revision
+**
+*/
diff --git a/gst/nsf/nes_apu.c b/gst/nsf/nes_apu.c
new file mode 100644
index 00000000..0e2c9520
--- /dev/null
+++ b/gst/nsf/nes_apu.c
@@ -0,0 +1,1246 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/nes_apu.h b/gst/nsf/nes_apu.h
new file mode 100644
index 00000000..fd94798d
--- /dev/null
+++ b/gst/nsf/nes_apu.h
@@ -0,0 +1,354 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/nsf.c b/gst/nsf/nsf.c
new file mode 100644
index 00000000..8b89f23d
--- /dev/null
+++ b/gst/nsf/nsf.c
@@ -0,0 +1,648 @@
+/*
+** 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$
+*/
+
+#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;
+ void *mem;
+
+ 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]);
+ }
+ mem = nsf->cpu;
+ free (mem);
+ }
+}
+
+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) {
+ void *mem = (*nsf)->data;
+
+ free (mem);
+ }
+
+ 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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/nsf.h b/gst/nsf/nsf.h
new file mode 100644
index 00000000..2f960367
--- /dev/null
+++ b/gst/nsf/nsf.h
@@ -0,0 +1,173 @@
+/*
+** 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$
+*/
+
+#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] ; /* NESM\x1A */
+ uint8 version ; /* spec version */
+ uint8 num_songs ; /* total num songs */
+ uint8 start_song ; /* 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] ; /* name of song */
+ uint8 artist_name[32] ; /* artist name */
+ uint8 copyright[32] ; /* copyright info */
+ uint16 ntsc_speed __PACKED__; /* playback speed (if NTSC) */
+ uint8 bankswitch_info[8] ; /* initial code banking */
+ uint16 pal_speed __PACKED__; /* playback speed (if PAL) */
+ uint8 pal_ntsc_bits ; /* NTSC/PAL determination bits */
+ uint8 ext_sound_type ; /* type of external sound gen. */
+ uint8 reserved[4] ; /* 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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/osd.h b/gst/nsf/osd.h
new file mode 100644
index 00000000..0a2bb64a
--- /dev/null
+++ b/gst/nsf/osd.h
@@ -0,0 +1,123 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/types.h b/gst/nsf/types.h
new file mode 100644
index 00000000..d1fa7498
--- /dev/null
+++ b/gst/nsf/types.h
@@ -0,0 +1,125 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/vrc7_snd.c b/gst/nsf/vrc7_snd.c
new file mode 100644
index 00000000..07d27514
--- /dev/null
+++ b/gst/nsf/vrc7_snd.c
@@ -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.
+**
+**
+** vrc7_snd.c
+**
+** VRCVII sound hardware emulation
+** Thanks to Charles MacDonald (cgfm2@hooked.net) for donating code.
+** $Id$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/vrc7_snd.h b/gst/nsf/vrc7_snd.h
new file mode 100644
index 00000000..9521d4f7
--- /dev/null
+++ b/gst/nsf/vrc7_snd.h
@@ -0,0 +1,99 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/vrcvisnd.c b/gst/nsf/vrcvisnd.c
new file mode 100644
index 00000000..e6b5eb3e
--- /dev/null
+++ b/gst/nsf/vrcvisnd.c
@@ -0,0 +1,241 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
diff --git a/gst/nsf/vrcvisnd.h b/gst/nsf/vrcvisnd.h
new file mode 100644
index 00000000..d141aef7
--- /dev/null
+++ b/gst/nsf/vrcvisnd.h
@@ -0,0 +1,112 @@
+/*
+** 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$
+*/
+
+#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$
+** Revision 1.1 2006/07/13 15:07:28 wtay
+** Based on patches by: Johan Dahlin <johan at gnome dot org>
+** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
+** * configure.ac:
+** * gst/nsf/Makefile.am:
+** * gst/nsf/dis6502.h:
+** * gst/nsf/fds_snd.c:
+** * gst/nsf/fds_snd.h:
+** * gst/nsf/fmopl.c:
+** * gst/nsf/fmopl.h:
+** * gst/nsf/gstnsf.c:
+** * gst/nsf/gstnsf.h:
+** * gst/nsf/log.c:
+** * gst/nsf/log.h:
+** * gst/nsf/memguard.c:
+** * gst/nsf/memguard.h:
+** * gst/nsf/mmc5_snd.c:
+** * gst/nsf/mmc5_snd.h:
+** * gst/nsf/nes6502.c:
+** * gst/nsf/nes6502.h:
+** * gst/nsf/nes_apu.c:
+** * gst/nsf/nes_apu.h:
+** * gst/nsf/nsf.c:
+** * gst/nsf/nsf.h:
+** * gst/nsf/osd.h:
+** * gst/nsf/types.h:
+** * gst/nsf/vrc7_snd.c:
+** * gst/nsf/vrc7_snd.h:
+** * gst/nsf/vrcvisnd.c:
+** * gst/nsf/vrcvisnd.h:
+** Added NSF decoder plugin. Fixes 151192.
+**
+** 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
+**
+*/
+