aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormescalinum <mescalinum@users.sourceforge.net>2010-08-19 20:03:23 +0000
committermescalinum <mescalinum@users.sourceforge.net>2010-08-19 20:03:23 +0000
commit78b7eb75ae6877bb67c7c3d2f99a9bd3332e4921 (patch)
treed51a36114b8c818d4c3efaf2cb5f73632633e511
parentfb0c2e4416d7a54f5318710b256dd794cc8317c8 (diff)
TMS5220 chip emulation
svn path=/trunk/externals/ffext/; revision=13845
-rw-r--r--tms5220~/Makefile297
-rw-r--r--tms5220~/README.txt6
-rw-r--r--tms5220~/tms5220/driver.h0
-rw-r--r--tms5220~/tms5220/tms5220.c701
-rw-r--r--tms5220~/tms5220/tms5220.h15
-rw-r--r--tms5220~/tms5220/tms5220.txt86
-rw-r--r--tms5220~/tms5220/tms5220r.c105
-rw-r--r--tms5220~/tms5220~-help.pd30
-rw-r--r--tms5220~/tms5220~-meta.pd8
-rw-r--r--tms5220~/tms5220~.c120
10 files changed, 1368 insertions, 0 deletions
diff --git a/tms5220~/Makefile b/tms5220~/Makefile
new file mode 100644
index 0000000..4c72e4d
--- /dev/null
+++ b/tms5220~/Makefile
@@ -0,0 +1,297 @@
+## Pd library template version 1.0.1
+# For instructions on how to use this template, see:
+# http://puredata.info/docs/developer/MakefileTemplate
+LIBRARY_NAME = tms5220~
+
+# add your .c source files to the SOURCES variable, help files will be
+# included automatically
+SOURCES = tms5220~.c
+
+# For objects that only build on certain platforms, add those to the SOURCES
+# line for the right platforms.
+SOURCES_android =
+SOURCES_cygwin =
+SOURCES_macosx =
+SOURCES_iphoneos =
+SOURCES_linux =
+SOURCES_windows =
+
+# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will
+# be included automatically
+PDOBJECTS =
+
+# example patches and related files, in the 'examples' subfolder
+EXAMPLES =
+
+# manuals and related files, in the 'manual' subfolder
+MANUAL =
+
+# if you want to include any other files in the source and binary tarballs,
+# list them here. This can be anything from header files, example patches,
+# documentation, etc. README.txt and LICENSE.txt are required and therefore
+# automatically included
+EXTRA_DIST =
+
+
+
+#------------------------------------------------------------------------------#
+#
+# you shouldn't need to edit anything below here, if we did it right :)
+#
+#------------------------------------------------------------------------------#
+
+# get library version from meta file
+LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd)
+
+# where Pd lives
+PD_PATH = ../../pd
+# where to install the library
+prefix = /usr/local
+libdir = $(prefix)/lib
+pkglibdir = $(libdir)/pd-externals
+objectsdir = $(pkglibdir)
+
+
+INSTALL = install
+INSTALL_FILE = $(INSTALL) -p -m 644
+INSTALL_DIR = $(INSTALL) -p -m 755 -d
+
+CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g
+LDFLAGS =
+LIBS =
+ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \
+ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows)
+
+DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ORIGDIR=pd-$(LIBRARY_NAME)_$(LIBRARY_VERSION)
+
+UNAME := $(shell uname -s)
+ifeq ($(UNAME),Darwin)
+ CPU := $(shell uname -p)
+ ifeq ($(CPU),arm) # iPhone/iPod Touch
+ SOURCES += $(SOURCES_iphoneos)
+ EXTENSION = pd_darwin
+ OS = iphoneos
+ IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
+ CC=$(IPHONE_BASE)/gcc
+ CPP=$(IPHONE_BASE)/cpp
+ CXX=$(IPHONE_BASE)/g++
+ ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk
+ IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6
+ OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer
+ CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ else # Mac OS X
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = macosx
+ OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast
+# build universal 32-bit on 10.4 and 32/64 on newer
+ ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8)
+ FAT_FLAGS = -arch ppc -arch i386 -mmacosx-version-min=10.4
+ else
+ FAT_FLAGS = -arch ppc -arch i386 -arch x86_64 -mmacosx-version-min=10.4
+ SOURCES += $(SOURCES_iphoneos)
+ endif
+ CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib
+ # if the 'pd' binary exists, check the linking against it to aid with stripping
+ LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTBINDIR=$(DISTDIR)-$(OS)
+# install into ~/Library/Pd on Mac OS X since /usr/local isn't used much
+ pkglibdir=$(HOME)/Library/Pd
+ endif
+endif
+ifeq ($(UNAME),Linux)
+ SOURCES += $(SOURCES_linux)
+ EXTENSION = pd_linux
+ OS = linux
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS += -fPIC
+ LDFLAGS += -Wl,--export-dynamic -shared -fPIC
+ LIBS += -lc
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
+endif
+ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME)))
+ SOURCES += $(SOURCES_cygwin)
+ EXTENSION = dll
+ OS = cygwin
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS +=
+ LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src
+ LIBS += -lc -lpd
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+ifeq (MINGW,$(findstring MINGW,$(UNAME)))
+ SOURCES += $(SOURCES_windows)
+ EXTENSION = dll
+ OS = windows
+ OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer -march=i686 -mtune=pentium4
+ CFLAGS += -mms-bitfields
+ LDFLAGS += -s -shared -Wl,--enable-auto-import
+ LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+
+CFLAGS += $(OPT_CFLAGS)
+
+
+.PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags
+
+all: $(SOURCES:.c=.$(EXTENSION))
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o "$*.o" -c "$*.c"
+
+%.$(EXTENSION): %.o
+ $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS)
+ chmod a-x "$*.$(EXTENSION)"
+
+# this links everything into a single binary file
+$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o
+ $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS)
+ chmod a-x $(LIBRARY_NAME).$(EXTENSION)
+
+
+install: libdir_install
+
+# The meta and help files are explicitly installed to make sure they are
+# actually there. Those files are not optional, then need to be there.
+libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples install-manual
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || (\
+ $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \
+ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION))))
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+
+# install library linked as single binary
+single_install: $(LIBRARY_NAME) install-doc install-exec
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION)
+
+install-doc:
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || \
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt
+ $(INSTALL_FILE) LICENSE.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/LICENSE.txt
+
+install-examples:
+ test -z "$(strip $(EXAMPLES))" || \
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \
+ for file in $(EXAMPLES); do \
+ $(INSTALL_FILE) examples/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples; \
+ done
+
+install-manual:
+ test -z "$(strip $(MANUAL))" || \
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual && \
+ for file in $(MANUAL); do \
+ $(INSTALL_FILE) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \
+ done
+
+
+clean:
+ -rm -f -- $(SOURCES:.c=.o)
+ -rm -f -- $(SOURCES:.c=.$(EXTENSION))
+ -rm -f -- $(LIBRARY_NAME).o
+ -rm -f -- $(LIBRARY_NAME).$(EXTENSION)
+
+distclean: clean
+ -rm -f -- $(DISTBINDIR).tar.gz
+ -rm -rf -- $(DISTBINDIR)
+ -rm -f -- $(DISTDIR).tar.gz
+ -rm -rf -- $(DISTDIR)
+ -rm -f -- $(ORIGDIR).tar.gz
+ -rm -rf -- $(ORIGDIR)
+
+
+$(DISTBINDIR):
+ $(INSTALL_DIR) $(DISTBINDIR)
+
+libdir: all $(DISTBINDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR)
+# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR)
+
+$(DISTDIR):
+ $(INSTALL_DIR) $(DISTDIR)
+
+$(ORIGDIR):
+ $(INSTALL_DIR) $(ORIGDIR)
+
+dist: $(DISTDIR)
+ $(INSTALL_FILE) Makefile $(DISTDIR)
+ $(INSTALL_FILE) README.txt $(DISTDIR)
+ $(INSTALL_FILE) LICENSE.txt $(DISTDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR)
+ test -z "$(strip $(EXAMPLES))" || \
+ $(INSTALL_DIR) $(DISTDIR)/examples && \
+ for file in $(EXAMPLES); do \
+ $(INSTALL_FILE) examples/$$file $(DISTDIR)/examples; \
+ done
+ test -z "$(strip $(MANUAL))" || \
+ $(INSTALL_DIR) $(DISTDIR)/manual && \
+ for file in $(MANUAL); do \
+ $(INSTALL_FILE) manual/$$file $(DISTDIR)/manual; \
+ done
+ tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR)
+
+# make a Debian source package
+dpkg-source:
+ debclean
+ make distclean dist
+ mv $(DISTDIR) $(ORIGDIR)
+ tar --exclude-vcs -czpf ../$(ORIGDIR).orig.tar.gz $(ORIGDIR)
+ rm -f -- $(DISTDIR).tar.gz
+ rm -rf -- $(DISTDIR) $(ORIGDIR)
+ cd .. && dpkg-source -b $(LIBRARY_NAME)
+
+etags:
+ etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h
+
+showsetup:
+ @echo "PD_PATH: $(PD_PATH)"
+ @echo "objectsdir: $(objectsdir)"
+ @echo "LIBRARY_NAME: $(LIBRARY_NAME)"
+ @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)"
+ @echo "SOURCES: $(SOURCES)"
+ @echo "PDOBJECTS: $(PDOBJECTS)"
+ @echo "ALLSOURCES: $(ALLSOURCES)"
+ @echo "UNAME: $(UNAME)"
+ @echo "CPU: $(CPU)"
+ @echo "pkglibdir: $(pkglibdir)"
+ @echo "DISTDIR: $(DISTDIR)"
+ @echo "ORIGDIR: $(ORIGDIR)"
diff --git a/tms5220~/README.txt b/tms5220~/README.txt
new file mode 100644
index 0000000..ccce2bd
--- /dev/null
+++ b/tms5220~/README.txt
@@ -0,0 +1,6 @@
+TMS5220 IC emulation for pure-data
+written by Federico Ferri
+Released under GPL-3 license.
+
+TMS5220 emulation core comes from MAME sources and it's written by Frank Palazzolo and others.
+
diff --git a/tms5220~/tms5220/driver.h b/tms5220~/tms5220/driver.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tms5220~/tms5220/driver.h
diff --git a/tms5220~/tms5220/tms5220.c b/tms5220~/tms5220/tms5220.c
new file mode 100644
index 0000000..1e61979
--- /dev/null
+++ b/tms5220~/tms5220/tms5220.c
@@ -0,0 +1,701 @@
+/**********************************************************************************************
+
+ TMS5220 simulator
+
+ Written for MAME by Frank Palazzolo
+ With help from Neill Corlett
+ Additional tweaking by Aaron Giles
+
+***********************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "driver.h"
+#include "tms5220.h"
+
+
+/* Pull in the ROM tables */
+#include "tms5220r.c"
+
+
+/* these contain data that describes the 128-bit data FIFO */
+#define FIFO_SIZE 16
+static unsigned char fifo[FIFO_SIZE];
+static int fifo_head;
+static int fifo_tail;
+static int fifo_count;
+static int bits_taken;
+
+
+/* these contain global status bits */
+static int speak_external;
+static int talk_status;
+static int buffer_low;
+static int buffer_empty;
+static int irq_pin;
+
+static void (*irq_func)(void);
+
+
+/* these contain data describing the current and previous voice frames */
+static unsigned short old_energy = 0;
+static unsigned short old_pitch = 0;
+static int old_k[10] = {0,0,0,0,0,0,0,0,0,0};
+
+static unsigned short new_energy = 0;
+static unsigned short new_pitch = 0;
+static int new_k[10] = {0,0,0,0,0,0,0,0,0,0};
+
+
+/* these are all used to contain the current state of the sound generation */
+static unsigned short current_energy = 0;
+static unsigned short current_pitch = 0;
+static int current_k[10] = {0,0,0,0,0,0,0,0,0,0};
+
+static unsigned short target_energy = 0;
+static unsigned short target_pitch = 0;
+static int target_k[10] = {0,0,0,0,0,0,0,0,0,0};
+
+static int interp_count = 0; /* number of interp periods (0-7) */
+static int sample_count = 0; /* sample number within interp (0-24) */
+static int pitch_count = 0;
+
+static int u[11] = {0,0,0,0,0,0,0,0,0,0,0};
+static int x[10] = {0,0,0,0,0,0,0,0,0,0};
+
+static int randbit = 0;
+
+
+/* Static function prototypes */
+static void process_command (void);
+static int extract_bits (int count);
+static int parse_frame (int removeit);
+static void check_buffer_low (void);
+static void cause_interrupt (void);
+
+
+/*#define DEBUG_5220*/
+#ifdef DEBUG_5220
+ static FILE *f;
+#endif
+
+
+/**********************************************************************************************
+
+ tms5220_reset -- resets the TMS5220
+
+***********************************************************************************************/
+
+void tms5220_reset (void)
+{
+ /* initialize the FIFO */
+ memset (fifo, 0, sizeof (fifo));
+ fifo_head = fifo_tail = fifo_count = bits_taken = 0;
+
+ /* initialize the chip state */
+ speak_external = talk_status = buffer_empty = irq_pin = 0;
+ buffer_low = 1;
+
+ /* initialize the energy/pitch/k states */
+ old_energy = new_energy = current_energy = target_energy = 0;
+ old_pitch = new_pitch = current_pitch = target_pitch = 0;
+ memset (old_k, 0, sizeof (old_k));
+ memset (new_k, 0, sizeof (new_k));
+ memset (current_k, 0, sizeof (current_k));
+ memset (target_k, 0, sizeof (target_k));
+
+ /* initialize the sample generators */
+ interp_count = sample_count = pitch_count = 0;
+ randbit = 0;
+ memset (u, 0, sizeof (u));
+ memset (x, 0, sizeof (x));
+
+ #ifdef DEBUG_5220
+ f = fopen ("tms.log", "w");
+ #endif
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_reset -- reset the TMS5220
+
+***********************************************************************************************/
+
+void tms5220_set_irq (void (*func)(void))
+{
+ irq_func = func;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_data_write -- handle a write to the TMS5220
+
+***********************************************************************************************/
+
+void tms5220_data_write (int data)
+{
+ /* add this byte to the FIFO */
+ if (fifo_count < FIFO_SIZE)
+ {
+ fifo[fifo_tail] = data;
+ fifo_tail = (fifo_tail + 1) % FIFO_SIZE;
+ fifo_count++;
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Added byte to FIFO (size=%2d)\n", fifo_count);
+ #endif
+ }
+ else
+ {
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Ran out of room in the FIFO!\n");
+ #endif
+ }
+
+ /* update the buffer low state */
+ check_buffer_low ();
+}
+
+
+/**********************************************************************************************
+
+ tms5220_status_read -- read status from the TMS5220
+
+ From the data sheet:
+ bit 0 = TS - Talk Status is active (high) when the VSP is processing speech data.
+ Talk Status goes active at the initiation of a Speak command or after nine
+ bytes of data are loaded into the FIFO following a Speak External command. It
+ goes inactive (low) when the stop code (Energy=1111) is processed, or
+ immediately by a buffer empty condition or a reset command.
+ bit 1 = BL - Buffer Low is active (high) when the FIFO buffer is more than half empty.
+ Buffer Low is set when the "Last-In" byte is shifted down past the half-full
+ boundary of the stack. Buffer Low is cleared when data is loaded to the stack
+ so that the "Last-In" byte lies above the half-full boundary and becomes the
+ ninth data byte of the stack.
+ bit 2 = BE - Buffer Empty is active (high) when the FIFO buffer has run out of data
+ while executing a Speak External command. Buffer Empty is set when the last bit
+ of the "Last-In" byte is shifted out to the Synthesis Section. This causes
+ Talk Status to be cleared. Speed is terminated at some abnormal point and the
+ Speak External command execution is terminated.
+
+***********************************************************************************************/
+
+int tms5220_status_read (void)
+{
+ /* clear the interrupt pin */
+ irq_pin = 0;
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Status read: TS=%d BL=%d BE=%d\n", talk_status, buffer_low, buffer_empty);
+ #endif
+
+ return (talk_status << 7) | (buffer_low << 6) | (buffer_empty << 5);
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_ready_read -- returns the ready state of the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_ready_read (void)
+{
+ return (fifo_count < FIFO_SIZE-1);
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_int_read -- returns the interrupt state of the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_int_read (void)
+{
+ return irq_pin;
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_process -- fill the buffer with a specific number of samples
+
+***********************************************************************************************/
+
+void tms5220_process(unsigned char *buffer, unsigned int size)
+{
+ int buf_count=0;
+ int i, interp_period;
+
+tryagain:
+
+ /* if we're not speaking, parse commands */
+ while (!speak_external && fifo_count > 0)
+ process_command ();
+
+ /* if there's nothing to do, bail */
+ if (!size)
+ return;
+
+ /* if we're empty and still not speaking, fill with nothingness */
+ if (!speak_external)
+ goto empty;
+
+ /* if we're to speak, but haven't started, wait for the 9th byte */
+ if (!talk_status)
+ {
+ if (fifo_count < 9)
+ goto empty;
+
+ /* parse but don't remove the first frame, and set the status to 1 */
+ parse_frame (0);
+ talk_status = 1;
+ buffer_empty = 0;
+ }
+
+ /* loop until the buffer is full or we've stopped speaking */
+ while ((size > 0) && speak_external)
+ {
+ int current_val;
+
+ /* if we're ready for a new frame */
+ if ((interp_count == 0) && (sample_count == 0))
+ {
+ /* Parse a new frame */
+ if (!parse_frame (1))
+ break;
+
+ /* Set old target as new start of frame */
+ current_energy = old_energy;
+ current_pitch = old_pitch;
+ for (i = 0; i < 10; i++)
+ current_k[i] = old_k[i];
+
+ /* is this a zero energy frame? */
+ if (current_energy == 0)
+ {
+ //printf("processing frame: zero energy\n");
+ target_energy = 0;
+ target_pitch = current_pitch;
+ for (i = 0; i < 10; i++)
+ target_k[i] = current_k[i];
+ }
+
+ /* is this a stop frame? */
+ else if (current_energy == (energytable[15] >> 6))
+ {
+ //printf("processing frame: stop frame\n");
+ current_energy = energytable[0] >> 6;
+ target_energy = current_energy;
+ speak_external = talk_status = 0;
+ interp_count = sample_count = pitch_count = 0;
+
+ /* generate an interrupt if necessary */
+ cause_interrupt ();
+
+ /* try to fetch commands again */
+ goto tryagain;
+ }
+ else
+ {
+ /* is this the ramp down frame? */
+ if (new_energy == (energytable[15] >> 6))
+ {
+ //printf("processing frame: ramp down\n");
+ target_energy = 0;
+ target_pitch = current_pitch;
+ for (i = 0; i < 10; i++)
+ target_k[i] = current_k[i];
+ }
+ /* Reset the step size */
+ else
+ {
+ //printf("processing frame: Normal\n");
+ //printf("*** Energy = %d\n",current_energy);
+ //printf("proc: %d %d\n",last_fbuf_head,fbuf_head);
+
+ target_energy = new_energy;
+ target_pitch = new_pitch;
+
+ for (i = 0; i < 4; i++)
+ target_k[i] = new_k[i];
+ if (current_pitch == 0)
+ for (i = 4; i < 10; i++)
+ {
+ target_k[i] = current_k[i] = 0;
+ }
+ else
+ for (i = 4; i < 10; i++)
+ target_k[i] = new_k[i];
+ }
+ }
+ }
+ else if (interp_count == 0)
+ {
+ /* Update values based on step values */
+ //printf("\n");
+
+ interp_period = sample_count / 25;
+ current_energy += (target_energy - current_energy) / interp_coeff[interp_period];
+ if (old_pitch != 0)
+ current_pitch += (target_pitch - current_pitch) / interp_coeff[interp_period];
+
+ //printf("*** Energy = %d\n",current_energy);
+
+ for (i = 0; i < 10; i++)
+ {
+ current_k[i] += (target_k[i] - current_k[i]) / interp_coeff[interp_period];
+ }
+ }
+
+ if (old_energy == 0)
+ {
+ /* generate silent samples here */
+ current_val = 0x00;
+ }
+ else if (old_pitch == 0)
+ {
+ /* generate unvoiced samples here */
+ randbit = (rand () % 2) * 2 - 1;
+ current_val = (randbit * current_energy) / 4;
+ }
+ else
+ {
+ /* generate voiced samples here */
+ if (pitch_count < sizeof (chirptable))
+ current_val = (chirptable[pitch_count] * current_energy) / 256;
+ else
+ current_val = 0x00;
+ }
+
+ /* Lattice filter here */
+
+ u[10] = current_val;
+
+ for (i = 9; i >= 0; i--)
+ {
+ u[i] = u[i+1] - ((current_k[i] * x[i]) / 32768);
+ }
+ for (i = 9; i >= 1; i--)
+ {
+ x[i] = x[i-1] + ((current_k[i-1] * u[i-1]) / 32768);
+ }
+
+ x[0] = u[0];
+
+ /* clipping, just like the chip */
+
+ if (u[0] > 511)
+ buffer[buf_count] = 127;
+ else if (u[0] < -512)
+ buffer[buf_count] = -128;
+ else
+ buffer[buf_count] = u[0] >> 2;
+
+ /* Update all counts */
+
+ size--;
+ sample_count = (sample_count + 1) % 200;
+
+ if (current_pitch != 0)
+ pitch_count = (pitch_count + 1) % current_pitch;
+ else
+ pitch_count = 0;
+
+ interp_count = (interp_count + 1) % 25;
+ buf_count++;
+ }
+
+empty:
+
+ while (size > 0)
+ {
+ buffer[buf_count] = 0x00;
+ buf_count++;
+ size--;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ process_command -- extract a byte from the FIFO and interpret it as a command
+
+***********************************************************************************************/
+
+static void process_command (void)
+{
+ unsigned char cmd;
+
+ /* if there are stray bits, ignore them */
+ if (bits_taken)
+ {
+ bits_taken = 0;
+ fifo_count--;
+ fifo_head = (fifo_head + 1) % FIFO_SIZE;
+ }
+
+ /* grab a full byte from the FIFO */
+ if (fifo_count > 0)
+ {
+ cmd = fifo[fifo_head] & 0x70;
+ fifo_count--;
+ fifo_head = (fifo_head + 1) % FIFO_SIZE;
+
+ /* only real command we handle now is speak external */
+ if (cmd == 0x60)
+ {
+ speak_external = 1;
+
+ /* according to the datasheet, this will cause an interrupt due to a BE condition */
+ if (!buffer_empty)
+ {
+ buffer_empty = 1;
+ cause_interrupt ();
+ }
+ }
+ }
+
+ /* update the buffer low state */
+ check_buffer_low ();
+}
+
+
+
+/**********************************************************************************************
+
+ extract_bits -- extract a specific number of bits from the FIFO
+
+***********************************************************************************************/
+
+static int extract_bits (int count)
+{
+ int val = 0;
+
+ while (count--)
+ {
+ val = (val << 1) | ((fifo[fifo_head] >> bits_taken) & 1);
+ bits_taken++;
+ if (bits_taken >= 8)
+ {
+ fifo_count--;
+ fifo_head = (fifo_head + 1) % FIFO_SIZE;
+ bits_taken = 0;
+ }
+ }
+ return val;
+}
+
+
+
+/**********************************************************************************************
+
+ parse_frame -- parse a new frame's worth of data; returns 0 if not enough bits in buffer
+
+***********************************************************************************************/
+
+static int parse_frame (int removeit)
+{
+ int old_head, old_taken, old_count;
+ int bits, indx, i, rep_flag;
+
+ /* remember previous frame */
+ old_energy = new_energy;
+ old_pitch = new_pitch;
+ for (i = 0; i < 10; i++)
+ old_k[i] = new_k[i];
+
+ /* clear out the new frame */
+ new_energy = 0;
+ new_pitch = 0;
+ for (i = 0; i < 10; i++)
+ new_k[i] = 0;
+
+ /* if the previous frame was a stop frame, don't do anything */
+ if (old_energy == (energytable[15] >> 6))
+ return 1;
+
+ /* remember the original FIFO counts, in case we don't have enough bits */
+ old_count = fifo_count;
+ old_head = fifo_head;
+ old_taken = bits_taken;
+
+ /* count the total number of bits available */
+ bits = fifo_count * 8 - bits_taken;
+
+ /* attempt to extract the energy index */
+ bits -= 4;
+ if (bits < 0)
+ goto ranout;
+ indx = extract_bits (4);
+ new_energy = energytable[indx] >> 6;
+
+ /* if the index is 0 or 15, we're done */
+ if (indx == 0 || indx == 15)
+ {
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, " (4-bit energy=%d frame)\n",new_energy);
+ #endif
+
+ /* clear fifo if stop frame encountered */
+ if (indx == 15)
+ {
+ fifo_head = fifo_tail = fifo_count = bits_taken = 0;
+ removeit = 1;
+ }
+ goto done;
+ }
+
+ /* attempt to extract the repeat flag */
+ bits -= 1;
+ if (bits < 0)
+ goto ranout;
+ rep_flag = extract_bits (1);
+
+ /* attempt to extract the pitch */
+ bits -= 6;
+ if (bits < 0)
+ goto ranout;
+ indx = extract_bits (6);
+ new_pitch = pitchtable[indx] / 256;
+
+ /* if this is a repeat frame, just copy the k's */
+ if (rep_flag)
+ {
+ for (i = 0; i < 10; i++)
+ new_k[i] = old_k[i];
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, " (11-bit energy=%d pitch=%d rep=%d frame)\n", new_energy, new_pitch, rep_flag);
+ #endif
+ goto done;
+ }
+
+ /* if the pitch index was zero, we need 4 k's */
+ if (indx == 0)
+ {
+ /* attempt to extract 4 K's */
+ bits -= 18;
+ if (bits < 0)
+ goto ranout;
+ new_k[0] = k1table[extract_bits (5)];
+ new_k[1] = k2table[extract_bits (5)];
+ new_k[2] = k3table[extract_bits (4)];
+ new_k[3] = k4table[extract_bits (4)];
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, " (29-bit energy=%d pitch=%d rep=%d 4K frame)\n", new_energy, new_pitch, rep_flag);
+ #endif
+ goto done;
+ }
+
+ /* else we need 10 K's */
+ bits -= 39;
+ if (bits < 0)
+ goto ranout;
+ new_k[0] = k1table[extract_bits (5)];
+ new_k[1] = k2table[extract_bits (5)];
+ new_k[2] = k3table[extract_bits (4)];
+ new_k[3] = k4table[extract_bits (4)];
+ new_k[4] = k5table[extract_bits (4)];
+ new_k[5] = k6table[extract_bits (4)];
+ new_k[6] = k7table[extract_bits (4)];
+ new_k[7] = k8table[extract_bits (3)];
+ new_k[8] = k9table[extract_bits (3)];
+ new_k[9] = k10table[extract_bits (3)];
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, " (50-bit energy=%d pitch=%d rep=%d 10K frame)\n", new_energy, new_pitch, rep_flag);
+ #endif
+
+done:
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Parsed a frame successfully - %d bits remaining\n", bits);
+ #endif
+
+ /* if we're not to remove this one, restore the FIFO */
+ if (!removeit)
+ {
+ fifo_count = old_count;
+ fifo_head = old_head;
+ bits_taken = old_taken;
+ }
+
+ /* update the buffer_low status */
+ check_buffer_low ();
+ return 1;
+
+ranout:
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Ran out of bits on a parse!\n");
+ #endif
+
+ /* this is an error condition; mark the buffer empty and turn off speaking */
+ buffer_empty = 1;
+ talk_status = speak_external = 0;
+ fifo_count = fifo_head = fifo_tail = 0;
+
+ /* generate an interrupt if necessary */
+ cause_interrupt ();
+ return 0;
+}
+
+
+
+/**********************************************************************************************
+
+ check_buffer_low -- check to see if the buffer low flag should be on or off
+
+***********************************************************************************************/
+
+static void check_buffer_low (void)
+{
+ /* did we just become low? */
+ if (fifo_count < 8)
+ {
+ /* generate an interrupt if necessary */
+ if (!buffer_low)
+ cause_interrupt ();
+ buffer_low = 1;
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Buffer low set\n");
+ #endif
+ }
+
+ /* did we just become full? */
+ else
+ {
+ buffer_low = 0;
+
+ #ifdef DEBUG_5220
+ if (f) fprintf (f, "Buffer low cleared\n");
+ #endif
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ cause_interrupt -- generate an interrupt
+
+***********************************************************************************************/
+
+static void cause_interrupt (void)
+{
+ irq_pin = 1;
+ if (irq_func) irq_func ();
+}
diff --git a/tms5220~/tms5220/tms5220.h b/tms5220~/tms5220/tms5220.h
new file mode 100644
index 0000000..614d7bc
--- /dev/null
+++ b/tms5220~/tms5220/tms5220.h
@@ -0,0 +1,15 @@
+#ifndef tms5220_h
+#define tms5220_h
+
+void tms5220_reset (void);
+void tms5220_set_irq (void (*func)(void));
+
+void tms5220_data_write (int data);
+int tms5220_status_read (void);
+int tms5220_ready_read (void);
+int tms5220_int_read (void);
+
+void tms5220_process (unsigned char *buffer, unsigned int size);
+
+#endif
+
diff --git a/tms5220~/tms5220/tms5220.txt b/tms5220~/tms5220/tms5220.txt
new file mode 100644
index 0000000..8af747c
--- /dev/null
+++ b/tms5220~/tms5220/tms5220.txt
@@ -0,0 +1,86 @@
+*****************************
+
+ TI TMS5220 Emulator
+ (c) Frank Palazzolo
+
+*****************************
+
+The TI TMS5220 Speech chip uses Linear-Predictive decoding scheme to produce
+speech from very compact data. This scheme is very similar to the U.S.
+Federal Standard LPC-10e coding system, which was developed soon after
+this chip.
+
+It is virtually identical to the chip used in the landmark "Speak 'N Spell"
+toy produced in the '70s.
+
+Acknowledgements:
+-----------------
+
+I would like to thank Larry Brantingham, the original designer of the chip,
+for his technical help.
+
+I would also like to thank Neill Cortlett, who first showed that this chip
+could be emulated in real-time in his Multi-Gauntlet Emulator.
+
+Theory of operation:
+--------------------
+
+The TI speech chip contains a 128-bit parallel-in, serial-out FIFO, a
+10-pole digital lattice filter which models the vocal tract, and a D/A
+converter. It was originally design to operate either with a microcomputer
+interface, or with a serial speech ROM. The Speech ROM functionality is
+not emulated as no arcade games require it.
+
+The input data is writen a byte at a time into the chip, and it is
+decoded bitwise into variable length frames.
+
+Possible Frame Types are as follows:
+
+ Energy RF Pitch K1 K2 K3 K4 K5 K6 K7 K8 K9 K10
+ -----------------------------------------------------------------
+
+Silent 0000
+Stop 1111
+Repeat XXXX 1 XXXXXX
+Unvoiced XXXX 0 000000 XXXXX XXXXX XXXX XXXX
+Voiced XXXX 0 XXXXXX XXXXX XXXXX XXXX XXXX XXXX XXXX XXXX XXX XXX XXX
+
+Stop Frame: Stops the current speech
+Repeat Frame: Uses the digital filter coefficients from the previous frame,
+ with new Energy and Pitch values
+Unvoiced Frame: Uses Noise generator to feed 4 pole digital filter
+ (All other coefficients are set to zero)
+Voiced Frame: Uses Pulse Generator to feed 10 pole digital filter
+
+All parameters (Energy, Pitch, K1-K10) are indexes into a lookup table for
+actual values (see TMS5220R.c)
+
+K1-K10 are reflection coefficients for the lattice filter.
+
+Each frame is used to generate 200 samples, and 8 times during each frame,
+(every 25 samples), these values are linearly interpolated to smooth out
+frame transitions.
+
+The Noise generator is based on a shift-register type random-bit generator.
+The Pulse generator is based on a lookup table.
+
+API:
+----
+
+TBD
+
+More:
+-----
+
+For further technical info, the data sheet is floating around on the net.
+(I believe the name is TMS.PDF) If you can't find a copy, email me and
+I'll point you towards it. Feel free to contact me if you have a question.
+
+Frank Palazzolo
+
+palazzol@tir.com
+
+
+
+
+Downloaded from: http://files.emuwiki.com/tms5220.7z
diff --git a/tms5220~/tms5220/tms5220r.c b/tms5220~/tms5220/tms5220r.c
new file mode 100644
index 0000000..93ba249
--- /dev/null
+++ b/tms5220~/tms5220/tms5220r.c
@@ -0,0 +1,105 @@
+/* TMS5220 ROM Tables */
+
+/* This is the energy lookup table (4-bits -> 10-bits) */
+
+const unsigned short energytable[0x10]={
+0x0000,0x00C0,0x0140,0x01C0,0x0280,0x0380,0x0500,0x0740,
+0x0A00,0x0E40,0x1440,0x1C80,0x2840,0x38C0,0x5040,0x7FC0};
+
+/* This is the pitch lookup table (6-bits -> 8-bits) */
+
+const unsigned short pitchtable [0x40]={
+0x0000,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,
+0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00,0x1E00,
+0x1F00,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,
+0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2D00,0x2F00,0x3100,
+0x3300,0x3500,0x3600,0x3900,0x3B00,0x3D00,0x3F00,0x4200,
+0x4500,0x4700,0x4900,0x4D00,0x4F00,0x5100,0x5500,0x5700,
+0x5C00,0x5F00,0x6300,0x6600,0x6A00,0x6E00,0x7300,0x7700,
+0x7B00,0x8000,0x8500,0x8A00,0x8F00,0x9500,0x9A00,0xA000};
+
+/* These are the reflection coefficient lookup tables */
+
+/* K1 is (5-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k1table [0x20]={
+0x82C0,0x8380,0x83C0,0x8440,0x84C0,0x8540,0x8600,0x8780,
+0x8880,0x8980,0x8AC0,0x8C00,0x8D40,0x8F00,0x90C0,0x92C0,
+0x9900,0xA140,0xAB80,0xB840,0xC740,0xD8C0,0xEBC0,0x0000,
+0x1440,0x2740,0x38C0,0x47C0,0x5480,0x5EC0,0x6700,0x6D40};
+
+/* K2 is (5-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k2table [0x20]={
+0xAE00,0xB480,0xBB80,0xC340,0xCB80,0xD440,0xDDC0,0xE780,
+0xF180,0xFBC0,0x0600,0x1040,0x1A40,0x2400,0x2D40,0x3600,
+0x3E40,0x45C0,0x4CC0,0x5300,0x5880,0x5DC0,0x6240,0x6640,
+0x69C0,0x6CC0,0x6F80,0x71C0,0x73C0,0x7580,0x7700,0x7E80};
+
+/* K3 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k3table [0x10]={
+0x9200,0x9F00,0xAD00,0xBA00,0xC800,0xD500,0xE300,0xF000,
+0xFE00,0x0B00,0x1900,0x2600,0x3400,0x4100,0x4F00,0x5C00};
+
+/* K4 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k4table [0x10]={
+0xAE00,0xBC00,0xCA00,0xD800,0xE600,0xF400,0x0100,0x0F00,
+0x1D00,0x2B00,0x3900,0x4700,0x5500,0x6300,0x7100,0x7E00};
+
+/* K5 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k5table [0x10]={
+0xAE00,0xBA00,0xC500,0xD100,0xDD00,0xE800,0xF400,0xFF00,
+0x0B00,0x1700,0x2200,0x2E00,0x3900,0x4500,0x5100,0x5C00};
+
+/* K6 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k6table [0x10]={
+0xC000,0xCB00,0xD600,0xE100,0xEC00,0xF700,0x0300,0x0E00,
+0x1900,0x2400,0x2F00,0x3A00,0x4500,0x5000,0x5B00,0x6600};
+
+/* K7 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k7table [0x10]={
+0xB300,0xBF00,0xCB00,0xD700,0xE300,0xEF00,0xFB00,0x0700,
+0x1300,0x1F00,0x2B00,0x3700,0x4300,0x4F00,0x5A00,0x6600};
+
+/* K8 is (3-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k8table [0x08]={
+0xC000,0xD800,0xF000,0x0700,0x1F00,0x3700,0x4F00,0x6600};
+
+/* K9 is (3-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k9table [0x08]={
+0xC000,0xD400,0xE800,0xFC00,0x1000,0x2500,0x3900,0x4D00};
+
+/* K10 is (3-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */
+
+const short k10table [0x08]={
+0xCD00,0xDF00,0xF100,0x0400,0x1600,0x2000,0x3B00,0x4D00};
+
+/* chirp table */
+
+static char chirptable[41]={
+0x00, 0x2a, 0xd4, 0x32,
+0xb2, 0x12, 0x25, 0x14,
+0x02, 0xe1, 0xc5, 0x02,
+0x5f, 0x5a, 0x05, 0x0f,
+0x26, 0xfc, 0xa5, 0xa5,
+0xd6, 0xdd, 0xdc, 0xfc,
+0x25, 0x2b, 0x22, 0x21,
+0x0f, 0xff, 0xf8, 0xee,
+0xed, 0xef, 0xf7, 0xf6,
+0xfa, 0x00, 0x03, 0x02,
+0x01
+};
+
+/* interpolation coefficients */
+
+static char interp_coeff[8] = {
+8, 8, 8, 4, 4, 2, 2, 1
+};
+
diff --git a/tms5220~/tms5220~-help.pd b/tms5220~/tms5220~-help.pd
new file mode 100644
index 0000000..c01898c
--- /dev/null
+++ b/tms5220~/tms5220~-help.pd
@@ -0,0 +1,30 @@
+#N canvas 1 25 574 513 10;
+#X obj 108 206 tms5220~;
+#X floatatom 123 285 5 0 0 0 - - -;
+#X floatatom 138 262 5 0 0 0 - - -;
+#X floatatom 153 239 5 0 0 0 - - -;
+#X obj 108 390 dac~;
+#X obj 175 343 hsl 128 15 0 1 0 0 empty empty OUTPUT_LEVEL -2 -8 0
+10 -262144 -1 -1 0 1;
+#X obj 108 342 *~ 0;
+#X msg 172 383 \; pd dsp 1;
+#X text 204 238 <-- interrupt;
+#X text 202 260 <-- ready;
+#X text 201 283 <-- status bits;
+#X msg 108 127 write \$1;
+#X msg 135 166 reset;
+#X text 43 443 (C) Federico Ferri - 2010;
+#X msg 108 97 11 \, 20 \, 34 \, 128 \, 255;
+#X text 275 94 <-- send sequences of data using write method;
+#X text 294 115 (check the TMS5220 datasheet);
+#X connect 0 0 6 0;
+#X connect 0 1 1 0;
+#X connect 0 2 2 0;
+#X connect 0 3 3 0;
+#X connect 5 0 6 1;
+#X connect 5 0 7 0;
+#X connect 6 0 4 0;
+#X connect 6 0 4 1;
+#X connect 11 0 0 0;
+#X connect 12 0 0 0;
+#X connect 14 0 11 0;
diff --git a/tms5220~/tms5220~-meta.pd b/tms5220~/tms5220~-meta.pd
new file mode 100644
index 0000000..38451af
--- /dev/null
+++ b/tms5220~/tms5220~-meta.pd
@@ -0,0 +1,8 @@
+#N canvas 10 10 200 200 10;
+#N canvas 20 20 420 300 META 0;
+#X text 10 10 META this is a prototype of a libdir meta file;
+#X text 10 30 NAME tms5220~;
+#X text 10 50 AUTHOR Federico Ferri;
+#X text 10 70 LICENSE GPL;
+#X text 10 90 VERSION 0.1;
+#X restore 10 10 pd META;
diff --git a/tms5220~/tms5220~.c b/tms5220~/tms5220~.c
new file mode 100644
index 0000000..ce119cd
--- /dev/null
+++ b/tms5220~/tms5220~.c
@@ -0,0 +1,120 @@
+/* (C) 2010 Federico Ferri <mescalinum@gmail.com>
+ * this software is gpl'ed software, read the file "README.txt" for details
+ */
+
+#include "tms5220/tms5220.c"
+
+#include "m_pd.h"
+
+static t_class *tms5220_tilde_class;
+
+typedef struct _tms5220_tilde {
+ t_object x_obj;
+
+ // status outlets
+ t_int status;
+ t_outlet *x_status;
+ t_int ready;
+ t_outlet *x_ready;
+ t_int interrupt;
+ t_outlet *x_interrupt;
+
+ t_float dummy;
+} t_tms5220_tilde;
+
+void tms5220_tilde_reset(t_tms5220_tilde *x) {
+ tms5220_reset();
+}
+
+void *tms5220_tilde_new(t_symbol *s, int argc, t_atom *argv) {
+ t_tms5220_tilde *x = (t_tms5220_tilde *)pd_new(tms5220_tilde_class);
+
+ x->status = x->ready = x->interrupt = 0;
+
+ //inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ //floatinlet_new(&x->x_obj, &x->f_x);
+
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_status = outlet_new(&x->x_obj, &s_float);
+ x->x_ready = outlet_new(&x->x_obj, &s_float);
+ x->x_interrupt = outlet_new(&x->x_obj, &s_float);
+
+ tms5220_tilde_reset(x);
+
+ return (void *)x;
+}
+
+void tms5220_tilde_free(t_tms5220_tilde *x) {
+}
+
+void tms5220_tilde_write(t_tms5220_tilde *x, t_floatarg data) {
+ tms5220_data_write((int)data);
+}
+
+void tms5220_tilde_update_status(t_tms5220_tilde *x) {
+ t_int new_status, new_ready, new_interrupt;
+
+ new_status = tms5220_status_read();
+ new_ready = tms5220_ready_read();
+ new_interrupt = tms5220_int_read();
+
+ if(new_interrupt != x->interrupt) {
+ outlet_float(x->x_interrupt, new_interrupt);
+ x->interrupt = new_interrupt;
+ }
+
+ if(new_ready != x->ready) {
+ outlet_float(x->x_ready, new_ready);
+ x->ready = new_ready;
+ }
+
+ if(new_status != x->status) {
+ outlet_float(x->x_status, new_status);
+ x->status = new_status;
+ }
+}
+
+t_int *tms5220_tilde_perform(t_int *w) {
+ t_tms5220_tilde *x = (t_tms5220_tilde *)(w[1]);
+ t_sample *in = (t_sample *)(w[2]);
+ t_sample *out = (t_sample *)(w[3]);
+ int n = (int)(w[4]);
+
+ unsigned char *bytebuf = (unsigned char *)malloc(sizeof(unsigned char)*n);
+
+ if(!bytebuf) {error("FATAL: cannot allocate signal buffer"); return w;}
+
+ tms5220_process(bytebuf, n);
+ unsigned char *pb = bytebuf;
+
+ while (n--) *out++ = (0.5+((t_sample)*pb++))/127.5;
+
+ free(bytebuf);
+
+ tms5220_tilde_update_status(x);
+
+ return (w+5);
+}
+
+void tms5220_tilde_dsp(t_tms5220_tilde *x, t_signal **sp) {
+ dsp_add(tms5220_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void tms5220_tilde_setup(void) {
+ tms5220_tilde_class = class_new(gensym("tms5220~"),
+ (t_newmethod)tms5220_tilde_new,
+ (t_method)tms5220_tilde_free,
+ sizeof(t_tms5220_tilde),
+ CLASS_DEFAULT, A_GIMME, 0);
+
+ CLASS_MAINSIGNALIN(tms5220_tilde_class, t_tms5220_tilde, dummy);
+
+ class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_dsp, gensym("dsp"), 0);
+ //class_addfloat(tms5220_tilde_class, (t_method)tms5220_tilde_write);
+ class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_write, gensym("write"), A_DEFFLOAT, 0);
+ class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_reset, gensym("reset"), 0);
+
+ post("tms5220~: TSM5220 IC emulation");
+ post("tms5220~: external by Federico Ferri <mescalinum@gmail.com>");
+ post("tms5220~: based on code by Frank Palazzolo <palazzol@tir.com>");
+}